| /* |
| * The Apache Software License, Version 1.1 |
| * |
| * Copyright (c) 2002 The Apache Software Foundation. All rights |
| * reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. 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. |
| * |
| * 3. The end-user documentation included with the redistribution, if |
| * any, must include the following acknowlegement: |
| * "This product includes software developed by the |
| * Apache Software Foundation (http://www.apache.org/)." |
| * Alternately, this acknowlegement may appear in the software itself, |
| * if and wherever such third-party acknowlegements normally appear. |
| * |
| * 4. The names "The Jakarta Project", "Ant", and "Apache Software |
| * Foundation" must not be used to endorse or promote products derived |
| * from this software without prior written permission. For written |
| * permission, please contact apache@apache.org. |
| * |
| * 5. Products derived from this software may not be called "Apache" |
| * nor may "Apache" appear in their names without prior written |
| * permission of the Apache Group. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 THE APACHE SOFTWARE FOUNDATION OR |
| * ITS CONTRIBUTORS 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. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation. For more |
| * information on the Apache Software Foundation, please see |
| * <http://www.apache.org/>. |
| */ |
| package org.apache.ant.builder; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.io.FilenameFilter; |
| import java.io.IOException; |
| import java.lang.reflect.Method; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.StringTokenizer; |
| import java.util.jar.JarOutputStream; |
| import java.util.zip.ZipEntry; |
| |
| /** |
| * A helper class which allows the build files which have been converted to |
| * code to be built. |
| * |
| * @author <a href="mailto:conor@apache.org">Conor MacNeill</a> |
| * @created 16 February 2002 |
| */ |
| public class BuildHelper { |
| |
| /** |
| * Simple data class for storing info about a fileset. |
| * |
| * @author <a href="mailto:conor@apache.org">Conor MacNeill</a> |
| * @created 18 February 2002 |
| */ |
| private static class FileSetInfo { |
| /** The root directory of this fileset */ |
| private File root; |
| /** the list of files in the file set */ |
| private File[] files; |
| |
| } |
| |
| /** The properties which have been defined in the build */ |
| private Map properties = new HashMap(); |
| |
| /** Path objects created in the build */ |
| private Map paths = new HashMap(); |
| |
| /** Filesets created in the build */ |
| private Map filesets = new HashMap(); |
| |
| /** |
| * Set a property for the build |
| * |
| * @param propertyName the name of the property |
| * @param propertyValue the value of the property |
| */ |
| protected void setProperty(String propertyName, String propertyValue) { |
| if (!properties.containsKey(propertyName)) { |
| String value = resolve(propertyValue); |
| properties.put(propertyName, value); |
| } |
| } |
| |
| /** |
| * Create a Jar |
| * |
| * @param basedir the base directpory from which files are added to the |
| * jar |
| * @param metaInfDir the directory containing the META-INF for the jar |
| * @param metaInfIncludes the files to be included in the META-INF area |
| * of the jar |
| * @param jarFile the file in which the Jar is created |
| */ |
| protected void jar(String basedir, String jarFile, String metaInfDir, |
| String metaInfIncludes) { |
| try { |
| File base = new File(resolve(basedir)); |
| File jar = new File(resolve(jarFile)); |
| JarOutputStream jos |
| = new JarOutputStream(new FileOutputStream(jar)); |
| addToJar(jos, base, null); |
| if (metaInfDir != null) { |
| File[] metaFileSet = buildFileSet(metaInfDir, metaInfIncludes); |
| addFilesToJar(jos, new File(resolve(metaInfDir)), |
| metaFileSet, "META-INF"); |
| } |
| jos.close(); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| throw new RuntimeException("Unable to Jar file"); |
| } |
| } |
| |
| /** |
| * Compile a set of files |
| * |
| * @param srcDir the source directory |
| * @param destDir where the compiled classes will go |
| * @param classpathRef the id of a path object with the classpath for |
| * the build |
| */ |
| protected void javac(String srcDir, String destDir, String classpathRef) { |
| List javaFiles = new ArrayList(); |
| String src = resolve(srcDir); |
| StringTokenizer tokenizer = new StringTokenizer(src, ":"); |
| while (tokenizer.hasMoreTokens()) { |
| File srcLocation = new File(tokenizer.nextToken()); |
| getJavaFiles(srcLocation, javaFiles); |
| } |
| |
| File dest = new File(resolve(destDir)); |
| int numArgs = javaFiles.size() + 2; |
| if (classpathRef != null) { |
| numArgs += 2; |
| } |
| String[] args = new String[numArgs]; |
| int index = 0; |
| args[index++] = "-d"; |
| args[index++] = dest.getPath(); |
| if (classpathRef != null) { |
| String path = (String)paths.get(resolve(classpathRef)); |
| args[index++] = "-classpath"; |
| args[index++] = path; |
| } |
| for (Iterator i = javaFiles.iterator(); i.hasNext(); ) { |
| args[index++] = ((File)i.next()).getPath(); |
| } |
| |
| try { |
| Class c = Class.forName("com.sun.tools.javac.Main"); |
| Object compiler = c.newInstance(); |
| Method compile = c.getMethod("compile", |
| new Class[]{(new String[]{}).getClass()}); |
| compile.invoke(compiler, new Object[]{args}); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| throw new RuntimeException("Compile failed"); |
| } |
| } |
| |
| /** |
| * Copy a directory |
| * |
| * @param fromDir the source directory name |
| * @param toDir the destination directory name |
| */ |
| protected void copyFileset(String fromDir, String toDir) { |
| File from = new File(resolve(fromDir)); |
| File to = new File(resolve(toDir)); |
| copyDir(from, to); |
| } |
| |
| /** |
| * Add a fileset to this build helper |
| * |
| * @param name the name of the fileset (its id) |
| * @param root the root directory of the fileset |
| * @param files the files in the fileset |
| */ |
| protected void addFileSet(String name, File root, File[] files) { |
| FileSetInfo info = new FileSetInfo(); |
| info.root = root; |
| info.files = files; |
| filesets.put(name, info); |
| } |
| |
| /** |
| * Copy a fileset given a reference to the source fileset |
| * |
| * @param toDir the name of the destination directory |
| * @param fileSetRef the fileset to be copied |
| */ |
| protected void copyFilesetRef(String fileSetRef, String toDir) { |
| FileSetInfo fileset = (FileSetInfo)filesets.get(resolve(fileSetRef)); |
| if (fileset != null) { |
| File to = new File(resolve(toDir)); |
| copyFileList(fileset.root, fileset.files, to); |
| } |
| } |
| |
| /** |
| * Make a directory |
| * |
| * @param dirName the name of the directory path to be created. |
| */ |
| protected void mkdir(String dirName) { |
| File dir = new File(resolve(dirName)); |
| dir.mkdirs(); |
| } |
| |
| /** |
| * Create a path object |
| * |
| * @param pathName the name of the path object in the build |
| */ |
| protected void createPath(String pathName) { |
| String path = ""; |
| paths.put(pathName, path); |
| } |
| |
| /** |
| * Add a fileset to a path |
| * |
| * @param pathName the name of the path |
| * @param filesetDir the base directory of the fileset |
| * @param filesetIncludes the files to be included in the fileset |
| */ |
| protected void addFileSetToPath(String pathName, String filesetDir, |
| String filesetIncludes) { |
| File[] files = buildFileSet(filesetDir, filesetIncludes); |
| String currentPath = (String)paths.get(pathName); |
| for (int i = 0; i < files.length; ++i) { |
| if (currentPath == null || currentPath.length() == 0) { |
| currentPath = files[i].getPath(); |
| } else { |
| currentPath = currentPath + File.pathSeparator |
| + files[i].getPath(); |
| } |
| } |
| paths.put(pathName, currentPath); |
| } |
| |
| /** |
| * Add a new element to a path |
| * |
| * @param pathName the name of the path object to be updated |
| * @param location the location to be added to the path |
| */ |
| protected void addPathElementToPath(String pathName, String location) { |
| String pathElement = resolve(location).replace('/', File.separatorChar); |
| String currentPath = (String)paths.get(pathName); |
| if (currentPath == null || currentPath.length() == 0) { |
| currentPath = pathElement; |
| } else { |
| currentPath = currentPath + File.pathSeparator + pathElement; |
| } |
| paths.put(pathName, currentPath); |
| } |
| |
| /** |
| * Add an existing path to another path |
| * |
| * @param pathName the name of the path to which the path is to be added |
| * @param pathNameToAdd the name of the path to be added. |
| */ |
| protected void addPathToPath(String pathName, String pathNameToAdd) { |
| String pathToAdd = (String)paths.get(pathNameToAdd); |
| if (pathToAdd == null || pathToAdd.length() == 0) { |
| return; |
| } |
| |
| String currentPath = (String)paths.get(pathName); |
| if (currentPath == null || currentPath.length() == 0) { |
| currentPath = pathToAdd; |
| } else { |
| currentPath = currentPath + File.pathSeparator + pathToAdd; |
| } |
| paths.put(pathName, currentPath); |
| } |
| |
| /** |
| * Get the set of Java files to be compiled |
| * |
| * @param srcDir the directory to search (recursively searched) |
| * @param javaFiles the list of files to which Java files are added |
| */ |
| private void getJavaFiles(File srcDir, List javaFiles) { |
| File[] files = srcDir.listFiles(); |
| for (int i = 0; i < files.length; ++i) { |
| if (files[i].isDirectory()) { |
| getJavaFiles(files[i], javaFiles); |
| } else if (files[i].getPath().endsWith(".java")) { |
| javaFiles.add(files[i]); |
| } |
| } |
| } |
| |
| /** |
| * Copy a file |
| * |
| * @param from the source location |
| * @param dest the destination location |
| */ |
| private void copyFile(File from, File dest) { |
| if (from.exists()) { |
| dest.getParentFile().mkdirs(); |
| try { |
| FileInputStream in = new FileInputStream(from); |
| FileOutputStream out = new FileOutputStream(dest); |
| byte[] buf = new byte[1024 * 16]; |
| int count = 0; |
| count = in.read(buf, 0, buf.length); |
| while (count != -1) { |
| out.write(buf, 0, count); |
| count = in.read(buf, 0, buf.length); |
| } |
| |
| in.close(); |
| out.close(); |
| } catch (IOException ioe) { |
| ioe.printStackTrace(); |
| throw new RuntimeException("Unable to copy files"); |
| } |
| } |
| } |
| |
| /** |
| * Copy a list of files from one directory to another, preserving the |
| * relative paths |
| * |
| * @param root the root of the source directory |
| * @param files the files to be copied |
| * @param to the destination directory |
| */ |
| private void copyFileList(File root, File[] files, File to) { |
| for (int i = 0; i < files.length; ++i) { |
| if (files[i].getName().equals("CVS")) { |
| continue; |
| } |
| String name |
| = files[i].getPath().substring(root.getPath().length() + 1); |
| File dest = new File(to, name); |
| if (files[i].isDirectory()) { |
| copyDir(files[i], dest); |
| } else { |
| copyFile(files[i], dest); |
| } |
| } |
| } |
| |
| /** |
| * Copy a directory |
| * |
| * @param from the source directory |
| * @param to the destination directory |
| */ |
| private void copyDir(File from, File to) { |
| to.mkdirs(); |
| File[] files = from.listFiles(); |
| copyFileList(from, files, to); |
| } |
| |
| /** |
| * Add a directory to a Jar |
| * |
| * @param jos the JarOutputStream representing the Jar being created |
| * @param dir the directory to be added to the jar |
| * @param prefix the prefix in the jar at which the directory is to be |
| * added |
| * @exception IOException if the files cannot be added to the jar |
| */ |
| private void addToJar(JarOutputStream jos, File dir, String prefix) |
| throws IOException { |
| File[] files = dir.listFiles(); |
| addFilesToJar(jos, dir, files, prefix); |
| } |
| |
| /** |
| * Add a set of files to a jar |
| * |
| * @param jos the JarOutputStream representing the Jar being created |
| * @param dir the directory fro which the files are taken |
| * @param prefix the prefix in the jar at which the directory is to be |
| * added |
| * @param files the list of files to be added to the jar |
| * @exception IOException if the files cannot be added to the jar |
| */ |
| private void addFilesToJar(JarOutputStream jos, File dir, |
| File[] files, String prefix) throws IOException { |
| for (int i = 0; i < files.length; i++) { |
| String name = files[i].getPath().replace('\\', '/'); |
| name = name.substring(dir.getPath().length() + 1); |
| if (prefix != null) { |
| name = prefix + "/" + name; |
| } |
| ZipEntry ze = new ZipEntry(name); |
| jos.putNextEntry(ze); |
| if (files[i].isDirectory()) { |
| addToJar(jos, files[i], name); |
| } else { |
| FileInputStream fis = new FileInputStream(files[i]); |
| int count = 0; |
| byte[] buf = new byte[8 * 1024]; |
| count = fis.read(buf, 0, buf.length); |
| while (count != -1) { |
| jos.write(buf, 0, count); |
| count = fis.read(buf, 0, buf.length); |
| } |
| fis.close(); |
| } |
| } |
| } |
| |
| /** |
| * Build a simple fileset. Only simple inclusion filtering is supported |
| * - no complicated patterns. |
| * |
| * @param filesetDir the base directory of the fileset |
| * @param filesetIncludes the simple includes spec for the fileset |
| * @return the fileset expressed as an array of File instances. |
| */ |
| private File[] buildFileSet(String filesetDir, String filesetIncludes) { |
| if (filesetDir == null) { |
| return new File[0]; |
| } |
| final String includes = resolve(filesetIncludes); |
| if (includes.indexOf("**") != -1) { |
| throw new RuntimeException("Simple fileset cannot handle ** " |
| + "style includes"); |
| } |
| int index = 0; |
| if (includes.charAt(0) == '*') { |
| index = 1; |
| } |
| if (includes.indexOf("*", index) != -1) { |
| throw new RuntimeException("Simple fileset cannot handle * " |
| + "style includes except at start"); |
| } |
| |
| File base = new File(resolve(filesetDir)); |
| return base.listFiles( |
| new FilenameFilter() { |
| public boolean accept(File dir, String name) { |
| if (includes.startsWith("*")) { |
| return name.endsWith(includes.substring(1)); |
| } else { |
| return name.equals(includes); |
| } |
| } |
| }); |
| } |
| |
| /** |
| * Resolve the property references in a string |
| * |
| * @param propertyValue the string to be resolved |
| * @return the string with property references replaced by their current |
| * value. |
| */ |
| private String resolve(String propertyValue) { |
| String newValue = propertyValue; |
| while (newValue.indexOf("${") != -1) { |
| int index = newValue.indexOf("${"); |
| int endIndex = newValue.indexOf("}", index); |
| String propertyName = newValue.substring(index + 2, endIndex); |
| String repValue = (String)properties.get(propertyName); |
| newValue = newValue.substring(0, index) + |
| repValue + newValue.substring(endIndex + 1); |
| } |
| return newValue; |
| } |
| } |
| |