001    /*
002     * Jpkg - Java library and tools for operating system package creation.
003     *
004     * Copyright (c) 2007 Three Rings Design, Inc.
005     * All rights reserved.
006     *
007     * Redistribution and use in source and binary forms, with or without
008     * modification, are permitted provided that the following conditions
009     * are met:
010     * 1. Redistributions of source code must retain the above copyright
011     *    notice, this list of conditions and the following disclaimer.
012     * 2. Redistributions in binary form must reproduce the above copyright
013     *    notice, this list of conditions and the following disclaimer in the
014     *    documentation and/or other materials provided with the distribution.
015     * 3. Neither the name of the copyright owner nor the names of contributors
016     *    may be used to endorse or promote products derived from this software
017     *    without specific prior written permission.
018     *
019     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
020     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
021     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
022     * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
023     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
024     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
025     * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
026     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
027     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
028     * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
029     * POSSIBILITY OF SUCH DAMAGE.
030     */
031    package com.threerings.jpkg.ant.dpkg;
032    
033    import java.io.File;
034    import java.io.IOException;
035    import java.util.ArrayList;
036    import java.util.List;
037    import java.util.Map.Entry;
038    
039    import org.apache.commons.io.IOUtils;
040    import org.apache.tools.ant.BuildException;
041    import org.apache.tools.ant.Project;
042    
043    import com.threerings.antidote.field.BaseField;
044    import com.threerings.antidote.field.OptionalField;
045    import com.threerings.antidote.field.RequiredField;
046    import com.threerings.antidote.property.FileProperty;
047    import com.threerings.antidote.property.StringProperty;
048    import com.threerings.jpkg.PathPermissions;
049    import com.threerings.jpkg.PermissionsMap;
050    import com.threerings.jpkg.ant.dpkg.dependencies.BaseDependency;
051    import com.threerings.jpkg.ant.dpkg.dependencies.Dependencies;
052    import com.threerings.jpkg.ant.dpkg.dependencies.PackageInfoDependency;
053    import com.threerings.jpkg.ant.dpkg.info.Info;
054    import com.threerings.jpkg.ant.dpkg.permissions.Permissions;
055    import com.threerings.jpkg.ant.dpkg.scripts.Scripts;
056    import com.threerings.jpkg.ant.dpkg.scripts.runner.PackageScript;
057    import com.threerings.jpkg.ant.dpkg.scripts.runner.ScriptRunner;
058    import com.threerings.jpkg.ant.dpkg.scripts.runner.UnexpectedScriptTypeException;
059    import com.threerings.jpkg.debian.PackageInfo;
060    import com.threerings.jpkg.debian.MaintainerScript.Type;
061    
062    import static com.threerings.antidote.MutabilityHelper.requiresValidation;
063    
064    /**
065     * The <dpkg> task <package> field. Holds all information needed to generate a given package.
066     */
067    public class Package extends BaseField
068    {
069        // from Field
070        public String getFieldName ()
071        {
072            return "package";
073        }
074    
075        /**
076         * Ant adder field: Set the package meta information.
077         */
078        public void addInfo (Info info)
079        {
080            _info.setField(info);
081        }
082    
083        /**
084         * Ant adder field: Set the list of maintainer scripts.
085         */
086        public void addScripts (Scripts scripts)
087        {
088            _scripts.setField(scripts);
089        }
090    
091        /**
092         * Ant adder field: Set the list of path permissions.
093         */
094        public void addPermissions (Permissions permissions)
095        {
096            _permissions.setField(permissions);
097        }
098    
099        /**
100         * Ant adder field: Set the list of package dependencies.
101         */
102        public void addDependencies (Dependencies dependencies)
103        {
104            _dependencies.setField(dependencies);
105        }
106    
107        /**
108         * Ant setter field: destroot. The directory where the root of the package starts.
109         */
110        public void setDestroot (String value)
111        {
112            _destroot.setValue(value);
113        }
114    
115        /**
116         * Ant setter field: filename. Optionally set the filename of the package output.
117         */
118        public void setFilename (String value)
119        {
120            _filenameProp.setValue(value);
121        }
122    
123        /**
124         * Returns the user data converted into a {@link PackageInfo} object. Cannot be called before validate().
125         */
126        public PackageInfo createPackageInfo (String distribution, String prefix)
127        {
128            final Info info = _info.getField();
129            final DpkgData data = new DpkgData(info.getPackageNameAsString(), info.getVersionAsString(),
130                distribution, prefix);
131            final PackageInfo packageInfo = info.getPackageInfo();
132    
133            if (_scripts.isSet()) {
134                appendPackageScripts(_scripts.getField().getPackageScripts(), packageInfo, data);
135            }
136    
137            if (_permissions.isSet()) {
138                appendPermissionsMap(_permissions.getField().getPermissionsMap(prefix), packageInfo);
139            }
140    
141            if (_dependencies.isSet()) {
142                appendDependencies(_dependencies.getField().getDependencies(), packageInfo);
143            }
144    
145            // log the fully populated package info data to the verbose log.
146            log(packageInfo.toString(), Project.MSG_VERBOSE);
147    
148            return packageInfo;
149        }
150    
151        /**
152         * Returns the destroot to use for this package. Cannot be called before validate().
153         */
154        public File getDestroot ()
155        {
156            return _destroot.getValue();
157        }
158    
159        /**
160         * Returns the filename to use for this package. Cannot be called before validate().
161         */
162        public String getFilename ()
163        {
164            requiresValidation(_filename);
165            return _filename;
166        }
167    
168        @Override // from BaseComponent
169        protected void validateField ()
170        {
171            // validate the fields
172            switch (validateChildFields(_info, _scripts, _permissions, _dependencies)) {
173                case ALL_INVALID:
174                case SOME_INVALID:
175                    return;
176    
177                case ALL_VALID:
178                    break;
179            }
180    
181            // validate the required properties
182            switch (validateProperties(_destroot)) {
183                case ALL_INVALID:
184                case SOME_INVALID:
185                    return;
186    
187                case ALL_VALID:
188                    break;
189            }
190    
191            // validate the optional properties
192            switch (validateOptionalProperties(_filenameProp)) {
193                case ALL_INVALID:
194                case SOME_INVALID:
195                    return;
196    
197                case ALL_VALID:
198                    break;
199            }
200    
201            _filename = generateFilename(_info.getField());
202        }
203    
204        /**
205         * Add any defined PackageScripts to ScriptRunners and add them to the PackageInfo.
206         */
207        private void appendPackageScripts (List<PackageScript> scripts, PackageInfo packageInfo, DpkgData data)
208        {
209            // write all the script source out to the debug log
210            printScriptsDebugging(scripts, data);
211    
212            // for each script type, encode all scripts of that type into a ScriptRunner
213            // and add that runner to the PackageInfo object.
214            for (final Type scriptType : Type.values()) {
215                final List<PackageScript> foundScripts = new ArrayList<PackageScript>();
216                for (final PackageScript script : scripts) {
217                    if (script.getTypes().contains(scriptType)) {
218                        foundScripts.add(script);
219                    }
220                }
221    
222                if (foundScripts.size() > 0) {
223                    try {
224                        final ScriptRunner runner = new ScriptRunner(scriptType, foundScripts, data);
225                        packageInfo.setMaintainerScript(runner);
226    
227                        // write the runner source out to the debug log
228                        printRunnerDebugging(runner);
229    
230                    } catch (final IOException e) {
231                        throw new BuildException(e);
232    
233                    } catch (final UnexpectedScriptTypeException uste) {
234                        throw new BuildException(uste);
235                    }
236                }
237            }
238        }
239    
240        /**
241         * Add the PermissionsMap to the PackageInfo.
242         */
243        private void appendPermissionsMap (PermissionsMap permissionsMap, PackageInfo packageInfo)
244        {
245            // write the permissions map to the debug log
246            log("Defined path permissions:\n", Project.MSG_VERBOSE);
247            log(permissionsMap.toString(), Project.MSG_VERBOSE);
248    
249            for (final Entry<String, PathPermissions> entry : permissionsMap.getPermissions()) {
250                packageInfo.addPathPermissions(entry.getKey(), entry.getValue());
251    
252            }
253        }
254    
255        /**
256         * Add a list of {@link BaseDependency} objects to the {@link PackageInfo} object.
257         */
258        private void appendDependencies (List<PackageInfoDependency> dependencies, PackageInfo packageInfo)
259        {
260            for (final PackageInfoDependency dependency : dependencies) {
261                dependency.addToPackageInfo(packageInfo);
262            }
263        }
264    
265        /**
266         * Print a list of PackageScripts to the debug log.
267         */
268        private void printScriptsDebugging (List<PackageScript> scripts, DpkgData data)
269        {
270            for (final PackageScript script : scripts) {
271                try {
272                    log("Defined PackageScript: name=[" + script.getFriendlyName() + "] " +
273                        "type=[" + script.getTypes() + "], source=[" +
274                        IOUtils.toString(script.getSource(data)) + "].", Project.MSG_VERBOSE);
275    
276                } catch (final Exception e) {
277                    throw new BuildException(e);
278                }
279            }
280        }
281    
282        /**
283         * Print the ScriptRunner to the debug log.
284         */
285        private void printRunnerDebugging (ScriptRunner runner)
286            throws IOException
287        {
288            log("Defined Script Runner for type " + runner.getType() + " :", Project.MSG_VERBOSE);
289            log(IOUtils.toString(runner.getStream()), Project.MSG_VERBOSE);
290        }
291    
292        /**
293         * Set the filename for this package field. Use the user supplied string if set, otherwise use
294         * a default.
295         */
296        private String generateFilename (Info info)
297        {
298            String filename;
299            if (_filenameProp.isSet()) {
300                filename = _filenameProp.getValue();
301    
302            } else {
303                filename = info.getPackageNameAsString() + "_" + info.getVersionAsString() + EXTENSION;
304            }
305            return filename;
306        }
307    
308        /** The default file extension for the package output file. */
309        private static final String EXTENSION = ".dpkg";
310    
311        /** The filename, either from the user property or a default. */
312        private String _filename;
313    
314        /** Ant adder/setter fields. */
315        private final RequiredField<Info> _info = new RequiredField<Info>(Info.class, this);
316        private final OptionalField<Scripts> _scripts = new OptionalField<Scripts>(Scripts.class, this);
317        private final OptionalField<Permissions> _permissions = new OptionalField<Permissions>(Permissions.class, this);
318        private final OptionalField<Dependencies> _dependencies = new OptionalField<Dependencies>(Dependencies.class, this);
319        private final FileProperty _destroot = new FileProperty("destroot", this);
320        private final StringProperty _filenameProp = new StringProperty("filename", this);
321    }