6
votes

Comment réutiliser des sources compilées dans différentes machines

Pour accélérer notre processus de développement, nous divisons les tests et exécutons chaque partie sur plusieurs agents en parallèle. Cependant, la compilation des sources de test semble prendre la plupart du temps pour les étapes de test.

Pour éviter cela, nous pré-compilons les tests en utilisant sbt test: compile et construisons une image docker avec des cibles compilées .

Plus tard, cette image est utilisée dans chaque agent pour exécuter les tests. Cependant, il semble recompiler les tests et les sources de l'application même si les classes compilées existent.

Existe-t-il un moyen de faire en sorte que sbt utilise les cibles compilées existantes?

Mise à jour: Vers donner plus de contexte

La question se rapporte strictement à scala et sbt (d'où la balise sbt).

Notre processus CI est divisé en plusieurs phases. C'est à peu près quelque chose comme ça.

  • étape 1: Utilisez SBT pour compiler le projet Scala en bitecode java en utilisant sbt compile Nous compilons les sources de test dans le même test en utilisant sbt test: compile Le les targes sont regroupées dans une image docker et transférées vers le référentiel distant,

  • étape 2: Nous utilisons plusieurs agents pour fractionner et exécuter des tests en parallèle. Les tests s'exécutent à partir de l'image docker intégrée, l'environnement est donc le même. Cependant, l'exécution du test sbt entraîne la recompilation du projet même via le bitecode compilé existe.

Pour clarifier cela, je veux essentiellement compiler sur une machine et exécuter les sources de test compilées sur une autre sans recompiler

Mettre à jour p >

Je ne pense pas que https://stackoverflow.com/a/37440714/8261 est le même problème car contrairement à lui, je ne monte pas de volumes ou ne construit pas sur la machine hôte. Tout est compilé et exécuté dans docker, mais en deux étapes de construction. Les heures de modification du fichier et les chemins sont conservés de la même manière à cause de cela.

La sortie de débogage a quelque chose comme ceci

Initial source changes: 
    removed:Set()
    added: Set()
    modified: Set()
Invalidated products: Set(/app/target/scala-2.12/classes/Class1.class, /app/target/scala-2.12/classes/graph/Class2.class, ...)
External API changes: API Changes: Set()
Modified binary dependencies: Set()
Initial directly invalidated classes: Set()

Sources indirectly invalidated by:
    product: Set(/app/Class4.scala, /app/Class5.scala, ...)
    binary dep: Set()
    external source: Set()
All initially invalidated classes: Set()
All initially invalidated sources:Set(/app/Class4.scala, /app/Class5.scala, ...)
Recompiling all 304 sources: invalidated sources (266) exceeded 50.0% of all sources
Compiling 302 Scala sources and 2 Java sources to /app/target/scala-2.12/classes ...

Elle n'a pas de changements de source initiale, mais les produits sont invalidés.

Mise à jour: projet minimal à reproduire

J'ai créé un projet sbt minimal pour reproduire le problème. https://github.com/pulasthibandara/sbt-docker-recomplile p >

Comme vous pouvez le voir, rien ne change entre les étapes de construction, à part l'exécution de la deuxième étape dans une nouvelle étape (nouveau conteneur).


4 commentaires

Pour que cela soit clair, je veux essentiellement compiler sur une machine et exécuter les sources de test compilées sur une autre sans recompiler.


Votre question ne contient pas certains détails qui fourniraient une réponse appropriée. Par exemple, pouvez-vous profiter d'un langage intermédiaire comme celui qui existe en Java ou C # pour fournir une certaine indépendance de plate-forme, ou compilez-vous en code natif? S'il s'agit de code natif, toutes les plates-formes exécutent-elles le même processeur?


Salut Robert, Notre processus CI est divisé en plusieurs phases. C'est à peu près quelque chose comme ça. - étape 1: Utiliser SBT pour compiler le projet Scala en bitecode java en utilisant sbt compile Nous compilons les sources de test dans le même test en utilisant sbt test: compile Les targes sont regroupées dans un image docker et poussée vers le référentiel distant, - étape 2: Nous utilisons plusieurs agents pour fractionner et exécuter des tests en parallèle. Les tests s'exécutent à partir de l'image docker intégrée, l'environnement est donc le même. Cependant, l'exécution du test sbt provoque la recompilation du projet même si le bitecode compilé existe.


Copie possible de Recompilations inutiles par SBT


3 Réponses :


1
votes

Utilisation de SBT:

Je pense qu'il y a déjà une réponse à cela ici: https://stackoverflow.com/a/37440714/8261

Il semble difficile de faire exactement ce qu'il faut. Bonne chance!

Éviter SBT:

Si l'approche ci-dessus est trop difficile (c'est-à-dire obtenir sbt test pour considérer que vos classes de test n'ont pas besoin de recompilation), vous pouvez à la place éviter d'utiliser sbt mais à la place, exécutez directement votre suite de tests en utilisant java .

Si vous pouvez demander à sbt d'enregistrer la commande java qu'il utilise pour exécuter votre suite de tests (par exemple en utilisant la journalisation du débogage), vous pouvez exécuter cette commande sur vos agents d'exécution de test directement, ce qui empêcherait complètement sbt de recompiler les choses.

(Vous devrez peut-être écrire la commande java dans un fichier de script, si le chemin de classe est trop long pour passer comme argument de ligne de commande dans votre shell. J'ai déjà dû le faire pour un grand projet.)

Ce serait une approche beaucoup plus pirate que celle ci-dessus, mais qui pourrait être plus rapide à mettre en œuvre.


5 commentaires

Je ne pense pas que stackoverflow.com/a/37440714/8261 soit le même problème car contrairement à lui, je ne le fais pas monter des volumes ou construire sur la machine hôte. Tout est compilé et exécuté dans docker, mais en deux étapes de construction. Les heures et les chemins modifiés du fichier sont conservés de la même manière pour cette raison.


Il est clair que quelque chose diffère entre l'étape de construction et l'étape de test, car SBT détecte une différence et recompile vos sources de test. Vous remarquerez que si vous exécutez " sbt test: compile " puis " sbt test " l'un après l'autre sur la même machine, il ne se recompile pas. La réponse liée montre comment déboguer l'algorithme de détection de recompilation des besoins de SBT, bien que sa configuration Docker soit un peu différente de la vôtre, oui.


J'ai créé un projet sbt minimal pour reproduire le problème. github.com/pulasthibandara/sbt-docker-recomplile Comme vous pouvez le voir, rien ne change entre les étapes de construction, autre que l'exécution de la deuxième étape dans une nouvelle étape (nouveau conteneur).


Merci pour le repro. Je pense que mon conseil est le même que précédemment: vous devrez soit 1) déboguer l'algo SBT "recompiler" selon la question liée (ce qui devrait être plus facile avec ce cas de repro isolé) ou 2) appeler votre cadre de test en dehors de SBT pour éviter ce problème.


Oui, vos conseils sur les horodatages ont certainement aidé. Merci! J'ajoute la solution comme réponse ici: stackoverflow.com/a/54138157/7050167



0
votes

Une solution possible pourrait être de définir votre propre tâche sbt sans dépendances ou d'essayer de changer la tâche de test. Par exemple, vous pouvez créer une tâche pour exécuter un exécuteur JUnit si tel était votre cadre de test. Pour définir une tâche, consultez this sur la mise en œuvre de tâches.

Vous pouvez même aller jusqu'à compiler l'envoi du code et exécuter les télécommandes à partir de la même tâche car c'est n'importe quel code scala que vous voulez. Extrait du manuel de référence sbt

Vous pouvez définir votre propre tâche ou planifier de redéfinir une tâche existante. Dans les deux cas, le même aspect; utilisez: = pour associer du code à la clé de tâche


0 commentaires

7
votes

Alors que https://stackoverflow.com/a/37440714/8261 indiquait la bonne direction, le problème sous-jacent et la solution était différente.

Problème

SBT semble tout recompiler quand il est exécuté à différentes étapes d'une construction de docker. En effet, docker compresse les images créées à chaque étape, ce qui supprime la partie en millisecondes de lastModifiedDate des sources.

SBT dépend de lastModifiedDate pour déterminer si les sources ont changé, et depuis sa différence (la partie en millisecondes) la compilation déclenche une recompilation complète.

Solution

J'ai résolu le problème en définissant la variable env SBT_OPTS dans le fichier docker comme

ENV SBT_OPTS="${SBT_OPTS} -Dsbt.io.jdktimestamps=true"

Le projet de test a été mis à jour avec cette solution de contournement.


0 commentaires