5. Factoriser un fichier de build▲
5-1. Principes de factorisation▲
Un fichier de build doit être considéré comme un élément de code à part entière et doit donc suivre la même logique et réflexion lors de sa création. Ce fichier sera donc également factorisé et refactoré. Ne perdez pas de vue en effet qu'un fichier de build sur un projet important peut rapidement atteindre plusieurs centaines - voire milliers - de lignes.
Parmi les différents critères auxquels il faudra veiller, on peut citer entre autres :
- Commentaires du code
- Principe de responsabilité unique dans un même target
- Pas de redondance du code
- Extraction des fonctionnalités communes dans une bibliothèque partagée
Il est évidemment très simple de commenter son fichier de build.
Et nous avons déjà vu comment il est possible de créer des target, de les appeler, de créer des dépendances entre eux, ou encore comment envoyer des paramètres et recevoir des valeurs en retour. Par conséquent il est également très simple de répondre à la notion de responsabilité.
La redondance de code quant elle peut également être évitée via la factorisation dans d'autres targets.
Si on se place maintenant dans une problématique plus large avec la création de fichiers de build pour plusieurs projets, la notion de bibliothèque de target peut devenir très intéressante de façon à regrouper certaines tâches communément réalisées.
Microsoft utilise à cette fin l'extension « .targets », comme c'est le cas pour le fichier « Microsoft.CSharp.targets » par exemple.
5-2. Utilisation de fichier .targets▲
Il est possible de faire référence à un autre fichier de projet afin de mutualiser du code. Pour cela, on utilise l'élément « Import ».
<Project
xmlns
=
"http://schemas.microsoft.com/developer/msbuild/2003"
>
<Import
Project
=
"Commun.targets"
/>
</Project>
Notez que cet élément peut être déclaré à n'importe quel endroit du fichier de projet.
Il faut visualiser le fonctionnement de cette commande comme une copie du contenu du fichier importé à l'emplacement où la commande est déclarée.
5-3. Précautions à observer▲
Si l'utilisation de fichier d'import est très pratique pour factoriser et partager le code - elle est d'ailleurs massivement utilisée par Microsoft pour mutualiser les fichiers de projet - certaines précautions sont à observer.
5-3-1. Positionnement de la commande d'Import▲
L'emplacement auquel on déclare la commande d'import peut avoir des conséquences sur le fonctionnement du fichier de build.
Pour s'en rendre compte, il faut se souvenir qu'un fichier de build est toujours traité de bas en haut. Ainsi on peut prendre deux exemples : le premier nous montrera la redéfinition d'une propriété, et le suivant nous permettra de faire une redéfinition d'un target.
Pour ces exemples, nous allons nous baser sur le fichier commun suivant :
<Project
xmlns
=
"http://schemas.microsoft.com/developer/msbuild/2003"
>
<PropertyGroup>
<MaPropriete>
Valeur Par Défaut</MaPropriete>
</PropertyGroup>
<Target
Name
=
"Go"
>
<Message
Text
=
"Dans le fichier commun : $(MaPropriete)"
/>
</Target>
</Project>
5-3-1-1. Redéfinition d'une propriété▲
Comme les propriétés sont initialisées de haut en bas, selon l'emplacement de la ligne d'Import, on peut permettre de redéfinir la valeur d'une propriété.
Ainsi dans le cas suivant :
<Project
xmlns
=
"http://schemas.microsoft.com/developer/msbuild/2003"
>
<PropertyGroup>
<MaPropriete>
Nouvelle valeur</MaPropriete>
</PropertyGroup>
<Import
Project
=
"Commun.targets"
/>
</Project>
Si on visualise la copie du contenu du fichier « Commun.targets », on aurait l'assignation de « Nouvelle Valeur » dans la propriété, puis l'assignation de « Valeur Par Défaut ». En toute logique, nous obtenons le résultat d'exécution suivant :
Microsoft (R) Build Engine Version 3.5.30729.1
[Microsoft .NET Framework, Version 2.0.50727.3053]
Copyright (C) Microsoft Corporation 2007. All rights reserved.
Build started 12/05/2009 21:46:46.
Project "C:\Temp\build.proj" on node 0 (default targets).
Dans le fichier commun : Valeur Par Défaut
Done Building Project "C:\Temp\build.proj" (default targets).
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:00.23
Si en revanche, nous positionnons la commande d'Import en début de fichier, alors le résultat change totalement :
<Project
xmlns
=
"http://schemas.microsoft.com/developer/msbuild/2003"
>
<Import
Project
=
"Commun.targets"
/>
<PropertyGroup>
<MaPropriete>
Nouvelle valeur</MaPropriete>
</PropertyGroup>
</Project>
Microsoft (R) Build Engine Version 3.5.30729.1
[Microsoft .NET Framework, Version 2.0.50727.3053]
Copyright (C) Microsoft Corporation 2007. All rights reserved.
Build started 12/05/2009 21:48:24.
Project "C:\Temp\build.proj" on node 0 (default targets).
Dans le fichier commun : Nouvelle valeur
Done Building Project "C:\Temp\build.proj" (default targets).
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:00.03
Si on déclare deux propriétés statiques avec le même nom, c'est alors la
deuxième valeur qui sera assignée à la propriété (ie. celle qui est
présente le plus bas dans le fichier).
Si on déclare deux propriétés dynamiques (ou une statique et une dynamique) avec le même nom,
c'est la valeur de la dernière qui est créée (donc exécutée) qui sera assignée à la propriété.
C'est sur ce principe que l'on peut changer la valeur d'une propriété.
Se référer à la section « Changer la valeur d'une
propriété » pour plus d'informations.
5-3-1-2. Redéfinition d'un target▲
Si l'on re-déclare un target nommé « Go » dans le fichier .proj, alors on pourra redéfinir le target original selon l'emplacement de la commande d'Import.
Supposons par exemple le cas suivant :
<Project
xmlns
=
"http://schemas.microsoft.com/developer/msbuild/2003"
>
<Target
Name
=
"Go"
>
<Message
Text
=
"Dans le fichier .proj : $(MaPropriete)"
/>
</Target>
<Import
Project
=
"Commun.targets"
/>
</Project>
Microsoft (R) Build Engine Version 3.5.30729.1
[Microsoft .NET Framework, Version 2.0.50727.3053]
Copyright (C) Microsoft Corporation 2007. All rights reserved.
Build started 12/05/2009 21:50:13.
Project "C:\Temp\build.proj" on node 0 (default targets).
Dans le fichier commun : Valeur Par Défaut
Done Building Project "C:\Temp\build.proj" (default targets).
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:00.01
Ici nous allons rencontrer le target « Go » tout d'abord dans sa définition dans le fichier .proj, puis la version du fichier « .targets ». C'est donc naturellement elle qui sera exécutée.
Par contre, si on définit la commande d'import en en-tête de fichier, alors c'est la version du fichier .proj qui sera conservée.
<Project
xmlns
=
"http://schemas.microsoft.com/developer/msbuild/2003"
>
<Import
Project
=
"Commun.targets"
/>
<Target
Name
=
"Go"
>
<Message
Text
=
"Dans le fichier .proj : $(MaPropriete)"
/>
</Target>
</Project>
Microsoft (R) Build Engine Version 3.5.30729.1
[Microsoft .NET Framework, Version 2.0.50727.3053]
Copyright (C) Microsoft Corporation 2007. All rights reserved.
Build started 12/05/2009 21:55:17.
Project "C:\Temp\build.proj" on node 0 (default targets).
Dans le fichier .proj : Valeur Par Défaut
Done Building Project "C:\Temp\build.proj" (default targets).
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:00.01
Si on déclare deux targets avec le même nom, alors seul le dernier existe réellement.
C'est ainsi que l'on peut « surcharger » ou redéfinir un target.
Cette solution est massivement utilisée dans les fichiers de Visual Studio, de façon
à laisser au développeur des « points d'accroche » dans l'automatisation de certaines tâches
(compilation, intégration continue, ...)
5-3-2. Importer un fichier du source control▲
Il faut savoir que les commandes d'Import sont exécutées en priorité, avant même l'initialisation des propriétés et items statiques.
De plus, le fichier que l'on importe doit impérativement se trouver sur le disque.
Par conséquent il n'est pas possible de référencer un fichier dans le Source Control, et il n'est pas possible de faire un « Get » de fichier dans le Source Control avant que les imports ne soient effectués.
Par conséquent, si vous désirez vraiment utiliser un fichier commun présent dans le Source Control, la seule solution est au sein d'une tâche du fichier de faire un Get pour copier le fichier localement, et puis de déléguer les appels au fichier commun en utilisant la tâche MSBuild.
On perd évidemment dans ce cas la possibilité de redéfinition de propriétés et de targets, et nous devons donc coder nous-même des redirections.