Partage
  • Partager sur Facebook
  • Partager sur Twitter

Visual Studio 2017 - pre-build step

annuler un build ?

    1 août 2020 à 6:06:36

    Bonjour ,

    J'aimerais avoir quelques infos concernant le build process de Visual Studio / MSBuild et les pre-build step.

    Pour vous mettre en contexte , mon but est de lancer un script au pre-build step de tout mes solutions qui deplacerais les fichier resultant de potentiellement exactement le meme build , et si c'est le cas , que le build ne se produise pas. C'est clair ?

    Voici un exemple de ce que je veux faire :
    builder A en x64 debug avec un projet dependant B et C en version 1.
    Je lance un prebuild step script qui verifie si Av1 est deja builder quelque part , si c'est le cas il le deplace et on annule le build.
    Sinon le build de B est lancé. Son pre-build step verifie si les .o / resultat de build sont deja quelque part, si c'est le cas il les deplace et annule le build.
    Idem pour C.

    Tout ca peux sembler étrange. Mon but n'est pas d'écourter le build d'une solution , mais d'ecourte les build lorsqu'on change de solution.
    Ainsi , si j'ai une branche git X ayant comme build Av1=>Bv1/Cv1 et une branche Y ayant comme build Av2=>Bv2/Cv2 ainsi qu'une branche Z ayant comme build Av3=>Bv1/Cv2 etc ..  , si je checkout les branche et build a repetition , je ne veut pas que les sous projets / projets se build on place , je veux reprendre celui qui a deja été builder. Un "congélateur" de version local si on veut ..

    Merci d'avance pour votre aide !

    -
    Edité par CrevetteMagique 1 août 2020 à 6:09:42

    • Partager sur Facebook
    • Partager sur Twitter
      1 août 2020 à 14:09:29

      Salut,

      Déjà, pourquoi voudrais tu faire tenir les builds de tes différents projets dans git?

      Que tu  fournisse une archive / un installateur avec l'ensemble des résultat finaux de la compilation de chacun des projets est une chose utile, mais les fichiers temporaires générés pendant la compilation (principalement les fichiers objets et autres en-têtes pré-compilées) n'ont absolument aucune raison de se retrouver sur  le serveur git, vu  qu'ils sont spécifiques à  un système bien particulier (le tien).

      Ensuite, pourquoi voudrais tu faire des "checkout à répétitions"?

      Car, comprenons nous bien: passer sur une branche de développement pour travailler sur cette branche est un comportement "cohérent" avec git.

      Mais, dans ce cas, il faudra bien que  les modifications que tu apporte à cette branche soient intégrée à l'exécutable, et donc, que la compilation soit effectuée.

      On peut même envisager de passer sur une branche (ou sur un tag) correspondant à une version publiée du projet.  Mais, de deux choses l'une:

      ou bien, tu veux récupérer l'exécutable correspondant à cette branche / ce tag, et il existe (avec tous les bugs et toutes les fonctionnalités qui n'existaient pas encore à l'époque), ou bien tu souhaites le compiler toi-même, potentiellement avec des réglages différents que  ceux  utilisés pour créer la version livrée.

      Et, dans ce deuxième cas, il faudra bien que tu passe par la compilation de l'ensemble des projets de ta solution avec les réglages que  tu as fait.

      Enfin, pourquoi voudrais tu, après avoir fait un checkout sur une version donnée, effectuer un nouveau  checkout ?

      Pour passer sur une autre version du projet? soit, mais tu  te retrouve exactement dans la situation que je viens d'exposer, et "tout est à recommencer".

      Pour refaire un checkout sur la même version? quel serait l'intérêt de le faire?

      Au final, dis toi que, quand tu clone ton projet, ce que tu devrais obtenir n'est -- a priori -- que le code source de ton projet / ta solution, et que tu et que tu n'as -- normalement -- pas d'autre choix que de lancer le processus complet de  compilation / génération, quelle que soit la branche sur laquelle tu travailles ;)

      Si tu as des versions "taguées" ou des branches qui correspondent  à l'état de ton projet lors de ses différentes "publications", et que tu ne veux pas "perdre ton temps" à "recompiler une version spécifiques", il te reste toujours la possibilité de récupérer l'archive / l'installateur correspondant à  la version en question, mais, dans ce cas, tu deviens un "simple utilisateur lambda" du programme, et tu ne sera sans doute pas en mesure de corriger un bug ou d'apporter une évolution "minime" au projet ;)

      Alors, je sais très bien que  certains projets prennent un temps bête à  la compilation.  Cependant, tu  dois te dire que c'est sans doute la moins mauvaise des solutions, car, récupérer le résultat de la compilation exécutée chez "quelqu'un d'autre" comme base de travail te fait courir le risque d'obtenir quelque chose qui ne soit pas fonctionnel à cause de réglages différents / d'une situation du système différente :p

      • Partager sur Facebook
      • Partager sur Twitter
      Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
        1 août 2020 à 17:15:30

        koala01 a écrit:

        Salut,

        Déjà, pourquoi voudrais tu faire tenir les builds de tes différents projets dans git?

        Que tu  fournisse une archive / un installateur avec l'ensemble des résultat finaux de la compilation de chacun des projets est une chose utile, mais les fichiers temporaires générés pendant la compilation (principalement les fichiers objets et autres en-têtes pré-compilées) n'ont absolument aucune raison de se retrouver sur  le serveur git, vu  qu'ils sont spécifiques à  un système bien particulier (le tien).

        Ensuite, pourquoi voudrais tu faire des "checkout à répétitions"?

        Car, comprenons nous bien: passer sur une branche de développement pour travailler sur cette branche est un comportement "cohérent" avec git.

        Mais, dans ce cas, il faudra bien que  les modifications que tu apporte à cette branche soient intégrée à l'exécutable, et donc, que la compilation soit effectuée.

        On peut même envisager de passer sur une branche (ou sur un tag) correspondant à une version publiée du projet.  Mais, de deux choses l'une:

        ou bien, tu veux récupérer l'exécutable correspondant à cette branche / ce tag, et il existe (avec tous les bugs et toutes les fonctionnalités qui n'existaient pas encore à l'époque), ou bien tu souhaites le compiler toi-même, potentiellement avec des réglages différents que  ceux  utilisés pour créer la version livrée.

        Et, dans ce deuxième cas, il faudra bien que tu passe par la compilation de l'ensemble des projets de ta solution avec les réglages que  tu as fait.

        Enfin, pourquoi voudrais tu, après avoir fait un checkout sur une version donnée, effectuer un nouveau  checkout ?

        Pour passer sur une autre version du projet? soit, mais tu  te retrouve exactement dans la situation que je viens d'exposer, et "tout est à recommencer".

        Pour refaire un checkout sur la même version? quel serait l'intérêt de le faire?

        Au final, dis toi que, quand tu clone ton projet, ce que tu devrais obtenir n'est -- a priori -- que le code source de ton projet / ta solution, et que tu et que tu n'as -- normalement -- pas d'autre choix que de lancer le processus complet de  compilation / génération, quelle que soit la branche sur laquelle tu travailles ;)

        Si tu as des versions "taguées" ou des branches qui correspondent  à l'état de ton projet lors de ses différentes "publications", et que tu ne veux pas "perdre ton temps" à "recompiler une version spécifiques", il te reste toujours la possibilité de récupérer l'archive / l'installateur correspondant à  la version en question, mais, dans ce cas, tu deviens un "simple utilisateur lambda" du programme, et tu ne sera sans doute pas en mesure de corriger un bug ou d'apporter une évolution "minime" au projet ;)

        Alors, je sais très bien que  certains projets prennent un temps bête à  la compilation.  Cependant, tu  dois te dire que c'est sans doute la moins mauvaise des solutions, car, récupérer le résultat de la compilation exécutée chez "quelqu'un d'autre" comme base de travail te fait courir le risque d'obtenir quelque chose qui ne soit pas fonctionnel à cause de réglages différents / d'une situation du système différente :p


        Bonjour, deja je ne veux pas tenir mes different build dans git , mais dans un dossier local.
        Mon but n'est pas d'effectuer ces operations lors d'un checkout , mais lors d'un build.
        Par exemple , si dans la journée j'ai builder un projet C qui contient une dependance vers le projet Bv1 , les build de Bv1 et C sont zipper dans un dossier en local.
        Ensuite , si plus tard, je build un projet A qui contient une dependance vers le projet Bv1 , comme il a deja ete builder , lors de la compilation de A , je me sert de ce qui a deja ete compiler dans Bv1 pour ecourter le build de A ( sinon A recompile Bv1 et C recompile Bv1 ). Nous travaillons sur une solution ayant plusieurs sous projets.

        Nous avons par exemple cette forme de projet :
        Av1 -> Bv1 , Cv1 , Dv1, Ev1 , Fv1 etc ...
        Zv1 -> Bv1 , Cv1 , Dv2, Ev2, Fv2 etc ...

        Ainsi si dans ma journée je travail sur Av1 et Zv1 , je veut eviter de recompiler Bv1 et Cv1 entre eux , et eviter de recompiler les autre si je reswitch entre les projet.

        C'est plus clair ?

        Donc is je fais un checkout de Av1 et je build -> on compile tout.
        2h plus tard dans la journée , je switch pour travailler sur Zv1 -> on recompile Bv1 et Cv1 s'ils ont des changements , sinon on utilise ce qui a été builder par Av1
        On reviens sur Av1 ( on a recu la reponse qu'on attendais par exemple ) -> on recompile Av1. Imaginons qu'en compilant Zv1 nous avons effectuer des changements dans Bv1 et Cv1 -> en recompilant Av1 , comme les pre-build de Bv1 et Cv1 ont changer , on a pas a le rebuilder , on utilise ce qui a été builder par Zv1 ..

        En ce moment , si je travail sur 2 branches differente et que je veux comparer rapidement un resultat -> je checkout le premier , je recompile , tout compile. Je test , note le resultat. Je switch de branche , essentiellement y'a que le projet parent qui a changer et pas les sous module. Comme j'ai changé de branche , en lancant la compilation , tout sera rebuilder. Je veut eviter ca : si un sous module pointe vers une version qui a deja ete builder , je veut la reutiliser entre les builds des projets parent ..

        De plus , c'est pour utiliser en local seulement. Ultimement ca pourrais etre utilise sur notre pipeline de build farm ( nous recuperons deja un 'resultat de build' fait par un autre systeme ici ) afin d'eviter les recompilations entre les differents projets : ainsi si nos hook de monitoring jenkins builds souvent dans la journée , les projets utilisant des sous modules exactement a la meme version sans changement, ne recompilera pas ces sous module. Ainsi , le premier build de la journée prendra plus de temps , puis les autres seront ecourtés selon la version de leur sous module. aux lieu de tous les recompiler ..
        • Partager sur Facebook
        • Partager sur Twitter
          1 août 2020 à 18:27:40

          Bon, de toute évidence, tu n'as pas vraiment compris que ce problème est normalement géré par ton système de build...

          En effet, mettons que j'ai deux projets (pour faire simple):

          • un projet A qui contient les fichiers sources a, b et c
          • un projet B qui contient les source d, e et f

          Mettons aussi que le projet A dépende des fonctionnalités fournies par le projet B.

          Et mettons enfin que je vienne de récupérer les sources des deux projets pour pouvoir compiler A .

          La première fois que je vais compiler A, le système de build va vérifier s'il existe des objets pour les fichier a, b et c et, comme A dépend de B, s'il existe des fichiers objets pour les fichier d, e et f.

          Comme je viens tout juste de récupérer les sources des deux projets, il semble bien clair qu'aucun de ces fichiers objet n'existe.  Le système de build va donc compiler les fichiers a, b, c, d, e et f et générer les fichiers objets correspondants.

          Par la suite, si je relance une compilation, ce qui  va se passer c'est que le système de build va --  effectivement -- trouver les fichiers objets pour a, b, c, d, e et f.

          Il va donc comparer le "timestamp" (faisons simple: la date et l'heure) des fichiers objets avec celui des fichiers sources et il ne va re générer un fichier objet pour l'un des fichiers sources que si le timestamp du fichier objet est plus vieux que celui du fichier source correspondant.

          En effet, si le timestamp du fichier source est plus récent que celui du fichier objet, cela indique très clairement que le fichier source a été modifié après que le fichier objet ait été généré, et donc, que le fichier objet doit être généré à nouveau pour prendre les dernières modifications en compte.

          Bien sur, si un fichier objet doit être généré à nouveau, la "nouvelle version" du fichier objet deviendra "plus récente" que la version de la bibliothèque (le fichier .lib / .a / dll) qui se trouve sur le système, et la bibliothèque devra -- elle aussi -- être générée à nouveau.

          Et, enfin, la bibliothèque fournissant les fonctionnalités de B venant d'être générée à nouveau  étant plus récente que la version de l'application A qui est présente, le système de build décidera d'effectuer à nouveau l'édition de liens, en utilisant tous les fichiers objets présents (sans les générer à nouveau) pour intégrer les modifications apportées à la bibliothèque au travers des modifications apportées, par exemple, au fichier d.

          Tu ne pourras pas empêcher ce "jeu  de domino" qui veut que:

          • si un fichier source a été modifié par rapport au fichier objet, il faut générer à nouveau le fichier objet
          • si un fichier objet a été généré après la bibliothèque, il faut  générer à nouveau la bibliothèque
          • si une bibliothèque a été générée après l'application, il faut générer à nouveau l'application (bon, ce n'est pas tout à fait vrai avec les bibliothèques dynamiques, mais passons)

          Par contre, si la bibliothèque a été générée dans le cadre du développement d'un autre projet (appelons le Z), après avoir modifier un des fichiers sources (mettons le fichier e), le système de build est parfaitement en mesure de se rendre compte qu'il faut -- bel et bien -- générer à nouveau l'application (effectuer une édition des liens) mais qu'il n'est pas nécessaire de générer les fichiers objets pour les fichiers sources d,e et f.

          Après, on peut -- effectivement -- configurer le système de build pour qu'il parte systématiquement d'une solution vide, dans laquelle l'ensemble des fichiers objets et des bibliothèques supprimé(e)s pour forcer l'ensemble des fichiers objets à être générés à  nouveau.

          Si tel est le cas chez toi, la solution ne passe, très certainement pas par une étape de pré build, car cela ne servira à  rien :P  Dans ce genre de situation, il faut corriger la configuration de ton système de build ;)

          Enfin, il se peut que  les différents projets soient, tout simplement, "mal organisés" et que le système de build ait du mal à retrouver les fichiers générés lors de la compilation d'un projet ou de l'autre.

          Mais, dans ce cas, la seule solution valable passe par une réorganisation des projets et, le cas échéant, par la correction de la configuration du système de build, et elle ne passe surement pas non plus par la mise en place d'une étape de pré build ;)

          • Partager sur Facebook
          • Partager sur Twitter
          Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
            2 août 2020 à 4:57:45

            Salut Koala et merci pour cette (koala style) réponse :D

            Je comprend bien ce que tu me dis , cependant je crois que ton exemple n'illustre toujours pas mon problème je vais donc donner un autre exemple qui pourrait etre plus parlant.

            J'ai un projet qui s'appelle "CLI_calculatrice" v1.
            J'ai un projet qui s'appelle "Interface_QT" qui depend du projet "CLI_calculatrice" v1
            J'ai un projet qui s'appelle "Interface_GTK" qui depend du projet "CLI_calculatrice" v1

            Si je clone "Interface_QT" dans un repo , je build -> CLI_calculatrice sera builder en premier , puis Interface_QT.
            Si je clone "Interface_GTK" dans un repo , je build -> CLI_calculatrice sera builder ( puisque dans une solution/workspace différent, c'est cette partie que je souhaite écourter ) puis Interface_GTK.

            Ce que je veut :
            1 - Lors du build de Interface_QT -> je verifie si CLI v1 est présent dans FREEZER_PATH sinon je "freeze" la v1 de CLI_interface dans FREEZER_PATH
            Lors du build de Interface_GTK, -> je verifie si CLI v1 est présent dans FREEZER_PATH et comme Interface_QT l'a deja builder , je veut reprendre ce resultat plutot que de rebuilder CLI v1.

            C'est peut-etre moi qui a mal compris , mais ton exemple de pipeline de build ne se passe que dans le contexte ou tu reste dans le meme workspace/solution. Mon but est de reproduire ce comportement de "ne rebuild pas les source si les .o sont deja présent".Par exemple lorsqu'on rebuild une solution alors que ses dependances n'ont pas de changement , les dependances ne sont pas rebuilder alors le compilation time est écourté. Je veut appliquer ce comportement a travers les différent workpsace ( toujours sur la meme machine , les builds ne sont pas partager ) et solution qui partagent des dependances.

            Je sais que c'est faisable puisque j'ai deja travailler sur un systeme de freezer de version comme celui la, cependant dans ce pemier projet on utilisait nos propres makesfiles / dans un docker / avec gcc / en appelant des script de build en python custom. Inserer le freezer dans ce pipeline etait plutot trivial.

            Cette fois je tente de faire la meme chose , sauf qu'on utilise maintenant exclusivment Visual Studio / MSBuild ( qui build a partir du .sln ). Je cherche donc le moyen d'inserer ce principe de freezer dans le pipeline de build de MSBuild.

            J'ai cru qu'un pre-build step qui copierais les .o correspondant a la bonne endroit , court circuiterais les build step justement au moment de la verification ordinaire qui verifie si la dependance a deja été builder.

            Je crois que ma situation est plus clair maintenant :)

            -
            Edité par CrevetteMagique 2 août 2020 à 5:00:35

            • Partager sur Facebook
            • Partager sur Twitter
              2 août 2020 à 20:47:51

              CrevetteMagique a écrit:

              Cette fois je tente de faire la meme chose , sauf qu'on utilise maintenant exclusivment Visual Studio / MSBuild ( qui build a partir du .sln ). Je cherche donc le moyen d'inserer ce principe de freezer dans le pipeline de build de MSBuild.

              Voilà exactement ce qui se passe quand des incompétents décident de prendre des décisions ineptes sans réfléchir aux conséquences qu'elles vont entrainer: de gens dont les compétences mériteraient d'évoluer perdent plus de temps à  essayer de mettre une solution scabreuse à  un problème qui n'aurait jamais du se présenter qu'à faire évoluer leur projet :p :-°

              Car je le répète: même si je ne doute pas que la méthode qui consiste à geler l'état d'un dossier puisse fonctionner dans certaines circonstances, cela n'en reste pas moins qu'un pis aller destiné à  palier les faiblesses d'un système de build défaillant.

              Or, malgré tout le mal que je peux penser (ou non, va savoir :D ) des outils de chez microsoft, je peux t'assurer que MSBuild ne souffre pas de cette faiblesse particulière ;)

              Donc, commençons par le commencement :

              Retire  toi cette idée saugrenue de la tête de "geler" l'état des dossiers de tes différents projets.  Cela ne sert à rien, cela n'apportera rien.

              Une fois cette étape importante franchie, nous allons essayer de partir sur des bases communes, et donc, tenter d'être sur que nous travaillons avec une situation de départ sensiblement identique:

              Pour ma part, j'ai créé trois projets différents, dans des solutions différentes, dans une arborescence proche de

              projects 
                  |-> application_1
                  |-> application_2
                  |-> lib_1

              et j'ai -- bien sur -- ajouté lib_1 comme dépendance à application_1 et à  application_2, ce qui me permet de compiler les deux applications de manière séparée.

              Seulement, ainsi que  tu l'as si bien fait remarquer, si je viens à ouvrir application_1.sln pour la compiler et que, tout de suite après, j'ouvre application_2.sln pour la compiler également, les deux compilations vont à chaque fois ... se taper l'intégralité de la compilation de lib_1.


              Ce qui est bien, c'est que tu as très précisément compris la raison de cet état de fait: c'est parce que les deux projets sont dans des solutions différentes et que le système de build ne fait pas le lien entre les dépendances de application_1 et celles de application_2.

              La seule chose, c'est que tu te retrouves à essayer de palier ce fait par une solution tout à fait bancale, alors que tu as déjà exprimé (sans doute sans t'en rendre compte!) la solution à ton problème ;)

              Si les projets application_1.vcxproj et application_2.vcxproj ne font pas partie de la même solution, le plus simple ne serait-il pas -- tout simplement -- de créer une solution qui contiendrait ces deux projets???

              On va donc créer -- quitte  à ne pas la commiter sur le dépôt git -- une "hyper solution" qui regroupera tous ces projets qui fonctionnent si bien tous seuls, et, pour cela:

              • Dans Visual Studio, de choisir le menu "nouveau" ->"nouveau projet" et de choisir le modèle "projet vide"
              • Une fois le projet créé, il va suffire, pour chaque projet dont on a besoin, de cliquer droit sur la solution dans l'explorateur de solution et de choisir le menu "ajouter" -> "projet existant", puis d'aller chercher le fichier .vcxproj correspondant au projet dont on a besoin

              Une fois tous les projets ajoutés à  la solution, il faudra rappeler les dépendances pour chacun de ces projet.  Cela se fait  très simplement en cliquant droit sur le projet dans l'explorateur de solution et en choisissant l'entrée "dépendances de build" ->dépendances du projet

              Enfin, selon la manière dont les projets sont configurés (entre autre pour aller chercher les bibliothèques dont ils dépendent), il se peut qu'il faille ajouter la valeur %(projectDir)$(Configuration) à la ligne "editeur de liens"->"répertoires de bibliothèques supplémentaires" pour chacune des projet que tu veux pouvoir gérer ensemble.

              ET C'EST TOUT!!!!

              Si tu lance une re génération (ou un nettoyage suivi d'une génération) complète de ta solution, tu pourra remarquer que les différentes dépendances ne seront -- effectivement -- compilées qu'une seule fois, quel que soit le nombre de projets différents qui en ont besoin ;)

              • Partager sur Facebook
              • Partager sur Twitter
              Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait

              Visual Studio 2017 - pre-build step

              × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
              × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
              • Editeur
              • Markdown