125 votes

Compilation conditionnelle et objectifs-cadres

Il y a quelques endroits mineurs où le code de mon projet pourrait être considérablement amélioré si le framework cible était une version plus récente. J'aimerais être en mesure de mieux exploiter la compilation conditionnelle en C# pour les modifier si nécessaire.

Quelque chose comme :

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

Certains de ces symboles sont-ils gratuits ? Dois-je injecter ces symboles dans le cadre de la configuration du projet ? Cela semble assez facile à faire puisque je saurai quel framework est visé par MSBuild.

/p:DefineConstants="NET40"

Comment les gens gèrent-ils cette situation ? Créez-vous différentes configurations ? Faites-vous passer les constantes par la ligne de commande ?

1 votes

0 votes

Si vous voulez une solution simple et pré-cuite dans VS, veuillez voter pour cette voix d'utilisateur, visualstudio.uservoice.com/forums/121579-visual-studio/ .

1 votes

Jetez également un coup d'œil à ce lien. Assez explicatif. blogs.msmvps.com/punitganshani/2015/06/21/

120voto

Todd Points 3037

L'une des meilleures façons d'y parvenir est de créer différentes configurations de construction dans votre projet :

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

Et dans une de vos configurations par défaut :

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

Ce qui définit la valeur par défaut si elle n'est pas définie ailleurs. Dans le cas ci-dessus, l'OutputPath vous donnera un assemblage séparé à chaque fois que vous construisez chaque version.

Créez ensuite une cible AfterBuild pour compiler vos différentes versions :

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

Cet exemple recompilera l'ensemble du projet avec la variable Framework définie sur NET20 après la première compilation (en compilant les deux et en supposant que la première compilation était la NET35 par défaut ci-dessus). Les valeurs des définitions conditionnelles seront correctement définies à chaque compilation.

De cette manière, vous pouvez même exclure certains fichiers dans le fichier du projet si vous le souhaitez sans avoir à #ifdef les fichiers :

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

ou même des références

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

0 votes

Parfait. J'avais juste assez d'expérience dans le piratage du format msbuild pour savoir que cela pouvait être fait, mais pas assez de temps pour comprendre tous les détails. Merci beaucoup !

0 votes

Si vous ajoutez une référence à cette réponse sur ma question connexe ( stackoverflow.com/questions/2923181 ), je vous marquerai comme étant la solution dans ce cas. En fait, cela résout les deux problèmes en même temps.

7 votes

Merci pour la réponse, mais maintenant VS2010 inclut déjà une nouvelle balise nommée "TargetFrameworkVersion", maintenant pour chaque groupe de propriétés avec condition, seule TargetFrameworkVersion est modifiée, avons-nous encore besoin de tous ces éléments pour que cela fonctionne ?

46voto

Jeremy Cook Points 241

Une alternative qui fonctionne pour moi jusqu'à présent est d'ajouter ce qui suit au fichier du projet :

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

Cette opération prend la valeur de la propriété TargetFrameworkVersion, qui ressemble à "v3.5", remplace le "v" et le "." pour obtenir "NET35" (en utilisant la nouvelle propriété Fonctions de propriété ). Il supprime ensuite toute valeur "NETxx" existante et l'ajoute à la fin des DefinedConstants. Il est peut-être possible de rationaliser cela, mais je n'ai pas le temps de bricoler.

En regardant dans l'onglet Build des propriétés du projet dans VS, vous verrez la valeur résultante dans la section des symboles de compilation conditionnelle. Le changement de la version du framework cible dans l'onglet Application modifie alors le symbole automatiquement. Vous pouvez alors utiliser #if NETxx les directives du préprocesseur de la manière habituelle. Le changement de projet dans VS ne semble pas entraîner la perte du PropertyGroup personnalisé.

Notez que cela ne semble pas vous donner quelque chose de différent pour les options de cible du profil client, mais ce n'est pas un problème pour moi.

0 votes

Jeremy, wow merci c'est parfait puisque je construis déjà séparément dans ma solution de construction.

0 votes

+1. Qui aurait cru qu'il serait si difficile de trouver "$(DefineConstants.Contains('...)". ? ? Merci

0 votes

J'ai finalement retrouvé le chemin de cette page, car j'avais besoin de me rafraîchir la mémoire sur la façon dont j'ai intégré ces constantes magiques dans ma construction. Je revisite aujourd'hui le même projet, pour subdiviser la bibliothèque, et j'ai besoin des symboles pour m'accompagner dans certaines des subdivisions. Je viens de regarder au-dessus et j'ai remarqué que votre réponse est déjà dûment reconnue dans le fichier .CSPROJ original.

15voto

Nathaniel Roark Points 71

J'ai eu des problèmes avec ces solutions, peut-être parce que mes constantes initiales étaient préconstruites par ces propriétés.

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

Visual Studio 2010 a également généré une erreur à cause des points-virgules, affirmant qu'il s'agit de caractères illégaux. Le message d'erreur m'a donné un indice car je pouvais voir les constantes préconstruites séparées par des virgules, suivies éventuellement de mon point-virgule "illégal". Après quelques reformatages et manipulations, j'ai pu trouver une solution qui fonctionne pour moi.

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

Je posterais bien une capture d'écran de la boîte de dialogue Advanced Compiler Settings (ouverte en cliquant sur le bouton "Advanced Compile Options..." dans l'onglet Compile de votre projet). Mais en tant que nouvel utilisateur, je n'ai pas la réputation de le faire. Si vous pouviez voir la capture d'écran, vous verriez les constantes personnalisées remplies automatiquement par le groupe de propriétés et vous diriez alors "Il faut que je me procure un peu de ça".

EDIT : J'ai eu ce représentant étonnamment vite Merci les gars ! Voici la capture d'écran :

Advanced Compiler Settings

3voto

Azarien Points 41

Dans un fichier .csproj, après un fichier existant <DefineConstants>DEBUG;TRACE</DefineConstants> ajoutez ceci :

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

Faites-le pour les configurations de construction Debug et Release. Utilisez ensuite dans votre code :

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

3 votes

Les paramètres par défaut et nommés ne sont pas une fonctionnalité du cadre .NET 4, mais une fonctionnalité du compilateur .NET 4. Ils peuvent également être utilisés dans des projets ciblant .NET 2 ou .NET 3, à condition qu'ils soient compilés dans Visual Studio 2010. Il s'agit simplement d'un sucre syntaxique. D'autre part, dynamic est une fonctionnalité de .NET framework 4, et vous ne pouvez pas l'utiliser dans des projets ciblant des frameworks antérieurs à celui-ci.

2voto

ghanashyaml Points 56

@Azarien, votre réponse peut être combinée avec celle de Jeremy afin de garder le tout à un seul endroit plutôt que Debug|Release etc.

Pour moi, c'est la combinaison des deux variantes qui fonctionne le mieux, c'est-à-dire l'inclusion de conditions dans le code à l'aide de #if NETXX et la construction pour différentes versions de frameworks en une seule fois.

J'ai ces éléments dans mon fichier .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>

et dans les cibles :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X