Koşullu derleme ve çerçeve hedefleri


124

Hedef çerçeve daha yeni bir sürüm olsaydı, projem için kodun büyük ölçüde iyileştirilebileceği birkaç küçük yer var. Bunları gerektiği gibi değiştirmek için C # 'da koşullu derlemeden daha iyi yararlanabilmek istiyorum.

Gibi bir şey:

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

Bu sembollerden herhangi biri ücretsiz geliyor mu? Proje konfigürasyonunun bir parçası olarak bu sembolleri enjekte etmem gerekiyor mu? MSBuild'den hangi çerçevenin hedeflendiğini bileceğim için bunu yapmak yeterince kolay görünüyor.

/p:DefineConstants="NET40"

İnsanlar bu durumla nasıl başa çıkıyor? Farklı konfigürasyonlar mı oluşturuyorsunuz? Sabitleri komut satırından mı geçiriyorsunuz?



VS'de önceden pişirilmiş basit bir çözüm istiyorsanız, lütfen bu kullanıcı sesini oylayın, visualstudio.uservoice.com/forums/121579-visual-studio/… .
JohnC

1
Bu bağlantıya da bir göz atın. Oldukça açıklayıcı. blogs.msmvps.com/punitganshani/2015/06/21/…
Marco Alves

proje grupları, nuget restore ve nuget ref grupları, güzel çözüm: shazwazza.com/post/…
OzBob

Yanıtlar:


119

Bunu gerçekleştirmenin en iyi yollarından biri, projenizde farklı yapı yapılandırmaları oluşturmaktır:

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

Ve varsayılan yapılandırmalarınızdan birinde:

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

Başka bir yerde tanımlanmamışsa varsayılanı ayarlar. Yukarıdaki durumda, OutputPath her sürümü her oluşturduğunuzda size ayrı bir derleme verecektir.

Ardından, farklı sürümlerinizi derlemek için bir AfterBuild hedefi oluşturun:

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

Bu örnek, ilk derlemeden sonra Framework değişkeninin NET20 olarak ayarlandığı tüm projeyi yeniden derleyecektir (her ikisini de derleyerek ve ilk yapının yukarıdan varsayılan NET35 olduğunu varsayarsak). Her derlemenin koşullu tanımlama değerleri doğru şekilde ayarlanmış olacaktır.

Bu şekilde, dosyaları #ifdef yapmak zorunda kalmadan proje dosyasındaki belirli dosyaları hariç tutabilirsiniz:

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

hatta referanslar

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

Mükemmel. Msbuild formatını hackleme konusunda, yapılabileceğini bilmek için yeterli deneyime sahiptim, ancak tüm detayları anlamak için yeterli zamanım yoktu. Çok teşekkür ederim!
mckamey

İlgili soruma bu yanıta bir referans eklerseniz ( stackoverflow.com/questions/2923181 ), sizi orada çözüm olarak işaretleyeceğim. Bu aslında ikisini de aynı anda çözer.
mckamey

7
Cevabınız için teşekkürler, ancak artık VS2010 zaten "TargetFrameworkVersion" adlı yeni bir etiket içeriyor, şimdi koşullu her özellik grubu için yalnızca TargetFrameworkVersion değiştirildi, çalışmasını sağlamak için hala tüm bunlara ihtiyacımız var mı?
Akash Kava

Bu cevap sadece çerçeve için tanımlanmış sabitlere sahip olmakla ilgili değil, aynı zamanda çoklu çerçeveler için de inşa
etmekle ilgili

4
Bu gönderi benim için çalıştı ama MSBuild'de iyi değilim ve bunu çözmem biraz zaman aldı. Örnek olarak çalışan bir proje yaptım. dev6.blob.core.windows.net/blog-images/DualTargetFrameworks.zip
TheDev6

44

Şimdiye kadar benim için çalışan bir alternatif, aşağıdakileri proje dosyasına eklemektir:

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

Bu, "v3.5" gibi TargetFrameworkVersion özelliğinin değerini alır, "v" ve "" nin yerini alır. "NET35" almak için (yeni Özellik İşlevleri özelliğini kullanarak ). Daha sonra var olan herhangi bir "NETxx" değerini kaldırır ve bunu DefinedConstants'ın sonuna ekler. Bunu düzene koymak mümkün olabilir, ancak keman çalacak vaktim yok.

VS'deki proje özelliklerinin Build sekmesine baktığınızda, sonuçtaki değeri koşullu derleme sembolleri bölümünde göreceksiniz. Uygulama sekmesindeki hedef çerçeve sürümünü değiştirmek, ardından sembolü otomatik olarak değiştirir. Daha sonra #if NETxxönişlemci direktiflerini her zamanki gibi kullanabilirsiniz . VS'de projeyi değiştirmek, özel PropertyGroup'u kaybetmiyor gibi görünüyor.

Bunun, Müşteri Profili hedef seçenekleri için size farklı bir şey vermediğini unutmayın, ancak bu benim için bir sorun değil.


Jeremy, vay canına, bu mükemmel çünkü zaten derleme çözümümde ayrı ayrı inşa ediyorum.
Greg Finzer

+1. "$ (DefineConstants.Contains ('..." ?? Teşekkürler
CAD bloke

Sonunda bu sayfaya giden yolu tekrar buldum, çünkü bu sihirli sabitleri yapıma nasıl dahil ettiğimi tazelemeye ihtiyacım vardı. Bugün kütüphaneyi alt bölümlere ayırmak için aynı projeyi yeniden ziyaret ediyorum ve bazı alt bölümlere benimle birlikte gitmek için sembollere ihtiyacım var. Sadece yukarı baktım ve cevabınızın orijinal .CSPROJ dosyasında zaten gerektiği gibi onaylandığını fark ettim.
David A. Gray

15

Muhtemelen ilk sabitlerimin bu özellikler tarafından önceden oluşturulmuş olması nedeniyle bu çözümlerle ilgili sorunlar yaşadım.

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

Visual Studio 2010, noktalı virgüllerden dolayı, bunların geçersiz karakterler olduğunu iddia ederek bir hata da çıkardı. Hata mesajı, önceden oluşturulmuş sabitlerin virgülle ayrılmış olduğunu ve sonunda "geçersiz" noktalı virgülümün geldiğini görebildiğim için bana bir ipucu verdi. Biraz yeniden biçimlendirme ve masajdan sonra benim için çalışan bir çözüm bulabildim.

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

Gelişmiş Derleyici Ayarları iletişim kutusunun bir ekran görüntüsünü yayınlardım (projenizin Derleme sekmesindeki "Gelişmiş Derleme Seçenekleri ..." düğmesine tıklanarak açılır). Ancak yeni bir kullanıcı olarak bunu yapacak temsilcim yok. Ekran görüntüsünü görebilseydiniz, özellik grubu tarafından özel sabitlerin otomatik olarak doldurulduğunu görürdünüz ve sonra "Bana bundan biraz almam lazım" diyeceksiniz.


DÜZENLEME: Bu rep şaşırtıcı derecede hızlı oldu .. Teşekkürler çocuklar! İşte o ekran görüntüsü:

Gelişmiş Derleyici Ayarları


4

Sabitleri temizleyerek başlayın:

<PropertyGroup>
  <DefineConstants/>
</PropertyGroup>

Ardından, hata ayıklama, izleme ve diğer sabitleri oluşturun:

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

Son olarak, çerçeve sabitlerinizi oluşturun:

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

Bu yaklaşımın çok okunabilir ve anlaşılır olduğunu düşünüyorum.


3

Bir .csproj dosyasında, mevcut bir <DefineConstants>DEBUG;TRACE</DefineConstants>satırdan sonra şunu ekleyin:

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

Bunu hem Debug hem de Release derleme yapılandırmaları için yapın. Ardından kodunuzda kullanın:

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

3
varsayılan ve adlandırılmış parametreler .NET framework 4'ün bir özelliği değil, .NET 4 derleyicisinin bir özelliğidir. Visual Studio 2010'da derlendikleri sürece .NET 2 veya .NET 3'ü hedefleyen projelerde de kullanılabilirler. Bu sadece sözdizimsel şekerdir. Öte yandan dinamik, .NET framework 4'ün bir özelliğidir ve bundan önce çerçeveleri hedefleyen projelerde kullanamazsınız.
Thanasis Ioannidis

2

@Azarien, cevabınız, Debug | Release vb. Yerine tek bir yerde tutmak için Jeremy ile birleştirilebilir.

Benim için her iki varyasyonu birleştirmek en iyi sonucu verir, yani #if NETXX kullanarak koddaki koşulları dahil etmek ve ayrıca tek seferde farklı çerçeve sürümleri için oluşturmak.

.Csproj dosyamda bunlar var:

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

ve hedeflerde:

  <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 derleme sistemini kullanıyorsanız, onun önceden tanımlanmış sembollerini kullanabilirsiniz (ki bu aslında sizin örneğinizle zaten eşleşir ve sizin üzerinde herhangi bir değişiklik gerektirmez .csproj!):

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

Önceden tanımlanmış sembollerin listesi Çapraz Platform Araçları ve #if (C # Referansı) ile Kitaplıklar Geliştirme bölümünde belgelenmiştir :

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

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

.NET Çekirdek: NETCOREAPP , NETCOREAPP1_0, NETCOREAPP1_1, NETCOREAPP2_0, NETCOREAPP2_1, NETCOREAPP2_2,NETCOREAPP3_0

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.