001    /*
002     * Copyright 2010 The Apache Software Foundation.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     * 
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.vafer.jdeb.ant;
017    
018    import java.io.File;
019    import java.io.FileInputStream;
020    import java.io.FileOutputStream;
021    import java.util.ArrayList;
022    import java.util.Collection;
023    
024    import org.apache.tools.ant.BuildException;
025    import org.apache.tools.ant.taskdefs.MatchingTask;
026    import org.apache.tools.ant.taskdefs.Tar;
027    import org.apache.tools.ant.types.FileSet;
028    import org.vafer.jdeb.Console;
029    import org.vafer.jdeb.DataProducer;
030    import org.vafer.jdeb.Processor;
031    import org.vafer.jdeb.changes.TextfileChangesProvider;
032    import org.vafer.jdeb.descriptors.PackageDescriptor;
033    import org.vafer.jdeb.producers.FileSetDataProducer;
034    
035    /**
036     * TODO generalize with DebMaker
037     * 
038     * AntTask for creating debian archives.
039     * Even supports signed changes files.
040     * 
041     * @author Torsten Curdt <tcurdt@vafer.org>
042     */
043            
044    public class DebAntTask extends MatchingTask {
045    
046        /** The Debian package produced */
047        private File deb;
048    
049        /** The directory containing the control files to build the package */
050        private File control;
051    
052        /** The file containing the PGP keys */
053        private File keyring;
054    
055        /** The key to use in the keyring */
056        private String key;
057    
058        /** The passphrase for the key to sign the changes file */
059        private String passphrase;
060    
061        /** The file to read the changes from */
062        private File changesIn;
063    
064        /** The file where to write the changes to */
065        private File changesOut;
066    
067        /** The file where to write the changes of the changes input to */
068        private File changesSave;
069    
070        /** The compression method used for the data file (none, gzip or bzip2) */
071        private String compression = "gzip";
072    
073        /** Trigger the verbose mode detailing all operations */
074        private boolean verbose;
075    
076        private Collection dataProducers = new ArrayList();
077    
078    
079        public void setDestfile( File deb ) {
080            this.deb = deb;
081        }
082        
083        public void setControl( File control ) {
084            this.control = control;
085        }
086    
087        public void setChangesIn( File changes ) {
088            this.changesIn = changes;
089        }
090    
091        public void setChangesOut( File changes ) {
092            this.changesOut = changes;
093        }
094    
095        public void setChangesSave( File changes ) {
096            this.changesSave = changes;
097        }
098    
099        public void setKeyring( File keyring ) {
100            this.keyring = keyring;
101        }
102    
103        public void setKey( String key ) {
104            this.key = key;
105        }
106        
107        public void setPassphrase( String passphrase ) {
108            this.passphrase = passphrase;
109        }
110    
111        public void setCompression( String compression ) {
112            this.compression = compression;
113        }
114    
115        public void setVerbose( boolean verbose ) {
116            this.verbose = verbose;
117        }
118    
119        public void addFileSet( FileSet fileset ) {
120            dataProducers.add(new FileSetDataProducer(fileset));
121        }
122    
123        public void addTarFileSet( Tar.TarFileSet fileset ) {
124            dataProducers.add(new FileSetDataProducer(fileset));
125        }
126    
127        public void addData( Data data ) {
128            dataProducers.add(data);
129        }
130        
131        private boolean isPossibleOutput( File file ) {
132    
133            if (file.exists()) {
134                return file.isFile() && file.canWrite();
135            }
136    
137            return true;
138        }
139        
140        public void execute() {
141            
142            if (control == null || !control.isDirectory()) {
143                throw new BuildException("You need to point the 'control' attribute to the control directory.");
144            }
145    
146            if (changesIn != null) {
147                
148                if (!changesIn.isFile() || !changesIn.canRead()) {
149                    throw new BuildException("The 'changesIn' attribute needs to point to a readable file. " + changesIn + " was not found/readable.");             
150                }
151    
152                if (changesOut == null) {
153                    throw new BuildException("A 'changesIn' without a 'changesOut' does not make much sense.");
154                }
155                
156                if (!isPossibleOutput(changesOut)) {
157                    throw new BuildException("Cannot write the output for 'changesOut' to " + changesOut);              
158                }
159    
160                if (changesSave != null && !isPossibleOutput(changesSave)) {
161                    throw new BuildException("Cannot write the output for 'changesSave' to " + changesSave);                
162                }
163                
164            } else {
165                if (changesOut != null || changesSave != null) {
166                    throw new BuildException("The 'changesOut' or 'changesSave' attributes may only be used when there is a 'changesIn' specified.");                           
167                }
168            }
169    
170            if (!"gzip".equals(compression) && !"bzip2".equals(compression) && !"none".equals(compression)) {
171                throw new BuildException("The compression method '" + compression + "' is not supported");
172            }
173                    
174            if (dataProducers.size() == 0) {
175                throw new BuildException("You need to provide at least one reference to a tgz or directory with data.");
176            }
177    
178            if (deb == null) {
179                throw new BuildException("You need to point the 'destfile' attribute to where the deb is supposed to be created.");
180            }
181            
182            final File[] controlFiles = control.listFiles();
183            
184            final DataProducer[] data = new DataProducer[dataProducers.size()];
185            dataProducers.toArray(data);
186            
187            final Processor processor = new Processor(new Console() {
188                public void println(String s) {
189                    if (verbose) {
190                        log(s);
191                    }
192                }
193            }, null);
194            
195            final PackageDescriptor packageDescriptor;
196            try {
197                
198                log("Creating debian package: " + deb);
199                
200                packageDescriptor = processor.createDeb(controlFiles, data, deb, compression);
201    
202            } catch (Exception e) {
203                throw new BuildException("Failed to create debian package " + deb, e);
204            }
205    
206            final TextfileChangesProvider changesProvider;
207            
208            try {
209                if (changesOut == null) {
210                    return;
211                }
212    
213                log("Creating changes file: " + changesOut);
214    
215                // for now only support reading the changes form a textfile provider
216                changesProvider = new TextfileChangesProvider(new FileInputStream(changesIn), packageDescriptor);
217                
218                processor.createChanges(packageDescriptor, changesProvider, (keyring!=null)?new FileInputStream(keyring):null, key, passphrase, new FileOutputStream(changesOut));
219                            
220            } catch (Exception e) {
221                throw new BuildException("Failed to create debian changes file " + changesOut, e);
222            }
223    
224            try {
225                if (changesSave == null) {
226                    return;
227                }
228    
229                log("Saving changes to file: " + changesSave);
230    
231                changesProvider.save(new FileOutputStream(changesSave));
232                
233            } catch (Exception e) {
234                throw new BuildException("Failed to save debian changes file " + changesSave, e);
235            }
236                    
237        }
238    }