blob: 3beb7feaa4d789784567d3606360d2f293f23845 [file] [log] [blame]
/*
* Copyright 1999-2004 The Apache Software Foundation
*
* 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 org.apache.jk.ant;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Enumeration;
import java.util.Vector;
import org.apache.jk.ant.compilers.CcCompiler;
import org.apache.jk.ant.compilers.CompilerAdapter;
import org.apache.jk.ant.compilers.GcjCompiler;
import org.apache.jk.ant.compilers.GcjLinker;
import org.apache.jk.ant.compilers.LibtoolCompiler;
import org.apache.jk.ant.compilers.LibtoolLinker;
import org.apache.jk.ant.compilers.LinkerAdapter;
import org.apache.jk.ant.compilers.MsvcCompiler;
import org.apache.jk.ant.compilers.MsvcLinker;
import org.apache.jk.ant.compilers.MwccCompiler;
import org.apache.jk.ant.compilers.MwldLinker;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.taskdefs.Execute;
import org.apache.tools.ant.taskdefs.ExecuteStreamHandler;
import org.apache.tools.ant.taskdefs.PumpStreamHandler;
import org.apache.tools.ant.types.Commandline;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.PatternSet;
/** Global properties
Same idea as in javac, some user .properties will set the local preferences,
including machine-specific. If one is not specified we'll guess it. The
build file will be clean.
TODO: can we get configure to generate such a file ?
build.native.cc=gcc
# Path to libtool ( used as a backup )
build.native.libtool=
# Platform-specific flags for compilation.
build.native.extra_cflags=
*/
/* XXX add a optional "compiler" attribute
to not guess the compiler based on the executable name
present in a global property.
*/
/**
* Task to generate a .so file, similar with ( or using ) libtool.
* I hate libtool, so long term I would like to replace most of it
* with decent java code. Short term it'll just use libtool and
* hide some of the ugliness.
*
* arguments:
* <ul>
* <li>source
* </ul>
*
* <p>
*
* @author Costin Manolache
* @author Mike Anderson
* @author Ignacio J. Ortega
*/
public class SoTask extends Task {
protected String apxs;
// or FileSet ?
protected Vector src; //[FileSet]
protected PatternSet includes;
protected Path depends;
protected Path libs;
protected String module;
protected String soFile;
protected String soExt = ".so";
protected String cflags;
protected File buildDir;
protected int debug;
protected boolean optG=true;
protected boolean optWgcc=true;
protected boolean optimize=false;
protected boolean profile=false;
protected Vector defines = new Vector();
protected Vector imports = new Vector(); // used by the NetWare, win32 linkers
protected Vector exports = new Vector(); // used by the NetWare, win32 linkers
protected Vector modules = new Vector(); // used by the NetWare linker
protected Vector linkOpts = new Vector(); // used by the NetWare, win32 linkers
protected Vector altSoFiles = new Vector(); // used by the NetWare linker
protected Vector resources = new Vector(); // used by the win32 linker
// Computed fields
// protected Vector compileList; // [Source]
protected Vector srcList=new Vector();
protected CompilerAdapter compiler;
// protected GlobPatternMapper co_mapper;
public SoTask() {};
// Hack to allow individual compilers/linkers to work
// as regular Tasks, independnetly.
public void duplicateTo(SoTask so) {
// This will act as a proxy for the child task
so.project=project;
so.target=target;
so.location=location;
so.taskName=taskName;
so.taskType=taskType;
so.apxs=apxs;
so.src=src;
so.includes=includes;
so.depends=depends;
so.libs=libs;
so.module=module;
so.soFile=soFile;
so.soExt=soExt;
so.cflags=cflags;
so.buildDir=buildDir;
so.debug=debug;
so.optG=optG;
so.optWgcc=optWgcc;
so.optimize=optimize;
so.profile=profile;
so.defines=defines;
so.imports=imports;
so.exports=exports;
so.resources=resources;
so.modules=modules;
so.linkOpts=linkOpts;
so.srcList=srcList;
// so.compileList=compileList;
so.compiler=compiler;
// so.co_mapper=co_mapper;
so.altSoFiles=altSoFiles;
}
/** @deprecated use setTarget
*/
public void setSoFile(String s ) {
soFile=s;
}
/** Add debug information
*/
public void setDebug(boolean b) {
optG=b;
}
/** Add debug information
*/
public void setOptimize(boolean b) {
optimize=b;
}
/** Add profiling information
*/
public void setProfile(boolean b) {
profile=b;
}
/** Gcc warnings
*/
public void setGccWarn(boolean b) {
optWgcc=b;
}
/** Debug the <so> task
*/
public void setTaskDebug(int i) {
debug=i;
}
/** Add a -D option. Note that each define has
* an if/unless attribute
*/
public void addDef(Def var ) {
var.setProject( project );
defines.addElement(var);
}
/**
* Add an import file/symbol for NetWare or win32 platform
*
*
*/
public void addImport(JkData imp) {
imp.setProject( project );
imports.add(imp);
}
/**
* Add an export file/symbol for NetWare or win32 platform
*
*
*/
public void addExport(JkData exp) {
exp.setProject( project );
exports.add(exp);
}
/**
* Add an resource file on win32 platform
*
*
*/
public void addResource(JkData res) {
res.setProject( project );
resources.add(res);
}
/**
* Add a link option for NetWare or win32 platform
*
*
*/
public void addLinkOpt(JkData option) {
option.setProject( project );
linkOpts.add(option);
}
/**
* Add an NLMModule dependancy
*
*
*/
public void addNLMModule(JkData module) {
module.setProject( project );
modules.add(module);
}
/**
* Add an alternate target since some platforms (NetWare) have file name
* limitations.
*
*/
public void addAltSoFile(JkData altSo) {
altSo.setProject( project );
altSoFiles.add(altSo);
}
/** Set the target for this compilation. Don't include any
* directory or suffix ( not sure about prefix - we may want
* to add lib automatically for unix, and nothing on win/etc ? ).
*/
public void setTarget(String s ) {
soFile=s;
}
/** Set the extension for the target. This will depend on the platform
* we are compiling for.
*/
public void setExtension(String s ) {
soExt=s;
}
/** Directory where intermediary objects will be
* generated
*/
public void setBuildDir( File s ) {
buildDir=s;
}
public void setCflags(String s ) {
cflags=s;
}
/** Directory where the .so file will be generated
*/
public void setSoDir( String s ) {
}
public void addJniConfig( JniConfig jniCfg ) {
}
public void addApacheConfig( ApacheConfig apacheCfg ) {
}
/**
* Source files ( .c )
*
* @return a nested src element.
*/
public void addSrc(FileSet fl) {
if( src==null ) src=new Vector();
src.addElement(fl);
}
/**
* Include files
*/
public PatternSet createIncludes() {
includes=new PatternSet(); //Path(project);
return includes;
}
/**
* General dependencies. If any of the files is modified
* ( i.e. is newer than the oldest .o ) we'll recompile everything.
*
* This can be used for headers ( until we add support for makedepend)
* or any important file that could invalidate the build.
* Another example is checking httpd or apxs ( if a new version
* was installed, maybe some flags or symbols changed )
*/
public Path createDepends() {
depends=new Path(project);
return depends;
}
/**
* Libraries ( .a, .so or .dll ) files to link to.
*/
public Path createLibs() {
libs=new Path(project);
return libs;
}
/**
* The name of the target file.
* (XXX including extension - this should be automatically added )
*/
public void setModule(String modName) {
this.module = modName; // Should be this ?
}
// XXX Add specific code for Netware, Windows and platforms where libtool
// is problematic
// XXX Add specific code for Linux and platforms where things are
// clean, libtool should be just a fallback.
public void execute() throws BuildException {
compiler=findCompilerAdapter();
// co_mapper=compiler.getOMapper();
LinkerAdapter linker=findLinkerAdapter();
if( soFile==null )
throw new BuildException("No target ( " + soExt + " file )");
if (src == null)
throw new BuildException("No source files");
// XXX makedepend-type dependencies - how ??
// We could generate a dummy Makefile and parse the content...
findSourceFiles();
// Copy all settings into compiler
this.duplicateTo(compiler);
compiler.compile( srcList );
// XXX move this checking to linker
File soTarget=new File( buildDir, soFile + soExt );
if( compiler.getCompiledFiles().size() == 0 && soTarget.exists()) {
// No dependency, no need to relink
return;
}
this.duplicateTo(linker);
linker.link(srcList);
}
public CompilerAdapter findCompilerAdapter() {
CompilerAdapter compilerAdapter;
String cc;
cc=project.getProperty("build.compiler.cc");
if( cc!=null ) {
if( "cc".equals( cc ) ) {
compilerAdapter=new CcCompiler();
compilerAdapter.setSoTask( this );
return compilerAdapter;
}
if( "gcj".equals( cc ) ) {
compilerAdapter=new GcjCompiler();
compilerAdapter.setSoTask( this );
return compilerAdapter;
}
if( cc.indexOf("mwccnlm") != -1 ) {
compilerAdapter=new MwccCompiler();
compilerAdapter.setSoTask( this );
return compilerAdapter;
}
if( cc.indexOf("cl") != -1 ) {
compilerAdapter=new MsvcCompiler();
compilerAdapter.setSoTask( this );
return compilerAdapter;
}
}
compilerAdapter=new LibtoolCompiler();
compilerAdapter.setSoTask( this );
return compilerAdapter;
}
public LinkerAdapter findLinkerAdapter() {
LinkerAdapter linkerAdapter;
String ld=project.getProperty("build.compiler.ld");
if( ld!=null ) {
if( ld.indexOf("mwldnlm") != -1 ) {
linkerAdapter=new MwldLinker();
linkerAdapter.setSoTask( this );
return linkerAdapter;
}
if( ld.indexOf("link") != -1 ) {
linkerAdapter=new MsvcLinker();
linkerAdapter.setSoTask( this );
return linkerAdapter;
}
// if( "ld".equals( cc ) ) {
// linkerAdapter=new LdLinker();
// linkerAdapter.setSoTask( this );
// return cc;
// }
}
String cc=project.getProperty("build.compiler.cc");
if( "gcj".equals( cc ) ) {
linkerAdapter=new GcjLinker();
linkerAdapter.setSoTask( this );
return linkerAdapter;
}
linkerAdapter=new LibtoolLinker();
linkerAdapter.setSoTask( this );
return linkerAdapter;
}
/** Find all source files declared with <src> elements
*/
public void findSourceFiles() {
if (buildDir == null) buildDir = project.getBaseDir();
Enumeration e=src.elements();
while( e.hasMoreElements() ) {
FileSet fs=(FileSet)e.nextElement();
DirectoryScanner ds=fs.getDirectoryScanner( project );
String localList[]= ds.getIncludedFiles();
if (localList.length == 0)
throw new BuildException("No source files ");
for( int i=0; i<localList.length; i++ ) {
srcList.addElement( new Source( fs.getDir(project), localList[i]));
}
}
}
/** If any file declared in <depend> element has changed, we'll do
a full rebuild.
*/
public boolean checkDepend(long oldestO, File oldestOFile) {
if( depends==null )
return false;
String dependsA[]=depends.list();
for( int i=0; i< dependsA.length; i++ ) {
File f=new File( dependsA[i] );
if( ! f.exists() ) {
log("Depend not found " + f );
return true;
}
if( f.lastModified() > oldestO ) {
log( "Depend " + f + " newer than " + oldestOFile );
return true;
}
}
return false;
}
// ==================== Execution utils ====================
protected ExecuteStreamHandler streamhandler = null;
protected ByteArrayOutputStream outputstream = null;
protected ByteArrayOutputStream errorstream = null;
public int execute( Commandline cmd ) throws BuildException
{
createStreamHandler();
Execute exe = new Execute(streamhandler, null);
exe.setAntRun(project);
exe.setWorkingDirectory(buildDir);
exe.setCommandline(cmd.getCommandline());
int result=0;
try {
result=exe.execute();
} catch (IOException e) {
throw new BuildException(e, location);
}
return result;
}
public void createStreamHandler() throws BuildException {
// try {
outputstream= new ByteArrayOutputStream();
errorstream = new ByteArrayOutputStream();
streamhandler =
new PumpStreamHandler(new PrintStream(outputstream),
new PrintStream(errorstream));
// } catch (IOException e) {
// throw new BuildException(e,location);
// }
}
public void closeStreamHandler() {
try {
if (outputstream != null)
outputstream.close();
if (errorstream != null)
errorstream.close();
outputstream=null;
errorstream=null;
} catch (IOException e) {}
}
}