View Javadoc

1   package net.sf.jpkgmk;
2   
3   import java.io.File;
4   import java.io.IOException;
5   
6   import net.sf.jpkgmk.pkgmap.PkgMapBuilder;
7   import net.sf.jpkgmk.prototype.Prototype;
8   import net.sf.jpkgmk.prototype.PrototypeParser;
9   import net.sf.jpkgmk.util.FileUtil;
10  
11  import org.apache.commons.logging.Log;
12  import org.apache.commons.logging.LogFactory;
13  
14  /**
15   * <p>
16   * Pure java implementation of the <code>pkgmk</code> command. Should be a 1:1 port so that it can be used as a drop-in replacement.
17   * For the code that was ported see:
18   * <a href="http://cvs.opensolaris.org/source/xref/caiman/slim_source/usr/src/cmd/pkgcmds/pkgmk">pkgmk of opensolaris</a> and
19   * <a href="http://prdownloads.sourceforge.net/pkgbuild/">pkgbuild on sourceforge</a>
20   * </p>
21   * 
22   * <p>
23   * This implementation should be useful if you want to create your solaris packages regardless on which platform
24   * you are currently developing. Additionally it has the advantage that it can be used with maven/ant without the need of native platform calls.
25   * </p>
26   * 
27   * <p>
28   * The original solaris package providing this functionality has the name <code>SUNWpkgcmdsu</code>.
29   * </p>
30   * <p>
31   * Some further docs about packaging can be found at
32   * <a href="http://www.ibiblio.org/pub/packages/solaris/sparc/html/creating.solaris.packages.html">creating solaris packages</a>
33   * and <a href="http://blogs.sun.com/thaniwa/page/SolarisEn">solaris pkgmk tools</a>
34   * </p>
35   * 
36   * @author gommma (gommma AT users.sourceforge.net)
37   * @author Last changed by: $Author: gommma $
38   * @version $Revision: 2 $ $Date: 2008-08-20 21:14:19 +0200 (Mi, 20 Aug 2008) $
39   * @since 1.0
40   */
41  public class PkgMk
42  {
43  
44  	private Log log = LogFactory.getLog(PkgMk.class);
45  	
46  	/**
47  	 * flag to use GZIP command. Is set to <code>true</code> by default.
48  	 */ 
49  	private boolean gzip = true;
50  	
51  	private PkgInfo packageInfo;
52  	private Prototype prototype;
53  
54  	private File baseSourceDir;
55  	private File baseTargetDir;
56  	private String targetFileName;
57  	private boolean overwriteIfExists=true;
58  
59  	/**
60  	 * The result file of the package creation
61  	 */
62  	private File gzipFile;
63  	
64  	
65  	/**
66  	 * @param baseSourceDir The base source directory for searching all required packaging files
67  	 */
68  	public PkgMk(File baseSourceDir)
69  	{
70  		this(baseSourceDir, null, null, null, null);
71  	}
72  
73  	/**
74  	 * @param baseSourceDir The base source directory for searching all required packaging files
75  	 * @param baseTargetDir The base directory in which the whole new package should be created. Can be null
76  	 */
77  	public PkgMk(File baseSourceDir, File baseTargetDir)
78  	{
79  		this(baseSourceDir, baseTargetDir, null, null, null);
80  	}
81  
82  	/**
83  	 * @param baseSourceDir The base source directory for searching all required packaging files
84  	 * @param baseTargetDir The base directory in which the whole new package should be created. Can be null
85  	 * @param packageInfo The pkginfo object. Can be null if the file exists in the given source dir
86       * @param prototype The prototype object. Can be null if the file exists in the given source dir
87  	 */
88  	public PkgMk(File baseSourceDir, File baseTargetDir, PkgInfo packageInfo, Prototype prototype)
89  	{
90  		this(baseSourceDir, baseTargetDir, null, packageInfo, prototype);
91  	}
92  	
93  	/**
94  	 * @param baseSourceDir The base source directory for searching all required packaging files
95  	 * @param baseTargetDir The base directory in which the whole new package should be created. Can be null
96  	 * @param targetFileName The name to be used for the created tar.gz file. Can be null
97  	 * @param packageInfo The pkginfo object. Can be null if the file exists in the given source directory
98       * @param prototype The prototype object. Can be null if the file exists in the given source directory
99  	 */
100 	public PkgMk(File baseSourceDir, File baseTargetDir, String targetFileName, PkgInfo packageInfo, Prototype prototype)
101 	{
102 		super();
103 
104 		this.baseSourceDir = baseSourceDir;
105 		
106 		if(packageInfo == null) {
107 			this.packageInfo = resolvePackageInfo(this.baseSourceDir);
108 		}
109 		else {
110 			this.packageInfo = packageInfo;
111 		}
112 
113 		if(targetFileName == null) {
114 			this.targetFileName = autoCreatePackageFilename(this.packageInfo);			
115 		}
116 		else {
117 			this.targetFileName = targetFileName;
118 		}
119 		
120 		if(prototype == null) {
121 			this.prototype = resolvePrototype(this.baseSourceDir);
122 		}
123 		else {
124 			this.prototype = prototype;
125 		}
126 		
127 		if(baseTargetDir == null) {
128 			this.baseTargetDir = new File(System.getProperty("java.io.tmpdir"), this.packageInfo.getPkg());
129 			log.info("No target directory specified. Defaulting to "+ this.baseTargetDir);
130 		}
131 		else {
132 			if(baseTargetDir.exists()) {
133 				throw new IllegalArgumentException("The given target directory already exists - please specify a non existing target directory. " + baseTargetDir);
134 			}
135 			this.baseTargetDir = baseTargetDir;
136 		}
137 		
138 		String packageName = this.packageInfo.getPkg();
139 		File pkgFile = new File(packageName);
140 		if(pkgFile.exists()) {
141 			throw new IllegalArgumentException("The file or directory " + pkgFile.getAbsolutePath() + " already exists. Operation terminated...");
142 		}
143 
144 		// Validate this object
145 		validate();
146 	}
147 
148 	private void validate() {
149 		if(this.prototype == null) {
150 			throw new IllegalStateException("No prototype found - please define a prototype file");
151 		}
152 		if(this.packageInfo == null) {
153 			throw new IllegalStateException("No pkginfo found - please define a pkginfo file");
154 		}
155 		if(this.baseSourceDir == null) {
156 			throw new IllegalStateException("The field 'baseSourceDir' is not set");
157 		}
158 		if(this.baseTargetDir == null) {
159 			throw new IllegalStateException("The field 'baseTargetDir' is not set");
160 		}
161 		
162 	}
163 
164 
165 	private PkgInfo resolvePackageInfo(File basedir) {
166         // Search prototype file in baseSourceDir
167         File pkgInfoFile = findPkgInfo(basedir);
168 		try {
169 			PkgInfo packageInfo = new PkgInfoParser().parse(pkgInfoFile);
170 			return packageInfo;
171 		} catch (IOException e) {
172 			throw new PackageException("Exception while reading pkginfo file '" + pkgInfoFile + "'", e);
173 		}
174 	}
175 
176 	private Prototype resolvePrototype(File basedir) {
177 		// Search prototype file in baseSourceDir
178 		File prototypeFile = findPrototype(basedir);
179 		try {
180 			Prototype proto = new PrototypeParser().parse(prototypeFile);
181 			return proto;
182 		} catch (IOException e) {
183 			throw new PackageException("Exception while reading prototype file '" + prototypeFile + "'", e);
184 		}
185 	}
186 
187 	private File findPrototype(File basedir2) {
188 		File prototype = new File(basedir2, "prototype");
189 		File prototype2 = new File(basedir2, "Prototype");
190 		if(prototype.isFile()) {
191 			return prototype;
192 		}
193 		else if(prototype2.isFile()){
194 			return prototype2;
195 		}
196 		else {
197 			return null;
198 		}
199 	}
200 	
201 	private File findPkgInfo(File basedir2) 
202 	{
203 		File pkginfo = new File(basedir2, "pkginfo");
204 		File pkginfo2 = new File(basedir2, "Pkginfo");
205 		if(pkginfo.isFile()) {
206 			return pkginfo;
207 		}
208 		else if(pkginfo2.isFile()){
209 			return pkginfo2;
210 		}
211 		else {
212 		    throw new PackageException("Did not find pkginfo file in directory " + basedir2);
213 		}
214 	}
215 
216 
217 	private static String autoCreatePackageFilename(PkgInfo packageInfo) 
218 	{
219 		if(packageInfo == null) {
220 			throw new NullPointerException("The parameter 'packageInfo' must not be null");
221 		}
222 		return packageInfo.generateFilename();
223 	}
224 
225 
226 	/**
227 	 * @return The pkginfo object of the created package
228 	 */
229 	public PkgInfo getPackageInfo() {
230 		return packageInfo;
231 	}
232 
233 	/**
234 	 * @return The prototype object that was used for creating the package
235 	 */
236 	Prototype getPrototype() {
237 		return prototype;
238 	}
239 
240 	
241 	public boolean isOverwriteIfExists() {
242 		return overwriteIfExists;
243 	}
244 
245 	/**
246 	 * Whether or not to overwrite the files in the target directory if this directory already exists
247 	 * @param overwriteIfExists
248 	 */
249 	public void setOverwriteIfExists(boolean overwriteIfExists) {
250 		this.overwriteIfExists = overwriteIfExists;
251 	}
252 
253 	public File getGzipFile()
254 	{
255 		return this.gzipFile;
256 	}
257 	
258 
259 	/**
260 	 * Creates the whole package
261 	 * @throws IOException
262 	 */
263 	public void create() throws IOException
264 	{
265 		clean();
266 		
267 		// At first create the baseSourceDir if it does not exist yet
268 		FileUtil.createDir(this.baseTargetDir, true);
269 
270 		//create a "pkginfo" file
271 		this.packageInfo.create(this.baseTargetDir);
272 		
273 		// Create the pkgMap builder from the Prototype	
274 		PkgMapBuilder pkgMapBuilder = new PkgMapBuilder(this.baseTargetDir, this.prototype);
275 		//create "pkgmap" file
276 		pkgMapBuilder.create();
277 
278 		// gzip
279 		if(this.gzip) {
280 			File tarFile = new File(this.baseTargetDir, this.targetFileName + ".tar");
281 			FileUtil.createTar(this.baseTargetDir, tarFile);
282 			this.gzipFile = FileUtil.createGzip(tarFile);
283 		}
284 
285 		log.info("done - package created in directory '" + this.baseTargetDir + "': archive file: " + gzipFile);
286 	}
287 
288 	private void clean() 
289 	{
290 		int fileCount = FileUtil.countFiles(this.baseTargetDir);
291 		if(fileCount > 0) {
292 			if(!overwriteIfExists) {
293 				throw new PackageException("Will not overwrite the existing target directory '" + this.baseTargetDir + "'. Aborting package creation");
294 			}
295 			else {
296 				// Remove all files and directories
297 				log.info("Deleting directory '" + this.baseTargetDir + "' before creating the new package...");
298 				FileUtil.deleteRecursively(this.baseTargetDir);
299 			}
300 		}
301 	}
302 
303 	/**
304 	 * @see java.lang.Object#toString()
305 	 */
306 	@Override
307 	public String toString() {
308 		StringBuffer sb = new StringBuffer();
309 		sb.append(getClass().getName()).append("[");
310 		sb.append("baseSourceDir=").append(this.baseSourceDir);
311 		sb.append(", baseTargetDir=").append(this.baseTargetDir);
312 		sb.append(", targetFileName=").append(this.targetFileName);
313 		sb.append(", gzipFile=").append(this.gzipFile);
314 		sb.append(", overwriteIfExists=").append(this.overwriteIfExists);
315 		sb.append(", gzip=").append(this.gzip);
316 		sb.append(", packageInfo=").append(this.packageInfo);
317 		sb.append(", prototype=").append(this.prototype);
318 		sb.append("]");
319 		return sb.toString();
320 	}
321 	
322 	
323 	
324 	// TODO UnitTest complete creation of a complex package
325 	
326 	
327 	/**
328 	 * Entry point for the application
329 	 * @param args
330 	 */
331 	public static void main(String[] args)
332 	{
333 		Log log = LogFactory.getLog(PkgMk.class);
334 		try {
335 			// Current working directory is used as rootDir
336 			File workingDir = new File(System.getProperty("user.dir"));
337 			log.info("Current work dir: " + workingDir.getAbsolutePath());
338 			
339 			PkgMk pkgmk = new PkgMk(workingDir);
340 			pkgmk.create();
341 			
342  			System.exit(0);
343 			
344 		} catch (IOException e) {
345 			log.error("Exception while creating package: " + e, e);
346 			System.exit(1);
347 		}
348 	}
349 
350 	
351 //	########################################################################
352 //	# usage
353 //	########################################################################
354 //	private static void printUsage()
355 //	{
356 //		StringBuffer sb = new StringBuffer();
357 //		sb.append("Usage of $0");
358 //		sb.append("> $0 output [-g] [-user <username>] [-group <group>]");
359 //		sb.append( "             -p <pkg> -n <name> -v <version> -a <arch> [-c <category>]");
360 //		sb.append("            [-b <baseSourceDir>] [-d <description>] [-vd <vendor>] [-o <flag>...]");
361 //		sb.append( "  output             : output file name");
362 //		sb.append( "  [Details of Option]");
363 //		sb.append( "  -g                 : create gzipped package");
364 //		sb.append( "  -p  <pkg>          : package name (ex. SUMWgcc)");
365 //		sb.append( "  -n  <name>         : application name (ex. gcc)");
366 //		sb.append( "  -v  <version>      : application version (ex. 4.2.0)");
367 //		sb.append( "  -a  <arch>         : architecture, i86pc,sum4u...");
368 //		sb.append( "  -c  <category>     : category of the application (ex. system, application, GNOME2)");
369 //		sb.append( "                       (default = appilcation)");
370 //		sb.append( "  -b  <baseSourceDir>      : base directory of the application (default = /)");
371 //		sb.append( "  -d  <description>  : package description");
372 //		sb.append( "  -vd <vendor>       : vendor name (default = unknown)");
373 //		sb.append( "  -o  <flag>         : if you want to add other flags, please use it");
374 //		sb.append( "                       (ex. -o \"SUMW_PRODNAME=SumOS\" -o \"SUMW_PRODVERS=5.10\"");
375 //		System.out.println(sb.toString());
376 //	}
377 
378 
379 }