Commits

Anonymous committed 8483cf3

This adds a protoc plugin for maven and refactors the build of the java library to use it.

Comments (0)

Files changed (12)

    You will need to place the protoc executable in ../src.  (If you
    built it yourself, it should already be there.)
 
-3) Run the tests:
-
-     $ mvn test
-
-   If some tests fail, this library may not work correctly on your
-   system.  Continue at your own risk.
-
 4) Install the library into your Maven repository:
 
      $ mvn install
+     
+     Note:  If this command is run from the top-level directory, it
+     will build and instal the latest version of the plugin.
+     Otherwise, Maven will download the latest stable version from
+     the central repository.
 
 5) If you do not use Maven to manage your own build, you can build a
    .jar file to use:
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <parent>
-    <groupId>com.google</groupId>
-    <artifactId>google</artifactId>
-    <version>1</version>
+    <groupId>com.google.protobuf</groupId>
+    <artifactId>protobuf</artifactId>
+    <version>2.0.4-SNAPSHOT</version>
+    <relativePath>../pom.xml</relativePath>
   </parent>
-  <groupId>com.google.protobuf</groupId>
   <artifactId>protobuf-java</artifactId>
-  <version>2.0.4-SNAPSHOT</version>
   <packaging>jar</packaging>
   <name>Protocol Buffer Java API</name>
-  <description>
-    Protocol Buffers are a way of encoding structured data in an efficient yet
-    extensible format.
-  </description>
-  <inceptionYear>2008</inceptionYear>
-  <url>http://code.google.com/p/protobuf</url>
-  <licenses>
-    <license>
-      <name>New BSD license</name>
-      <url>http://www.opensource.org/licenses/bsd-license.php</url>
-      <distribution>repo</distribution>
-    </license>
-  </licenses>
-  <scm>
-    <url>http://code.google.com/p/protobuf/source/browse</url>
-    <connection>
-      scm:svn:http://protobuf.googlecode.com/svn/trunk/
-    </connection>
-  </scm>
   <dependencies>
     <dependency>
       <groupId>junit</groupId>
   <build>
     <plugins>
       <plugin>
-        <artifactId>maven-compiler-plugin</artifactId>
-        <configuration>
-          <source>1.5</source>
-          <target>1.5</target>
-        </configuration>
-      </plugin>
-      <plugin>
         <artifactId>maven-surefire-plugin</artifactId>
         <configuration>
           <includes>
           </includes>
         </configuration>
       </plugin>
-      <plugin>
-        <artifactId>maven-antrun-plugin</artifactId>
+       <plugin>
+        <groupId>com.google.protobuf.tools</groupId>
+        <artifactId>maven-protoc-plugin</artifactId>
         <executions>
-          <execution>
-            <id>generate-sources</id>
-            <phase>generate-sources</phase>
-            <configuration>
-              <tasks>
-                <mkdir dir="target/generated-sources" />
-                <exec executable="../src/protoc">
-                  <arg value="--java_out=target/generated-sources" />
-                  <arg value="--proto_path=../src" />
-                  <arg value="../src/google/protobuf/descriptor.proto" />
-                </exec>
-              </tasks>
-              <sourceRoot>target/generated-sources</sourceRoot>
-            </configuration>
-            <goals>
-              <goal>run</goal>
-            </goals>
-          </execution>
-          <execution>
-            <id>generate-test-sources</id>
-            <phase>generate-test-sources</phase>
-            <configuration>
-              <tasks>
-                <mkdir dir="target/generated-test-sources" />
-                <exec executable="../src/protoc">
-                  <arg value="--java_out=target/generated-test-sources" />
-                  <arg value="--proto_path=../src" />
-                  <arg value="--proto_path=src/test/java" />
-                  <arg value="../src/google/protobuf/unittest.proto" />
-                  <arg value="../src/google/protobuf/unittest_import.proto" />
-                  <arg value="../src/google/protobuf/unittest_mset.proto" />
-                  <arg
-                    value="src/test/java/com/google/protobuf/multiple_files_test.proto" />
-                  <arg
-                    value="../src/google/protobuf/unittest_optimize_for.proto" />
-                  <arg
-                    value="../src/google/protobuf/unittest_custom_options.proto" />
-                </exec>
-              </tasks>
-              <testSourceRoot>target/generated-test-sources</testSourceRoot>
-            </configuration>
-            <goals>
-              <goal>run</goal>
-            </goals>
-          </execution>
+            <execution>
+                <id>generate-sources</id>
+                <goals>
+                    <goal>compile</goal>
+                </goals>
+                <phase>generate-sources</phase>
+                <configuration>
+                    <protoSourceRoot>${basedir}/../src/</protoSourceRoot>
+                    <includes>
+                        <param>google/protobuf/descriptor.proto</param>
+                    </includes>
+                </configuration>
+            </execution>
+            <execution>
+                <id>generate-test-sources-1</id>
+                <goals>
+                    <goal>testCompile</goal>
+                </goals>
+                <phase>generate-test-sources</phase>
+                <configuration>
+                    <protoTestSourceRoot>${basedir}/../src/</protoTestSourceRoot>
+                    <includes>
+                        <param>google/protobuf/unittest*.proto</param>
+                    </includes>
+                </configuration>
+            </execution>
+            <execution>
+                <id>generate-test-sources-2</id>
+                <goals>
+                    <goal>testCompile</goal>
+                </goals>
+                <phase>generate-test-sources</phase>
+                <configuration>
+                    <additionalProtopathElements>
+                        <param>${basedir}/../src/</param>
+                    </additionalProtopathElements>
+                </configuration>
+            </execution>
+            <execution>
+                <id>generate-test-sources-3</id>
+                <goals>
+                    <goal>testCompile</goal>
+                </goals>
+                <phase>generate-test-sources</phase>
+                <configuration>
+                    <additionalProtopathElements>
+                        <param>${basedir}/../src/</param>
+                    </additionalProtopathElements>
+                </configuration>
+            </execution>
         </executions>
+        <configuration>
+            <protocExecutable>
+                ${basedir}/../src/protoc
+            </protocExecutable>
+        </configuration>
       </plugin>
     </plugins>
   </build>

java/src/main/java/com/google/protobuf/ProtocolMessageEnum.java

+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+package com.google.protobuf;
+
+import com.google.protobuf.Descriptors.EnumDescriptor;
+import com.google.protobuf.Descriptors.EnumValueDescriptor;
+
+/**
+ * Interface of useful methods added to all enums generated by the protocol
+ * compiler.
+ */
+public interface ProtocolMessageEnum {
+
+  /**
+   * Return the value's numeric value as defined in the .proto file.
+   */
+  int getNumber();
+
+  /**
+   * Return the value's descriptor, which contains information such as
+   * value name, number, and type.
+   */
+  EnumValueDescriptor getValueDescriptor();
+
+  /**
+   * Return the enum type's descriptor, which contains information
+   * about each defined value, etc.
+   */
+  EnumDescriptor getDescriptorForType();
+}

java/src/test/java/com/google/protobuf/multiple_files_test.proto

-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 Google Inc.  All rights reserved.
-// http://code.google.com/p/protobuf/
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * 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.
-//     * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
-// OWNER OR 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.
-
-// Author: kenton@google.com (Kenton Varda)
-//
-// A proto file which tests the java_multiple_files option.
-
-
-import "google/protobuf/unittest.proto";
-
-package protobuf_unittest;
-
-option java_multiple_files = true;
-option java_outer_classname = "MultipleFilesTestProto";
-
-message MessageWithNoOuter {
-  message NestedMessage {
-    optional int32 i = 1;
-  }
-  enum NestedEnum {
-    BAZ = 3;
-  }
-  optional NestedMessage nested = 1;
-  repeated TestAllTypes foreign = 2;
-  optional NestedEnum nested_enum = 3;
-  optional EnumWithNoOuter foreign_enum = 4;
-}
-
-enum EnumWithNoOuter {
-  FOO = 1;
-  BAR = 2;
-}
-
-service ServiceWithNoOuter {
-  rpc Foo(MessageWithNoOuter) returns(TestAllTypes);
-}
-
-extend TestAllExtensions {
-  optional int32 extension_with_outer = 1234567;
-}

java/src/test/proto/com/google/protobuf/multiple_files_test.proto

+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * 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.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS 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 COPYRIGHT
+// OWNER OR 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.
+
+// Author: kenton@google.com (Kenton Varda)
+//
+// A proto file which tests the java_multiple_files option.
+
+
+import "google/protobuf/unittest.proto";
+
+package protobuf_unittest;
+
+option java_multiple_files = true;
+option java_outer_classname = "MultipleFilesTestProto";
+
+message MessageWithNoOuter {
+  message NestedMessage {
+    optional int32 i = 1;
+  }
+  enum NestedEnum {
+    BAZ = 3;
+  }
+  optional NestedMessage nested = 1;
+  repeated TestAllTypes foreign = 2;
+  optional NestedEnum nested_enum = 3;
+  optional EnumWithNoOuter foreign_enum = 4;
+}
+
+enum EnumWithNoOuter {
+  FOO = 1;
+  BAR = 2;
+}
+
+service ServiceWithNoOuter {
+  rpc Foo(MessageWithNoOuter) returns(TestAllTypes);
+}
+
+extend TestAllExtensions {
+  optional int32 extension_with_outer = 1234567;
+}
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+     <groupId>com.google</groupId>
+     <artifactId>google</artifactId>
+     <version>1</version>
+  </parent>
+  <groupId>com.google.protobuf</groupId>
+  <artifactId>protobuf</artifactId>
+  <version>2.0.4-SNAPSHOT</version>
+  <packaging>pom</packaging>
+  <name>Protocol Buffers</name>
+  <description>
+    Protocol Buffers are a way of encoding structured data in an efficient yet
+    extensible format.
+  </description>
+  <inceptionYear>2008</inceptionYear>
+  <url>http://code.google.com/p/protobuf</url>
+  <licenses>
+    <license>
+      <name>New BSD license</name>
+      <url>http://www.opensource.org/licenses/bsd-license.php</url>
+      <distribution>repo</distribution>
+    </license>
+  </licenses>
+  <scm>
+    <url>http://code.google.com/p/protobuf/source/browse</url>
+    <connection>
+      scm:svn:http://protobuf.googlecode.com/svn/trunk/
+    </connection>
+  </scm>
+  <build>
+   <pluginManagement>
+    <plugins>
+      <plugin>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <configuration>
+          <source>1.5</source>
+          <target>1.5</target>
+        </configuration>
+        <inherited>true</inherited>
+      </plugin>
+    </plugins>
+   </pluginManagement>
+  </build>
+  <modules>
+    <module>java</module>
+    <module>tools/maven-plugin</module>
+  </modules>
+</project>

src/google/protobuf/testdata/golden_packed_fields_message

Binary file added.

tools/maven-plugin/pom.xml

+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.google.protobuf</groupId>
+        <artifactId>protobuf</artifactId>
+        <version>2.0.4-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <groupId>com.google.protobuf.tools</groupId>
+    <artifactId>maven-protoc-plugin</artifactId>
+    <packaging>maven-plugin</packaging>
+    <name>Maven Protoc Plugin</name>
+    <version>0.0.1-SNAPSHOT</version>
+    <prerequisites>
+        <maven>2.0.6</maven>
+    </prerequisites>
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.5</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-plugin-api</artifactId>
+            <version>2.0.9</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.collections</groupId>
+            <artifactId>google-collections</artifactId>
+            <version>0.8</version>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.plexus</groupId>
+            <artifactId>plexus-utils</artifactId>
+            <version>1.5.4</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-project</artifactId>
+            <version>2.0.9</version>
+        </dependency>
+    </dependencies>
+</project>

tools/maven-plugin/src/main/java/com/google/protobuf/maven/AbstractProtocMojo.java

+package com.google.protobuf.maven;
+
+import static com.google.common.base.Join.join;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Sets.newHashSet;
+import static java.util.Arrays.asList;
+import static java.lang.String.format;
+import static java.util.Collections.list;
+import static org.codehaus.plexus.util.FileUtils.cleanDirectory;
+import static org.codehaus.plexus.util.FileUtils.copyStreamToFile;
+import static org.codehaus.plexus.util.FileUtils.forceDeleteOnExit;
+import static org.codehaus.plexus.util.FileUtils.getFiles;
+
+import com.google.common.collect.ImmutableSet;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.project.MavenProjectHelper;
+import org.codehaus.plexus.util.cli.CommandLineException;
+import org.codehaus.plexus.util.io.RawInputStreamFacade;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+abstract class AbstractProtocMojo extends AbstractMojo {
+
+  private static final String PROTO_FILE_SUFFIX = ".proto";
+
+  private static final String DEFAULT_INCLUDES = "**/*" + PROTO_FILE_SUFFIX;
+
+  /**
+   * The current Maven project.
+   *
+   * @parameter default-value="${project}"
+   * @readonly
+   * @required
+   */
+  protected MavenProject project;
+
+  /**
+   * A helper used to add resources to the project.
+   *
+   * @component
+   * @required
+   */
+  protected MavenProjectHelper projectHelper;
+
+  /**
+   * This is the path to the {@code protoc} executable. By default it will
+   * search the {@code $PATH}.
+   *
+   * @parameter default-value="protoc"
+   * @required
+   */
+  private String protocExecutable;
+
+  /**
+   * @parameter
+   */
+  private File[] additionalProtopathElements = new File[]{};
+
+  /**
+   * Since {@code protoc} cannot access jars, proto files in dependenceies are
+   * extracted to this location and deleted on exit. This directory is always
+   * cleaned during execution.
+   *
+   * @parameter expression="${java.io.tmpdir}/maven-protoc"
+   * @required
+   */
+  private File temporaryProtoFileDirectory;
+
+  /**
+   *
+   * @parameter
+   */
+  private Set<String> includes = ImmutableSet.of(DEFAULT_INCLUDES);
+
+  /**
+   * @parameter
+   */
+  private Set<String> excludes = ImmutableSet.of();
+
+  /**
+   * Executes the mojo.
+   */
+  public void execute() throws MojoExecutionException, MojoFailureException {
+    checkParameters();
+    final File protoSourceRoot = getProtoSourceRoot();
+    if (protoSourceRoot.exists()) {
+      try {
+        ImmutableSet<File> protoFiles =
+            findProtoFilesInDirectory(protoSourceRoot);
+        if (protoFiles.isEmpty()) {
+          getLog().info("No proto files to compile.");
+        } else {
+          ImmutableSet<File> derivedProtopathElements =
+              makeProtopathFromJars(
+                  temporaryProtoFileDirectory, getDependencyArtifactFiles());
+          final File outputDirectory = getOutputDirectory();
+          outputDirectory.mkdirs();
+          Protoc protoc =
+              new Protoc.Builder(protocExecutable, outputDirectory)
+                  .addProtopathElement(protoSourceRoot)
+                  .addProtopathElements(derivedProtopathElements)
+                  .addProtopathElements(asList(additionalProtopathElements))
+                  .addProtoFiles(protoFiles)
+                  .build();
+          final int exitStatus = protoc.compile();
+          if (exitStatus != 0) {
+            throw new MojoFailureException(
+                "Protoc did not exit cleanly.  Review output for more information.");
+          }
+          attachFiles();
+        }
+
+      } catch (IOException e) {
+        throw new MojoExecutionException("An IO error occured", e);
+      } catch (IllegalArgumentException e) {
+        throw new MojoFailureException("Protoc failed to execute because: "
+            + e.getMessage(), e);
+      } catch (CommandLineException e) {
+        throw new MojoExecutionException(
+            "An error occured while invoking protoc.", e);
+      }
+    } else {
+      getLog()
+          .info(
+              format(
+                  "%s does not exist.  Review the configuration or consider disabling the plugin.",
+                  protoSourceRoot));
+    }
+  }
+
+  private void checkParameters() {
+    checkNotNull(project, "project");
+    checkNotNull(projectHelper, "projectHelper");
+    checkNotNull(protocExecutable, "protocExecutable");
+    final File protoSourceRoot = getProtoSourceRoot();
+    checkNotNull(protoSourceRoot);
+    checkArgument(!protoSourceRoot.isFile(),
+        "protoSourceRoot is a file, not a diretory");
+    checkNotNull(temporaryProtoFileDirectory, "temporaryProtoFileDirectory");
+    checkState(!temporaryProtoFileDirectory.isFile(),
+        "temporaryProtoFileDirectory is a file, not a directory");
+    final File outputDirectory = getOutputDirectory();
+    checkNotNull(outputDirectory);
+    checkState(!outputDirectory.isFile(),
+        "the outputDirectory is a file, not a directory");
+  }
+
+  protected abstract File getProtoSourceRoot();
+
+  protected abstract List<Artifact> getDependencyArtifacts();
+
+  protected abstract File getOutputDirectory();
+
+  protected abstract void attachFiles();
+
+  /**
+   * Gets the {@link File} for each dependency artifact.
+   *
+   * @return A set of all dependency artifacts.
+   */
+  private ImmutableSet<File> getDependencyArtifactFiles() {
+    Set<File> dependencyArtifactFiles = newHashSet();
+    for (Artifact artifact : getDependencyArtifacts()) {
+      dependencyArtifactFiles.add(artifact.getFile());
+    }
+    return ImmutableSet.copyOf(dependencyArtifactFiles);
+  }
+
+  /**
+   *
+   * @throws IOException
+   */
+  ImmutableSet<File> makeProtopathFromJars(
+      File temporaryProtoFileDirectory, Iterable<File> classpathElementFiles)
+      throws IOException {
+    checkNotNull(classpathElementFiles, "classpathElementFiles");
+    // clean the temporary directory to ensure that stale files aren't used
+    if (temporaryProtoFileDirectory.exists()) {
+      cleanDirectory(temporaryProtoFileDirectory);
+    }
+    Set<File> protoDirectories = newHashSet();
+    for (File classpathElementFile : classpathElementFiles) {
+      checkArgument(classpathElementFile.isFile(), "%s is not a file",
+          classpathElementFile);
+      // create the jar file. the constructor validates.
+      JarFile classpathJar = null;
+      try {
+        classpathJar = new JarFile(classpathElementFile);
+      } catch (IOException e) {
+        throw new IllegalArgumentException(format(
+            "%s was not a readable artifact", classpathElementFile));
+      }
+      for (JarEntry jarEntry : list(classpathJar.entries())) {
+        final String jarEntryName = jarEntry.getName();
+        if (jarEntry.getName().endsWith(PROTO_FILE_SUFFIX)) {
+          final File uncompressedCopy =
+              new File(new File(temporaryProtoFileDirectory, classpathJar
+                  .getName()), jarEntryName);
+          uncompressedCopy.getParentFile().mkdirs();
+          copyStreamToFile(new RawInputStreamFacade(classpathJar
+              .getInputStream(jarEntry)), uncompressedCopy);
+          protoDirectories.add(uncompressedCopy);
+        }
+      }
+
+    }
+    forceDeleteOnExit(temporaryProtoFileDirectory);
+    return ImmutableSet.copyOf(protoDirectories);
+  }
+
+  ImmutableSet<File> findProtoFilesInDirectory(File directory)
+      throws IOException {
+    checkNotNull(directory);
+    checkArgument(directory.isDirectory(), "%s is not a directory", directory);
+    // TODO(gak): plexus-utils needs generics
+    @SuppressWarnings("unchecked")
+    List<File> protoFilesInDirectory =
+        getFiles(directory, join(",", includes), join(",", excludes));
+    return ImmutableSet.copyOf(protoFilesInDirectory);
+  }
+
+  ImmutableSet<File> findProtoFilesInDirectories(
+      Iterable<File> directories) throws IOException {
+    checkNotNull(directories);
+    Set<File> protoFiles = newHashSet();
+    for (File directory : directories) {
+      protoFiles.addAll(findProtoFilesInDirectory(directory));
+    }
+    return ImmutableSet.copyOf(protoFiles);
+  }
+
+}

tools/maven-plugin/src/main/java/com/google/protobuf/maven/Protoc.java

+package com.google.protobuf.maven;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Lists.newLinkedList;
+import static com.google.common.collect.Sets.newHashSet;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+import org.codehaus.plexus.util.cli.CommandLineException;
+import org.codehaus.plexus.util.cli.CommandLineUtils;
+import org.codehaus.plexus.util.cli.Commandline;
+import org.codehaus.plexus.util.cli.DefaultConsumer;
+import org.codehaus.plexus.util.cli.StreamConsumer;
+
+import java.io.File;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * This class represents an invokable configuration of the {@code protoc}
+ * compiler. The actual executable is invoked using the plexus
+ * {@link Commandline}.
+ * 
+ * This class currently only supports generating java source files.
+ * 
+ * @author gak@google.com (Gregory Kick)
+ */
+final class Protoc {
+  private final String executable;
+  private final ImmutableSet<File> protoPathElements;
+  private final ImmutableSet<File> protofiles;
+  private final File javaOutputDirectory;
+
+  /**
+   * Constructs a new instance. This should only be used by the {@link Builder}.
+   * 
+   * @param executable The path to the {@code protoc} executable.
+   * @param protoPath The directories in which to search for imports.
+   * @param protoFiles The proto source files to compile.
+   * @param javaOutputDirectory The directory into which the java source files
+   *        will be generated.
+   */
+  private Protoc(String executable, ImmutableSet<File> protoPath,
+      ImmutableSet<File> protoFiles, File javaOutputDirectory) {
+    this.executable = checkNotNull(executable, "executable");
+    this.protoPathElements = checkNotNull(protoPath, "protoPath");
+    this.protofiles = checkNotNull(protoFiles, "protoFiles");
+    this.javaOutputDirectory =
+        checkNotNull(javaOutputDirectory, "javaOutputDirectory");
+  }
+
+  /**
+   * Invokes the {@code protoc} compiler using the configuration specified at
+   * construction.
+   * 
+   * @return The exit status of {@code protoc}.
+   * @throws CommandLineException
+   */
+  public int compile() throws CommandLineException {
+    Commandline cl = new Commandline(executable);
+    cl.addArguments(buildProtocCommand().toArray(new String[] {}));
+    StreamConsumer output = new DefaultConsumer();
+    StreamConsumer error = new DefaultConsumer();
+    return CommandLineUtils.executeCommandLine(cl, null, output, error);
+  }
+
+  /**
+   * Creates the command line arguments.
+   * 
+   * This method has been made visible for testing only.
+   * 
+   * @return A list consisting of the executable followed by any arguments.
+   */
+  ImmutableList<String> buildProtocCommand() {
+    final List<String> command = newLinkedList();
+    // add the executable
+    for (File protoPathElement : protoPathElements) {
+      command.add("--proto_path=" + protoPathElement);
+    }
+    command.add("--java_out=" + javaOutputDirectory);
+    for (File protoFile : protofiles) {
+      command.add(protoFile.toString());
+    }
+    return ImmutableList.copyOf(command);
+  }
+
+  /**
+   * This class builds {@link Protoc} instances.
+   * 
+   * @author gak@google.com (Gregory Kick)
+   */
+  static final class Builder {
+    private final String executable;
+    private final File javaOutputDirectory;
+    private Set<File> protopathElements;
+    private Set<File> protoFiles;
+
+    /**
+     * Constructs a new builder. The two parameters are present as they are
+     * required for all {@link Protoc} instances.
+     * 
+     * @param executable The path to the {@code protoc} executable.
+     * @param javaOutputDirectory The directory into which the java source files
+     *        will be generated.
+     * @throws NullPointerException If either of the arguments are {@code null}.
+     * @throws IllegalArgumentException If the {@code javaOutputDirectory} is
+     *         not a directory.
+     */
+    public Builder(String executable, File javaOutputDirectory) {
+      this.executable = checkNotNull(executable, "executable");
+      this.javaOutputDirectory = checkNotNull(javaOutputDirectory);
+      checkArgument(javaOutputDirectory.isDirectory());
+      this.protoFiles = newHashSet();
+      this.protopathElements = newHashSet();
+    }
+
+    /**
+     * Adds a proto file to be compiled. Proto files must be on the protopath
+     * and this method will fail if a proto file is added without first adding a
+     * parent directory to the protopath.
+     * 
+     * @param protoFile
+     * @return The builder.
+     * @throws IllegalStateException If a proto file is added without first
+     *         adding a parent directory to the protopath.
+     * @throws NullPointerException If {@code protoFile} is {@code null}.
+     */
+    public Builder addProtoFile(File protoFile) {
+      checkNotNull(protoFile);
+      checkArgument(protoFile.isFile());
+      checkArgument(protoFile.getName().endsWith(".proto"));
+      checkProtoFileIsInProtopath(protoFile);
+      protoFiles.add(protoFile);
+      return this;
+    }
+
+    private void checkProtoFileIsInProtopath(File protoFile) {
+      assert protoFile.isFile();
+      checkState(checkProtoFileIsInProtopathHelper(protoFile.getParentFile()));
+    }
+
+    private boolean checkProtoFileIsInProtopathHelper(File directory) {
+      assert directory.isDirectory();
+      if (protopathElements.contains(directory)) {
+        return true;
+      } else {
+        final File parentDirectory = directory.getParentFile();
+        return (parentDirectory == null) ? false
+            : checkProtoFileIsInProtopathHelper(parentDirectory);
+      }
+    }
+
+    /**
+     * @see #addProtoFile(File)
+     */
+    public Builder addProtoFiles(Iterable<File> protoFiles) {
+      for (File protoFile : protoFiles) {
+        addProtoFile(protoFile);
+      }
+      return this;
+    }
+
+    /**
+     * Adds the {@code protopathElement} to the protopath.
+     * 
+     * @param protopathElement A directory to be searched for imported protocol
+     *        buffer definitions.
+     * @return The builder.
+     * @throws NullPointerException If {@code protopathElement} is {@code null}.
+     * @throws IllegalArgumentException If {@code protpathElement} is not a
+     *         directory.
+     */
+    public Builder addProtopathElement(File protopathElement) {
+      checkNotNull(protopathElement);
+      checkArgument(protopathElement.isDirectory());
+      protopathElements.add(protopathElement);
+      return this;
+    }
+
+    /**
+     * @see #addProtopathElement(File)
+     */
+    public Builder addProtopathElements(Iterable<File> protopathElements) {
+      for (File protopathElement : protopathElements) {
+        addProtopathElement(protopathElement);
+      }
+      return this;
+    }
+
+    /**
+     * @return A configured {@link Protoc} instance.
+     * @throws IllegalStateException If no proto files have been added.
+     */
+    public Protoc build() {
+      checkState(!protoFiles.isEmpty());
+      return new Protoc(executable, ImmutableSet.copyOf(protopathElements),
+          ImmutableSet.copyOf(protoFiles), javaOutputDirectory);
+    }
+  }
+}

tools/maven-plugin/src/main/java/com/google/protobuf/maven/ProtocCompileMojo.java

+package com.google.protobuf.maven;
+
+import com.google.common.collect.ImmutableList;
+
+import org.apache.maven.artifact.Artifact;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * This mojo executes the {@code protoc} compiler for generating java sources
+ * from protocol buffer definitions. It also searches dependency artifacts for
+ * proto files and includes them in the protopath so that they can be
+ * referenced. Finally, it adds the proto files to the project as resources so
+ * that they are included in the final artifact.
+ *
+ * @phase generate-sources
+ * @goal compile
+ * @requiresDependencyResolution compile
+ */
+
+public final class ProtocCompileMojo extends AbstractProtocMojo {
+
+  /**
+   * The source directories containing the sources to be compiled.
+   *
+   * @parameter default-value="${basedir}/src/main/proto"
+   * @required
+   */
+  private File protoSourceRoot;
+
+  /**
+   * This is the directory into which the {@code .java} will be created.
+   *
+   * @parameter default-value="${project.build.directory}/generated-sources/protoc"
+   * @required
+   */
+  private File outputDirectory;
+
+  @Override
+  protected List<Artifact> getDependencyArtifacts() {
+    // TODO(gak): maven-project needs generics
+    @SuppressWarnings("unchecked")
+    List<Artifact> compileArtifacts = project.getCompileArtifacts();
+    return compileArtifacts;
+  }
+
+  @Override
+  protected File getOutputDirectory() {
+    return outputDirectory;
+  }
+
+  @Override
+  protected File getProtoSourceRoot() {
+    return protoSourceRoot;
+  }
+
+  @Override
+  protected void attachFiles() {
+    project.addCompileSourceRoot(outputDirectory.getAbsolutePath());
+    projectHelper.addResource(project, protoSourceRoot.getAbsolutePath(),
+        ImmutableList.of("**/*.proto"), ImmutableList.of());
+  }
+}

tools/maven-plugin/src/main/java/com/google/protobuf/maven/ProtocTestCompileMojo.java

+package com.google.protobuf.maven;
+
+import com.google.common.collect.ImmutableList;
+
+import org.apache.maven.artifact.Artifact;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ *
+ * @phase generate-test-sources
+ * @goal testCompile
+ * @requiresDependencyResolution test
+ */
+public final class ProtocTestCompileMojo extends AbstractProtocMojo {
+
+  /**
+   * The source directories containing the sources to be compiled.
+   *
+   * @parameter default-value="${basedir}/src/test/proto"
+   * @required
+   */
+  private File protoTestSourceRoot;
+
+  /**
+   * This is the directory into which the {@code .java} will be created.
+   *
+   * @parameter default-value="${project.build.directory}/generated-test-sources/protoc"
+   * @required
+   */
+  private File outputDirectory;
+
+  @Override
+  protected void attachFiles() {
+    project.addTestCompileSourceRoot(outputDirectory.getAbsolutePath());
+    projectHelper.addTestResource(project, protoTestSourceRoot.getAbsolutePath(),
+        ImmutableList.of("**/*.proto"), ImmutableList.of());
+  }
+
+  @Override
+  protected List<Artifact> getDependencyArtifacts() {
+    // TODO(gak): maven-project needs generics
+    @SuppressWarnings("unchecked")
+    List<Artifact> testArtifacts = project.getTestArtifacts();
+    return testArtifacts;
+  }
+
+  @Override
+  protected File getOutputDirectory() {
+    return outputDirectory;
+  }
+
+  @Override
+  protected File getProtoSourceRoot() {
+    return protoTestSourceRoot;
+  }
+
+}