다음을 통해 공유


Visual Studio 빌드 프로세스 확장

Visual Studio 빌드 프로세스는 프로젝트 파일로 가져온 일련의 MSBuild .targets 파일로 정의됩니다. Visual Studio 프로젝트처럼 SDK를 사용하는 경우 이러한 가져오기는 암시적으로 이루어집니다. 이러한 가져온 파일 중 하나인 Microsoft.Common.targets는 빌드 프로세스의 여러 지점에서 사용자 지정 작업을 실행할 수 있도록 확장될 수 있습니다. 이 문서에서는 Visual Studio 빌드 프로세스를 확장하는 데 사용할 수 있는 세 가지 방법을 설명합니다.

  • 사용자 지정 대상을 만들고 BeforeTargetsAfterTargets 특성을 사용하여 실행 시기를 지정합니다.

  • 공통 대상에 정의된 DependsOn 속성을 재정의합니다.

  • 공통 대상(Microsoft.Common.targets 또는 가져오는 파일)에 정의된 미리 정의된 특정 대상을 재정의합니다.

AfterTargets 및 BeforeTargets

사용자 지정 대상에 AfterTargetsBeforeTargets 특성을 사용하여 실행 시기를 지정할 수 있습니다.

다음 예제에서는 AfterTargets 특성을 사용하여 출력 파일에 무언가를 수행하는 사용자 지정 대상을 추가하는 방법을 보여 줍니다. 이 경우에는 새 폴더 CustomOutput에 출력 파일을 복사합니다. 이 예제에서는 또한, BeforeTargets 특성을 사용하고 CoreClean 대상보다 먼저 사용자 지정 정리 작업이 실행되도록 지정하여 CustomClean 대상이 있는 사용자 지정 빌드 작업을 통해 만들어진 파일을 정리하는 방법도 보여 줍니다.

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
     <TargetFramework>netcoreapp3.1</TargetFramework>
     <_OutputCopyLocation>$(OutputPath)..\..\CustomOutput\</_OutputCopyLocation>
  </PropertyGroup>

  <Target Name="CustomAfterBuild" AfterTargets="Build">
    <ItemGroup>
      <_FilesToCopy Include="$(OutputPath)**\*"/>
    </ItemGroup>
    <Message Text="_FilesToCopy: @(_FilesToCopy)" Importance="high"/>

    <Message Text="DestFiles:
        @(_FilesToCopy->'$(_OutputCopyLocation)%(RecursiveDir)%(Filename)%(Extension)')"/>

    <Copy SourceFiles="@(_FilesToCopy)"
          DestinationFiles=
          "@(_FilesToCopy->'$(_OutputCopyLocation)%(RecursiveDir)%(Filename)%(Extension)')"/>
  </Target>

  <Target Name="CustomClean" BeforeTargets="CoreClean">
    <Message Text="Inside Custom Clean" Importance="high"/>
    <ItemGroup>
      <_CustomFilesToDelete Include="$(_OutputCopyLocation)**\*"/>
    </ItemGroup>
    <Delete Files='@(_CustomFilesToDelete)'/>
  </Target>
</Project>

Warning

사전 정의된 대상과 다른 이름을 사용해야 합니다(예: 여기서 사용자 지정 빌드 대상은 AfterBuild가 아닌 CustomAfterBuild임). 사전 정의된 대상이 이 대상을 역시 지정하는 SDK 가져오기에 의해 재정의되기 떄문입니다. 사전 정의된 대상 목록은 이 문서의 끝부분에 있는 를 참조하세요.

DependsOn 속성 확장

빌드 프로세스를 확장하는 또 다른 방법은 DependsOn 속성(예: BuildDependsOn)을 사용하여 표준 대상보다 먼저 실행해야 하는 대상을 지정하는 것입니다.

이 방법은 다음 섹션에서 설명하는 사전 정의된 대상을 재정의하는 것이 좋습니다. 사전 정의된 대상을 재정의하는 것은 아직 지원되는 오래된 방법이지만 MSBuild에서 대상의 정의를 순차적으로 평가하므로 프로젝트를 가져오는 다른 프로젝트에서 이미 재정의한 대상을 재정의하는 것을 방지할 방법이 없습니다. 따라서 예를 들어 다른 모든 프로젝트를 가져온 후 프로젝트에서 정의된 마지막 AfterBuild 대상은 빌드 중 사용되는 대상이 됩니다.

공통 대상 전체의 DependsOn 특성에서 사용되는 DependsOnTargets 속성을 재정의하여 의도하지 않은 대상의 재정의를 방지할 수 있습니다. 예를 들어 Build 대상은 "$(BuildDependsOn)"DependsOnTargets 특성 값을 포함합니다. 고려할 사항은 다음과 같습니다.

<Target Name="Build" DependsOnTargets="$(BuildDependsOn)"/>

이 XML 조각은 Build 대상 전에 실행할 수 있음을 나타내며 BuildDependsOn 속성에 지정된 모든 대상은 먼저 실행되어야 합니다. BuildDependsOn 속성은 다음으로 정의됩니다.

<PropertyGroup>
    <BuildDependsOn>
        $(BuildDependsOn);
        BeforeBuild;
        CoreBuild;
        AfterBuild
    </BuildDependsOn>
</PropertyGroup>

프로젝트 파일의 끝에서 BuildDependsOn이라는 다른 속성을 선언하여 이 속성 값을 재정의할 수 있습니다. SDK 스타일 프로젝트에서는 명시적 가져오기를 사용해야 합니다. 암시적 가져오기 및 명시적 가져오기를 참조하여 마지막 가져오기 후에 DependsOn 속성을 넣을 수 있습니다. 새 속성에서 이전 BuildDependsOn 속성을 포함하여 대상 목록의 시작과 끝에 새 대상을 추가할 수 있습니다. 예시:

<PropertyGroup>
    <BuildDependsOn>
        MyCustomTarget1;
        $(BuildDependsOn);
        MyCustomTarget2
    </BuildDependsOn>
</PropertyGroup>

<Target Name="MyCustomTarget1">
    <Message Text="Running MyCustomTarget1..."/>
</Target>
<Target Name="MyCustomTarget2">
    <Message Text="Running MyCustomTarget2..."/>
</Target>

프로젝트 파일을 가져온 프로젝트는 사용자 지정한 내용을 덮어쓰지 않고도 이러한 속성을 추가로 확장할 수 있습니다.

DependsOn 속성을 재정의하려면

  1. 재정의하려는 공통 대상에서 미리 정의된 DependsOn 속성을 식별합니다. 일반적으로 재정의된 DependsOn 속성의 목록은 다음 표를 참조하세요.

  2. 프로젝트 파일의 끝에서 속성의 다른 인스턴스를 정의합니다. 새 속성에 원래 속성을 포함합니다(예: $(BuildDependsOn)).

  3. 속성 정의 앞 또는 뒤에 사용자 지정 대상을 정의합니다.

  4. 프로젝트 파일을 빌드합니다.

일반적으로 재정의된 DependsOn 속성

Property name 추가된 대상은 이 시점 이전에 실행됩니다.
BuildDependsOn 주 빌드 진입점. 전체 빌드 프로세스 앞이나 뒤에 사용자 지정 대상을 삽입하려는 경우 이 속성을 재정의합니다.
RebuildDependsOn Rebuild입니다.
RunDependsOn 최종 빌드 출력의 실행(.EXE 경우)
CompileDependsOn 컴파일(Compile 대상). 컴파일 단계 앞이나 뒤에 사용자 지정 프로세스를 삽입하려는 경우 이 속성을 재정의합니다.
CreateSatelliteAssembliesDependsOn 위성 어셈블리 생성
CleanDependsOn Clean 대상(모든 중간 및 최종 빌드 출력 삭제). 사용자 지정 빌드 프로세스에서 출력을 정리하려는 경우 이 속성을 재정의합니다.
PostBuildEventDependsOn PostBuildEvent 대상
PublishBuildDependsOn 빌드 게시
ResolveAssemblyReferencesDependsOn ResolveAssemblyReferences 대상(지정된 종속성에 대한 종속성의 전이적 폐쇄 찾기). ResolveAssemblyReference을 참조하세요.

예: BuildDependsOn 및 CleanDependsOn

다음 예제는 BeforeTargetsAfterTargets 예제와 비슷하지만 비슷한 기능을 구현하는 방법을 보여 줍니다. BuildDependsOn을 사용해 빌드를 확장하여 빌드 후 출력 파일을 복사하는 고유 작업 CustomAfterBuild를 추가할 뿐만 아니라 CleanDependsOn을 사용하여 해당하는 CustomClean 작업을 추가합니다.

이 예제에서는 SDK 스타일 프로젝트입니다. 이 문서 앞부분에서 SDK 스타일 프로젝트에 대해 설명한 대로 Visual Studio에서 프로젝트 파일을 생성할 때 사용하는 Sdk 특성 대신 수동 가져오기 메서드를 사용해야 합니다.

<Project>
  <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk"/>

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk"/>

  <PropertyGroup>
    <BuildDependsOn>
      $(BuildDependsOn);CustomAfterBuild
    </BuildDependsOn>

    <CleanDependsOn>
      $(CleanDependsOn);CustomClean
    </CleanDependsOn>

    <_OutputCopyLocation>$(OutputPath)..\..\CustomOutput\</_OutputCopyLocation>
  </PropertyGroup>

  <Target Name="CustomAfterBuild">
    <ItemGroup>
      <_FilesToCopy Include="$(OutputPath)**\*"/>
    </ItemGroup>
    <Message Importance="high" Text="_FilesToCopy: @(_FilesToCopy)"/>

    <Message Text="DestFiles:
      @(_FilesToCopy-&gt;'$(_OutputCopyLocation)%(RecursiveDir)%(Filename)%(Extension)')"/>

    <Copy SourceFiles="@(_FilesToCopy)"
          DestinationFiles="@(_FilesToCopy-&gt;'$(_OutputCopyLocation)%(RecursiveDir)%(Filename)%(Extension)')"/>
  </Target>

  <Target Name="CustomClean">
    <Message Importance="high" Text="Inside Custom Clean"/>
    <ItemGroup>
      <_CustomFilesToDelete Include="$(_OutputCopyLocation)**\*"/>
    </ItemGroup>
    <Delete Files="@(_CustomFilesToDelete)"/>
  </Target>
</Project>

요소의 순서가 중요합니다. BuildDependsOnCleanDependsOn 요소는 표준 SDK 대상 파일을 가져온 후에 표시되어야 합니다.

미리 정의된 대상 재정의

공통 .targets 파일에는 빌드 프로세스의 일부 주요 대상의 전후에 호출되는 미리 정의된 빈 대상 집합이 포함되어 있습니다. 예를 들어 MSBuild는 메인 CoreBuild 대상 전에 BeforeBuild 대상을 호출하고 CoreBuild 대상 후에 AfterBuild 대상을 호출합니다. 기본적으로 공통 대상의 빈 대상은 아무것도 수행하지 않지만 프로젝트 파일에서 원하는 대상을 정의하여 해당 기본 동작을 재정의할 수 있습니다. 이 문서의 앞부분에서 설명한 방법이 선호되지만 이 방법을 사용하는 이전 코드가 발생할 수 있습니다.

프로젝트에서 SDK(예: Microsoft.Net.Sdk)를 사용하는 경우 명시적 가져오기 및 암시적 가져오기에 설명된 대로 암시적 가져오기를 명시적 가져오기로 변경해야 합니다.

미리 정의된 대상을 재정의하려면

  1. 프로젝트에서 Sdk 특성을 사용하는 경우 명시적 가져오기 구문으로 변경합니다. 명시적 가져오기 및 암시적 가져오기를 참조하세요.

  2. 재정의하려는 공통 대상에서 미리 정의된 대상을 식별합니다. 안전하게 재정의할 수 있는 대상의 전체 목록은 다음 표를 참조하세요.

  3. 프로젝트 파일의 끝 부분, </Project> 태그 바로 앞과 명시적 SDK 가져오기 뒤에 대상을 정의합니다. 예시:

    <Project>
        <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
        ...
        <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
        <Target Name="BeforeBuild">
            <!-- Insert tasks to run before build here -->
        </Target>
        <Target Name="AfterBuild">
            <!-- Insert tasks to run after build here -->
        </Target>
    </Project>
    

    최상위 Project 요소의 Sdk 특성이 제거되었습니다.

  4. 프로젝트 파일을 빌드합니다.

미리 정의된 대상 테이블

다음 표는 재정의할 수 있는 공통 대상에서 모든 대상을 표시합니다.

대상 이름 설명
BeforeCompile, AfterCompile 이러한 대상 중 하나에 삽입된 작업은 핵심 컴파일이 완료되기 전이나 후에 실행됩니다. 대부분의 사용자 지정은 이러한 두 개의 대상 중 하나에서 수행됩니다.
BeforeBuild, AfterBuild 이러한 대상 중 하나에 삽입된 작업은 빌드의 모든 작업 전이나 후에 실행됩니다. 참고:BeforeBuildAfterBuild 대상은 프로젝트 파일 대부분의 끝에 삽입된 주석에서 이미 정의되어 있습니다. 따라서 프로젝트 파일에 빌드 전후 이벤트를 쉽게 추가할 수 있습니다.
BeforeRebuild, AfterRebuild 이러한 대상 중 하나에 삽입된 작업은 핵심 다시 빌드 기능이 호출되기 전 또는 후에 실행됩니다. Microsoft.Common.targets에서 대상 실행 순서는 차례로 BeforeRebuild, Clean, BuildAfterRebuild입니다.
BeforeClean, AfterClean 이러한 대상 중 하나에 삽입된 작업은 핵심 정리 기능이 호출되기 전 또는 후에 실행됩니다.
BeforePublish, AfterPublish 이러한 대상 중 하나에 삽입된 작업은 핵심 게시 기능이 호출되기 전 또는 후에 실행됩니다.
BeforeResolveReferences, AfterResolveReferences 이러한 대상 중 하나에 삽입된 작업은 어셈블리 참조가 확인되기 전이나 후에 실행됩니다.
BeforeResGen, AfterResGen 이러한 대상 중 하나에 삽입된 작업은 어셈블리 리소스가 생성되기 전이나 후에 실행됩니다.

빌드 시스템 및 .NET SDK에는 더 많은 대상이 있습니다. MSBuild 대상 - SDK 및 기본 빌드 대상을 참조하세요.

사용자 지정 대상에 대한 모범 사례

속성 DependsOnTargetsBeforeTargets 둘 다 대상이 다른 대상 앞에 실행되도록 지정할 수 있지만 다양한 시나리오에서 둘 다 필요합니다. 종속성 요구 사항이 지정된 대상은 다릅니다. 사용자 자신의 대상만 제어할 수 있으며 시스템 대상 또는 기타 가져온 대상을 안전하게 수정할 수 없으므로 방법 선택에 제약이 있습니다.

사용자 지정 대상을 작성할 때 다음 일반 지침에 따라 대상이 의도한 순서대로 실행되도록 합니다.

  1. DependsOnTargets 특성을 사용하여 대상이 실행되기 전에 수행해야 하는 대상을 지정합니다. 제어하는 대상 체인의 경우, 각 대상은 DependsOnTargets에서 체인의 이전 구성원을 지정할 수 있습니다.

  2. 제어하지 않지만 이전에 실행해야 하는 대상(예: 빌드 초기에 실행해야 하는 대상의 경우 BeforeTargets)에는 BeforeTargets="PrepareForBuild"를 사용합니다.

  3. 제어하지 않는 대상에 대해서는 필요한 출력을 보장하는 AfterTargets를 사용합니다. 예를 들어 참조 목록을 수정할 항목에 대해 AfterTargets="ResolveReferences"를 지정합니다.

  4. 이 두 가지를 함께 사용할 수 있습니다. 예들 들어 DependsOnTargets="GenerateAssemblyInfo" BeforeTargets="BeforeCompile"입니다.

명시적 가져오기 및 암시적 가져오기

Visual Studio에서 생성된 프로젝트는 일반적으로 프로젝트 요소의 Sdk 특성을 사용합니다. 이러한 유형의 프로젝트를 SDK 스타일 프로젝트라고 합니다. MSBuild 프로젝트 SDK 사용을 참조하세요. 예를 들면 다음과 같습니다.

<Project Sdk="Microsoft.Net.Sdk">

프로젝트에서 Sdk 특성을 사용하는 경우 프로젝트 파일의 시작 부분에 하나, 끝에 하나씩 두 개의 가져오기가 암시적으로 추가됩니다.

암시적 가져오기는 프로젝트 파일의 첫 번째 줄에 Project 요소 뒤에 이와 같은 가져오기 문이 있는 것과 같습니다:

<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />

다음 가져오기 문을 프로젝트 파일의 마지막 줄로 지정합니다.

<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />

이 구문을 명시적 SDK 가져오기라고 합니다. 이 명시적 구문을 사용하는 경우 프로젝트 요소에서 Sdk 특성을 생략해야 합니다.

암시적 SDK 가져오기는 다음과 같이 이전 프로젝트 파일에서 일반적인 구문인 특정 "공통" .props 또는 .targets 파일을 가져오는 것과 같습니다.

<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />

<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

이러한 이전 참조는 이 섹션의 앞부분에 표시된 명시적 SDK 구문으로 바꿔야 합니다.

명시적 SDK 구문을 사용하면 첫 번째 가져오기 전이나 최종 SDK 가져오기 후에 자체 코드를 추가할 수 있습니다. 즉, 첫 번째 가져오기 전에 가져온 .props 파일에 적용될 속성을 설정하여 동작을 변경할 수 있으며, 최종 가져오기 후에 SDK .targets 파일 중 하나에 정의된 대상을 재정의할 수 있습니다. 이 방법을 사용하면 다음에 설명하는 대로 BeforeBuild 또는 AfterBuild를 재정의할 수 있습니다.

다음 단계

MSBuild를 사용하여 빌드를 사용자 지정할 수 있는 작업은 훨씬 더 많습니다. 빌드 사용자 지정을 참조하세요.