<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>proguard Archives - fuga_digital</title>
	<atom:link href="https://fugadigital.com/tag/proguard/feed/" rel="self" type="application/rss+xml" />
	<link>https://fugadigital.com/tag/proguard/</link>
	<description>Naturalmente digital</description>
	<lastBuildDate>Sat, 08 Apr 2017 06:22:40 +0000</lastBuildDate>
	<language>es</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.3</generator>
	<item>
		<title>Distribuyendo una app de escritorio en Java</title>
		<link>https://fugadigital.com/distribuyendo-una-app-de-escritorio-en-java/</link>
					<comments>https://fugadigital.com/distribuyendo-una-app-de-escritorio-en-java/#respond</comments>
		
		<dc:creator><![CDATA[Arturo]]></dc:creator>
		<pubDate>Sat, 05 Apr 2014 14:33:09 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[ant]]></category>
		<category><![CDATA[app]]></category>
		<category><![CDATA[appbundler]]></category>
		<category><![CDATA[buid]]></category>
		<category><![CDATA[deploy]]></category>
		<category><![CDATA[desktop]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[launch4j]]></category>
		<category><![CDATA[multiplatform]]></category>
		<category><![CDATA[proguard]]></category>
		<guid isPermaLink="false">http://fugadigital.startupdigital.net/?p=9</guid>

					<description><![CDATA[<p>Java es un gran lenguaje, y como todos tiene ventajas y desventajas. Al momento de distribuir nuestra aplicación, se pueden realizar (y mejor aún, automatizar) algunas adecuaciones para ofrecer un paquete fácil de usar, pues una de las desventajas en Java es la falta de una integración adecuada para cada una de las plataformas soportadas. &#8230; </p>
<p class="link-more"><a href="https://fugadigital.com/distribuyendo-una-app-de-escritorio-en-java/" class="more-link">Continuar leyendo<span class="screen-reader-text"> "Distribuyendo una app de escritorio en Java"</span></a></p>
<p>La entrada <a href="https://fugadigital.com/distribuyendo-una-app-de-escritorio-en-java/">Distribuyendo una app de escritorio en Java</a> se publicó primero en <a href="https://fugadigital.com">fuga_digital</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Java es un gran lenguaje, y como todos tiene ventajas y desventajas. Al momento de distribuir nuestra aplicación, se pueden realizar (y mejor aún, automatizar) algunas adecuaciones para ofrecer un paquete fácil de usar, pues una de las desventajas en Java es la falta de una integración adecuada para cada una de las plataformas soportadas.</p>
<figure id="attachment_18" aria-describedby="caption-attachment-18" style="width: 300px" class="wp-caption aligncenter"><a href="https://fugadigital.com/wp-content/uploads/2014/04/brick-bicycle.jpg"><img fetchpriority="high" decoding="async" src="https://fugadigital.com/wp-content/uploads/2014/04/brick-bicycle-300x214.jpg" alt="Impresión de algunos al recibir un JAR y sus librerías" width="300" height="214" class="size-medium wp-image-18" srcset="https://fugadigital.com/wp-content/uploads/2014/04/brick-bicycle-300x214.jpg 300w, https://fugadigital.com/wp-content/uploads/2014/04/brick-bicycle.jpg 638w" sizes="(max-width: 300px) 100vw, 300px" /></a><figcaption id="caption-attachment-18" class="wp-caption-text">Impresión de algunos al recibir un JAR y sus librerías</figcaption></figure>
<p>Siguiendo esta guía vamos a generar un proceso en Ant que construirá paquetes para Windows, OS X, Linux y multiplataforma. Los paquetes específicos para plataforma tendrán un ejecutable nativo, en tanto que el multiplataforma llevará el JAR optimizado. Los tres paquetes incluirán también archivos varios necesarios para la distribución como licencias o instructivos.</p>
<p>El JAR de la aplicación usado para las distribuciones será optimizado en tamaño, desempeño y código usando <a href="http://proguard.sourceforge.net/">Proguard</a>, que es una excelente herramienta para pulir un JAR a distribuir. Para generar el ejecutable de Windows se usará la herramienta <a href="http://launch4j.sourceforge.net/">Launch4j</a>. Para la app de OS X se usará <a href="https://java.net/projects/appbundler">jarbundler</a>.</p>
<p><span id="more-9"></span></p>
<p>Primero hay que crear un directorio para almacenar las herramientas a usar, un buen lugar puede ser dentro de la raiz del proyecto pero puedes ponerla donde mejor te ajuste. <a href="http://sourceforge.net/projects/proguard/files/">Descarga Proguard</a> y coloca el JAR (proguard-*.**.jar) en tu directorio de herramientas de construcción. Ahora <a href="http://sourceforge.net/projects/launch4j/files/">Descarga Launch4j</a> y descomprime el paquete en el mismo lugar donde hayas colocado Proguard. Finalmente <a href="https://java.net/projects/appbundler/downloads">desarcarga appblundler</a> y coloca el jar en el mismo directorio que las herramientas anteriores.</p>
<p>Si usas Linux x64 y en caso de que quieras hacer el ejecutable de Windows, es muy probable que debas agregar soporte para procesar librerías de 32 bits (por el binario <em>windres</em> incluido en la distribución de Linux). Si usas una distro basada en Debian puedes habilitar el uso de paquetes de 32 bits con las siguientes instrucciones:</p>
<p></p><pre class="urvanov-syntax-highlighter-plain-tag">$ sudo dpkg --add-architecture i386
$ sudo aptitude update
$ sudo aptitude install ia32-libs</pre><p> </p>
<p><small>Notas: Esta guia la he implementado en #! 11 x64 y NetBeans 8 y posiblemente necesites hacer adaptaciones dependiendo de tu SO e IDE. Al ir pegando el código, revisa los valores de las variables o propiedades para ajustarlas a tus necesidades, algunos valores incluyen rutas que deberás actualizar.</small></p>
<p>Primero vamos a definir un nuevo archivo build que contendrá la automatización del proceso. Para esto necesitamos crear un nuevo archivo: <em>build-dist.xml</em>. Hazlo copiando el archivo <em>build.xml</em> en la misma ubicación (la raiz del proyecto).</p>
<p>Edita <em>build.xml</em> dejándolo con algo como:</p><pre class="urvanov-syntax-highlighter-plain-tag"><?xml version="1.0" encoding="UTF-8"?>

<project default="default" basedir=".">
    <description>Builds, tests, and runs the application.</description>
    <import file="nbproject/build-impl.xml"/>
    <import file="build-dist.xml" />

    <target name="-post-jar">
        <echo message="${line.separator}*** Run the Ant target 'dist-generate-pack' to generate ditribution packages${line.separator} " />
        <antcall target="dist-generate-pack"/>
    </target>
</project></pre><p>Nota la importación de <em>build-dist.xml</em> y el llamado <code>antcall</code> para controlar la ejecución del proceso.</p>
<p>Ahora edita <em>build-dist.xml</em> para dejarlo con la siguiente estructura inicial:</p>
<p></p><pre class="urvanov-syntax-highlighter-plain-tag"><?xml version="1.0" encoding="UTF-8"?>

<project default="default" basedir=".">
    <description>Generate distributable packages.</description>

    <!-- Import project properties -->
    <property file="nbproject/project.properties"/>
    <!-- Directory containing resources to add -->
    <property name="resources.dir" value="resources"/>
    <!-- Directory containing external tools -->
    <property name="build.ext.dir"     value="build-ext"/>
    <!-- Directory resources for  -->
    <property name="ext.resources.dir" value="${build.ext.dir}/resources"/>
    <property name="newline"           value="${line.separator}"/>

    <target name="dist-generate-pack">
        <property file="nbproject/project.properties"/>

        <echo message="${line.separator} - Distribution generation process will start...${line.separator}${line.separator}"/>

        <antcall target="dist-optimize-app"/>
        <!--<antcall target="dist-single-jar"/>-->
        <!--<antcall target="dist-wrap-win"/>-->
        <!--<antcall target="dist-wrap-osx"/>-->
        <!--<antcall target="dist-wrap-linux"/>-->
        <!--<antcall target="dist-pack-win"/>-->
        <!--<antcall target="dist-pack-osx"/>-->
        <!--<antcall target="dist-pack-linux"/>-->
        <!--<antcall target="dist-pack-multi"/>-->
        <!--<antcall target="dist-clean"/>-->
    </target>

</project></pre><p>Lo que haremos ahora será agregar cada bloque correspondiente a cada llamado <code>antcall</code>. Descomenta/comenta los llamados según vayas avanzando para hacer pruebas.</p>
<p>Lo primero será pulir nuestro JAR utilizando Proguard. Esta aplicación se encargará de remover código sin usar, optimizar a nivel bytecode nuestra aplicación y además obfuscar la estructura del fuente, todo esto nos viene genial.</p>
<p>Agrega el siguiente <code>target</code> a <em>build-dist.xml</em>:</p><pre class="urvanov-syntax-highlighter-plain-tag"><!-- Shrink, optimize and obfuscate application -->
    <target name="dist-optimize-app">
        <!-- Config -->
        <!-- JRE directory location -->
        <property name="ob.jre.dir"            value="/opt/java-oracle/jre"/>
        <!-- vm.jar for IBM's JVM, classes.jar for OS X -->
        <property name="ob.runtime.jar"        value="${ob.jre.dir}/lib/rt.jar"/>
        <!-- location of proguard relative to project root dir -->
        <property name="ob.proguard.jar"       value="${build.ext.dir}/proguard-4.11.jar"/>
        <!-- Javac classpath, defined in nbproject/project.properties -->
        <property name="ob.javac.cp"           value="${javac.classpath}"/>
        <!-- The jar app location, defined in nbproject/project.properties -->
        <property name="ob.input.jar"          value="${dist.jar}"/>
        <!-- New name (w/o ext) for input jar file -->
        <property name="ob.input.name"         value="${application.title}-orig"/>
        <!-- The resultant jar location -->
        <property name="ob.output.dir"         value="${dist.dir}"/>
        <!-- Name (w/o ext) for the resultant .jar & .map files  -->
        <property name="ob.output.name"        value="${application.title}"/>
        <!-- 5 optimization pases are enough -->
        <property name="ob.optimizations"      value="5"/>

        <!-- Routine -->
        <dirname property="ob.input.dir"       file="${ob.input.jar}"/>
        <property name="ob.input.mov.jar"      value="${ob.input.dir}/${ob.input.name}.jar"/>
        <move file="${ob.input.jar}"           tofile="${ob.input.mov.jar}"/>
        <property name="ob.output.jar"         value="${ob.output.dir}/${ob.output.name}.jar"/>
        <property name="ob.output.map"         value="${ob.output.dir}/${ob.output.name}.map"/>

        <echo message="${newline}** Shrinking, optimizing and obfuscating application..."/>
        <echo message="   Input jar:           ${ob.input.jar}"/>
        <echo message="   Input jar new name:  ${ob.input.mov.jar}"/>
        <echo message="   Output jar:          ${ob.output.jar}"/>
        <echo message="   Mapping file:        ${ob.output.map}"/>
        <mkdir dir="${ob.output.dir}"/>
        <taskdef resource="proguard/ant/task.properties" classpath="${ob.proguard.jar}"/>
        <proguard printmapping="${ob.output.map}"
                  overloadaggressively="true"
                  ignorewarnings="false"
                  optimize="true"
                  optimizationpasses="${ob.optimizations}"
                  obfuscate="true"
                  shrink="true"
                  verbose="false"
                  renamesourcefileattribute="SourceFile"
                  repackageclasses=""
                  printseeds="on">

            <libraryjar path="${ob.javac.cp}"/>
            <libraryjar file="${ob.runtime.jar}"/>
            <!-- for javax.crypto classes -->
            <libraryjar file="${ob.jre.dir}/lib/jce.jar"/>

            <injar  file="${ob.input.mov.jar}"/>
            <outjar file="${ob.output.jar}"/>


            <!-- ### Specific configuration ### -->
            <keep name="com.fugadigital.util.Money"/>
            <!-- I added some JDialog subclasses that I want to keep -->
            <keep access="public" extends="javax.swing.JDialog">
                <method access="public protected"/>
                <constructor access="public" parameters="java.awt.Frame"/>
            </keep>


            <!-- ### Standard application configuration ### -->
            <adaptclassstrings />
            <adaptresourcefilenames />

            <!-- Preserve all annotations -->
            <keepattribute name="*Annotation*"/>
            <keepattribute name="InnerClasses"/>
            <keepattribute name="Signature"/>
            <keepattribute name="Deprecated"/>
            <keepattribute name="EnclosingMethod"/>
            <keepattribute name="SourceFile"/>
            <keepattribute name="LineNumberTable"/>

            <keep implements="java.sql.Driver"/>
            <keep extends="ch.qos.logback.core.filter.Filter"/>

            <!-- Preserve main method -->
            <keep name="**${application.title}">
                <method access    ="public static"
                        type      ="void"
                        name      ="main"
                        parameters="java.lang.String[]"/>
            </keep>


            <keep extends="javax.swing.plaf.ComponentUI">
                <method access    ="public static"
                        type      ="javax.swing.plaf.ComponentUI"
                        name      ="createUI"
                        parameters="javax.swing.JComponent"/>
            </keep>

            <keep annotation="javax.persistence.*" type="class">
                <field />
                <method />
            </keep>

            <!-- Preserve all native method names and the names of their classes. -->
            <keepclasseswithmembernames>
                <method access="native"/>
            </keepclasseswithmembernames>

            <!-- Preserve the methods that are required in all enumeration classes. -->
            <keepclassmembers extends="java.lang.Enum">
                <method access="public static"
				type="**[]"
				name="values"
				parameters=""/>
                <method access="public static"
				type="**"
				name="valueOf"
				parameters="java.lang.String"/>
            </keepclassmembers>

            <!-- Explicitly preserve all serialization members. The Serializable
                 interface is only a marker interface. If code contains serializable
                 classes that have to be backward compatible, refer to the manual. -->
            <keepnames implements="java.io.Serializable"/>
            <keepclassmembers implements="java.io.Serializable">
                <field  access    ="static final"
                        type      ="long"
                        name      ="serialVersionUID"/>
                <field  access    ="static final"
                        type      ="java.io.ObjectStreamField[]"
                        name      ="serialPersistentFields"/>
                <method access    ="private"
                        type      ="void"
                        name      ="writeObject"
                        parameters="java.io.ObjectOutputStream"/>
                <method access    ="private"
                        type      ="void"
                        name      ="readObject"
                        parameters="java.io.ObjectInputStream"/>
                <method type      ="java.lang.Object"
                        name      ="writeReplace"
                        parameters=""/>
                <method type      ="java.lang.Object"
                        name      ="readResolve"
                        parameters=""/>
            </keepclassmembers>
        </proguard>
        <echo message="   DONE"/>
    </target></pre><p></p>
<p>Verifica que la configuración de las propiedades sea correcta y construye tu paquete. Después de compilar y con &#8216;algo de suerte&#8217; se generará un nuevo JAR con el nombre definido en la propiedad <code>ob.jar.name</code>, este JAR es la aplicación optimizada. Observa que he dejado en la sección de código configuraciones de ejemplo particulares a una aplicación. En caso de que tengas errores consulta la traza y agrega las configuraciones propias para tu aplicación. Consulta el <a href="http://proguard.sourceforge.net/#manual/usage.html">manual de uso</a> en el sitio de Proguard para esto.</p>
<p>El segundo paso es generar un solo JAR que contenga la aplicación procesada y las librerías. Apendiza el siguiente <code>target</code> en <em>build-dist.xml</em>:</p>
<p></p><pre class="urvanov-syntax-highlighter-plain-tag"><!--  Generate fat jar -->
    <target name="dist-single-jar">
        <!-- Config -->
        <!-- The library dir -->
        <property name="single.lib.dir"        value="${dist.dir}/lib"/>
        <!-- The application JAR -->
        <property name="single.input.jar"      value="${dist.jar}"/>
        <!-- New name (w/o ext) for input jar file -->
        <property name="single.input.name"     value="${application.title}-slim"/>
        <!-- The location of the directory for the unified JAR -->
        <property name="single.output.dir"     value="${dist.dir}"/>
        <!-- Name (w/o ext) for the resultant .jar file  -->
        <property name="single.output.name"    value="${application.title}"/>

        <!-- Routine -->
        <dirname property="single.input.dir"   file="${single.input.jar}"/>
        <property name="single.input.mov.jar"  value="${single.input.dir}/${single.input.name}.jar"/>
        <move file="${single.input.jar}"       tofile="${single.input.mov.jar}"/>
        <property name="single.output.jar"     value="${single.output.dir}/${single.output.name}.jar"/>

        <echo message="${newline}** Generating fat jar..."/>
        <echo message="   Input jar:           ${single.input.jar}"/>
        <echo message="   Input jar new name:  ${single.input.mov.jar}"/>
        <echo message="   lib dir:             ${single.lib.dir}"/>
        <echo message="   Output jar:          ${single.output.jar}"/>

        <jar jarfile="${single.output.jar}">
            <zipfileset src="${single.input.mov.jar}" excludes="META-INF/*"/>
            <zipgroupfileset dir="${single.lib.dir}" includes="*.jar" excludes="META-INF/*"/>
            <manifest>
                <attribute name="SplashScreen-Image" value="com/recipeman/resources/splashscreen.png"/>
                <attribute name="Main-Class" value="${main.class}"/>
            </manifest>
        </jar>
        <echo message="   DONE"/>
    </target></pre><p></p>
<p>Igualmente, verifica la configuración de las propiedades del <code>target</code> y compila. El proceso de construcción ahora generará un nuevo archivo que contendrá a nuestra aplicación y sus librerías en un solo .jar.</p>
<p>&nbsp;<br />
Desde este momento tenemos un JAR más facil de distribuir. La tarea ahora es ofrecer métodos para ejecutar nuestra aplicación de una manera más amigable, algo que conseguiremos creando lanzadores nativos para algunas plataformas.</p>
<p>Para Windows generaremos un .exe a través de Launch4j. Crea un archivo .xml dentro del directorio del Launch4j para establecer la configuración necesaria. El siguiente ejemplo generará un ejecutable que requirá una JVM 1.6.0+ instalada para funcionar, redireccionando a la página de descarga en español para instalar JAVA cuando no esté disponible, y con preferencia por una JVM de 64bits:</p>
<p></p><pre class="urvanov-syntax-highlighter-plain-tag"><?xml version="1.0" encoding="UTF-8"?>
<launch4jConfig>
  <dontWrapJar>false</dontWrapJar>
  <headerType>gui</headerType>
  <errTitle></errTitle>
  <cmdLine></cmdLine>
  <chdir></chdir>
  <priority>normal</priority>
  <downloadUrl>http://java.com/es/download</downloadUrl>
  <supportUrl></supportUrl>
  <stayAlive>false</stayAlive>
  <manifest></manifest>
  <icon>../resources/app_multi.ico</icon>
  <jre>
    <path></path>
    <bundledJre64Bit>false</bundledJre64Bit>
    <minVersion>1.6.0</minVersion>
    <maxVersion></maxVersion>
    <jdkPreference>preferJre</jdkPreference>
    <runtimeBits>64/32</runtimeBits>
  </jre>
</launch4jConfig></pre><p></p>
<p>Agrega el ícono que usarás para el .exe en el mismo directorio en que has puesto la configuración anterior. Consulta la <a href="http://launch4j.sourceforge.net/docs.html">documentación</a> para configuraciones más específicas.</p>
<p>Ahora, para ejecutar el proceso, agrega a <em>build-dist.xml</em> el siguiente <code>target</code>:</p>
<p></p><pre class="urvanov-syntax-highlighter-plain-tag"><!--  Generate the Windows wrapper -->
    <target name="dist-wrap-win">
        <!-- Config -->
        <!-- Jar to use -->
        <property name="wrap.input.jar"        value="${dist.jar}"/>
        <!-- The resultant files location -->
        <property name="wrap.output.dir"       value="${dist.dir}"/>
        <!-- -Name for the resultant executable file  -->
        <property name="wrap.output.exe"       value="${application.title}.exe"/>
        <!-- Application main class -->
        <property name="wrap.main.class"       value="${main.class}"/>
        <!-- Launch4j base directory -->
        <property name="wrap.launch4j.dir"     value="${build.ext.dir}/launch4j"/>

        <!-- Routine -->
        <echo message="${newline}** Generating wrapper..."/>
        <echo message="   Launch4j jar:        ${wrap.launch4j.dir}/launch4j.jar"/>
        <echo message="   Input jar:           ${wrap.input.jar}"/>
        <echo message="   Output file:         ${dist.dir}/${wrap.output.exe}"/>
        <taskdef name="launch4j"
                classname="net.sf.launch4j.ant.Launch4jTask"
                classpath="${wrap.launch4j.dir}/launch4j.jar
                        :${wrap.launch4j.dir}/lib/xstream.jar"/>

        <launch4j
                configfile="${wrap.launch4j.dir}/conf-nb.xml"
                jar="${wrap.input.jar}"
                outfile="${dist.dir}/${wrap.output.exe}"
        />
        <echo message="   DONE"/>
    </target></pre><p></p>
<p>&nbsp;<br />
Para generar la aplicación de OS X usaremos el siguiente target:</p>
<p></p><pre class="urvanov-syntax-highlighter-plain-tag"><!--  Generate the OS X wrapper -->
    <taskdef name="bundleapp"
             classname="com.oracle.appbundler.AppBundlerTask"   
             classpath="build-ext/appbundler-1.0.jar" />
    <target name="dist-wrap-osx">
        <!-- Config -->
        <!-- Jar to use -->
        <property name="wrap.input.jar"        value="${dist.jar}"/>
        <!-- The resultant files location -->
        <property name="wrap.output.dir"       value="${dist.dir}"/>
        <!-- Name for the resultant application directory  -->
        <property name="wrap.output.app"       value="${application.title}.app"/>
        <!-- Resources  -->
        <property name="wrap.icon.file"        value="GenericApp.icns"/>

        <!-- Routine -->
        <property name="app.dir" value="${dist.dir}/${wrap.output.app}"/>
        <echo message="${newline}** Generating wrapper..."/>
        <echo message="   Input jar:           ${wrap.input.jar}"/>
        <echo message="   Output file:         ${app.dir}"/>

        <bundleapp outputdirectory="dist"
            name="${application.title}"
            displayname="${application.title}"
            identifier="components.${application.title}"
            mainclassname="${main.class}">
            <classpath file="${wrap.input.jar}"/>
        </bundleapp>
        <delete file="${app.dir}/Contents/Resources/${wrap.icon.file}"/>
        <copy todir="${app.dir}/Contents/Resources">
          <fileset dir="${ext.resources.dir}" includes="${wrap.icon.file}"/>
        </copy>
    </target></pre><p></p>
<p>Para linux usaremos la estrategia de <a href="http://www.linuxjournal.com/content/add-binary-payload-your-shell-scripts">agregar payloads</a> a scripts, que apendizará un lanzador y el JAR en <a href="https://coderwall.com/p/ssuaxa">un solo archivo</a>. Crea el lanzador dentro del directorio de herramientas de construcción con el siguiente contenido:</p>
<p></p><pre class="urvanov-syntax-highlighter-plain-tag">#!/bin/sh

MYSELF=`which "$0" 2>/dev/null`
[ $? -gt 0 -a -f "$0" ] && MYSELF="./$0"
java=java
if test -n "$JAVA_HOME"; then
    java="$JAVA_HOME/bin/java"
fi
exec "$java" $java_args -jar $MYSELF "$@"
exit 1</pre><p></p>
<p>Ahora, agrega a <em>build-dist.xml</em> un nuevo <code>tarjet</code> con la ejecución del cat que unirá el lanzador y el JAR:</p>
<p></p><pre class="urvanov-syntax-highlighter-plain-tag"><!--  Generate the GNU/Linux wrapper -->
    <target name="dist-wrap-linux">
        <!-- Config -->
        <!-- Jar to use -->
        <property name="wrap.input.jar"        value="${dist.jar}"/>
        <!-- launcher script location -->
        <property name="wrap.nixwrap.path"     value="${build.ext.dir}/jar2nix.sh"/>
        <!-- The resultant files location -->
        <property name="wrap.output.dir"       value="${dist.dir}"/>
        <!-- -Name for the resultant .run file  -->
        <property name="wrap.output.run"       value="${application.title}.run"/>

        <!-- Routine -->
        <echo message="${newline}** Generating wrapper..."/>
        <echo message="   Input jar:           ${wrap.input.jar}"/>
        <echo message="   Output file:         ${dist.dir}/${wrap.output.run}"/>
        <exec executable="sh" output="/dev/null">
            <arg value="-c"/>
            <arg value="cat ${wrap.nixwrap.path} ${wrap.input.jar} > ${dist.dir}/${wrap.output.run}"/>
        </exec>
        <exec executable="chmod">
            <arg value="+x"/>
            <arg value="${dist.dir}/${wrap.output.run}"/>
        </exec>

        <echo message="   DONE"/>
    </target></pre><p></p>
<p>(Si te preguntas por que usar <code>sh</code> como ejecutable en lugar de <code>cat</code> directamente, es porque la manera en que se envían los parametros impide la redirección y tanto el atributo <code>output</code> de <code>&lt;exec&gt;</code> como el uso del subelemento <code>&lt;redirector&gt;</code> modifican la salida generando archivos corruptos.)</p>
<p><a href="https://fugadigital.com/wp-content/uploads/2014/04/cat-package.jpg"><img decoding="async" src="https://fugadigital.com/wp-content/uploads/2014/04/cat-package-247x300.jpg" alt="cat-package" width="247" height="300" class="aligncenter size-medium wp-image-19" srcset="https://fugadigital.com/wp-content/uploads/2014/04/cat-package-247x300.jpg 247w, https://fugadigital.com/wp-content/uploads/2014/04/cat-package.jpg 400w" sizes="(max-width: 247px) 100vw, 247px" /></a></p>
<p>Para terminar vamos a agregar las instrucciones que generan los archivos de ditribución: un ZIP para Windows, un TAR.GZ para OS X, un TAR.BZ2 para Linux y un TAR.GZ multiplataforma.</p>
<p>Los tres <code>target</code> siguientes generarán archivos comprimidos que contendrán un directorio raiz, éste a su vez contendrá el wrapper o jar y un directorio con documentos varios.</p>
<p>Para Windows agrega el siguiente <code>target</code>:</p><pre class="urvanov-syntax-highlighter-plain-tag"><!--  Generate Windows zip file -->
    <target name="dist-pack-win">
        <!-- Config -->
        <!-- Location of the wrapper -->
        <property name="pack.wrap.dir"         value="${dist.dir}"/>
        <!-- Wrapper filename -->
        <property name="pack.wrap.file"        value="${application.title}.exe"/>
        <!-- Directory with resources to include -->
        <property name="pack.resources.dir"    value="${resources.dir}/app"/>
        <!-- Name for included resurces directoy -->
        <property name="pack.resources.name"    value="app"/>
        <!-- Location of the compressed file -->
        <property name="pack.output.dir"       value="${dist.dir}"/>
        <!-- compressed package filename -->
        <property name="pack.output.file"      value="${application.title}-win.zip"/>
        <!-- Directory name of the root compressed file contents -->
        <property name="pack.root.dir"         value="${application.title}"/>

        <!-- Routine -->
        <echo message="${newline}** Generating distribution..."/>
        <echo message="   Wrapper file:        ${pack.wrap.dir}/${pack.wrap.file}"/>
        <echo message="   Resources dir:       ${pack.resources.dir}"/>
        <echo message="   Internal root dir:   ${pack.root.dir}"/>

        <zip destfile="${pack.output.dir}/${pack.output.file}">
            <zipfileset
                prefix="${pack.root.dir}"
                dir="${pack.wrap.dir}"
                includes="${pack.wrap.file}"/>
            <zipfileset
                prefix="${pack.root.dir}/${pack.resources.name}"
                dir="${pack.resources.dir}"
                includes="**"/>
        </zip>
        <echo message="   DONE"/>
    </target></pre><p></p>
<p>Para OS X agrega el siguiente <code>target</code>:</p><pre class="urvanov-syntax-highlighter-plain-tag"><!--  Generate OS X tar.gz file -->
    <target name="dist-pack-osx">
        <!-- Config -->
        <!-- Location of the wrapper -->
        <property name="pack.wrap.dir"         value="${dist.dir}"/>
        <!-- Wrapper filename -->
        <property name="pack.wrap.file"        value="${application.title}.app"/>
        <!-- Directory with resources to include -->
        <property name="pack.resources.dir"    value="${resources.dir}/app"/>
        <!-- Name for included resurces directoy -->
        <property name="pack.resources.name"    value="app"/>
        <!-- Location of the compressed file -->
        <property name="pack.output.dir"       value="${dist.dir}"/>
        <!-- compressed package filename -->
        <property name="pack.output.file"      value="${application.title}-osx.tar.gz"/>
        <!-- Directory name of the root compressed file contents -->
        <property name="pack.root.dir"         value="${application.title}"/>

        <!-- Routine -->
        <echo message="${newline}** Generating distribution..."/>
        <echo message="   Wrapper file:        ${pack.wrap.dir}/${pack.wrap.file}"/>
        <echo message="   Resources dir:       ${pack.resources.dir}"/>
        <echo message="   Internal root dir:   ${pack.root.dir}"/>

        <tar destfile="${pack.output.dir}/${pack.output.file}" compression="gzip">
            <tarfileset
                prefix="${pack.root.dir}"
                dir="${pack.wrap.dir}"
                excludes="**/JavaAppLauncher"
                includes="${pack.wrap.file}/**"/>
            <tarfileset
                prefix="${pack.root.dir}/${pack.wrap.file}/Contents/MacOS"
                dir="${pack.wrap.dir}/${pack.wrap.file}/Contents/MacOS"
                mode="755"
                includes="JavaAppLauncher"/>
            <tarfileset
                prefix="${pack.root.dir}/${pack.resources.name}"
                dir="${pack.resources.dir}"
                includes="**"/>
        </tar>
        <echo message="   DONE"/>
    </target></pre><p></p>
<p>Para Linux agrega el siguiente <code>target</code>:</p><pre class="urvanov-syntax-highlighter-plain-tag"><!--  Generate Linux tar.bz2 file -->
    <target name="dist-pack-linux">
        <!-- Config -->
        <!-- Location of the wrapper -->
        <property name="pack.wrap.dir"         value="${dist.dir}"/>
        <!-- Wrapper filename -->
        <property name="pack.wrap.file"        value="${application.title}.run"/>
        <!-- Directory with resources to include -->
        <property name="pack.resources.dir"    value="${resources.dir}/app"/>
        <!-- Name for included resurces directoy -->
        <property name="pack.resources.name"    value="app"/>
        <!-- Location of the compressed file -->
        <property name="pack.output.dir"       value="${dist.dir}"/>
        <!-- compressed package filename -->
        <property name="pack.output.file"      value="${application.title}-linux.tar.bz2"/>
        <!-- Directory name of the root compressed file contents -->
        <property name="pack.root.dir"         value="${application.title}"/>

        <!-- Routine -->
        <echo message="${newline}** Generating distribution..."/>
        <echo message="   Wrapper file:        ${pack.wrap.dir}/${pack.wrap.file}"/>
        <echo message="   Resources dir:       ${pack.resources.dir}"/>
        <echo message="   Internal root dir:   ${pack.root.dir}"/>

        <tar destfile="${pack.output.dir}/${pack.output.file}" compression="bzip2">
            <tarfileset
                prefix="${pack.root.dir}"
                dir="${pack.wrap.dir}"
                includes="${pack.wrap.file}"
                mode="755"/>
            <tarfileset
                prefix="${pack.root.dir}/${pack.resources.name}"
                dir="${pack.resources.dir}"
                includes="**"/>
        </tar>
        <echo message="   DONE"/>
    </target></pre><p></p>
<p>Para la distribución multiplataforma agrega el siguiente <code>target</code>:</p><pre class="urvanov-syntax-highlighter-plain-tag"><!--  Generate multiplatform tar.gz file -->
    <target name="dist-pack-multi">
        <!-- Config -->
        <!-- Location of the jar -->
        <property name="pack.jar.dir"         value="${dist.dir}"/>
        <!-- Wrapper filename -->
        <property name="pack.jar.file"        value="${application.title}.jar"/>
        <!-- Directory with resources to include -->
        <property name="pack.resources.dir"    value="${resources.dir}/app_multi"/>
        <!-- Name for included resurces directoy -->
        <property name="pack.resources.name"    value="app"/>
        <!-- Location of the compressed file -->
        <property name="pack.output.dir"       value="${dist.dir}"/>
        <!-- compressed package filename -->
        <property name="pack.output.file"      value="${application.title}-multi.tar.gz"/>
        <!-- Directory name of the root compressed file contents -->
        <property name="pack.root.dir"         value="${application.title}"/>

        <!-- Routine -->
        <echo message="${newline}** Generating distribution..."/>
        <echo message="   Wrapper file:        ${pack.jar.dir}/${pack.jar.file}"/>
        <echo message="   Resources dir:       ${pack.resources.dir}"/>
        <echo message="   Internal root dir:   ${pack.root.dir}"/>

        <tar destfile="${pack.output.dir}/${pack.output.file}" compression="gzip">
            <tarfileset
                prefix="${pack.root.dir}"
                dir="${pack.jar.dir}"
                includes="${pack.jar.file}"/>
            <tarfileset
                prefix="${pack.root.dir}"
                dir="${resources.dir}"
                includes="run.*"
                mode="755"/>
            <tarfileset
                prefix="${pack.root.dir}/${pack.resources.name}"
                dir="${pack.resources.dir}"
                includes="**"/>
        </tar>
        <echo message="   DONE"/>
    </target></pre><p>En este <code>target</code> además estoy agregando archivos <em>run.*</em> que son lanzadores comunes para ejecutar un JAR en diferentes plataformas.</p>
<p>Este último paso es realmente innecesario y elimina todos los archivos y directorios excepto nuestros paquetes distribuibles:</p><pre class="urvanov-syntax-highlighter-plain-tag"><!-- Delete leftovers -->
    <target name="dist-clean">
        <property file="nbproject/project.properties"/>
        <echo message="${newline}** Deleting leftovers..."/>
        <delete includeemptydirs="true">
            <fileset dir="${dist.dir}"
                excludes="**/${application.title}-win.zip,
                            **/${application.title}-osx.tar.gz,
                            **/${application.title}-linux.tar.bz2,
                            **/${application.title}-multi.tar.gz"
            />
        </delete>

        <echo message="   DONE"/>
    </target></pre><p></p>
<p>El archivo de construcción seguro es ahora considerablemente más largo pero muy versatil y adaptable. Aun cuando hay muchas mejoras posibles, esta es una buena base, así que agrega los cambios propios para tu proyecto y facilita la vida a tus usuarios (y la tuya por supuesto).</p>
<p>La entrada <a href="https://fugadigital.com/distribuyendo-una-app-de-escritorio-en-java/">Distribuyendo una app de escritorio en Java</a> se publicó primero en <a href="https://fugadigital.com">fuga_digital</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://fugadigital.com/distribuyendo-una-app-de-escritorio-en-java/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
