조건부 컴파일 및 프레임 워크 대상


124

대상 프레임 워크가 최신 버전 인 경우 내 프로젝트의 코드를 대폭 개선 할 수있는 사소한 부분이 있습니다. C #의 조건부 컴파일을 더 잘 활용하여 필요에 따라 전환 할 수 있기를 바랍니다.

다음과 같은 것 :

#if NET40
using FooXX = Foo40;
#elif NET35
using FooXX = Foo35;
#else NET20
using FooXX = Foo20;
#endif

무료로 제공되는 기호가 있습니까? 프로젝트 구성의 일부로 이러한 기호를 삽입해야합니까? MSBuild에서 어떤 프레임 워크가 대상이되는지 알기 때문에 쉽게 할 수 있습니다.

/p:DefineConstants="NET40"

사람들은이 상황을 어떻게 처리하고 있습니까? 다른 구성을 만들고 있습니까? 명령 줄을 통해 상수를 전달하고 있습니까?



VS에서 간단한 사전 제작 된 솔루션을 원한다면이 사용자 음성 인 visualstudio.uservoice.com/forums/121579-visual-studio/…에 찬성 투표하십시오 .
JohnC 2015-04-25

1
이 링크도 살펴보십시오. 꽤 설명 적입니다. blogs.msmvps.com/punitganshani/2015/06/21/…
Marco Alves

프로젝트 그룹, 복원 및 nuget nuget 심판 그룹, 좋은 솔루션 : shazwazza.com/post/...
OzBob

답변:


119

이를 수행하는 가장 좋은 방법 중 하나는 프로젝트에서 다른 빌드 구성을 만드는 것입니다.

<PropertyGroup Condition="  '$(Framework)' == 'NET20' ">
  <DefineConstants>NET20</DefineConstants>
  <OutputPath>bin\$(Configuration)\$(Framework)</OutputPath>
</PropertyGroup>


<PropertyGroup Condition="  '$(Framework)' == 'NET35' ">
  <DefineConstants>NET35</DefineConstants>
  <OutputPath>bin\$(Configuration)\$(Framework)</OutputPath>
</PropertyGroup>

그리고 기본 구성 중 하나에서 :

<Framework Condition=" '$(Framework)' == '' ">NET35</Framework>

다른 곳에서 정의되지 않은 경우 기본값을 설정합니다. 위의 경우 OutputPath는 각 버전을 빌드 할 때마다 별도의 어셈블리를 제공합니다.

그런 다음 AfterBuild 대상을 만들어 다른 버전을 컴파일합니다.

<Target Name="AfterBuild">
  <MSBuild Condition=" '$(Framework)' != 'NET20'"
    Projects="$(MSBuildProjectFile)"
    Properties="Framework=NET20"
    RunEachTargetSeparately="true"  />
</Target>

이 예제는 첫 번째 빌드 후에 Framework 변수를 NET20으로 설정하여 전체 프로젝트를 다시 컴파일합니다 (둘 다 컴파일하고 첫 번째 빌드가 위에서 기본 NET35라고 가정). 각 컴파일에는 조건부 정의 값이 올바르게 설정됩니다.

이러한 방식으로 파일을 #ifdef 할 필요없이 프로젝트 파일에서 특정 파일을 제외 할 수도 있습니다.

<Compile Include="SomeNet20SpecificClass.cs" Condition=" '$(Framework)' == 'NET20' " />

또는 참조

<Reference Include="Some.Assembly" Condition="" '$(Framework)' == 'NET20' " >
  <HintPath>..\Lib\$(Framework)\Some.Assembly.dll</HintPath>
</Reference>

완전한. 나는 그것이 할 수 있다는 것을 알기 위해 msbuild 형식을 해킹 한 경험이 충분했지만 모든 세부 사항을 파악할 시간이 충분하지 않았습니다. 대단히 감사합니다!
mckamey

내 관련 질문 ( stackoverflow.com/questions/2923181 ) 에이 답변에 대한 참조를 추가하면 거기에 해결책으로 표시하겠습니다. 이것은 실제로 두 가지를 동시에 해결합니다.
mckamey

7
답변 해 주셔서 감사합니다.하지만 이제 VS2010에는 "TargetFrameworkVersion"이라는 새 태그가 이미 포함되어 있습니다. 이제 조건이있는 각 속성 그룹에 대해 TargetFrameworkVersion 만 변경됩니다.이 모든 작업을 수행하려면 여전히이 모든 것이 필요합니까?
Akash Kava

이 답변은 프레임 워크에 대한 상수를 정의하는 것뿐만 아니라 여러 프레임 워크를위한 빌드에 관한 것입니다.
katbyte

4
이 게시물은 나를 위해 일했지만 MSBuild에 능숙하지 않아 알아내는 데 시간이 걸렸습니다. 예제로 작동하는 프로젝트를 만들었습니다. dev6.blob.core.windows.net/blog-images/DualTargetFrameworks.zip
TheDev6

44

지금까지 나를 위해 일하는 대안은 프로젝트 파일에 다음을 추가하는 것입니다.

 <PropertyGroup>
    <DefineConstants Condition=" !$(DefineConstants.Contains(';NET')) ">$(DefineConstants);$(TargetFrameworkVersion.Replace("v", "NET").Replace(".", ""))</DefineConstants>
    <DefineConstants Condition=" $(DefineConstants.Contains(';NET')) ">$(DefineConstants.Remove($(DefineConstants.LastIndexOf(";NET"))));$(TargetFrameworkVersion.Replace("v", "NET").Replace(".", ""))</DefineConstants>
  </PropertyGroup>

"v3.5"와 같은 TargetFrameworkVersion 속성의 값을 사용하여 "v"및 "."를 대체합니다. "NET35"를 얻으려면 (새 속성 함수 기능 사용) 그런 다음 기존 "NETxx"값을 제거하고이를 DefinedConstants 끝에 추가합니다. 이것을 능률화하는 것이 가능할 수도 있지만 나는 바이올린을 켤 시간이 없습니다.

VS에서 프로젝트 속성의 빌드 탭을 보면 조건부 컴파일 기호 섹션에 결과 값이 표시됩니다. 애플리케이션 탭에서 대상 프레임 워크 버전을 변경하면 기호가 자동으로 변경됩니다. 그런 다음 #if NETxx일반적인 방법으로 전 처리기 지시문 을 사용할 수 있습니다 . VS에서 프로젝트를 변경해도 사용자 정의 PropertyGroup이 손실되지 않는 것 같습니다.

이것은 Client Profile 대상 옵션에 대해 다른 것을주는 것 같지 않지만 나에게는 문제가되지 않습니다.


Jeremy, 와우 감사합니다. 이미 빌드 솔루션에서 별도로 빌드하고 있으므로 완벽합니다.
Greg Finzer 2011 년

+1. "$ (DefineConstants.Contains ( '...")를 찾기가 그렇게 어려울 것이라고 누가 생각했을까요? 감사합니다
CAD

이 마법 상수를 빌드에 추가하는 방법에 대한 재교육이 필요했기 때문에 마침내이 페이지로가는 길을 다시 찾았습니다. 저는 오늘 동일한 프로젝트를 다시 방문하여 라이브러리를 세분화하고 있으며 일부 세분화로 나눌 기호가 필요합니다. 방금 위를 살펴본 결과 귀하의 답변이 원본 .CSPROJ 파일에 이미 정식으로 인정 된 것으로 나타났습니다.
David A. Gray

15

초기 상수가 이러한 속성에 의해 미리 만들어 졌기 때문에 이러한 솔루션에 문제가있었습니다.

<DefineConstants />
<DefineDebug>true</DefineDebug>
<DefineTrace>true</DefineTrace>
<DebugSymbols>true</DebugSymbols>

Visual Studio 2010은 세미콜론으로 인해 잘못된 문자라고 주장하는 오류도 발생했습니다. 오류 메시지는 쉼표로 구분 된 미리 빌드 된 상수를 볼 수 있었고 결국 "불법"세미콜론이 뒤 따르는 것을 볼 수 있었기 때문에 힌트를주었습니다. 약간의 재 포맷 및 마사지 후 저에게 맞는 솔루션을 찾을 수있었습니다.

<PropertyGroup>
  <!-- Adding a custom constant will auto-magically append a comma and space to the pre-built constants.    -->
  <!-- Move the comma delimiter to the end of each constant and remove the trailing comma when we're done.  -->
  <DefineConstants Condition=" !$(DefineConstants.Contains(', NET')) ">$(DefineConstants)$(TargetFrameworkVersion.Replace("v", "NET").Replace(".", "")), </DefineConstants>
  <DefineConstants Condition=" $(DefineConstants.Contains(', NET')) ">$(DefineConstants.Remove($(DefineConstants.LastIndexOf(", NET"))))$(TargetFrameworkVersion.Replace("v", "NET").Replace(".", "")), </DefineConstants>
  <DefineConstants Condition=" $(TargetFrameworkVersion.Replace('v', '')) >= 2.0 ">$(DefineConstants)NET_20_OR_GREATER, </DefineConstants>
  <DefineConstants Condition=" $(TargetFrameworkVersion.Replace('v', '')) >= 3.5 ">$(DefineConstants)NET_35_OR_GREATER</DefineConstants>
  <DefineConstants Condition=" $(DefineConstants.EndsWith(', ')) ">$(DefineConstants.Remove($(DefineConstants.LastIndexOf(", "))))</DefineConstants>
</PropertyGroup>

고급 컴파일러 설정 대화 상자의 스크린 샷을 게시합니다 (프로젝트의 컴파일 탭에서 "고급 컴파일 옵션 ..."버튼을 클릭하여 열림). 그러나 새로운 사용자로서 나는 그렇게 할 담당자가 부족합니다. 스크린 샷을 볼 수 있다면 속성 그룹에 의해 자동으로 채워지는 사용자 지정 상수가 표시되고 "그 중 일부를 가져와야합니다."라고 말할 것입니다.


편집 : 그 담당자는 놀랍도록 빠르게 .. 감사합니다! 그 스크린 샷은 다음과 같습니다.

고급 컴파일러 설정


4

상수 지우기로 시작하십시오.

<PropertyGroup>
  <DefineConstants/>
</PropertyGroup>

다음으로 디버그, 추적 및 다음과 같은 기타 상수를 빌드하십시오.

<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
    <DebugSymbols>true</DebugSymbols>
  <DebugType>full</DebugType>
  <Optimize>false</Optimize>
  <DefineConstants>TRACE;DEBUG;$(DefineConstants)</DefineConstants>
</PropertyGroup>

마지막으로 프레임 워크 상수를 빌드합니다.

<PropertyGroup Condition=" '$(TargetFrameworkVersion)' == 'v2.0' ">
  <DefineConstants>NET10;NET20;$(DefineConstants)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFrameworkVersion)' == 'v3.0' ">
  <DefineConstants>NET10;NET20;NET30;$(DefineConstants)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFrameworkVersion)' == 'v3.5' ">
  <DefineConstants>NET10;NET20;NET30;NET35;$(DefineConstants)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFrameworkVersion)' == 'v4.0' ">
  <DefineConstants>NET10;NET20;NET30;NET35;NET40;$(DefineConstants)</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFrameworkVersion)' == 'v4.5' ">
  <DefineConstants>NET10;NET20;NET30;NET35;NET40;NET45;$(DefineConstants)</DefineConstants>
</PropertyGroup>

이 접근 방식은 매우 읽기 쉽고 이해하기 쉽습니다.


3

.csproj 파일에서 기존 <DefineConstants>DEBUG;TRACE</DefineConstants>줄 뒤에 다음을 추가합니다.

<DefineConstants Condition=" '$(TargetFrameworkVersion.Replace(&quot;v&quot;,&quot;&quot;))' &gt;= '4.0' ">NET_40_OR_GREATER</DefineConstants>
<DefineConstants Condition=" '$(TargetFrameworkVersion.Replace(&quot;v&quot;,&quot;&quot;))' == '4.0' ">NET_40_EXACTLY</DefineConstants>

디버그 및 릴리스 빌드 구성 모두에 대해이 작업을 수행하십시오. 그런 다음 코드에서 사용하십시오.

#if NET_40_OR_GREATER
   // can use dynamic, default and named parameters
#endif

3
기본 및 명명 된 매개 변수는 .NET Framework 4의 기능이 아니라 .NET 4 컴파일러의 기능입니다. Visual Studio 2010에서 컴파일되는 한 .NET 2 또는 .NET 3을 대상으로하는 프로젝트에서도 사용할 수 있습니다. 이는 구문상의 설탕 일뿐입니다. 반면에 동적은 .NET 프레임 워크 4의 기능이며 이전의 프레임 워크를 대상으로하는 프로젝트에서는 사용할 수 없습니다.
Thanasis IOANNIDIS

2

@Azarien, 귀하의 답변은 Jeremy와 결합하여 Debug | Release 등이 아닌 한곳에 보관할 수 있습니다.

저에게는 두 변형을 결합하는 것이 가장 잘 작동합니다. 즉 #if NETXX를 사용하여 코드에 조건을 포함하고 한 번에 여러 프레임 워크 버전을 빌드하는 것입니다.

내 .csproj 파일에 다음이 있습니다.

  <PropertyGroup>
    <DefineConstants Condition=" '$(TargetFrameworkVersion.Replace(&quot;v&quot;,&quot;&quot;))' &gt;= '4.0' ">NET_40_OR_GREATER</DefineConstants>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(TargetFrameworkVersion.Replace(&quot;v&quot;,&quot;&quot;))' == '3.5' ">
    <DefineConstants>NET35</DefineConstants>
    <OutputPath>bin\$(Configuration)\$(TargetFrameworkVersion)</OutputPath>
  </PropertyGroup>

및 대상 :

  <Target Name="AfterBuild">
    <MSBuild Condition=" '$(TargetFrameworkVersion.Replace(&quot;v&quot;,&quot;&quot;))' &gt;= '4.0' "
      Projects="$(MSBuildProjectFile)"
      Properties="TargetFrameworkVersion=v3.5"
      RunEachTargetSeparately="true"  />
  </Target>

0

.NET Core 빌드 시스템을 사용하는 경우 미리 정의 된 기호를 사용할 수 있습니다 (실제로 이미 예제와 일치하며 변경할 필요가 없습니다 .csproj!).

#if NET40
using FooXX = Foo40;
#elif NET35
using FooXX = Foo35;
#else NET20
using FooXX = Foo20;
#endif

미리 정의 된 기호 목록은 크로스 플랫폼 도구#if (C # 참조)를 사용 하여 라이브러리 개발에 설명되어 있습니다 .

.NET 프레임 워크 : NETFRAMEWORK , NET20, NET35, NET40, NET45, NET451, NET452, NET46, NET461, NET462, NET47, NET471, NET472,NET48

.NET 표준 : NETSTANDARD , NETSTANDARD1_0, NETSTANDARD1_1, NETSTANDARD1_2, NETSTANDARD1_3, NETSTANDARD1_4, NETSTANDARD1_5, NETSTANDARD1_6, NETSTANDARD2_0,NETSTANDARD2_1

.NET 코어 : NETCOREAPP , NETCOREAPP1_0, NETCOREAPP1_1, NETCOREAPP2_0, NETCOREAPP2_1, NETCOREAPP2_2,NETCOREAPP3_0

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.