/* * Copyright (c) 2012, Danno Ferrin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Danno Ferrin nor the * names of contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */packageorg.bitbucket.shemnon.javafxplugin.tasksimportcom.sun.javafx.tools.packager.DeployParamsimportcom.sun.javafx.tools.packager.DeployParams.RunModeimportcom.sun.javafx.tools.packager.Logimportcom.sun.javafx.tools.packager.PackagerLibimportcom.sun.javafx.tools.packager.bundlers.Bundlerimportnet.sf.image4j.codec.bmp.BMPEncoderimportnet.sf.image4j.codec.ico.ICOEncoderimportorg.apache.tools.ant.taskdefs.condition.Osimportorg.bitbucket.shemnon.javafxplugin.IconInfoimportorg.gradle.api.file.FileCollectionimportorg.gradle.api.internal.ConventionTaskimportorg.gradle.api.tasks.InputDirectoryimportorg.gradle.api.tasks.InputFilesimportorg.gradle.api.tasks.OutputDirectoryimportorg.gradle.api.tasks.TaskActionimportorg.gradle.util.ConfigureUtilimportjavax.imageio.ImageIOimportjava.awt.geom.AffineTransformimportjava.awt.image.BufferedImageclassJavaFXDeployTaskextendsConventionTask{StringpackagingFileCollectionantJavaFXJarStringappIDStringappNamebooleanverbose=falseintwidth=1024intheight=768booleanembedJNLP=falseStringupdateMode="background"booleanofflineAllowed=trueStringcodebaseStringmainClassList<String>jvmArgs=[]Map<String,String>systemProperties=[:]List<String>arguments=[]// deploy/info attributesStringcategoryStringcopyrightStringdescriptionStringlicenseTypeStringvendorList<IconInfo>iconInfos=[]Stringversion// deploy/preferences attributesBooleaninstallSystemWidebooleanmenubooleanshortcut@InputFilesFileCollectioninputFiles@InputDirectoryFileresourcesDir@OutputDirectoryFiledistsDir@TaskActionprocessResources(){DeployParamsdeployParams=newDeployParams();deployParams.version=getVersion()// these deploy params are currently not set//java.lang.String preloader;//java.util.List<com.sun.javafx.tools.packager.Param> params;//java.util.List<com.sun.javafx.tools.packager.HtmlParam> htmlParams;//boolean embedCertificates;//boolean isExtension;//boolean isSwingApp;//boolean includeDT;//java.lang.String placeholder;//java.lang.String appId;//java.util.List<com.sun.javafx.tools.ant.Callback> callbacks;//java.util.List<com.sun.javafx.tools.packager.DeployParams.Template> templates;//java.lang.String jrePlatform;//java.lang.String fxPlatform;//java.lang.String fallbackApp;deployParams.width=getWidth()deployParams.height=getHeight()deployParams.setEmbeddedDimensions(getWidth()asString,getHeight()asString)// FIXME make a convention propertydeployParams.setOutdir(getDistsDir())deployParams.embedJNLP=getEmbedJNLP()deployParams.outfile=getAppName()//FIXME duplicate with app nameswitch(getPackaging().toLowerCase()){case"false":case"none":deployParams.bundleType=Bundler.BundleType.NONEdeployParams.targetFormat=nullbreak;case"all":case"true":deployParams.bundleType=Bundler.BundleType.ALLdeployParams.targetFormat=nullbreak;case"image":deployParams.bundleType=Bundler.BundleType.IMAGEdeployParams.targetFormat=nullbreak;case"installer":deployParams.bundleType=Bundler.BundleType.INSTALLERdeployParams.targetFormat=nullbreak;default:// assume the packageing is for a specific typedeployParams.bundleType=Bundler.BundleType.INSTALLERdeployParams.targetFormat=getPackaging()}deployParams.verbose=getVerbose()deployParams.id=getAppID()deployParams.appName=getAppName()// FIXME duplicate with titledeployParams.applicationClass=getMainClass()getInputFiles()each{Filef->deployParams.addResource(f.parentFile,f);}deployParams.title=getAppName()deployParams.category=getCategory()deployParams.copyright=getCopyright()deployParams.description=getDescription()deployParams.licenseType=getLicenseType()deployParams.vendor=getVendor()deployParams.systemWide=getInstallSystemWide()deployParams.needMenu=getMenu()deployParams.needShortcut=getShortcut()deployParams.allPermissions=true//FIXME hardcodeddeployParams.updateMode=getUpdateMode()deployParams.offlineAllowed=getOfflineAllowed()for(IconInfoii:getIconInfos()){deployParams.addIcon(ii.href,ii.kind,ii.width,ii.height,ii.depth,ii.mode);}if(getCodebase()!=null){try{deployParams.codebase=getCodebase()}catch(MissingPropertyExceptionignored){getLogger().error("JavaFXDeployTask.codebase is only available in JavaFX 8 or later, codebase setting is ignored")}}getJvmArgs().each{deployParams.addJvmArg(it)}getSystemProperties().each{k,v->deployParams.addJvmProperty(k,v)}deployParams.arguments=getArguments()FilepackageResourcesOutput=project.sourceSets['package'].output.resourcesDirprocessIcons(packageResourcesOutput)// hackdeployParams.class.classLoader.addURL(packageResourcesOutput.parentFile.toURI().toURL())PackagerLibpackager=newPackagerLib();Log.setLogger(newLog.Logger(getVerbose()){@Overridevoidinfo(Stringmsg){getLogger().info(msg)}@Overridevoidverbose(Stringmsg){if(getVerbose()){info(msg)}else{debug(msg)}}@Overridevoiddebug(Stringmsg){getLogger().debug(msg)}}asLog.Logger)packager.generateDeploymentPackages(deployParams)Log.setLogger(null)}deficon(Closureclosure){getIconInfos().add(newIconInfo(closure))}deficons(Closureclosure){Mapm=[:]ConfigureUtil.configure(closure,m)m.each{k,v->if(vinstanceofList){v.each{addIcon(k,it)}}else{addIcon(k,v)}}}protectedvoidaddIcon(Stringkind,Stringhref){IconInfoii=newIconInfo(href)ii.kind=kindgetIconInfos().add(ii)}protectedvoidloadConventialIcons(Stringkind){// this convention is non-configurable, hence it gets hard codedproject.fileTree('src/deploy/package',{include"$kind*.png"}).visit{fileDetails->addIcon(kind,fileDetails.path)}}protectedvoidprocessIcons(Filedestination){if(iconInfos.isEmpty()){// if nothing is configured, use the conventionloadConventialIcons('shortcut')loadConventialIcons('volume')loadConventialIcons('setup')}if(Os.isFamily(Os.FAMILY_MAC)){processMacOSXIcons(destination);}if(Os.isFamily(Os.FAMILY_WINDOWS)){processWindowsIcons(destination);}if(Os.isFamily(Os.FAMILY_UNIX)&&!Os.isFamily(Os.FAMILY_MAC)){processLinuxIcons(destination)}}protectedvoidprocessMacOSXIcons(Filedestination){processMacOSXIcns('shortcut',newFile(destination,"macosx/${project.javafx.appName}.icns"))processMacOSXIcns('volume',newFile(destination,"macosx/${project.javafx.appName}-volume.icns"))}defmacIcnsSizes=[16,32,128,256,512]protectedvoidprocessMacOSXIcns(Stringkind,FileiconLocation){// get explicitdefdest="$project.buildDir/icons/${kind}.iconset"project.mkdir(dest)booleancreateIcon=falsefor(IconInfoii:getIconInfos()){if(kind==ii.kind){BufferedImageicon=getImage(ii)if(icon==null){logger.error("Icon $ii.href for $ii.kind rejected from MacOSX bundling because $ii.href does not exist or it is not an image.")continue;}if(ii.width!=ii.height){logger.info("Icon $ii.href for $ii.kind rejected from MacOSX bundling because it is not square: $ii.width x $ii.height.")continue;}if(ii.scale!=1&&ii.scale!=2){logger.info("Icon $ii.href for $ii.kind rejected from MacOSX bundling because it has an invalid scale.")continue;}if(!macIcnsSizes.contains(ii.width)){logger.info("Icon $ii.href for $ii.kind rejected from MacOSX bundling because it is an unsupported dimension. $macIcnsSizes dimensions are supported.")continue;}ant.copy(file:ii.file,toFile:"$dest/icon_${ii.width}x${ii.height}${ii.scale == 2 ? '@2x': ''}.png")createIcon=true}}if(createIcon){project.exec{executable'iconutil'args('--convert','icns',dest)}ant.copy(file:"$project.buildDir/icons/${kind}.icns",toFile:iconLocation)}}protectedvoidprocessWindowsIcons(Filedestination){processWindowsIco('shortcut',newFile(destination,"windows/${project.javafx.appName}.ico"))processWidnowsBMP('setup',newFile(destination,"windows/${project.javafx.appName}-setup-icon.bmp"))}protectedvoidprocessWidnowsBMP(Stringkind,Filedestination){booleanprocessed=falsefor(IconInfoii:getIconInfos()){if(kind==ii.kind){BufferedImageicon=getImage(ii)if(icon==null){logger.error("Icon $ii.href for $ii.kind rejected from Windows bundling because $ii.href does not exist or it is not an image.")continue;}if(processed){logger.info("Icon $ii.href for $ii.kind rejected from Windows bundling because only one icon can be used.")continue;}doublescale=Math.min(Math.min(55.0/ icon.width, 58.0 /icon.height),1.0)BufferedImagebi=newBufferedImage((int)icon.width*scale,(int)icon.height*scale,BufferedImage.TYPE_INT_ARGB)defg=bi.graphicsdeft=newAffineTransform()t.scale(scale,scale)g.transform=tg.drawImage(icon,0,0,null)BMPEncoder.write(bi,destination)processed=true}}}protectedvoidprocessWindowsIco(Stringkind,Filedestination){Map<Integer,BufferedImage>images=newTreeMap<Integer,BufferedImage>()for(IconInfoii:iconInfos){if(kind==ii.kind){BufferedImageicon=getImage(ii)if(icon==null){logger.error("Icon $ii.href for $ii.kind rejected from Windows bundling because $ii.href does not exist or it is not an image.")continue;}if(ii.scale!=1){logger.info("Icon $ii.href for $ii.kind rejected from Widnows bundling because it has a scale other than '1'.")continue;}if(icon.width!=icon.height){logger.info("Icon $ii.href for $ii.kind rejected from Windows bundling because it is not square: $icon.width x $icon.height.")continue;}BufferedImagebi=newBufferedImage(icon.width,icon.height,BufferedImage.TYPE_INT_ARGB)bi.graphics.drawImage(icon,0,0,null)images.put(bi.width,bi)}}if(images){destination.parentFile.mkdirs()List<BufferedImage>icons=(images.values()asList).reverse()ICOEncoder.write(icons,destination)}}protectedvoidprocessLinuxIcons(Filedestination){IconInfolargestIconfor(IconInfoii:getIconInfos()){if('shortcut'==ii.kind){BufferedImageicon=getImage(ii)if(icon==null){logger.error("Icon $ii.href for $ii.kind rejected from Linux bundling because $ii.href does not exist or it is not an image.")continue;}if(largestIcon?.width<ii.width){if(largestIcon!=null){logger.info("Icon $largestIcon.href for $largestIcon.kind rejected from Linux bundling because it is not the largest icon.")}largestIcon=ii}else{logger.info("Icon $ii.href for $ii.kind rejected from Linux bundling because it is not the largest icon.")}}}if(largestIcon){ant.copy(file:largestIcon.file,toFile:newFile(destination,"linux/${project.javafx.appName.replaceAll('\\s', '')}.png"))}}BufferedImagegetImage(IconInfoii){if(ii._image==null){ii.file=getProject().file(ii.href)if(!ii.file.file){// try to resolve relative to outputii.file=newFile(getResourcesDir(),ii.href)}if(!ii.file.file)returnii._image=ImageIO.read(ii.file)if(ii.href.contains('@2x')){ii.width=ii._image.width/ 2 ii.height = ii._image.height /2ii.scale=2}else{ii.width=ii._image.widthii.height=ii._image.heightii.scale=1}}returnii._image}}