Objetivos de Compilación Condicional y Marco de Trabajo


Hay algunos lugares menores en los que el código de mi proyecto podría mejorarse drásticamente si el framework de destino fuera una versión más reciente. Me gustaría poder aprovechar mejor la compilación condicional en C# para cambiar estos según sea necesario.

Algo como:

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

¿Alguno de estos símbolos viene gratis? ¿Necesito inyectar estos símbolos como parte de la configuración del proyecto? Parece bastante fácil de hacer, ya que sabré de qué marco se está apuntando MSBuild.

/p:DefineConstants="NET40"

Actualización: Mi pregunta es ¿cómo está manejando la gente esta situación? ¿Estás creando diferentes configuraciones? ¿Está pasando las constantes a través de la línea de comandos?

Author: abatishchev, 2010-05-27

6 answers

Una de las mejores maneras de lograr esto es crear diferentes configuraciones de compilación en su proyecto:

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

Y en una de sus configuraciones por defecto:

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

Que establecería el valor predeterminado si no estuviera definido en ningún otro lugar. En el caso anterior, la ruta de salida le dará un ensamblado separado cada vez que compile cada versión.

Luego crea un destino AfterBuild para compilar tus diferentes versiones:

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

Este ejemplo recompilará todo el proyecto con la variable Framework establecida en NET20 después de la primera compilación (compilando ambas y asumiendo que la primera compilación fue la NET35 predeterminada desde arriba). Cada compilación tendrá los valores de definición condicional establecidos correctamente.

De esta manera puede incluso excluir ciertos archivos en el archivo de proyecto si desea sin tener que # ifdef los archivos:

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

O incluso referencias

<Reference Include="Some.Assembly" Condition="" '$(Framework)' == 'NET20' " >
  <HintPath>..\Lib\$(Framework)\Some.Assembly.dll</HintPath>
</Reference>
 114
Author: Todd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-28 12:15:18

Una alternativa que me está funcionando hasta ahora es agregar lo siguiente al archivo del proyecto:

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

Esto toma el valor de la propiedad TargetFrameworkVersion, que es como "v3.5", reemplaza a "v" y "."para obtener" NET35 " (usando la nueva característica Propiedad Functions). Luego elimina cualquier valor "NETxx" existente y lo agrega al final de las constantes definidas. Puede ser posible simplificar esto, pero no tengo tiempo para tocar el violín.

Mirando en la pestaña Build de la propiedades del proyecto en VS verá el valor resultante en la sección símbolos de compilación condicional. Al cambiar la versión del marco de trabajo de destino en la pestaña Aplicación, cambia el símbolo automáticamente. A continuación, puede utilizar las directivas del preprocesador #if NETxx de la manera habitual. Cambiar el proyecto en VS no parece perder el PropertyGroup personalizado.

Tenga en cuenta que esto no parece darle nada diferente para las opciones de destino del Perfil del cliente, pero eso no es un problema para mí.

 41
Author: Jeremy Cook,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-16 04:44:41

Tuve problemas con estas soluciones, posiblemente porque mis constantes iniciales fueron pre-construidas por estas propiedades.

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

Visual Studio 2010 también arrojó un error debido a los puntos y coma, alegando que son personajes ilegales. El mensaje de error me dio una pista ya que podía ver las constantes pre-construidas separadas por comas, eventualmente seguidas por mi punto y coma" ilegal". Después de un poco de reformateo y masaje, pude encontrar una solución que funciona para me.

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

Publicaría una captura de pantalla del diálogo Configuración Avanzada del Compilador (abierto haciendo clic en "Opciones avanzadas de compilación..."botón en la pestaña Compilar de tu proyecto). Pero como un nuevo usuario, me falta el representante para hacerlo. Si pudieras ver la captura de pantalla, verías las constantes personalizadas autocompletadas por el grupo de propiedades y entonces estarías diciendo, " Tengo que conseguir algo de eso."


EDIT: Consiguió ese representante sorprendentemente rápido.. Gracias chicos! He aquí que captura de pantalla:

Configuración Avanzada del Compilador

 12
Author: Nathaniel Roark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-18 12:33:18

Comience con borrar las constantes:

<PropertyGroup>
  <DefineConstants/>
</PropertyGroup>

A continuación, construya su depuración, seguimiento y otras constantes como:

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

Por último, construya sus 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>

Creo que este enfoque es muy legible y comprensible.

 3
Author: zDougie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-06 10:48:30

En a .archivo csproj, después de una línea <DefineConstants>DEBUG;TRACE</DefineConstants> existente, agregue esto:

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

Haga esto tanto para las configuraciones de compilación de depuración como de publicación. Luego use en su código:

#if NET_40_OR_GREATER
   // can use dynamic, default and named parameters
#endif
 2
Author: Azarien,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-25 11:29:40

@Azarien, tu respuesta se puede combinar con la de Jeremy para mantenerla en un solo lugar en lugar de Depurar|Liberar, etc.

Para mí, combinar ambas variaciones funciona mejor, es decir, incluir condiciones en el código usando #if NETXX y también construir para diferentes versiones de framework de una sola vez.

Tengo estos en mi .archivo 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>

Y en los objetivos:

  <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
Author: ghanashyaml,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-02 18:33:09