Biên soạn có điều kiện và mục tiêu khung


124

Có một vài nơi nhỏ mà mã cho dự án của tôi có thể được cải thiện mạnh mẽ nếu khung mục tiêu là phiên bản mới hơn. Tôi muốn có thể tận dụng tốt hơn việc biên dịch có điều kiện trong C # để chuyển đổi những thứ này khi cần thiết.

Cái gì đó như:

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

Có bất kỳ biểu tượng trong số này đến miễn phí? Tôi có cần tiêm các ký hiệu này như là một phần của cấu hình dự án không? Có vẻ như đủ dễ để làm vì tôi biết khung nào đang được nhắm mục tiêu từ MSBuild.

/p:DefineConstants="NET40"

Mọi người xử lý tình huống này như thế nào? Bạn đang tạo cấu hình khác nhau? Bạn đang đi qua các hằng số thông qua dòng lệnh?



Nếu bạn muốn một giải pháp nướng sẵn đơn giản trong VS, vui lòng bỏ phiếu cho giọng nói của người dùng này, visualstudio.uservoice.com/forums/121579-visual-studio/ Lỗi .
JohnC

1
Hãy xem liên kết này là tốt. Khá giải thích. blog.msmvps.com/astaitganshani/2015/06/21/ Cách
Marco Alves

các nhóm dự án, khôi phục nuget
OzBob

Câu trả lời:


119

Một trong những cách tốt nhất để thực hiện điều này là tạo các cấu hình xây dựng khác nhau trong dự án của bạn:

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

Và trong một trong các cấu hình mặc định của bạn:

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

Mà sẽ đặt mặc định nếu nó không được xác định ở bất cứ nơi nào khác. Trong trường hợp trên, OutputPath sẽ cung cấp cho bạn một hội đồng riêng biệt mỗi khi bạn xây dựng mỗi phiên bản.

Sau đó tạo mục tiêu AfterBuild để biên dịch các phiên bản khác nhau của bạn:

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

Ví dụ này sẽ biên dịch lại toàn bộ dự án với biến Framework được đặt thành NET20 sau lần xây dựng đầu tiên (biên dịch cả hai và giả sử rằng bản dựng đầu tiên là NET35 mặc định ở trên). Mỗi trình biên dịch sẽ có các giá trị xác định có điều kiện được đặt chính xác.

Theo cách này, bạn thậm chí có thể loại trừ các tệp nhất định trong tệp dự án nếu bạn muốn phải #ifdef các tệp:

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

hoặc thậm chí tài liệu tham khảo

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

Hoàn hảo. Tôi đã có đủ kinh nghiệm hack định dạng msbuild để biết nó có thể được thực hiện, nhưng không đủ thời gian để tìm ra tất cả các chi tiết. Cảm ơn rât nhiều!
mckamey

Nếu bạn thêm một tham chiếu cho câu trả lời này qua câu hỏi liên quan của tôi ( stackoverflow.com/questions/2923181 ), tôi sẽ đánh dấu bạn là giải pháp ở đó. Điều này thực sự giải quyết cả hai cùng một lúc.
mckamey

7
Cảm ơn câu trả lời, nhưng bây giờ VS2010 đã bao gồm một thẻ mới có tên "TargetFrameworkVersion", bây giờ đối với mỗi nhóm thuộc tính có điều kiện, chỉ có TargetFrameworkVersion được thay đổi, chúng ta vẫn cần tất cả những thứ này để làm cho nó hoạt động chứ?
Akash Kava

Câu trả lời này không chỉ là về việc xác định các hằng số cho khung mà còn xây dựng cho nhiều khung
katbyte

4
Bài đăng này làm việc cho tôi nhưng tôi không giỏi về MSBuild và phải mất một thời gian để tìm ra nó. Tôi đã làm một dự án hoạt động như một ví dụ. dev6.blob.core.windows.net/blog-images/DualTargetFrameworks.zip
TheDev6

44

Một thay thế đang làm việc cho tôi cho đến nay là thêm các mục sau vào tệp dự án:

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

Cái này lấy giá trị của thuộc tính TargetFrameworkVersion, giống như "v3.5", thay thế cho "v" và "." để có được "NET35" (sử dụng Hàm thuộc tính mới tính mới). Sau đó, nó sẽ xóa mọi giá trị "NETxx" hiện có và thêm nó vào cuối DefinedConstants. Có thể hợp lý hóa việc này, nhưng tôi không có thời gian để mân mê.

Nhìn vào tab Build của các thuộc tính dự án trong VS bạn sẽ thấy giá trị kết quả trong phần ký hiệu biên dịch có điều kiện. Thay đổi phiên bản khung đích trên tab Ứng dụng, sau đó tự động thay đổi biểu tượng. Sau đó, bạn có thể sử dụng các #if NETxxchỉ thị tiền xử lý theo cách thông thường. Thay đổi dự án trong VS dường như không mất Thuộc tính nhóm tùy chỉnh.

Lưu ý rằng điều này dường như không cung cấp cho bạn bất cứ điều gì khác biệt đối với các tùy chọn mục tiêu Hồ sơ khách hàng, nhưng đó không phải là vấn đề đối với tôi.


Jeremy, wow cảm ơn điều này là hoàn hảo vì tôi đã xây dựng riêng trong giải pháp xây dựng của mình.
Greg Finzer

+1. Ai có thể nghĩ rằng sẽ rất khó để tìm "$ (DefineConstants.Contains ('..." ?? Cảm ơn
CAD bloke

Cuối cùng tôi đã tìm được đường đến trang này một lần nữa, vì tôi cần xem lại cách tôi đưa những hằng số ma thuật này vào bản dựng của mình. Hôm nay tôi đang xem lại cùng một dự án, để chia nhỏ thư viện và tôi cần các biểu tượng để đi cùng tôi vào một số phân khu. Tôi chỉ nhìn lên trên và nhận thấy rằng câu trả lời của bạn đã được thừa nhận hợp lệ trong tệp .CSPROJ ban đầu.
David A. Gray

15

Tôi gặp vấn đề với các giải pháp này, có thể là do các hằng số ban đầu của tôi được xây dựng trước bởi các thuộc tính này.

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

Visual Studio 2010 cũng đã đưa ra một lỗi vì các dấu chấm phẩy, cho rằng chúng là các ký tự không hợp lệ. Thông báo lỗi cho tôi một gợi ý vì tôi có thể thấy các hằng số được xây dựng trước được phân tách bằng dấu phẩy, cuối cùng theo sau là dấu chấm phẩy "bất hợp pháp" của tôi. Sau một số định dạng lại và mát xa, tôi đã có thể đưa ra một giải pháp phù hợp với mình.

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

Tôi sẽ đăng một ảnh chụp màn hình của hộp thoại Cài đặt trình biên dịch nâng cao (được mở bằng cách nhấp vào nút "Tùy chọn biên dịch nâng cao ..." trên tab Biên dịch của dự án của bạn). Nhưng là một người dùng mới, tôi thiếu người đại diện để làm như vậy. Nếu bạn có thể xem ảnh chụp màn hình, bạn sẽ thấy các hằng số tùy chỉnh được nhóm thuộc tính tự động điền và sau đó bạn sẽ nói: "Tôi phải lấy cho tôi một số thứ đó."


EDIT: Có đại diện đó nhanh đáng ngạc nhiên .. Cảm ơn các bạn! Đây là ảnh chụp màn hình:

Cài đặt trình biên dịch nâng cao


4

Bắt đầu với việc xóa các hằng số:

<PropertyGroup>
  <DefineConstants/>
</PropertyGroup>

Tiếp theo, xây dựng gỡ lỗi, theo dõi và các hằng số khác như:

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

Cuối cùng, xây dựng các hằng khung của bạn:

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

Tôi nghĩ cách tiếp cận này rất dễ đọc và dễ hiểu.


3

Trong tệp .csproj, sau một <DefineConstants>DEBUG;TRACE</DefineConstants>dòng hiện có , hãy thêm dòng này:

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

Làm điều này cho cả cấu hình bản dựng Debug và Release. Sau đó sử dụng trong mã của bạn:

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

3
các tham số mặc định và được đặt tên không phải là một tính năng của .NET framework 4, mà là một tính năng của trình biên dịch .NET 4. Chúng cũng có thể được sử dụng trong các dự án nhắm mục tiêu .NET 2 hoặc .NET 3 miễn là chúng được biên dịch trong Visual Studio 2010. Nó chỉ là cú pháp cú pháp. Mặt khác, động là một tính năng của .NET framework 4 và bạn không thể sử dụng nó trong các khung nhắm mục tiêu dự án trước đó.
Thanocation Ioannidis

2

@Azarien, câu trả lời của bạn có thể được kết hợp với Jeremy để giữ nó ở một nơi thay vì Debug | Phát hành, v.v.

Đối với tôi, việc kết hợp cả hai biến thể hoạt động tốt nhất tức là bao gồm các điều kiện trong mã bằng cách sử dụng #if NETXX và cũng xây dựng cho các phiên bản khung khác nhau trong một lần.

Tôi có những thứ này trong tệp .csproj của mình:

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

và trong các mục tiêu:

  <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

Nếu bạn đang sử dụng hệ thống xây dựng .NET Core, bạn có thể sử dụng các ký hiệu được xác định trước của nó (thực sự phù hợp với ví dụ của bạn và không yêu cầu bất kỳ thay đổi nào đối với .csproj!):

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

Danh sách các ký hiệu được xác định trước được ghi lại trong Thư viện phát triển với Công cụ đa nền tảng#if (Tham khảo C #) :

.NET Framework: NETFRAMEWORK , NET20, NET35, NET40, NET45, NET451, NET452, NET46, NET461, NET462, NET47, NET471, NET472,NET48

NET Tiêu chuẩn: NETSTANDARD , NETSTANDARD1_0, NETSTANDARD1_1, NETSTANDARD1_2, NETSTANDARD1_3, NETSTANDARD1_4, NETSTANDARD1_5, NETSTANDARD1_6, NETSTANDARD2_0,NETSTANDARD2_1

NET Core: NETCOREAPP , NETCOREAPP1_0, NETCOREAPP1_1, NETCOREAPP2_0, NETCOREAPP2_1, NETCOREAPP2_2,NETCOREAPP3_0

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.