Implemented a TarCat Ant task to slurp tars directly into other tars. This allows us to preserve things like permissions and symlinks on hostile operating systems.
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@174 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/build-tools/ant-gwt/build.xml b/build-tools/ant-gwt/build.xml
new file mode 100644
index 0000000..43c0f27
--- /dev/null
+++ b/build-tools/ant-gwt/build.xml
@@ -0,0 +1,34 @@
+<project name="ant-gwt" default="build" basedir=".">
+ <property name="gwt.root" location="../.." />
+ <property name="project.tail" value="build-tools/ant-gwt" />
+ <import file="${gwt.root}/common.ant.xml" />
+
+ <target name="compile" description="Compiles this project">
+ <mkdir dir="${javac.out}" />
+ <gwt.javac>
+ <classpath>
+ <pathelement location="${gwt.tools.lib}/apache/ant-1.6.5.jar" />
+ </classpath>
+ </gwt.javac>
+ </target>
+
+ <target name="build" depends="compile" description="Packages this project into a jar">
+ <mkdir dir="${gwt.build.lib}" />
+ <gwt.jar destfile="${gwt.build.lib}/${ant.project.name}.jar">
+ <fileset dir="src" />
+ <fileset dir="${javac.out}" />
+ </gwt.jar>
+ </target>
+
+ <target name="checkstyle" description="Static analysis of source">
+ <gwt.checkstyle>
+ <fileset dir="src" />
+ </gwt.checkstyle>
+ </target>
+
+ <target name="clean" description="Cleans this project's intermediate and output files">
+ <delete dir="${project.build}" />
+ <delete file="${project.lib}" />
+ </target>
+
+</project>
diff --git a/build-tools/ant-gwt/src/com/google/gwt/ant/taskdefs/TarCat.java b/build-tools/ant-gwt/src/com/google/gwt/ant/taskdefs/TarCat.java
new file mode 100644
index 0000000..45f12b9
--- /dev/null
+++ b/build-tools/ant-gwt/src/com/google/gwt/ant/taskdefs/TarCat.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2006 Google Inc.
+ *
+ * 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.google.gwt.ant.taskdefs;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.Tar;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.bzip2.CBZip2InputStream;
+import org.apache.tools.tar.TarEntry;
+import org.apache.tools.tar.TarInputStream;
+import org.apache.tools.tar.TarOutputStream;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Vector;
+import java.util.zip.GZIPInputStream;
+
+/**
+ * An extension to the Ant Tar task that supports slurping in other tar files
+ * without loss of information (such as permissions or symlinks). It behaves in
+ * all respects like the basic Tar task, but adds the nested element
+ * <includetar> which declares another tar file whose <i>contents</i>
+ * should be added to the file being created.
+ *
+ * In addition to preserving permissions and symlinks no matter what the host
+ * operating system is, there are performance advantages to this approach.
+ * Bypassing the file system cuts the disk activity to 50% or less. The
+ * intermediate files normally generated require data the size of the tar itself
+ * to be both written and read, not to mention the overhead of creating the
+ * individual files, which will generally have to be deleted later. Furthurmore,
+ * since the source and target are often zipped, the savings can be well over
+ * 50%.
+ *
+ * Example use:
+ *
+ * <pre>
+ * <taskdef name="tar.cat"
+ * classname="com.google.gwt.ant.taskdefs.TarCat"
+ * classpath="${gwt.build.lib}/ant-gwt.jar" />
+ * <tar.cat destfile="foo.tar.gz" compression="gzip" longfile="gnu">
+ * <!-- all normal tar attributes and elements supported -->
+ * <tarfileset dir="foo/src">
+ * <include name="*.class" />
+ * </tarfileset>
+ * <!-- tar.cat adds the ability to directly slurp in other tar files -->
+ * <includetar src="bar.tar.gz" compression="gzip" prefix="bar/" />
+ * </tar.cat>
+ * </pre>
+ */
+public class TarCat extends Tar {
+
+ /**
+ * This is a tar file that should be included into a tar operation.
+ */
+ public static class IncludeTar {
+ /**
+ * The compression method to use to access the included tar file.
+ */
+ private UntarCompressionMethod compression = new UntarCompressionMethod();
+
+ /**
+ * An association from a super Tar to a derived TarCat.
+ */
+ private TarFileSet wrapper;
+
+ /**
+ * Constructs a new IncludeTar.
+ *
+ * @param wrapper the association from a super Tar to a derived TarExt
+ */
+ public IncludeTar(TarFileSet wrapper) {
+ this.wrapper = wrapper;
+ }
+
+ /**
+ * Set decompression algorithm to use; default=none.
+ *
+ * Allowable values are
+ * <ul>
+ * <li>none - no compression
+ * <li>gzip - Gzip compression
+ * <li>bzip2 - Bzip2 compression
+ * </ul>
+ *
+ * @param method compression method
+ */
+ public void setCompression(UntarCompressionMethod method) {
+ compression = method;
+ }
+
+ /**
+ * If the prefix attribute is set, all files in the fileset are prefixed
+ * with that path in the archive. optional.
+ *
+ * @param prefix the path prefix.
+ */
+ public void setPrefix(String prefix) {
+ wrapper.setPrefix(prefix);
+ }
+
+ /**
+ * Set the name/location of a tar file to add to a tar operation.
+ *
+ * @param tarFile the tar file to read
+ */
+ public void setSrc(File tarFile) {
+ wrapper.setFile(tarFile);
+ }
+ }
+
+ /**
+ * Straight copy from
+ * {@link org.apache.tools.ant.taskdefs.Untar.UntarCompressionMethod} due to
+ * access restrictions.
+ */
+ public static final class UntarCompressionMethod extends EnumeratedAttribute {
+
+ private static final String BZIP2 = "bzip2";
+ private static final String GZIP = "gzip";
+ private static final String NONE = "none";
+
+ public UntarCompressionMethod() {
+ super();
+ setValue(NONE);
+ }
+
+ public String[] getValues() {
+ return new String[] {NONE, GZIP, BZIP2};
+ }
+
+ private InputStream decompress(final File file, final InputStream istream)
+ throws IOException, BuildException {
+ final String value = getValue();
+ if (GZIP.equals(value)) {
+ return new GZIPInputStream(istream);
+ } else {
+ if (BZIP2.equals(value)) {
+ final char[] magic = new char[] {'B', 'Z'};
+ for (int i = 0; i < magic.length; i++) {
+ if (istream.read() != magic[i]) {
+ throw new BuildException("Invalid bz2 file." + file.toString());
+ }
+ }
+ return new CBZip2InputStream(istream);
+ }
+ }
+ return istream;
+ }
+ }
+
+ /**
+ * The set of tars to include in this tar operation.
+ */
+ Vector includeTars = new Vector();
+
+ /**
+ * A set of tarfileset wrappers mapped to includeTars.
+ */
+ Vector includeTarWrappers = new Vector();
+
+ /**
+ * Creates a TarExt task instance.
+ */
+ public TarCat() {
+ }
+
+ /**
+ * Add a new tar to include in this tar operation.
+ */
+ public IncludeTar createIncludeTar() {
+ /*
+ * Create a dummy tarfileset to hold our own includeTars and add it to the
+ * super class. This is how we get the super class to call us back during
+ * execution.
+ */
+ TarFileSet wrapper = super.createTarFileSet();
+ IncludeTar includeTar = new IncludeTar(wrapper);
+ includeTars.addElement(includeTar);
+ includeTarWrappers.add(wrapper);
+ return includeTar;
+ }
+
+ protected void tarFile(File file, TarOutputStream tOut, String vPath,
+ TarFileSet tarFileSet) throws IOException {
+ // See if it's one of ours
+ int index = includeTarWrappers.indexOf(tarFileSet);
+ if (index < 0) {
+ super.tarFile(file, tOut, vPath, tarFileSet);
+ return;
+ }
+ IncludeTar includeTar = (IncludeTar) includeTars.get(index);
+ TarInputStream tIn = null;
+ try {
+ tIn = new TarInputStream(includeTar.compression.decompress(file,
+ new BufferedInputStream(new FileInputStream(file))));
+ TarEntry te = null;
+ while ((te = tIn.getNextEntry()) != null) {
+ vPath = te.getName();
+
+ // don't add "" to the archive
+ if (vPath.length() <= 0) {
+ continue;
+ }
+
+ if (te.isDirectory() && !vPath.endsWith("/")) {
+ vPath += "/";
+ }
+
+ String prefix = tarFileSet.getPrefix();
+ // '/' is appended for compatibility with the zip task.
+ if (prefix.length() > 0 && !prefix.endsWith("/")) {
+ prefix = prefix + "/";
+ }
+ vPath = prefix + vPath;
+
+ te.setName(vPath);
+ tOut.putNextEntry(te);
+
+ if (te.getSize() > 0) {
+ byte[] buffer = new byte[8 * 1024];
+ while (true) {
+ int count = tIn.read(buffer, 0, buffer.length);
+ if (count < 0) {
+ break;
+ }
+ tOut.write(buffer, 0, count);
+ }
+ }
+ tOut.closeEntry();
+ }
+
+ } catch (IOException ioe) {
+ throw new BuildException("Error while expanding " + file.getPath(), ioe,
+ getLocation());
+ } finally {
+ if (tIn != null) {
+ try {
+ tIn.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+ }
+}
diff --git a/build-tools/build.xml b/build-tools/build.xml
index 7846150..e16d42c 100644
--- a/build-tools/build.xml
+++ b/build-tools/build.xml
@@ -6,6 +6,10 @@
<!-- "build" is the default when subprojects are directly targetted -->
<property name="target" value="build" />
+ <target name="ant-gwt" description="Builds GWT specific Ant extensions">
+ <gwt.ant dir="ant-gwt" />
+ </target>
+
<target name="customchecks" description="Build the checkstyle extensions">
<gwt.ant dir="customchecks" />
</target>
@@ -14,7 +18,7 @@
<gwt.ant dir="doctool" />
</target>
- <target name="-do" depends="customchecks, doctool" description="Run all subprojects"/>
+ <target name="-do" depends="ant-gwt, customchecks, doctool" description="Run all subprojects"/>
<target name="build" description="Builds GWT">
<antcall target="-do">
diff --git a/common.ant.xml b/common.ant.xml
index c01d278..cddd93f 100755
--- a/common.ant.xml
+++ b/common.ant.xml
@@ -143,17 +143,14 @@
</sequential>
</macrodef>
- <macrodef name="gwt.untar">
- <attribute name="src" />
- <attribute name="dest" />
+ <macrodef name="gwt.tgz.cat">
+ <attribute name="destfile" />
+ <element name="tar.elements" implicit="true" optional="true" />
<sequential>
- <!-- GNU tar handles permissions and symlinks correctly -->
- <exec executable="tar" failonerror="true">
- <arg value="-xpzf" />
- <arg file="@{src}" />
- <arg value="-C" />
- <arg file="@{dest}" />
- </exec>
+ <taskdef name="tar.cat" classname="com.google.gwt.ant.taskdefs.TarCat" classpath="${gwt.build.lib}/ant-gwt.jar" />
+ <tar.cat destfile="${project.dist}" compression="gzip" longfile="gnu">
+ <tar.elements />
+ </tar.cat>
</sequential>
</macrodef>
diff --git a/distro-source/common.ant.xml b/distro-source/common.ant.xml
index 5f344c9..082fde8 100755
--- a/distro-source/common.ant.xml
+++ b/distro-source/common.ant.xml
@@ -6,6 +6,18 @@
<property name="project.distname" value="gwt-${dist.platform}-${gwt.version}" />
<property name="project.staging" location="${gwt.build.staging}/${project.distname}" />
+ <patternset id="chmod.executables">
+ <include name="*Creator*" />
+ <include name="samples/*/*-shell" />
+ <include name="samples/*/*-compile" />
+ </patternset>
+
+ <patternset id="chmod.not.executables">
+ <exclude name="*Creator*" />
+ <exclude name="samples/*/*-shell*" />
+ <exclude name="samples/*/*-compile*" />
+ </patternset>
+
<!-- copies a single sample into the staging directory -->
<macrodef name="stage.sample">
<attribute name="lname" />
@@ -24,12 +36,6 @@
<include name="*" />
</fileset>
</copy>
- <chmod perm="a+x">
- <fileset dir="${project.staging}/samples/@{uname}">
- <include name="@{uname}-compile*" />
- <include name="@{uname}-shell*" />
- </fileset>
- </chmod>
</sequential>
</macrodef>
@@ -69,14 +75,9 @@
<stage.sample lname="simplexml" uname="SimpleXML" />
<antcall target="stage.platform" />
- <chmod perm="a+r">
- <dirset dir="${project.staging}" />
- <fileset dir="${project.staging}" />
- </chmod>
- <chmod perm="a+x">
- <dirset dir="${project.staging}" />
+ <chmod perm="755">
<fileset dir="${project.staging}">
- <include name="*Creator*" />
+ <patternset refid="chmod.executables" />
</fileset>
</chmod>
</target>
diff --git a/distro-source/linux/build.xml b/distro-source/linux/build.xml
index cefc2cb..27e4773 100755
--- a/distro-source/linux/build.xml
+++ b/distro-source/linux/build.xml
@@ -4,7 +4,7 @@
<property name="project.dist" location="${gwt.build.dist}/${project.distname}.tar.gz" />
<target name="stage.platform" description="Copies platform-specific items into the staging area">
- <gwt.untar src="${gwt.tools.redist}/mozilla/mozilla-1.7.12.tar.gz" dest="${project.staging}" />
+ <untar src="${gwt.tools.redist}/mozilla/mozilla-1.7.12.tar.gz" dest="${project.staging}" compression="gzip" />
<copy todir="${project.staging}">
<fileset dir="${gwt.tools.lib}/eclipse">
<include name="libswt-*gtk-3235.so" />
@@ -14,13 +14,18 @@
<target name="build" depends="stage" description="Packages the distro staging area">
<mkdir dir="${gwt.build.dist}" />
- <!-- GNU tar handles permissions and symlinks correctly -->
- <exec executable="tar" failonerror="true">
- <arg value="-cpzf" />
- <arg value="${project.dist}" />
- <arg value="-C" />
- <arg file="${gwt.build.staging}" />
- <arg value="${project.distname}" />
- </exec>
+ <gwt.tgz.cat destfile="${project.dist}">
+ <tarfileset dir="${gwt.build.staging}/${project.distname}" prefix="${project.distname}">
+ <!-- Mozilla pulled in through includetar -->
+ <exclude name="mozilla-1.7.12/**" />
+ <patternset refid="chmod.not.executables" />
+ </tarfileset>
+ <tarfileset dir="${gwt.build.staging}/${project.distname}" prefix="${project.distname}" mode="755">
+ <!-- Mozilla pulled in through includetar -->
+ <exclude name="mozilla-1.7.12/**" />
+ <patternset refid="chmod.executables" />
+ </tarfileset>
+ <includetar src="${gwt.tools.redist}/mozilla/mozilla-1.7.12.tar.gz" compression="gzip" prefix="${project.distname}" />
+ </gwt.tgz.cat>
</target>
</project>
diff --git a/distro-source/mac/build.xml b/distro-source/mac/build.xml
index 4c9d901..cb4e146 100755
--- a/distro-source/mac/build.xml
+++ b/distro-source/mac/build.xml
@@ -4,7 +4,16 @@
<property name="project.dist" location="${gwt.build.dist}/${project.distname}.tar.gz" />
<target name="stage.platform" description="Copies platform-specific items into the staging area">
- <gwt.untar src="${gwt.tools.redist}/webkit/WebKit-418.9.tar.gz" dest="${project.staging}" />
+ <!--
+ Try to untar WebKit into the staging directory. GNU tar handles
+ permissions and symlinks correctly. It's okay if we fail here.
+ -->
+ <exec executable="tar" failifexecutionfails="false" failonerror="false">
+ <arg value="-xpzf" />
+ <arg file="${gwt.tools.redist}/webkit/WebKit-418.9.tar.gz" />
+ <arg value="-C" />
+ <arg file="${project.staging}" />
+ </exec>
<copy todir="${project.staging}">
<fileset dir="${gwt.tools.lib}/eclipse">
<include name="libswt-*carbon-3235.jnilib" />
@@ -16,13 +25,18 @@
<target name="build" depends="stage" description="Packages the distro staging area">
<mkdir dir="${gwt.build.dist}" />
- <!-- GNU tar handles permissions and symlinks correctly -->
- <exec executable="tar" failonerror="true">
- <arg value="-cpzf" />
- <arg value="${project.dist}" />
- <arg value="-C" />
- <arg file="${gwt.build.staging}" />
- <arg value="${project.distname}" />
- </exec>
+ <gwt.tgz.cat destfile="${project.dist}">
+ <tarfileset dir="${gwt.build.staging}/${project.distname}" prefix="${project.distname}">
+ <!-- Frameworks pulled in through includetar -->
+ <exclude name="Frameworks/**" />
+ <patternset refid="chmod.not.executables" />
+ </tarfileset>
+ <tarfileset dir="${gwt.build.staging}/${project.distname}" prefix="${project.distname}" mode="755">
+ <!-- Frameworks pulled in through includetar -->
+ <exclude name="Frameworks/**" />
+ <patternset refid="chmod.executables" />
+ </tarfileset>
+ <includetar src="${gwt.tools.redist}/webkit/WebKit-418.9.tar.gz" compression="gzip" prefix="${project.distname}" />
+ </gwt.tgz.cat>
</target>
</project>
diff --git a/distro-source/windows/build.xml b/distro-source/windows/build.xml
index 38361a3..5bc73f7 100755
--- a/distro-source/windows/build.xml
+++ b/distro-source/windows/build.xml
@@ -14,9 +14,7 @@
<target name="build" depends="stage" description="Packages the distro staging area">
<mkdir dir="${gwt.build.dist}" />
<zip destfile="${project.dist}">
- <fileset dir="${gwt.build.staging}">
- <include name="${project.distname}/**" />
- </fileset>
+ <zipfileset dir="${gwt.build.staging}/${project.distname}" prefix="${project.distname}" />
</zip>
</target>
</project>
diff --git a/eclipse/build-tools/ant-gwt/.checkstyle b/eclipse/build-tools/ant-gwt/.checkstyle
new file mode 100644
index 0000000..afcc9b2
--- /dev/null
+++ b/eclipse/build-tools/ant-gwt/.checkstyle
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<fileset-config file-format-version="1.2.0" simple-config="false">
+ <fileset name="all" enabled="true" check-config-name="GWT Checks" local="false">
+ <file-match-pattern match-pattern="." include-pattern="true"/>
+ </fileset>
+</fileset-config>
diff --git a/eclipse/build-tools/ant-gwt/.classpath b/eclipse/build-tools/ant-gwt/.classpath
new file mode 100644
index 0000000..727a3f7
--- /dev/null
+++ b/eclipse/build-tools/ant-gwt/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="core/src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="var" path="GWT_TOOLS/lib/apache/ant-1.6.5.jar" sourcepath="/GWT_TOOLS/lib/apache/ant-1.6.5-src.zip"/>
+ <classpathentry kind="var" path="GWT_TOOLS/lib/tomcat/ant-launcher-1.6.5.jar"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/eclipse/build-tools/ant-gwt/.project b/eclipse/build-tools/ant-gwt/.project
new file mode 100644
index 0000000..5a5195a
--- /dev/null
+++ b/eclipse/build-tools/ant-gwt/.project
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>ant-gwt</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.atlassw.tools.eclipse.checkstyle.CheckstyleBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>com.atlassw.tools.eclipse.checkstyle.CheckstyleNature</nature>
+ </natures>
+ <linkedResources>
+ <link>
+ <name>core</name>
+ <type>2</type>
+ <location>GWT_ROOT/build-tools/ant-gwt</location>
+ </link>
+ </linkedResources>
+</projectDescription>