Il existe une fonctionnalité dans Visual Studio 2017 qui n’est pas évidente à repérer et qui consiste à avoir plusieurs frameworks cibles pour un projet donné. Traditionnellement dans Visual Studio, on crée un projet pour un framework donné, or depuis la multiplication récente des cibles de compilation (.NET Standard ou .NET Core), il peut être nécessaire de produire des assemblies destinées à des environnements différents à partir d’un même code.
On va, dans un premier temps, indiquer ce qui peut justifier l’utilisation de projets multi-target. Ensuite, on va indiquer la méthode pour générer des assemblies pour plusieurs frameworks cible. Enfin, on indiquera des directives à utiliser dans le code pour générer du code spécifique à une plateforme.
Multitude de plateformes cibles
.NET Standard
Différences entre une bibliothèque et un exécutable
.NET Core
Fichiers projet .csproj simplifiés
Liste de fichiers du projet
Référence entre projets
PackageReference vs packages.config
Configurer un projet multi-target
Utiliser des fichiers projet simplifiés dans Visual Studio
Configurer les packages NuGet en mode “PackageReference”
Rendre flexible la version d’une référence de package
Forcer l’utilisation d’un répertoire “packages” avec le mode “PackageReference”
Configurer un projet multi-target dans Visual Studio 2017
Pourquoi configurer plusieurs plateformes cible alors qu’on peut utiliser .NET Standard ?
NuGet est intégré à MSBuild
Possibilité d’avoir des symboles de préprocesseur dans le code.
Multitude de plateformes cibles
Les technologies Microsoft adressent un grand nombre d’environnements différents allant de systèmes d’exploitation comme Windows à des appareils mobiles comme les tablettes. D’autres parts, depuis quelques années, il est possible d’exécuter du code .NET sur d’autres plateformes que Windows. Cette ouverture a encore augmenté le nombre de plateformes sur lesquelles des technologies pouvaient s’exécuter: Linux, macOS, iOS, Android etc… Toutes ces plateformes ont des spécificités qui nécessitent, pour chacune d’entre elles, une implémentation particulière.
Pour Microsoft, un premier challenge a été de tenter d’uniformiser ses bibliothèques pour les rendre moins spécifiques à une plateforme. Ce premier travail a abouti à 3 grandes familles de technologies:
- .NET Framework déployable sur des machines Windows,
- .NET Core déployable sur Windows, Linux, macOS mais aussi sur des tablettes, smartphones et Xbox avec Universal Windows Platform (UWP).
- Xamarin déployable sur Android et iOS.
Cette uniformisation ne permet pas, à elle-seule, d’utiliser un même code pour adresser plusieurs plateformes. Si on développe une bibliothèque, on est obligé d’avoir un projet spécifique pour chaque plateforme et il est nécessaire de compiler tout son code pour chacune des plateformes.
Pour permettre de compiler du code pour plusieurs plateformes et ainsi, encapsuler la complexité d’un déploiement sur plusieurs plateformes, une approche de Microsoft a été d’introduire une abstraction supplémentaire avec le .NET Standard.
.NET Standard
.NET Standard permet d’introduire une couche supplémentaire entre le code et les plateformes où seront déployées les bibliothèques:
- Une bibliothèque .NET Standard définit un ensemble d’API qui sont communes à plusieurs plateformes.
- Une bibliothèque .NET Standard n’est pas liée à une plateforme. Ainsi faire une bibliothèque se baser sur une version de .NET Standard permet d’éviter de la faire se baser sur une plateforme particulière. Il n’y a donc, plus de lien entre la bibliothèque et la plateforme sur laquelle elle sera déployée.
Cette caractéristique permet d’éviter d’écrire du code pour une plateforme spécifique. Le code de la bibliothèque est écrit pour une version du .NET Standard. - La couche supplémentaire qu’est .NET Standard rend plus facile la mise à jour éventuelle d’une plateforme. Au lieu de compiler une bibliothèque pour une version spécifique de la plateforme, on la compile pour une version du .NET Standard.
Si la nouvelle version d’une plateforme est compatible avec le .NET Standard sur lequel se base des bibliothèques, on peut mettre à jour la plateforme sans craindre une incompatibilité de ces bibliothèques. En effet c’est le .NET Standard qui va garantir la compatibilité.
Différences entre une bibliothèque et un exécutable
L’approche .NET Standard ne convient pas dans tous les cas. Elle permet de faciliter le déploiement de bibliothèques en ajoutant une abstraction de façon à éviter de baser ces bibliothèques directement sur des plateformes. Cette approche est possible car dans la majorité des cas, une bibliothèque de classes n’utilisent pas d’API spécifiques à une plateforme donnée.
En revanche, si une bibliothèque utilise des API trop spécifiques à une plateforme (comme par exemple, WPF qui nécessite un système Windows), elle ne pourra pas se baser sur .NET Standard (.NET Standard ne comporte pas de classes WPF).
De la même façon, un exécutable est spécifique à une plateforme. On ne pourra pas baser une exécutable sur un .NET Standard. Un exécutable est implémenté pour une plateforme précise.
Pour rendre l’approche .NET Standard la plus efficace possible, il faut donc placer un maximum de code dans des bibliothèques qui se basent sur .NET Standard. Le reste du code, étant plus spécifique, se basera sur une plateforme précise.
.NET Core
En plus de .NET Standard qui permet une certaine abstraction, la technologie .NET Core est capable de compiler du code pour des plateformes différentes. En effet .NET Core est une implémentation de Microsoft qui a pour but de rendre la technologie .NET disponible sur plusieurs plateformes. Contrairement à .NET Standard, .NET Core permet de produire des bibliothèques de classes et des exécutables. A la compilation, on précise le framework cible et l’environnement où ces assemblies seront exécutées (Windows, Linux, MacOS etc…).
Ces assemblies sont, ainsi, déployables et exécutables sur des environnements et des plateformes différents.
Fichiers projet .csproj simplifiés
.NET Core et .NET Standard ont introduit une grande flexiblité dans la technologie .NET puisqu’ils permettent de facilement générer des assemblies exécutables sur des plateformes différentes. De façon à rendre ces technologies plus facilement implémentables sur des plateformes différentes de Windows, Microsoft a fait un grand effort pour simplifier l’installation d’un environnement de développement en passant par la CLI .NET Core (CLI pour Command Line Interface) et éviter l’installation trop complexe de Visual Studio. Dans son effort de simplification, les fichiers projet .csproj
ont aussi été considérablement simplifié pour ne conserver que le stricte minimum en élément de configuration de façon à ce qu’ils soient facilement éditables à la main.
Par exemple, le fichier .csproj
d’un projet permettant de produire un exécutable .NET Core contient les éléments suivants:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
</Project>
Avec la CLI .NET Core (cf. Commandes courantes de la CLI .NET Core), on peut créer un projet console en exécutant:
dotnet new console -n <nom du projet>
Liste de fichiers du projet
Contrairement aux fichiers .csproj
traditionels, par défaut le nouveau format n’indique pas explicitement les fichiers de code du projet. Tous les fichiers .cs
font partie du projet, il n’est pas nécessaire de les spécifier un à un.
On peut toutefois spécifier les fichiers un à un ou avec une wildcard en désactivant le comportement par défaut avec l’élément de configuration EnableDefaultCompileItems
et en utilisant l’option Compile
:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
</PropertyGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="InternalClass.cs" />
</ItemGroup>
</Project>
Référence entre projets
Les références entre projets sont indiquées en utilisant l’élément de configuration ProjectReference
:
<Project Sdk="Microsoft.NET.Sdk">
<!-- ... -->
<ItemGroup>
<ProjectReference Include="..\DotNetAssembly\DotNetAssembly.csproj" />
</ItemGroup>
</Project>
On peut rajouter une référence d’un projet vers un autre en exécutant la commande CLI suivante:
dotnet add <chemin .csproj où ajouter la dépendance> reference
<chemin .csproj à rajouter>
PackageReference vs packages.config
L’élément de configuration PackageReference
concerne les packages NuGet installés pour le projet. Par exemple, si on rajoute le package NuGet Microsoft.AspNet.WepApi.Core
au projet, le contenu du fichier projet .csproj
devient:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNet.WebApi.Core" Version="5.2.6" />
</ItemGroup>
</Project>
On peut ajouter une référence vers un package NuGet en exécutant la commande CLI suivante:
dotnet add <chemin .csproj> package <nom du package NuGet>
Le fichier .csproj
ne contient qu’une référence vers le package NuGet rajouté. Toutes les dépendances transitives du package ne sont pas indiquées dans le fichier. Les dépendances du packages NuGet ne sont pas rajoutées au fichier contrairement aux fichiers .csproj
traditionnels.
Dans les projets Visual Studio traditionnels, les références vers les packages NuGet installés et les dépendances transitives sont ajoutés dans le fichier packages.config
. D’autre part, le fichier projet .csproj
contient des références vers les assemblies du package NuGet.
Configurer un projet multi-target
On peut facilement configurer le projet pour compiler le code pour plusieurs frameworks cibles en modifiant l’élément de configuration TargetFramework
. Dorénavant, avec les nouveaux fichiers projet .csproj
, il faut juste renommer l’élément TargetFramework
en TargetFrameworks
(avec un “s”):
<TargetFrameworks>netcoreapp2.0;netstandard2.0</TargetFrameworks>
Dans cet exemple, on produit des assemblies pour une application .NET Core 2.0 et une bibliothèque de classes pour .NET Standard 2.0. Les éléments netcoreapp2.0
et netstandard2.0
sont appelés TFM (pour Target Framework Moniker). Chaque code TFM correspond à un framework cible particulier, par exemple:
.NET Core | netcoreapp2.0, netcoreapp2.1 |
.NET Framework | net20 |
net45, net452 |
|
net46, net462 |
|
net47, net471, net472 |
|
.NET Standard | netstandard2.0 |
On peut trouver une liste exhaustive des TFM utilisés sur la page suivante: docs.microsoft.com/fr-fr/nuget/reference/target-frameworks#supported-frameworks.
Utiliser des fichiers projet simplifiés dans Visual Studio
Le fichier projet .csproj
traditionnel dans Visual Studio contient énormément de détails comme:
- La liste des fichiers
.cs
de code du projet, - La liste des références d’assembly du projet qui peuvent être des assemblies tiers, des assemblies provenant d’un autre projet de la solution ou d’assemblies provenant de packages NuGet.
- La liste des packages NuGet installés ainsi que les dépendances de chaque package est présente dans un fichier
packages.config
présent dans le projet.
Ces fichiers .csproj
sont, malheureusement incontournables pour la plupart des projets générés dans Visual Studio en particulier les projets produisant des assemblies exécutables sur une plateforme Windows comme:
- Les applications Winforms,
- Les applications WPF,
- Les projets ASP.NET,
- Les applications Universal Windows (UWP).
En revanche il est possible d’utiliser des fichiers projet .csproj
avec une syntaxe simplifiée pour les assemblies de type:
- Bibliothèques de classes,
- Applications console,
- Applications ASP.NET Core,
- Les applications .NET Core.
Configurer les packages NuGet en mode “PackageReference”
Cette configuration est valable pour tous les types de projet. Dans le mode par défaut, les dépendances NuGet sont indiquées dans un fichier packages.config
présent dans le projet. Ce fichier contient plus précisement:
- Les packages NuGet installés explicitement et
- Les dépendances des packages NuGet installés.
Par exemple, si on installe dans un projet le package NuGet Microsoft.AspNet.Mvc
, ce package sera installé et ajouté dans le fichier packages.config
. D’autre part, les dépendances transitives de ce package seront aussi installées à savoir:
Microsoft.AspNet.WebPages
Microsoft.AspNet.Razor
Microsoft.Web.Infrastructure
La contenu du fichier packages.config
devient:
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.AspNet.Mvc" version="5.2.6" targetFramework="net461" />
<package id="Microsoft.AspNet.Razor" version="3.2.6" targetFramework="net461" />
<package id="Microsoft.AspNet.WebPages" version="3.2.6" targetFramework="net461" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net461" />
</packages>
Les assemblies contenues dans ces packages seront ajoutées au projet et visibles dans la partie “Références” (ou References) dans Visual Studio:
Pour configurer le mode “PackageReference” dans Visual Studio 2017:
- Cliquer sur le menu “Outils” (ou “Tools”) ⇒ “Options”
- Aller dans la partie “Gestionnaire de package Nuget” (ou “Nuget package manager”) ⇒ “Général”
- Dans la partie “Gestion de packages” (ou “package management”), sélectionner le paramètre “Format de gestion de package par défaut”: “PackageReference” au lieu de “packages.config”:
- Cliquer sur OK
Si avec cette configuration, on crée un nouveau projet (quel que soit le type) et si on ajoute un package NuGet, par exemple en exécutant dans la console du gestionnaire de package (i.e. Package Manager Console):
install-package Microsoft.AspNet.Mvc
La présentation du projet dans Visual Studio est un peu différente:
- Il n’y a plus de fichier
packages.config
- On ne voit plus qu’une référence vers le package NuGet installé, les dépendances transitives du package NuGet ne sont plus visibles:
Dans le fichier .csproj
, il y a une référence vers le package installé mais pas de liens vers les dépendances transitives du package:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- ... -->
<ItemGroup>
<PackageReference Include="Microsoft.AspNet.Mvc">
<Version>5.2.6</Version>
</PackageReference>
</ItemGroup>
<!-- ... -->
</Project>
De même, il n’y a pas de répertoire packages dans le répertoire du projet contenant les packages NuGet téléchargés. Les packages NuGet sont téléchargés dans le répertoire par défaut du cache c’est-à-dire:
%LocalAppData%\NuGet\Cache
L’utilisation du mode “PackageReference” présente plusieurs avantages:
- Il simplifie considérablement le fichier projet
.csproj
puisqu’il n’y a plus les références d’assemblies. En effet, ces références changeaient à chaque mise à jour du package. - On ne voit plus les dépendances transitives des packages qui alourdissaient les fichiers projet
.csproj
.
En revanche, la résolution des conflits de versions dans les packages NuGet est plus complexe. Pour faciliter cette résolution, on peut:
- Tenter de rendre plus flexible la version d’une référence d’un package,
- Forcer l’utilisation d’un répertoire
packages
de façon à voir les versions des dépendances transitives d’un package NuGet.
Pour plus de détails, voir Nuget en 5 min.
A la compilation, les assemblies sont directement copiées dans le répertoire de sortie.
Rendre flexible la version d’une référence de package
Au lieu de figer la version de la référence de package NuGet comme dans l’exemple précédent:
<PackageReference Include="Microsoft.AspNet.Mvc">
<Version>5.2.6</Version>
</PackageReference>
On peut utiliser des wildcards pour indiquer la version de la dépendance:
<PackageReference Include ="Microsoft.AspNet.Mvc" version="5.2.*"/>
On peut aussi indiquer des contraintes de version:
1.0 | 1.0 ≤ x | Version 1.0 ou supérieure |
(1.0,) | 1.0 < x | Version strictement supérieure à 1.0 |
[1.0] | x == 1.0 | Exactement la version 1.0 |
(,1.0] | x ≤ 1.0 | Version 1.0 ou antérieure |
(,1.0) | x < 1.0 | Version strictement antérieure à 1.0 |
[1.0,2.0] | 1.0 ≤ x ≤ 2.0 | Version entre la 1.0 et 2.0 |
(1.0) | indication non valide |
Par exemple:
<PackageReference Include ="Microsoft.AspNet.Mvc" version="[5.0.0,5.2)"/>
Forcer l’utilisation d’un répertoire “packages” avec le mode “PackageReference”
On peut toutefois forcer la présence du répertoire packages
dans le répertoire du projet en éditant le fichier .csproj
et en ajoutant l’élément de configuration RestorePackagePath
:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props"
Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<!-- ... -->
<RestorePackagesPath>packages</RestorePackagesPath>
</PropertyGroup>
<!-- ... -->
</Project>
Configurer un projet multi-target dans Visual Studio 2017
Comme indiqué plus haut, cette fonctionnalité est disponible pour les fichiers .csproj
à la syntaxe simplifée. Cette syntaxe est possible seulement pour certains types de projet:
- Bibliothèques de classes,
- Applications console,
- Applications ASP.NET Core,
- Les applications .NET Core
Ainsi dans Visual Studio, si on crée un projet de type Console pour le framework .NET Core, les références du projet sont présentées de façon différente. Elles sont disponibles dans la partie “Dépendances” (i.e. Dependancies) du projet:
Traditionnellement dans les propriétés du projet, il est possible de modifier le framework cible. Pour accéder aux propriétés du projet, il faut:
- Faire un clique droit sur le projet dans Visual Studio
- Cliquer sur Propriétés
- Dans l’onglet “Application”, on peut éditer le paramètre “framework cible”:
Pour configurer le projet pour cibler plusieurs frameworks, il faut:
- Faire un clique droit sur le projet.
- Cliquer sur “Modifier <nom du projet>” (i.e. “Edit <nom du projet>”).
- Il est possible d’éditer le fichier
.csproj
directement dans Visual Studio (dans les versions actuelles, il n’existe pas d’éléments graphiques dans Visual Studio pour effectuer cette modification).
Si “Modifier <nom du projet>” n’est pas présent, c’est que la syntaxe du fichier projet.csproj
n’est pas une syntaxe simplifiée.
Il n’y a pas d’assistants pour passer d’une syntaxe traditionnelle à une syntaxe simplifiée. - Comme précédemment, il faut modifier le paramètre
TargetFramework
en ajoutant des TFM (i.e. Target Framework Moniker):<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>netcoreapp2.1</TargetFramework> </PropertyGroup> </Project>
- Ajouter un “s” à
TargetFramework
, par exemple:<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFrameworks>netcoreapp2.1;netstandard2.0</TargetFrameworks> </PropertyGroup> </Project>
- Enregistrer le fichier
.csproj
.
Après rechargement du fichier par Visual Studio, les dépendances contiennent plusieurs frameworks cible:
En allant dans les propriétés du projet, on ne peut plus éditer le paramètre “Framework cible”:
Pourquoi configurer plusieurs plateformes cible alors qu’on peut utiliser .NET Standard ?
On peut se poser la question de savoir quelle est la nécessité de paramétrer plusieurs plateformes cible alors qu’il suffit de paramétrer une plateforme cible .NET Standard. Comme .NET Standard est compatible avec plusieurs frameworks (on peut voir les compatibilités sur la page docs.microsoft.com/fr-fr/dotnet/standard/net-standard), en configurant la plateforme cible .NET Standard, on s’assure d’être compatible avec tous les frameworks prenant en charge par le standard. C’est vrai, toutefois:
- Comme indiqué plus haut, .NET Standard permet de produire des assemblies seulement pour les bibliothèques de classes. On ne peut pas paramétrer une cible correspondant à .NET Standard pour un exécutable. Si on souhaite produire des exécutables différents pour un même code, il faut pouvoir paramétrer des frameworks cibles différents.
- Certains packages NuGet comportent des assemblies spécifiques à une plateforme donnée. Ces plateformes peuvent être différentes d’une version de .NET Standard. En paramétrant des frameworks cible précis, si la compilation réussit, on peut être sûr que les dépendances NuGet seront assurées pour les cibles paramétrées.
- Un des intérêts de cette fonctionnalité est de produire des assemblies pour plusieurs frameworks à partir d’un même code. Il est possible d’affiner la compilation en appliquant des directives de compilation pour des plateformes spécifiques. On s’assure, ainsi, à la compilation que le code compilé est compatible avec le framework auquel il est destiné. Ces directives de compilation seront indiquées par la suite.
NuGet est intégré à MSBuild
Pour permettre de simplifier les fichiers, NuGet a été intégré à MSBuild. Cette intégration a plusieurs avantages:
- L’exécution de NuGet fait partie d’une tâche MsBuild qu’on peut lancer directement en exécutant MsBuild. Par exemple, pour générer un package NuGet, on peut lancer la tâche suivante:
msbuild.exe "<chemin du fichier projet .csproj>" /t:pack
- On peut générer un package NuGet directement à la compilation du projet en allant dans les propriétés du projet dans Visual Studio, dans l’onglet “Package” et en cochant “Generate NuGet package on build”. On peut ainsi préciser des paramètres liés au package NuGet à générer, par exemple:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFrameworks>netcoreapp2.1;netstandard2.0</TargetFrameworks> <GeneratePackageOnBuild>True</GeneratePackageOnBuild> <Company>CompanyName</Company> <Authors>AuthorName</Authors> <Description>Description</Description> <Version>1.1.1</Version> </PropertyGroup> <!-- ... --> </Project>
Il existe un onglet de configuration dans Visual Studio dans les propriétés du projet:
- On peut appliquer des conditions d’application pour l’utilisation d’une référence de package dans le fichier projet
.csproj
:<ItemGroup> <PackageReference Include="Microsoft.AspNet.WebApi.Core" Version="5.2.6" Condition = "'$(TargetFramework)' == 'netstandard2.0'" /> </ItemGroup>
Pour plus d’informations: docs.microsoft.com/fr-fr/nuget/reference/msbuild-targets.
On peut effectuer le téléchargement des packages NuGet directement avec MsBuild dans une usine de build, on n’est plus obligé de configurer une étape supplémentaire pour exécuter nuget restore
.
Possibilité d’avoir des symboles de préprocesseur dans le code.
On peut préciser des symboles de préprocesseur pour que des parties du code soient compilées pour une plateforme cible spécifique.
Par exemple:
public static class MultiTargetCode
{
public static string GetTargetFramework()
{
#if NETCOREAPP2_1
return ".NET Core";
#elif NETFULL
return ".NET Framework";
#else
throw new NotImplementedException();
#endif
}
}
Quelques autres symboles de préprocesseur:
.NET Framework | NET20, NET46, NET461, NET462, NET47, NET471, NET472 |
.NET Standard | NETSTANDARD1_5, NETSTANDARD1_6, NETSTANDARD2_0 |
.NET Core | NETCOREAPP2_0, NETCOREAPP2_1 |
Une liste exhaustive de ces symboles se trouve sur la page suivante: docs.microsoft.com/fr-fr/dotnet/standard/frameworks.
- Target frameworks: https://docs.microsoft.com/en-us/dotnet/standard/frameworks
- Multi-Targeting and Porting a .NET Library to .NET Core 2.0: https://weblog.west-wind.com/posts/2017/Jun/22/MultiTargeting-and-Porting-a-NET-Library-to-NET-Core-20
- Multi-targeting in Visual Studio 2017: http://blog.peterritchie.com/Multi-targeting-in-Visual-Studio-2017/
- Ajouts au format csproj pour .NET Core: https://docs.microsoft.com/fr-fr/dotnet/core/tools/csproj
- Old csproj to new csproj: Visual Studio 2017 upgrade guide: https://natemcmaster.com/blog/2017/03/09/vs2015-to-vs2017-upgrade/
- Easily supporting multiple target frameworks (TFMs) with VS2017 and Nuget: http://blogs.microsoft.co.il/iblogger/2017/04/05/easily-supporting-multiple-target-frameworks-tfms-with-vs2017-and-nuget/
- NuGet is now fully integrated into MSBuild: https://blog.nuget.org/20170316/NuGet-now-fully-integrated-into-MSBuild.html
- Multiple Platform Targeting in Visual Studio 2017: http://dontcodetired.com/blog/post/Multiple-Platform-Targeting-in-Visual-Studio-2017
- How to target multiple frameworks in VS2017 final?: https://stackoverflow.com/questions/42670048/how-to-target-multiple-frameworks-in-vs2017-final
- Package destination of restore of .net-core projects is always global package directory: https://stackoverflow.com/questions/47311192/package-destination-of-restore-of-net-core-projects-is-always-global-package-di
- https://github.com/NuGet/Home/wiki/%5BSpec%5D-NuGet-settings-in-MSBuild: https://github.com/NuGet/Home/wiki/%5BSpec%5D-NuGet-settings-in-MSBuild
- NuGet pack and restore as MSBuild targets: https://docs.microsoft.com/fr-fr/nuget/reference/msbuild-targets