Portée des properties et items dans un script MSBuild 4

Portée des properties et items dans un script MSBuild

Il y a quelque temps je me suis posé la question de la portée des property et item dans un script MSBuild lors d’appel aux tâches CallTarget et MSBuild lorsque l’on modifie dynamiquement les valeurs dans une target. Ci-dessous le résultat de mes (longues) recherches.

Ce qu’il faut savoir :

  • La tâche CallTarget équivaut à la tâche MSBuild en spécifiant comme projet $(MSBuildProjectFile). Je vais donc me limiter à utiliser la tâche MSBuild.
  • En interne MSBuild utilise la classe Project pour représenter un projet.

Pour mes tests j’utilise le script suivant :

<PropertyGroup>
<MyProp>Static</MyProp>
</PropertyGroup>

<Target Name= »Print »>
<Message Text= »[Print] $(MyProp) » />
</Target>

<Target Name= »Update »>
<PropertyGroup>
<MyProp>Dynamic</MyProp>
</PropertyGroup>
</Target>

<Target Name= »Test1″>
<Message Text= »[Test1] [BeforeUpdate] $(MyProp) » />
<MSBuild Projects= »$(MSBuildProjectFile) » Targets= »Update » />
<Message Text= »[Test1] [AfterUpdate] $(MyProp) » />
</Target>

<Target Name= »Test2″>
<Message Text= »[Test2] [BeforeUpdate] $(MyProp) » />
<MSBuild Projects= »$(MSBuildProjectFile) » Targets= »Update » />
<MSBuild Projects= »$(MSBuildProjectFile) » Targets= »Print » />
<Message Text= »[Test2] [AfterUpdate] $(MyProp) » />
</Target>

Si je lance mon script avec les targets “Update” et “Print” j’obtiens le résultat suivant :

image

Comme on s’y attend, la valeur de “MyProp” affichée est la valeur mise à jour. Par contre si je lance mon script avec les targets “Test1” et “Print” j’obtiens le résultat suivant :

image

Bien que j’ai fait un appel à la target “Update” via la tâche MSBuild dans ma target “Test1”, la propriété “MyProp” à toujours la valeur “Static” après la mise à jour dans la target “Test1”. Par contre dans la target “Print”, j’ai bien la valeur mise à jour via la target “Update”.

L’appel au script en appelant la target “Test2” permet de comprendre un peu mieux le comportement de MSBuild :

image

L’appel au milieu de la target “Test2” à “Print” affiche la valeur mise à jour. Au travers de ces exemples j’en ai déduit le comportement suivant :

  • Une instance de la classe Project associé au fichier exécuté contient l’ensembles des valeurs des properties et items dans un contexte global.
  • Lorsqu’une target est exécuté, un contexte local est créé à partir d’une copie du contexte global et la target utilisera ce contexte local pour lire et mettre à jour les valeurs des variables.
  • A la fin de l’exécution de la target, les modifications du contexte local sont reportées sur le contexte global.

Donc tant qu’une target n’a pas fini son exécution, ses modifications sur les variables ne sont pas utilisables par les targets appelée via la tâche MSBuild. Il faut faire attention a ce mécanisme car l’on peut avoir de drôle de surprise comme le montre l’exemple suivant :

<Target Name= »Warning »>
<PropertyGroup>
<MyProp>Warning</MyProp>
</PropertyGroup>
<MSBuild Projects= »$(MSBuildProjectFile) » Targets= »Update » />
</Target>

Si l’on appel les targets “Warning” et “Print”, on pourrait s’attendre à ce que la target “Print” affiche la valeur “Dynamic” car l’appel à la target “Update” a lieu après la mise à jour dans “Warning” mais voilà le résultat :

image

Pourquoi ce résultat ? L’appel à la target “Update” met bien à jour le contexte global avec la valeur “Dynamic” mais lors de la fin de l’exécution de la target “Warning”, le contexte global est mis à jour avec le contexte de “Warning” qui n’a pas connaissance la mise à jour du contexte global et écrase donc les modifications faites par les targets appelées en interne.

On pourrait croire que cela s’arrête là mais il y a encore un point à voir via l’exemple suivant :

<Target Name= »Test3″>
<MSBuild Projects= »$(MSBuildProjectFile) » Targets= »Update » Properties= »a=a » />
</Target>

Si l’on appel le script avec les targets “Test3” et “Print” on pourrait s’attendre à avoir la valeur “Dynamic” mais voilà le résultat :

image

Pourquoi la valeur de “MyProp” n’est pas mise à jour ? Tout simplement car le moteur de MSBuild crée en fait une instance de la classe Project pour chaque couple unique (<nom du fichier>, <dictionnaire des propriétés passées en paramètre>). Dans le cas ci-dessus, lors de l’utilisation de la tâche MSBuild, nous passons en paramètre la propriété “a=a”, c’est donc le contexte d’une autre instance de la classe Project qui est mise à jour. Cela peut ce confirmer avec le dernier exemple :

<Target Name= »Test4″>
<MSBuild Projects= »$(MSBuildProjectFile) » Targets= »Update » Properties= »a=a » />
<MSBuild Projects= »$(MSBuildProjectFile) » Targets= »Print » Properties= »a=a » />
</Target>

Si l’on appel la target “Test4” le résultat obtenu est le suivant :

image

Les deux appel à MSBuild étant avec le même fichier et les mêmes propriétés, ils partagent la même instance de la classe Project et donc le même contexte.

Pour résumer voilà ce que j’ai déduit du comportement de MSBuild sur la portée des variables :

  • MSBuild crée une instance de la classe Project par couple unique (<nom du fichier>, <dictionnaire des propriétés passées en paramètre>).
  • MSBuild associe à chaque instance de Project un contexte global contenant les valeurs des variables.
  • Lorsqu’une target est exécuté pour une instance de Project, un contexte local est créé à partir d’une copie du contexte global de cette instance et la target utilisera ce contexte local pour lire et mettre à jour les valeurs des variables.
  • A la fin de l’exécution de la target, les modifications du contexte local sont reportées sur le contexte global de l’instance de Project en cours.

Carpe Diem.

Leave a Reply

  

  

  

CAPTCHA *