IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Build Java Discussion :

[EasyAnt][Manifest] génération d'un jar éxecutable


Sujet :

Build Java

  1. #1
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Février 2010
    Messages
    32
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2010
    Messages : 32
    Points : 31
    Points
    31
    Par défaut [EasyAnt][Manifest] génération d'un jar éxecutable
    Bonjour,

    Je rencontre une difficulté dans l'utilisation de EasyAnt. Mon programme se compile correctement, mais la ou je rencontre des difficultés c'est que mon jar n'est pas exécutable.

    J'ai bien trouvé un plugin manifest ici, mais il n'y aucun exemple d'utilisation du plugin.

    Voila j'ai rajouté ceci dans le fichier module.ivy

    <ea:plugin organisation="org.apache.easyant.plugins" module="manifest" revision="0.1" as="run" />
    Mais ou mettre les paramètres(manifest.main.classname, manifest.classpath)?

    Merci d'avance pour votre aide.

  2. #2
    Futur Membre du Club
    Inscrit en
    Mars 2010
    Messages
    5
    Détails du profil
    Informations forums :
    Inscription : Mars 2010
    Messages : 5
    Points : 6
    Points
    6
    Par défaut
    En fait il existe deux moyens de le faire :
    1. Lancer le programme depuis EasyAnt
    2. Rendre l'application exécutable en dehors d'EasyAnt, en modifiant le MANIFEST.MF

    La première configure EasyAnt pour lancer l'application lors de l'appel d'une target spécifique (:run)
    La seconde permet de rendre l'application exécutable en "double cliquant" sur le jar.

    Les deux solutions ne sont pas incompatible.

    Lancer une main class depuis easyant
    Pour lancer une mainclass depuis easyant il est possible d'utiliser le plugin "run-java".

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    <ea:plugin organisation="org.apache.easyant.plugins" module="run-java" revision="0.1" as="run" />
    Le plugin run-java est un plugin fournis et écrit par easyant, il possède donc la même organisation que les autres plugins. En effet tout les plugins easyant utilise l'organisation "org.apache.easyant.plugins". Cette information est optionnel, nous aurions donc pu laissez easyant utiliser la valeur par défaut et écrire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    <ea:plugin module="manifest" revision="0.1" as="run" />
    La documentation du plugin nous précise qu'il existe qu'une target (:run) qui n'est attaché à aucune phase.
    Je reconnait que l'information ne saute pas au yeux. Nous sommes cependant preneur de toutes idées pour rendre l'information plus accessible

    Il faut donc la lancer explicitement a la ligne de commande
    En éxécutant la commande, easyant nous préviens que nous avons oublié de renseigner un paramètre du plugin (une property).
    Missing value for property run.main.classname
    Même si le nom peut paraitre explicite, il est toujours utile de se reporté a la documentation pour savoir a quoi correspond cette property, si elle est obligatoire, si elle à une valeur par défaut, etc..

    Donc il faut toujours avoir la documentation d'ouverte ?
    Pas forcément, il y'a un petit système d'aide en ligne qui peut s'avérer utile.
    Pourquoi pas ne pas demander a easyant à quoi sert cette property :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    easyant -describe run.main.classname
    Project Manual
    --------------

    --- Available references for: run.main.classname in current project: standard-java-app ---
    No Phase found for name: run.main.classname
    No Target found for name: run.main.classname
    Property: run.main.classname
    Description: name of the main class to run
    Default: NONE
    Required: true
    --- End of (Describe) ---
    Ici, EasyAnt nous indique que l'argument fournis, n'est ni une phase, ni une target. C'est une property. Il nous explique que cette property est obligatoire, et nous donne une description.
    Ceci fonctionne aussi avec les targets / phase (cf ManCommand).

    Que faire une fois qu'on connait les paramètres à utiliser ?
    Il suffit de renseigner le paramètre :


    Rendre l'application exécutable en dehors d'EasyAnt, en modifiant le MANIFEST.MF
    Comme tu l'as souligné, il est possible d'utiliser le plugin manifest.
    Il contient en effet des paramètres optionnel permettant de spécifier la mainclass :
    • manifest.main.classname : la main class
    • manifest.classpath : le classpath utiliser pour exécuter l'application


    La property "manifest.classpath" est automatiquement rempli avec les dépendances présente dans le projet. Cependant il est primordial de forcer easyant à récupérer les dépendances dans un répertoire local dans le projet (j'expliquerait un peu plus bas pourquoi).
    Pour cela il faut placer la property "retrieve.dependencies" à true.

    Cette fonctionnalité à récemment été introduite , néanmoins je constate (hormis le détail de présentation) qu'il y a un sérieux manque de documentation sur le fonctionnement interne de cette target.

    Comment l'utiliser ?
    Il n'est pas nécessaire de le déclarer en tant que plugin car il fait partie des plugins inclue par la plus part des buildtypes.
    J'imagine que dans ton projet tu utilise le build type "build-std-java".
    Donc si on regarde la documentation , on constate qu'il s'appuie bien sur le plugin manifest.

    Il suffit de renseigner les paramètres :


    La documentation nous dis (encore une foi pas très explicitement) que le plugin contient une target (nommé :manifest-runnable) qui ajoute les attribue nécessaire pour rendre l'application exécutable.
    Cette target fait partie intégrante du cycle de vie du projet puisqu'elle est rajouter à la phase "prepare-package".
    Ainsi lorsque tu lancera
    ou n'importe quel autre target qui dépend de "package", le jar généré contiendra les informations nécessaire pour rendre l'application exécutable dans le fichier MANIFEST.MF.
    L'application sera donc exécutable sans utiliser EasyAnt.

    En attendant de compléter la documentation (dans la prochaine version) voici l'explication :

    Pour rendre l'application exécutable, dans certain cas il est nécessaire de référencer les dépendances dans le manifest.
    Ainsi si ton projet à une petite dizaine de dépendances tu devrais les référencer dans la property "manifest.classpath".
    Pour éviter cette opération fastidieuse, EasyAnt fournis un moyen de remplir cette property automatiquement en se basant sur les dépendances présente dans le module.ivy.

    A quoi peut donc servir la property "retrieve.dependencies"?
    Par défaut EasyAnt télécharge les dépendances et les places dans un cache (le cache par défaut se trouve dans $USER_HOME/.project/cache/).
    Mais il faut savoir que le référencement du classpath dans le MANIFEST.MF est relatif à la position du jar.
    Ainsi si ton projet dépend de foobar.jar la variable Class-Path dans le manifest devrait pointer dessus.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Class-Path: /path/to/foobar.jar
    Et la c'est le drame ! Comment distribuer l'application et toutes ces dépendances ?
    En effet, si on dois distribuer une application on aimerait que les dépendances soit centralisé (et pas cacher dans le cache d'easyant).
    Heuresement, EasyAnt nous empêche d'utiliser cette fonctionnalité si la property "retrieve.dependencies" n'est pas égale à "true".

    Cette property force easyant à récupéré les dépendances dans un sous répertoire du projet (par défaut dans le répertoire lib/main).

    Ainsi nos références dans le fichier manifest devrais être relative à l'arborescence du projet et donc ressembler à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Class-Path: lib/main/foobar.jar
    .

    N'hésite pas à écrire sur la mailing list si tu as d'autres questions, ou simplement des idées pour simplifier l'utilisation d'EasyAnt.

  3. #3
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Février 2010
    Messages
    32
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2010
    Messages : 32
    Points : 31
    Points
    31
    Par défaut
    Bonjour et merci de ta réponse très complète.

    Néanmoins j'ai quand même encore quelques difficultés. Tout d'abord voici mon fichier module.ivy :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    <ivy-module version="2.0" xmlns:ea="http://www.easyant.org"> 
     
    	<info organisation="org.apache.easyant" module="Basique4EasyAnt" revision="0.1" status="integration" >
    		<description>
    			this project is an example of a java standard application
    		</description>
    		<ea:build organisation="org.apache.easyant.buildtypes" module="build-std-java" revision="0.2">
    			<ea:property name="run.main.classname" value="org.gperlade.Jeu"/>
    			<ea:plugin organisation="org.apache.easyant.plugins" module="run-java" revision="0.1" as="run" />
    			<ea:property name="manifest.main.classname" value="org.gperlade.Jeu"/>
    			<ea:property name="retrieve.dependencies" value="true"/>
           	</ea:build>
     
    		<configurations>
    			<conf name="default" visibility="public"/>
    			<conf name="test" visibility="private"/>
    		</configurations>
     
    		<publications>
    			<artifact type="jar"/>
    		</publications>
     
    		</info>
     
    </ivy-module>
    Comme tu peut le voir, j'ai bien déclaré la property run.main.classname, ainsi que les 2 properties que tu ma conseillé de mettre pour rendre le jar exécutable, à savoir manifest.main.classname et retrieve.dependencies.

    Lorsque je lance la commande easyant run:run, mon application est correctement lancé. Mais la commande easyant package me retourne une erreur. Voici la fin du résultat de l'exécution :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    D:\Documents and Settings\gPerlade\.easyant\easyant-cache\org.apache.easyant.plu
    gins\manifest\ants\manifest-0.1.ant:78: V:\Stage\Dev\Basique4EasyAnt\lib\main do
    es not exist.
     
    Total time: 1 second
    D:\Documents and Settings\gPerlade\.easyant\easyant-cache\org.apache.easyant.plu
    gins\manifest\ants\manifest-0.1.ant:78: V:\Stage\Dev\Basique4EasyAnt\lib\main do
    es not exist.
    D:\Documents and Settings\gPerlade\.easyant\easyant-cache\org.apache.easyant.plu
    gins\manifest\ants\manifest-0.1.ant:78: V:\Stage\Dev\Basique4EasyAnt\lib\main do
    es not exist.
            at org.apache.tools.ant.types.AbstractFileSet.getDirectoryScanner(Abstra
    ctFileSet.java:474)
            at org.apache.tools.ant.types.FileSet.iterator(FileSet.java:69)
            at org.apache.tools.ant.types.resources.Union.getCollection(Union.java:1
    23)
            at org.apache.tools.ant.types.resources.Union.getCollection(Union.java:1
    07)
            at org.apache.tools.ant.types.resources.BaseResourceCollectionContainer.
    cacheCollection(BaseResourceCollectionContainer.java:265)
            at org.apache.tools.ant.types.resources.BaseResourceCollectionContainer.
    iterator(BaseResourceCollectionContainer.java:142)
            at org.apache.tools.ant.types.Path.iterator(Path.java:704)
            at org.apache.tools.ant.types.resources.Union.getCollection(Union.java:1
    23)
            at org.apache.tools.ant.types.resources.Union.list(Union.java:86)
            at org.apache.tools.ant.types.Path.list(Path.java:372)
            at org.apache.tools.ant.types.Path.list(Path.java:370)
            at org.apache.tools.ant.taskdefs.ManifestClassPath.execute(ManifestClass
    Path.java:79)
            at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:291)
            at sun.reflect.GeneratedMethodAccessor7.invoke(Unknown Source)
            at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAcces
    sorImpl.java:25)
            at java.lang.reflect.Method.invoke(Method.java:597)
            at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.jav
    a:106)
            at org.apache.tools.ant.Task.perform(Task.java:348)
            at org.apache.tools.ant.Target.execute(Target.java:390)
            at org.apache.tools.ant.Target.performTasks(Target.java:411)
            at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1360)
            at org.apache.tools.ant.Project.executeTarget(Project.java:1329)
            at org.apache.tools.ant.helper.DefaultExecutor.executeTargets(DefaultExe
    cutor.java:41)
            at org.apache.tools.ant.Project.executeTargets(Project.java:1212)
            at org.apache.easyant.core.EasyAntEngine.doBuild(EasyAntEngine.java:555)
     
            at org.apache.easyant.core.EasyAntEngine.runBuild(EasyAntEngine.java:610
    )
            at org.apache.easyant.core.EasyAntMain.runBuild(EasyAntMain.java:611)
            at org.apache.easyant.core.EasyAntMain.startAnt(EasyAntMain.java:169)
            at org.apache.tools.ant.launch.Launcher.run(Launcher.java:280)
            at org.apache.tools.ant.launch.Launcher.main(Launcher.java:109)

    Le message d'erreur me signale que le répertoire lib/main n'existe pas. J'ai donc créé le répertoire lib/main, sauf qu'il le supprime (le répertoire main, pas le répertoire lib)lui-même lorsque je relance easyant package.
    Comment créer un répertoire dont easyant a besoin sachant qu'il le détruit chaque fois que la commande est de nouveau lancé?

    J'ai donc essayé de rajouter une dépendance comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    		<dependencies>
    			<dependency org="junit" name="junit" rev="4.8.1" conf="test->default" />
    		</dependencies>
    Toujours la même erreur de répertoire. J'ai modifié ensuite l'attribut conf de la dépendances de conf="test->default" je suis passé à conf="default" .
    Cette fois ci la compilation à fonctionné, et le jar est exécutable.

    Il semblerai donc que Easyant requiert un minimum de 1 dépendance afin de rendre le jar exécutable. N'y aurait-il pas la une petite erreur qui empêcherai la génération d'un jar exécutable sans dépendances?

  4. #4
    Futur Membre du Club
    Inscrit en
    Mars 2010
    Messages
    5
    Détails du profil
    Informations forums :
    Inscription : Mars 2010
    Messages : 5
    Points : 6
    Points
    6
    Par défaut
    Il semblerai donc que Easyant requiert un minimum de 1 dépendance afin de rendre le jar exécutable. N'y aurait-il pas la une petite erreur qui empêcherai la génération d'un jar exécutable sans dépendances?
    C'est en effet un bug. Si tu t'enregistre sur le site d'easyant tu pourra saisir un nouveau bug.

    J'ai donc créé le répertoire lib/main, sauf qu'il le supprime (le répertoire main, pas le répertoire lib)lui-même lorsque je relance easyant package.
    Comment créer un répertoire dont easyant a besoin sachant qu'il le détruit chaque fois que la commande est de nouveau lancé?
    Lorsque tu utilise la property retrieve.dependencies, easyant créé à la volé les répertoires dont il a besoin et les synchronise.
    Je m'explique, en interne easyant s'appuie sur Apache Ivy pour résoudre les dépendances.
    Cette résolution de dépendances est faites par le plugin ivy provisionning.

    La documentation précise qu'il existe un parametre sync.lib.dir (initialisé a true par defaut) qui permet de dire si les repertoires lib (dans le cas ou on utilise retrieve.dependencies) doivent être synchronisé ou non.

    Pourquoi faut t'il synchronisé les répertoires ?
    Supposons que ton projet dépende d'une librairie X.
    Après quelques jours/mois, tu te rend compte que cette librairie ne t'es plus utile, donc tu la supprime de tes dépendances.
    Le fait de synchronisé, va permettre de nettoyer le répertoire lib automatiquement.
    Si il n'y as aucune dépendance, EasyAnt n'aura pas besoin du répertoire lib/main.

    Petite précision sur la notion de configuration dans les dépendances
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    <dependencies>
        <dependency org="junit" name="junit" rev="4.8.1" conf="test->default" />
    </dependencies>
    Comme expliqué quelques lignes plus haut, EasyAnt s'appuie sur Apache Ivy pour la gestion de dépendances. Le mécanisme de "conf" fait partie intégrante d'Ivy.
    Voici quelques informations pour bien comprendre son rôle.
    La notion de configuration dans ivy permet de qualifié le contexte d'utilisation d'une dépendance, et peut influencer le classpath.
    On peut la comparé à la notion de scope dans Maven, si ce n'est qu'elle offre une plus grande souplesse.
    Les principaux scopes dans maven sont :
    • compile : les dépendances nécessaire à la compilation (disponible dans tout les classpath)
    • runtime : les dépendances nécessaire lors de l'exécution de l'application
    • provided : permet de definir une dépendances qui doit être fournis par l'environement d'installation (Exemple: une application web JEE aura besoin de Servlet-api, cet dépendances est nécessaire à la compilation mais ne nécessite pas d'être inclue dans le war, car elle est présente dans tout les serveur d'application).
    • test : les dépendances utilisé pour les test (ne seront pas inclue dans le livrable : jar/war)

    Il existe deux autres scopes maven (import et system) un peu moins utilisé.

    Dans Ivy, les configurations ne sont pas figé, il est donc possible de créé ses propres configurations.
    Par exemple, il serait possible de qualifié ses dépendances en fonction de la plateforme d'exécution (Windows/Linux/Mac).
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    <configurations>
    	<conf name="windows" description="plateforme de destination : windows"/>
    	<conf name="linux" description=".."/>
    	<conf name="mac" description="..."/>
    </configurations>
    <dependencies>
    	<dependency org="myorg" name="windows-specific" rev="1.0" conf="windows"/>
    </dependencies>
    On pourrais imaginer aussi utilisé les configurations ivy pour faire différente version de l'application (Exemple: une version lite, une version avec la base de données, une version entreprise, une version gold etc...).

    Il est possible de définir un héritage entre plusieurs configuration.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    <configurations>
    	<conf name="default" description="default configuration"/>
    	<conf name="windows" description="plateforme de destination : windows" extends="default"/>
    	<conf name="linux" description=".." extends="default"/>
    	<conf name="mac" description="..." extends="default"/>
    </configurations>
    Il est interessant de regarder de plus pret la syntaxe utilisé dans l'attribue conf des dépendances.
    En effet quel est la différence sémentique entre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    <dependency org="junit" name="junit" rev="4.8.1" conf="default" />
    et
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    <dependency org="junit" name="junit" rev="4.8.1" conf="test->default" />


    Dans le premier cas, la dépendance est associé à la configuration default.
    Dans le second, la dépendances est associé à la configuration "test". On applique ensutie un filtre (un mapping) pour dire que ce qu'on appel "test" dans notre projet correspond au scope "default" du projet qu'on veut placer en dépendances.

    En général le scope "default" correspond à tout ce qui est necessaire à l'éxécution (donc toutes les dépendances de compile + runtime).

    Comme tu peut le voir le mécanisme est très puissant !
    En fait il est beaucoup plus flexible que ça, mais j'ai tenter de rebondir sur ton exemple.
    Si tu veux d'avantage de details sur le mapping je t'invite à regarder cette page de la documentation ivy.

  5. #5
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Février 2010
    Messages
    32
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2010
    Messages : 32
    Points : 31
    Points
    31
    Par défaut
    Merci de cette réponse, et des informations que vous m'avez fournit sur la configuration des dépendances.

    J'ai donc créé le ticket sur le site de EasyAnt pour rapporter ce bug.

  6. #6
    Futur Membre du Club
    Inscrit en
    Mars 2010
    Messages
    5
    Détails du profil
    Informations forums :
    Inscription : Mars 2010
    Messages : 5
    Points : 6
    Points
    6
    Par défaut
    La correction est disponible sur la version nightly build.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Génération d'un jar : Probleme de classpath ?
    Par boby62423 dans le forum Langage
    Réponses: 4
    Dernier message: 13/05/2009, 13h04
  2. D'ou vient le génération d'un jar
    Par Space23 dans le forum Maven
    Réponses: 6
    Dernier message: 06/04/2009, 12h00
  3. [EAR] [xdoclet] génération du ejb-jar.xml
    Par polo54 dans le forum Java EE
    Réponses: 5
    Dernier message: 15/01/2008, 15h17
  4. Génération de fichier jar
    Par meridien dans le forum Langage
    Réponses: 10
    Dernier message: 19/09/2007, 00h44
  5. Netbeans et manifest.mf pour un jar
    Par fraaalesi dans le forum NetBeans
    Réponses: 8
    Dernier message: 04/01/2007, 15h37

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo