web-dev-qa-db-fra.com

Compilation conditionnelle et cibles du cadre

Il y a quelques endroits mineurs où le code de mon projet pourrait être considérablement amélioré si le cadre cible était une version plus récente. J'aimerais pouvoir mieux tirer parti de la compilation conditionnelle en C # pour les changer au besoin.

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, car je saurai quel framework est ciblé par MSBuild.

/p:DefineConstants="NET40"

Mise à jour: Ma question est de savoir comment les gens gèrent cette situation? Créez-vous des configurations différentes? Passez-vous les constantes via la ligne de commande?

119
mckamey

L'une des meilleures façons d'y parvenir est de créer différentes configurations de build 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 l'une de vos configurations par défaut:

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

Ce qui définirait la valeur par défaut si elle n'était pas définie ailleurs. Dans le cas ci-dessus, OutputPath vous fournira un assembly séparé chaque fois que vous générez 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 le projet entier avec la variable Framework définie sur NET20 après la première génération (en compilant les deux et en supposant que la première génération était la NET35 par défaut ci-dessus). Chaque compilation aura les valeurs de définition conditionnelles définies correctement.

De cette manière, vous pouvez même exclure certains fichiers dans le fichier de projet si vous voulez 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>
117
Todd

Une alternative qui fonctionne pour moi jusqu'à présent consiste à ajouter ce qui suit au fichier de 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>

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

En regardant 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. La modification de la version du framework cible dans l'onglet Application modifie ensuite automatiquement le symbole. Vous pouvez ensuite utiliser #if NETxx les directives du préprocesseur de la manière habituelle. Changer le projet dans VS ne semble pas perdre le PropertyGroup personnalisé.

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

43
Jeremy Cook

J'ai eu des problèmes avec ces solutions, peut-être parce que mes constantes initiales ont été précompilées 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 en raison 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, éventuellement suivies de mon point-virgule "illégal". Après quelques reformatages et massages, 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 publierais une capture d'écran de la boîte de dialogue Paramètres avancés du compilateur (ouverte en cliquant sur le bouton "Options de compilation avancées ..." dans l'onglet Compiler de votre projet). Mais en tant que nouvel utilisateur, je n'ai pas le représentant pour 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, puis vous diriez: "Je dois m'en procurer une partie."


EDIT: J'ai obtenu ce représentant étonnamment rapide .. Merci les gars! Voici cette capture d'écran:

Advanced Compiler Settings

15
Nathaniel Roark

Commencez par effacer les constantes:

<PropertyGroup>
  <DefineConstants/>
</PropertyGroup>

Ensuite, créez votre débogage, trace et autres constantes comme:

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

Enfin, créez vos constantes de framework:

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

Je pense que cette approche est très lisible et compréhensible.

4
zDougie

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

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

Je les ai 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>
2
ghanashyaml

Dans un fichier .csproj, après un <DefineConstants>DEBUG;TRACE</DefineConstants> ligne, 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 cela pour les configurations de build Debug et Release. Utilisez ensuite dans votre code:

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