/* * Copyright 2015 i-net software * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.inet.gradle.setup.unix.deb; import java.io.File; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.OutputStreamWriter; import java.nio.file.Files; import java.nio.file.attribute.PosixFilePermission; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Scanner; import java.util.Set; import com.inet.gradle.setup.SetupBuilder; import com.inet.gradle.setup.Template; /** * Builder for the control, postinst and prerm files, that are required for the Debian package tool. * <dl> * <dt>control</dt> * <dd>contains settings for the package like dependencies, architecture, description</dd> * <dt>postinst</dt> * <dd>contains scripts and commands that are executed after the files are copied</dd> * <dt>prerm</dt> * <dd>contains scripts and commands that are executed before the files are removed</dd> * </dl> * * @author Stefan Heidrich */ class DebControlFileBuilder { private static final char NEWLINE = '\n'; private final Deb deb; private final SetupBuilder setup; private File buildDir; private Collection<String> confFiles = new ArrayList<>(); enum Script { PREINST, POSTINST, PRERM, POSTRM } Map<Script, StringBuilder> scriptHeadMap = new HashMap<>(); Map<Script, StringBuilder> scriptTailMap = new HashMap<>(); /** * the constructor setting the fields * * @param deb the task for the debian package * @param setup the generic task for all setups * @param buildDir the directory to build the package in */ DebControlFileBuilder( Deb deb, SetupBuilder setup, File buildDir ) { this.deb = deb; this.setup = setup; this.buildDir = buildDir; } /** * Create the configuration files for the Debian package based on the settings in the task. * * @throws Exception */ void build() throws Exception { createControlFile(); createConfFilesFile(); createScripts(); } /** * Creates the 'control' file for the Debian package * * @throws IOException if something could not be written to the file */ private void createControlFile() throws IOException { if( !buildDir.exists() ) { buildDir.mkdirs(); } else if( !buildDir.isDirectory() ) { throw new IllegalArgumentException( "The buildDir parameter must be a directory!" ); } FileOutputStream fileoutput = null; OutputStreamWriter controlWriter = null; try { File control = new File( buildDir, "control" ); fileoutput = new FileOutputStream( control ); controlWriter = new OutputStreamWriter( fileoutput, "UTF-8" ); putPackage( controlWriter ); putVersion( controlWriter ); putSection( controlWriter ); putPriority( controlWriter ); putArchitecture( controlWriter ); putInstallSize( controlWriter ); putRecommends( controlWriter ); putPreDepends( controlWriter ); putDepends( controlWriter ); putMaintainer( controlWriter ); putDescription( controlWriter ); putHomepage( controlWriter ); controlWriter.flush(); } finally { if( controlWriter != null ) { try { controlWriter.close(); } catch( IOException e ) { // IGNORE } } if( fileoutput != null ) { try { fileoutput.close(); } catch( IOException e ) { // IGNORE } } } } /** * Write the description to the file. The description is mandatory. If no description is declared a runtime exception will be thrown. * * @param controlWriter the writer for the file * @throws IOException if the was an error while writing to the file */ private void putDescription( OutputStreamWriter controlWriter ) throws IOException { String description = setup.getApplication(); String secondLine = ""; File longDescription = setup.getLongDescription( setup.getDefaultResourceLanguage() ); if( longDescription != null ) { try (Scanner scanner = new Scanner( longDescription, "UTF8" )) { secondLine = scanner.useDelimiter( "\\A" ).next(); } } if( secondLine.trim().length() == 0 ) { secondLine = setup.getDescription(); } controlWriter.write( "Description: " + description + NEWLINE + " " + secondLine + NEWLINE ); } /** * Write the maintainer to the file. The maintainer is mandatory. If no maintainer is declared a runtime exception will be thrown. * * @param controlWriter the writer for the file * @throws IOException if the was an error while writing to the file */ private void putMaintainer( OutputStreamWriter controlWriter ) throws IOException { String vendor = setup.getVendor(); if( vendor == null || vendor.length() == 0 ) { throw new RuntimeException( "No vendor declared in the setup configuration." ); } else { controlWriter.write( "Maintainer: " + vendor + " <" + deb.getMaintainerEmail() + ">" + NEWLINE ); } } /** * Write the dependencies to the file. If no dependencies are specified, the java dependencies will be used. * * @param controlWriter the writer for the file * @throws IOException if the was an error while writing to the file */ private void putDepends( OutputStreamWriter controlWriter ) throws IOException { String depends = deb.getDepends(); if( depends != null && depends.length() > 0 ) { controlWriter.write( "Depends: " + depends + NEWLINE ); } } /** * Writes the pre-dependencies to the file. * * @param controlWriter the writer for the file * @throws IOException if the was an error while writing to the file */ private void putPreDepends( OutputStreamWriter controlWriter ) throws IOException { String depends = deb.getDepends(); if( depends == null || depends.length() == 0 ) { depends = "debconf"; } controlWriter.write( "Pre-Depends: " + depends + NEWLINE ); } /** * Write the recommends to the file. * * @param controlWriter the writer for the file * @throws IOException if the was an error while writing to the file */ private void putRecommends( OutputStreamWriter controlWriter ) throws IOException { String recommends = deb.getRecommends(); if( recommends == null || recommends.length() == 0 ) { recommends = "openjdk-8-jre | openjdk-8-jdk | default-jre | default-jdk, libgtk2-perl"; } controlWriter.write( "Recommends: " + recommends + NEWLINE ); } /** * Write the installation size to the file. If no size is specified it will count the size of all files it has to install and round it to full megabytes. * * @param controlWriter the writer for the file * @throws IOException if the was an error while writing to the file */ private void putInstallSize( OutputStreamWriter controlWriter ) throws IOException { String installSize = deb.getInstallSize(); if( installSize == null || installSize.length() == 0 ) { long fileSize = 0; for( File file : deb.getSetupSource().getFiles() ) { if( file.isFile() ) { fileSize = fileSize + file.length(); } } for( File file : deb.getSource().getFiles() ) { if( file.isFile() ) { fileSize = fileSize + file.length(); } } installSize = String.valueOf( fileSize / 1024 ); // Size wird in KB angegeben und nicht in Bytes } controlWriter.write( "Installed-Size: " + installSize + NEWLINE ); } /** * Write the architecture to the file. If no architecture is specified then 'all' will be used. * * @param controlWriter the writer for the file * @throws IOException if the was an error while writing to the file */ private void putArchitecture( OutputStreamWriter controlWriter ) throws IOException { controlWriter.write( "Architecture: " + deb.getArchitecture() + NEWLINE ); } /** * Write the priority to the file. If no priority are specified then 'optional' will be used. * * @param controlWriter the writer for the file * @throws IOException if the was an error while writing to the file */ private void putPriority( OutputStreamWriter controlWriter ) throws IOException { String priority = deb.getPriority(); if( priority == null || priority.length() == 0 ) { priority = "optional"; } controlWriter.write( "Priority: " + priority + NEWLINE ); } /** * Write the section to the file. If no section is specified then 'java' will be used. * * @param controlWriter the writer for the file * @throws IOException if the was an error while writing to the file */ private void putSection( OutputStreamWriter controlWriter ) throws IOException { String section = deb.getSection(); if( section == null || section.length() == 0 ) { section = "java"; } controlWriter.write( "Section: " + section + NEWLINE ); } /** * Write the version to the file. The version is mandatory. If no version is declared a runtime exception will be thrown. * * @param controlWriter the writer for the file * @throws IOException if the was an error while writing to the file */ private void putVersion( OutputStreamWriter controlWriter ) throws IOException { String version = setup.getVersion(); if( version == null || version.length() == 0 ) { throw new RuntimeException( "No version declared in the setup configuration." ); } else { controlWriter.write( "Version: " + version + NEWLINE ); } } /** * Write the package to the file. The package is mandatory. If no package is declared a runtime exception will be thrown. * * @param controlWriter the writer for the file * @throws IOException if the was an error while writing to the file */ private void putPackage( OutputStreamWriter controlWriter ) throws IOException { String packages = setup.getAppIdentifier(); if( packages == null || packages.length() == 0 ) { throw new RuntimeException( "No package declared in the setup configuration." ); } else { controlWriter.write( "Package: " + packages + NEWLINE ); } } /** * Write the homepage to the file. * * @param controlWriter the writer for the file * @throws IOException if the was an error while writing to the file */ private void putHomepage( OutputStreamWriter controlWriter ) throws IOException { String homepage = deb.getHomepage(); if( homepage != null && homepage.length() > 0 ) { controlWriter.write( "Homepage: " + homepage + NEWLINE ); } } /** * Adds a config file * * @param file the config file */ public void addConfFile( String file ) { confFiles.add( file ); } /** * Creates the <tt>conffiles</tt> file with a listing of all created configuration files. * * @throws IOException on I/O failures */ private void createConfFilesFile() throws IOException { if( confFiles.size() > 0 ) { File cfile = new File( buildDir, "conffiles" ); try (FileWriter writer = new FileWriter( cfile )) { for( String confFile : confFiles ) { writer.write( '/' ); writer.write( confFile ); writer.write( '\n' ); } } Set<PosixFilePermission> perms = new HashSet<PosixFilePermission>(); perms.add( PosixFilePermission.OWNER_READ ); perms.add( PosixFilePermission.OWNER_WRITE ); perms.add( PosixFilePermission.GROUP_READ ); perms.add( PosixFilePermission.OTHERS_READ ); Files.setPosixFilePermissions( cfile.toPath(), perms ); } } /** * Adds a fragment to the specified install script at the tail section. * * @param script the install script * @param scriptFragment the fragment to add */ public void addTailScriptFragment( Script script, String scriptFragment ) { StringBuilder sb = scriptTailMap.get( script ); if( sb == null ) { sb = new StringBuilder(); scriptTailMap.put( script, sb ); } else { sb.append( "\n\n" ); } sb.append( scriptFragment ); } /** * Adds a fragment to the specified install script at the head section. * * @param script the install script * @param scriptFragment the fragment to add */ public void addHeadScriptFragment( Script script, String scriptFragment ) { StringBuilder sb = scriptHeadMap.get( script ); if( sb == null ) { sb = new StringBuilder(); scriptHeadMap.put( script, sb ); } else { sb.append( "\n\n" ); } sb.append( scriptFragment ); } /** * Creates the {post|pre}{inst|rm} install script files. Only scripts are generated when * they are required. * * @throws IOException on I/O failures */ private void createScripts() throws IOException { for( Script script : Script.values() ) { String scriptName = script.toString().toLowerCase(); Template tmpl = new Template( "unix/deb/template/" + scriptName + ".sh" ); StringBuilder head = scriptHeadMap.get( script ); StringBuilder tail = scriptTailMap.get( script ); if( head == null && tail == null ) { continue; } tmpl.setPlaceholder( "variables", deb.getVariablesTemplate() ); if( head != null ) { tmpl.setPlaceholder( "head", head.toString() ); } else { tmpl.setPlaceholder( "head", "" ); } if( tail != null ) { tmpl.setPlaceholder( "tail", tail.toString() ); } else { tmpl.setPlaceholder( "tail", "" ); } File file = new File( buildDir, scriptName ); tmpl.writeTo( file ); Set<PosixFilePermission> perms = new HashSet<PosixFilePermission>(); perms.add( PosixFilePermission.OWNER_READ ); perms.add( PosixFilePermission.OWNER_WRITE ); perms.add( PosixFilePermission.GROUP_READ ); perms.add( PosixFilePermission.OTHERS_READ ); perms.add( PosixFilePermission.OWNER_EXECUTE ); perms.add( PosixFilePermission.GROUP_EXECUTE ); perms.add( PosixFilePermission.OTHERS_EXECUTE ); Files.setPosixFilePermissions( file.toPath(), perms ); } } }