// **********************************************************************
//
// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved.
//
// This copy of Ice Protobuf is licensed to you under the terms
// described in the ICE_PROTOBUF_LICENSE file included in this
// distribution.
//
// **********************************************************************
//package Ice.Ant;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.taskdefs.ExecTask;
import org.apache.tools.ant.taskdefs.Execute;
import org.apache.tools.ant.taskdefs.PumpStreamHandler;
import org.apache.tools.ant.types.Commandline;
import org.apache.tools.ant.types.Commandline.Argument;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.StringReader;
import java.io.BufferedReader;
import java.io.BufferedWriter;
/**
* An ant task for protoc.
*
* Attributes specific to protoc:
*
* translator - The pathname of the translator (default: "protoc").
* protocpath - The value for the --proto_path translator option.
* outputdir - The value for the --java_out translator option.
* dependencyfile - The file in which dependencies are stored (default: ".pbdepend").
*
* Example:
*
*
*
*
*
*
*
*
*
*
*
*
*
*
* The element installs the protoctask task.
*/
public class ProtocTask extends org.apache.tools.ant.Task
{
public
ProtocTask()
{
_translator = null;
_outputDir = null;
_protocPath = null;
_outputDirString = null;
}
public void
setDependencyFile(File file)
{
_dependencyFile = file;
}
public void
setOutputdir(File dir)
{
_outputDir = dir;
_outputDirString = _outputDir.toString();
if(_outputDirString.indexOf(' ') != -1)
{
_outputDirString = '"' + _outputDirString + '"';
}
}
public void
setProtocpath(File dir)
{
_protocPath = dir.toString();
}
public void
setTranslator(File prog)
{
_translator = prog;
}
public FileSet
createFileset()
{
FileSet fileset = new FileSet();
_fileSets.add(fileset);
return fileset;
}
@SuppressWarnings("unchecked")
public void
execute()
throws BuildException
{
if(_fileSets.isEmpty())
{
throw new BuildException("No fileset specified");
}
//
// Read the set of dependencies for this task.
//
java.util.HashMap dependencies = readDependencies();
//
// Compose a list of the files that need to be translated. A
// file needs to translated if we can't find a dependency in
// the dependency table or if its dependency is not up-to-date
// anymore (the proto file changed since the dependency was
// last updated)
//
java.util.Vector buildList = new java.util.Vector();
java.util.Vector skipList = new java.util.Vector();
java.util.Iterator p = _fileSets.iterator();
while(p.hasNext())
{
FileSet fileset = (FileSet)p.next();
DirectoryScanner scanner = fileset.getDirectoryScanner(getProject());
scanner.scan();
String[] files = scanner.getIncludedFiles();
for(int i = 0; i < files.length; i++)
{
File proto = new File(fileset.getDir(getProject()), files[i]);
ProtoDependency depend = (ProtoDependency)dependencies.get(getTargetKey(proto.toString()));
if(depend == null || !depend.isUpToDate())
{
buildList.addElement(proto);
}
else
{
skipList.addElement(proto);
}
}
java.util.Iterator i = skipList.iterator();
while(i.hasNext())
{
File file = (File)i.next();
log("skipping " + file.getName());
}
}
//
// Run the translator
//
if(!buildList.isEmpty())
{
String translator;
if(_translator == null)
{
translator = "protoc";
}
else
{
translator = _translator.toString();
}
StringBuilder cmd = new StringBuilder(128);
//
// Add --java_out.
//
if(_outputDir != null)
{
cmd.append(" --java_out=");
cmd.append(stripDriveLetter(_outputDirString));
}
//
// Add --proto_path
//
if(_protocPath != null)
{
cmd.append(" --proto_path=");
cmd.append(stripDriveLetter(_protocPath));
}
//
// Add files to be translated
//
for(int i = 0; i < buildList.size(); i++)
{
File f = (File)buildList.elementAt(i);
cmd.append(" ");
String s = stripDriveLetter(f.toString());
if(s.indexOf(' ') != -1)
{
cmd.append('"');
cmd.append(s);
cmd.append('"');
}
else
{
cmd.append(s);
}
}
//
// Execute
//
log(translator + " " + cmd);
ExecTask task = (ExecTask)getProject().createTask("exec");
task.setFailonerror(true);
Argument arg = task.createArg();
arg.setLine(cmd.toString());
task.setExecutable(translator);
task.execute();
//
// Update dependency file.
//
for(int i = 0; i < buildList.size(); i++)
{
ProtoDependency depend = new ProtoDependency();
depend._timeStamp = new java.util.Date().getTime();
depend._dependency = ((File)buildList.elementAt(i)).toString();
dependencies.put(getTargetKey(depend._dependency), depend);
}
writeDependencies(dependencies);
}
}
private String
getTargetKey(String proto)
{
//
// Since the dependency file can be shared by several proto
// tasks we need to make sure that each dependency has a
// unique key. We use the name of the task, the output
// directory and the name of the proto file to be compiled.
//
// If there's two protoc tasks using the same dependency
// file, with the same output dir and which compiles the same
// protoc file they'll use the same dependency.
//
return "protoc " + _outputDir.toString() + " " + proto;
}
// This is to work around a bug with protoc, where it does not
// accept drive letters in path names. See
// http://bugzilla/bugzilla/show_bug.cgi?id=3349
//
private String
stripDriveLetter(String s)
{
if(s.length() > 1 && s.charAt(1) == ':')
{
return s.substring(2);
}
return s;
}
//
// Read the dependency file.
//
private java.util.HashMap
readDependencies()
{
if(_dependencyFile == null)
{
if(_outputDir != null)
{
_dependencyFile = new File(_outputDir, ".pbdepend");
}
else
{
_dependencyFile = new File(".pbdepend");
}
}
try
{
java.io.ObjectInputStream in = new java.io.ObjectInputStream(new java.io.FileInputStream(_dependencyFile));
java.util.HashMap dependencies = (java.util.HashMap)in.readObject();
in.close();
return dependencies;
}
catch(java.io.IOException ex)
{
}
catch(java.lang.ClassNotFoundException ex)
{
}
return new java.util.HashMap();
}
private void
writeDependencies(java.util.HashMap dependencies)
{
try
{
java.io.ObjectOutputStream out = new java.io.ObjectOutputStream(new FileOutputStream(_dependencyFile));
out.writeObject(dependencies);
out.close();
}
catch(java.io.IOException ex)
{
throw new BuildException("Unable to write dependencies in file " + _dependencyFile.getPath() + ": " + ex);
}
}
//
// A proto dependency.
//
// * the _timeStamp attribute contains the last time the proto
// file was compiled.
//
// * the _dependency attribute contains the .proto file.
//
private class ProtoDependency implements java.io.Serializable
{
private void writeObject(java.io.ObjectOutputStream out)
throws java.io.IOException
{
out.writeObject(_dependency);
out.writeLong(_timeStamp);
}
private void readObject(java.io.ObjectInputStream in)
throws java.io.IOException, java.lang.ClassNotFoundException
{
_dependency = (String)in.readObject();
_timeStamp = in.readLong();
}
public boolean
isUpToDate()
{
File dep = new File(_dependency);
if(!dep.exists() || _timeStamp < dep.lastModified())
{
return false;
}
return true;
}
public String _dependency;
public long _timeStamp;
}
private File _translator;
private File _dependencyFile;
private File _outputDir;
private String _outputDirString;
private String _protocPath;
private java.util.List _fileSets = new java.util.LinkedList();
}