폴더별 빌드 사용자 지정
MSBuild에서 가져올 특정 파일을 추가하여 기본 속성 설정을 재정의하고 사용자 지정 대상을 추가할 수 있습니다. 이러한 사용자 지정의 범위는 이러한 파일이 배치되는 위치별로 폴더 수준에서 제어할 수 있습니다.
이 문서에서는 다음 시나리오에 적용되는 사용자 지정에 대해 설명합니다.
- 솔루션의 여러 프로젝트에 대한 빌드 설정 사용자 지정
- 일반적인 파일 디렉터리에서 여러 솔루션에 대한 빌드 설정 사용자 지정
- 폴더의 복잡한 구조에서 하위 폴더에 대해 다를 수 있는 빌드 설정 사용자 지정
- 기본 설정, 기본 빌드 폴더 및 SDK에서 설정한 기타 동작(예:
Microsoft.Net.Sdk
)을 재정의합니다. - 여러 프로젝트 또는 솔루션에 적용되는 빌드 대상 추가 또는 사용자 지정
C++ 프로젝트를 사용하는 경우 C++ 빌드 사용자 지정 설명된 메서드를 사용할 수도 있습니다.
Directory.Build.props 및 Directory.Build.targets
원본이 포함된 루트 폴더에 Directory.Build.props라는 단일 파일에서 새 속성을 정의하여 모든 프로젝트에 새 속성을 추가할 수 있습니다.
MSBuild가 실행되면 Microsoft.Common.props 디렉터리 구조에서 Directory.Build.props 파일을 검색합니다. 파일을 찾으면 파일을 가져오고 파일 내에 정의된 속성을 읽습니다. Directory.Build.props 디렉터리 아래의 프로젝트에 사용자 지정을 제공하는 사용자 정의 파일입니다.
마찬가지로 Microsoft.Common.targets Directory.Build.targets찾습니다.
Directory.Build.props 가져온 파일 시퀀스에서 초기에 가져옵니다. 가져오기에서 사용되는 속성, 특히 대부분의 .NET 프로젝트 파일에서 .NET SDK를 사용하는 경우와 같이 Sdk
특성을 사용하여 암시적으로 가져오는 속성을 설정해야 하는 경우 중요할 수 있습니다.
메모
Linux 기반 파일 시스템은 대/소문자를 구분합니다. 파일 이름 Directory.Build.props의 대/소문자를 정확히 일치시키는지 또는 빌드 프로세스 중에 검색되지 않는지 확인합니다.
Directory.Build.props 예제
예를 들어 Visual Studio 솔루션의 모든 프로젝트에 대한 출력 디렉터리를 설정하는 Directory.Build.props 파일은 다음과 같습니다. 각 프로젝트의 출력은 자체 프로젝트 이름 아래에 배치됩니다. 이 예제에서 Directory.Build.props 파일은 솔루션 폴더에 있으며 그 아래에는 여러 프로젝트가 있습니다.
$(MSBuildProjectName)
속성은 각 프로젝트의 이름을 제공합니다.
Directory.Build.props 파일은 자체 빌드 중에 각 프로젝트로 가져오기 때문에 솔루션의 각 개별 프로젝트에 대해 적절한 값으로 평가됩니다.
솔루션을 정리하여 이전 출력 파일을 제거합니다.
msbuild /t:Clean SolutionName.sln
Directory.Build.props리포지토리의 루트에 새 파일을 만듭니다.
파일에 다음 XML을 추가합니다.
<Project> <PropertyGroup> <OutDir>C:\output\$(MSBuildProjectName)</OutDir> </PropertyGroup> </Project>
메모
$(OutDir)
속성은 출력의 절대 경로이며, 이 속성을 사용하면 .NET 프로젝트에서 일반적으로 사용되는 구성, 대상 프레임워크 또는 런타임에 대한 하위 폴더 생성을 무시합니다. 사용자 지정 출력 경로 아래에 일반적인 하위 폴더를 만들려면 속성BaseOutputPath
대신 사용합니다.MSBuild를 실행합니다. 프로젝트의 기존 Microsoft.Common.props 및 Microsoft.Common.targets 가져오기는 Directory.Build.props 파일을 찾아서 가져오며, 새 출력 폴더는 해당 폴더 아래의 모든 프로젝트에 사용됩니다.
검색 범위
Directory.Build.props 파일을 검색할 때 MSBuild는 $(MSBuildProjectFullPath)
프로젝트 위치에서 디렉터리 구조를 위로 이동한 후 Directory.Build.props 파일을 찾은 후 중지합니다. 예를 들어, $(MSBuildProjectFullPath)
c:\users\username\code\test\case1 위치에 있는 경우, MSBuild는 그 위치에서부터 검색을 시작하여 Directory.Build.props 파일을 찾을 때까지 디렉터리 구조를 위쪽으로 검색합니다. 다음의 디렉터리 구조와 같습니다.
c:\users\username\code\test\case1
c:\users\username\code\test
c:\users\username\code
c:\users\username
c:\users
c:\
솔루션 파일의 위치는 Directory.Build.props와 관련이 없습니다.
가져오기 순서
Directory.Build.props는 Microsoft.Common.props에서 초기에 가져오기 때문에, 이후에 정의된 속성은 사용할 수 없습니다. 따라서 아직 정의되지 않은 속성을 참조하지 마세요(비어 있는 것으로 평가됨).
Directory.Build.props 설정된 속성은 프로젝트 파일 또는 가져온 파일의 다른 위치에서 재정의할 수 있으므로 Directory.Build.props 설정은 프로젝트의 기본값을 지정하는 것으로 간주해야 합니다.
NuGet 패키지에서 .targets
파일을 가져온 후 Microsoft.Common.targets에서 Directory.Build.targets를 가져옵니다. 따라서 대부분의 빌드 논리에 정의된 속성과 대상을 재정의하거나 개별 프로젝트 집합에 관계없이 모든 프로젝트에 대한 속성을 설정할 수 있습니다.
속성을 설정하거나 이전 설정을 재정의하는 개별 프로젝트의 대상을 정의해야 하는 경우 최종 가져오기 후에 해당 논리를 프로젝트 파일에 배치합니다. SDK 스타일 프로젝트에서 이 작업을 수행하려면 먼저 SDK 스타일 특성을 동등한 가져오기로 바꿔야 합니다. MSBuild 프로젝트 SDK를 사용하는 방법 참조하세요.
메모
MSBuild 엔진은 프로젝트(PreBuildEvent
포함)에 대한 빌드 실행을 시작하기 전에 평가 중에 가져온 모든 파일을 읽으므로 이러한 파일은 PreBuildEvent
또는 빌드 프로세스의 다른 부분에 의해 수정되지 않을 것으로 예상됩니다. 수정 사항은 MSBuild.exe 또는 다음 Visual Studio 빌드를 호출할 때까지 적용되지 않습니다. 또한 빌드 프로세스에 다중 대상 지정 또는 빌드 종속 프로젝트와 같이 많은 프로젝트 빌드가 포함된 경우 Directory.build.props포함하여 가져온 파일은 각 개별 프로젝트 빌드에 대한 평가가 발생할 때 읽습니다.
사용 사례: 다단계 병합
이 표준 솔루션 구조가 있다고 가정해 보겠습니다.
\
MySolution.sln
Directory.Build.props (1)
\src
Directory.Build.props (2-src)
\Project1
\Project2
\test
Directory.Build.props (2-test)
\Project1Tests
\Project2Tests
모든 프로젝트 (1), src 프로젝트 (2-src)공통 속성 및 테스트 프로젝트 (2-test)대한 공통 속성을 사용하는 것이 좋습니다.
MSBuild가 "내부" 파일(2-src 및 2-test)을 "외부" 파일(1)과 올바르게 병합하려면 MSBuild가 Directory.Build.props 파일을 찾으면 추가 검사를 중지한다는 점을 고려해야 합니다. 검색을 계속하고 외부 파일에 병합하려면 다음 코드를 두 내부 파일에 배치합니다.
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
MSBuild의 일반적인 접근 방식에 대한 요약은 다음과 같습니다.
- 지정된 프로젝트의 경우, MSBuild는 솔루션 구조를 위로 올라가며 첫 번째 Directory.Build.props를 찾은 후 기본값과 병합하고 추가 검사를 중지합니다.
- 여러 수준을 찾아 병합하려면 "내부" 파일에서 "외부" 파일을
<Import...>
으로 분리합니다(이전에 표시됨). - "외부" 파일 자체도 그 위에 있는 항목을 가져오지 않으면 검색이 중지됩니다.
또는 간단히 말해서, 아무 것도 가져오지 않는 첫 번째 Directory.Build.props MSBuild가 중지되는 위치입니다.
가져오기 프로세스를 더 명시적으로 제어하려면 속성 $(DirectoryBuildPropsPath)
, $(ImportDirectoryBuildProps)
, $(DirectoryBuildTargetsPath)
및 $(ImportDirectoryBuildTargets)
사용합니다. 속성 $(DirectoryBuildPropsPath)
사용할 Directory.Build.props
파일의 경로를 지정합니다. 마찬가지로 $(DirectoryBuildTargetsPath)
Directory.Build.targets
파일의 경로를 지정합니다.
부울 속성 $(ImportDirectoryBuildProps)
및 $(ImportDirectoryBuildTargets)
기본적으로 true
설정되므로 MSBuild는 일반적으로 이러한 파일을 검색하지만 MSBuild에서 파일을 가져오지 못하도록 false
설정할 수 있습니다.
본보기
이 예제에서는 전처리된 출력을 사용하여 속성을 설정할 위치를 확인하는 방법을 보여 줍니다.
설정하려는 특정 속성의 사용량을 분석하는 데 도움이 되도록 /preprocess
또는 /pp
인수를 사용하여 MSBuild를 실행할 수 있습니다. 출력 텍스트는 암시적으로 가져온 Microsoft.Common.props 같은 시스템 가져오기 및 사용자 고유의 가져오기를 포함하여 모든 가져오기의 결과입니다. 이 출력을 사용하면 해당 값이 사용되는 위치를 기준으로 속성을 설정해야 하는 위치를 확인할 수 있습니다.
예를 들어 간단한 .NET Core 또는 .NET 5 이상 콘솔 앱 프로젝트가 있고 일반적으로 obj
중간 출력 폴더를 사용자 지정하려고 합니다. 이 경로를 지정하는 속성이 BaseIntermediateOutput
. 프로젝트 파일의 PropertyGroup
요소에 TargetFramework
같은 이미 설정된 다양한 다른 속성과 함께 배치하려고 하면 프로젝트를 빌드할 때 속성이 적용되지 않는 것을 발견하게 됩니다.
/pp
옵션을 사용하여 MSBuild를 실행하고 출력에서 BaseIntermediateOutputPath
검색하는 경우 그 이유를 확인할 수 있습니다. 이 경우 BaseIntermediateOutput
가 읽혀지고 Microsoft.Common.props
에서 사용됩니다.
Microsoft.Common.props 파일의 주석에는 속성 BaseIntermediateOutput
가 다른 속성 MSBuildProjectExtensionsPath
에서 사용되기 전에 여기에 설정되어야 한다고 나와 있습니다. 또한 BaseIntermediateOutputPath
처음 설정되면 기존 값에 대한 확인이 있으며, 정의되지 않은 경우 obj
설정됩니다.
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">obj\</BaseIntermediateOutputPath>
따라서 이 배치는 이 속성을 설정하려면 이보다 이전 위치에 지정해야 한다는 것을 알려줍니다. 전처리된 출력에서 이 코드 바로 앞에 Directory.Build.props
가져온 것을 볼 수 있으므로 BaseIntermediateOutputPath
설정할 수 있으며 원하는 효과를 가질 수 있을 만큼 일찍 설정됩니다.
다음의 약식 전처리 출력은 BaseIntermediateOutput
설정을 Directory.Build.props
에 배치한 결과를 보여 줍니다. 표준 가져오기의 맨 위에 있는 주석에는 파일 이름과 해당 파일을 가져오는 이유에 대한 유용한 정보가 포함됩니다.
<?xml version="1.0" encoding="IBM437"?>
<!--
============================================================================================================================================
c:\source\repos\ConsoleApp9\ConsoleApp9\ConsoleApp9.csproj
============================================================================================================================================
-->
<Project DefaultTargets="Build">
<!--
============================================================================================================================================
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk">
This import was added implicitly because the Project element's Sdk attribute specified "Microsoft.NET.Sdk".
C:\Program Files\dotnet\sdk\7.0.200-preview.22628.1\Sdks\Microsoft.NET.Sdk\Sdk\Sdk.props
============================================================================================================================================
-->
<!--
***********************************************************************************************
Sdk.props
WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and have
created a backup copy. Incorrect changes to this file will make it
impossible to load or build your projects from the command-line or the IDE.
Copyright (c) .NET Foundation. All rights reserved.
***********************************************************************************************
-->
<PropertyGroup xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!--
Indicate to other targets that Microsoft.NET.Sdk is being used.
This must be set here (as early as possible, before Microsoft.Common.props)
so that everything that follows can depend on it.
In particular, Directory.Build.props and nuget package props need to be able
to use this flag and they are imported by Microsoft.Common.props.
-->
<UsingMicrosoftNETSdk>true</UsingMicrosoftNETSdk>
<!--
Indicate whether the set of SDK defaults that makes SDK style project concise are being used.
For example: globbing, importing msbuild common targets.
Similar to the property above, it must be set here.
-->
<UsingNETSdkDefaults>true</UsingNETSdkDefaults>
</PropertyGroup>
<PropertyGroup Condition="'$(MSBuildProjectFullPath)' == '$(ProjectToOverrideProjectExtensionsPath)'" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<MSBuildProjectExtensionsPath>$(ProjectExtensionsPathForSpecifiedProject)</MSBuildProjectExtensionsPath>
</PropertyGroup>
<!--<Import Project="$(AlternateCommonProps)" Condition="'$(AlternateCommonProps)' != ''" />-->
<!--
============================================================================================================================================
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="'$(AlternateCommonProps)' == ''">
C:\Program Files\Microsoft Visual Studio\2022\Preview\MSBuild\Current\Microsoft.Common.props
============================================================================================================================================
-->
<!--
***********************************************************************************************
Microsoft.Common.props
WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and have
created a backup copy. Incorrect changes to this file will make it
impossible to load or build your projects from the command-line or the IDE.
Copyright (C) Microsoft Corporation. All rights reserved.
***********************************************************************************************
-->
<PropertyGroup>
<ImportByWildcardBeforeMicrosoftCommonProps Condition="'$(ImportByWildcardBeforeMicrosoftCommonProps)' == ''">true</ImportByWildcardBeforeMicrosoftCommonProps>
<ImportByWildcardAfterMicrosoftCommonProps Condition="'$(ImportByWildcardAfterMicrosoftCommonProps)' == ''">true</ImportByWildcardAfterMicrosoftCommonProps>
<ImportUserLocationsByWildcardBeforeMicrosoftCommonProps Condition="'$(ImportUserLocationsByWildcardBeforeMicrosoftCommonProps)' == ''">true</ImportUserLocationsByWildcardBeforeMicrosoftCommonProps>
<ImportUserLocationsByWildcardAfterMicrosoftCommonProps Condition="'$(ImportUserLocationsByWildcardAfterMicrosoftCommonProps)' == ''">true</ImportUserLocationsByWildcardAfterMicrosoftCommonProps>
<ImportDirectoryBuildProps Condition="'$(ImportDirectoryBuildProps)' == ''">true</ImportDirectoryBuildProps>
</PropertyGroup>
<!--
Determine the path to the directory build props file if the user did not disable $(ImportDirectoryBuildProps) and
they did not already specify an absolute path to use via $(DirectoryBuildPropsPath)
-->
<PropertyGroup Condition="'$(ImportDirectoryBuildProps)' == 'true' and '$(DirectoryBuildPropsPath)' == ''">
<_DirectoryBuildPropsFile Condition="'$(_DirectoryBuildPropsFile)' == ''">Directory.Build.props</_DirectoryBuildPropsFile>
<_DirectoryBuildPropsBasePath Condition="'$(_DirectoryBuildPropsBasePath)' == ''">$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), '$(_DirectoryBuildPropsFile)'))</_DirectoryBuildPropsBasePath>
<DirectoryBuildPropsPath Condition="'$(_DirectoryBuildPropsBasePath)' != '' and '$(_DirectoryBuildPropsFile)' != ''">$([System.IO.Path]::Combine('$(_DirectoryBuildPropsBasePath)', '$(_DirectoryBuildPropsFile)'))</DirectoryBuildPropsPath>
</PropertyGroup>
<!--
============================================================================================================================================
<Import Project="$(DirectoryBuildPropsPath)" Condition="'$(ImportDirectoryBuildProps)' == 'true' and exists('$(DirectoryBuildPropsPath)')">
c:\source\repos\ConsoleApp9\Directory.Build.props
============================================================================================================================================
-->
<!-- Directory.build.props
-->
<PropertyGroup>
<BaseIntermediateOutputPath>myBaseIntermediateOutputPath</BaseIntermediateOutputPath>
</PropertyGroup>
<!--
============================================================================================================================================
</Import>
C:\Program Files\Microsoft Visual Studio\2022\Preview\MSBuild\Current\Microsoft.Common.props
============================================================================================================================================
-->
<!--
Prepare to import project extensions which usually come from packages. Package management systems will create a file at:
$(MSBuildProjectExtensionsPath)\$(MSBuildProjectFile).<SomethingUnique>.props
Each package management system should use a unique moniker to avoid collisions. It is a wild-card import so the package
management system can write out multiple files but the order of the import is alphabetic because MSBuild sorts the list.
-->
<PropertyGroup>
<!--
The declaration of $(BaseIntermediateOutputPath) had to be moved up from Microsoft.Common.CurrentVersion.targets
in order for the $(MSBuildProjectExtensionsPath) to use it as a default.
-->
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">obj\</BaseIntermediateOutputPath>
<BaseIntermediateOutputPath Condition="!HasTrailingSlash('$(BaseIntermediateOutputPath)')">$(BaseIntermediateOutputPath)\</BaseIntermediateOutputPath>
<_InitialBaseIntermediateOutputPath>$(BaseIntermediateOutputPath)</_InitialBaseIntermediateOutputPath>
<MSBuildProjectExtensionsPath Condition="'$(MSBuildProjectExtensionsPath)' == '' ">$(BaseIntermediateOutputPath)</MSBuildProjectExtensionsPath>
<!--
Import paths that are relative default to be relative to the importing file. However, since MSBuildExtensionsPath
defaults to BaseIntermediateOutputPath we expect it to be relative to the project directory. So if the path is relative
it needs to be made absolute based on the project directory.
-->
<MSBuildProjectExtensionsPath Condition="'$([System.IO.Path]::IsPathRooted($(MSBuildProjectExtensionsPath)))' == 'false'">$([System.IO.Path]::Combine('$(MSBuildProjectDirectory)', '$(MSBuildProjectExtensionsPath)'))</MSBuildProjectExtensionsPath>
<MSBuildProjectExtensionsPath Condition="!HasTrailingSlash('$(MSBuildProjectExtensionsPath)')">$(MSBuildProjectExtensionsPath)\</MSBuildProjectExtensionsPath>
<ImportProjectExtensionProps Condition="'$(ImportProjectExtensionProps)' == ''">true</ImportProjectExtensionProps>
<_InitialMSBuildProjectExtensionsPath Condition=" '$(ImportProjectExtensionProps)' == 'true' ">$(MSBuildProjectExtensionsPath)</_InitialMSBuildProjectExtensionsPath>
</PropertyGroup>
...
관련 콘텐츠
- 빌드를사용자 지정합니다.