/** * Ant Task for building D programs. * * Authors: * Frank Benoit (benoit at tionex dot de) * Charles Sanders * * License: * Public Domain */ package descent.ant.tasks; /** * To use this anttask, it must be in the classpath of ANT. * Within eclipse this can be done in * Window->Preferences: ANT->Runtime->Classpath->Global Entries->Add Folder (add the anttask 'bin' folder) * * To make the task public to ANT a task definition is needed. * Either do it Eclipse globally: * Window->Preferences: ANT->Runtime->Classpath->Tasks->Add Task (Name: D, Folder choose the bin folder.) * or add the definition to the build.xml * * * To run ant from the commandline, ant.jar, ant-launcher.jar and this compiled class need to be on the * classpath. On linux you can use a script with this content: * #!/bin/sh * export ECLIPSE_DIR=/opt/eclipse * export ANT_DIR=$ECLIPSE_DIR/plugins/org.apache.ant_1.6.5 * export ANT_LIB_DIR=$ANT_DIR/lib * java -cp ~/descent/anttasks/bin/:$ANT_LIB_DIR/ant-launcher.jar:$ANT_LIB_DIR/ant.jar org.apache.tools.ant.launch.Launcher $* * */ import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.util.HashSet; import java.util.LinkedList; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.DirectoryScanner; import org.apache.tools.ant.Project; import org.apache.tools.ant.Task; import org.apache.tools.ant.taskdefs.Execute; import org.apache.tools.ant.taskdefs.PumpStreamHandler; import org.apache.tools.ant.taskdefs.condition.Os; import org.apache.tools.ant.types.Commandline; import org.apache.tools.ant.types.DirSet; import org.apache.tools.ant.types.FileSet; /* * mode="object" * compile all supplied sources into object files * mode="executable" * compile all supplied sources into object files and call the linker for building the executable */ public class D extends Task { LinkedList mMainModules = new LinkedList(); LinkedList mIncludedModules = new LinkedList(); LinkedList mIncludePaths = new LinkedList(); HashSet mCompileFiles = new HashSet(); HashSet mCompileFqns = new HashSet(); enum Mode { /// not valid, the mode has to be defined NOTSET, /// only build the object files OBJECTS, /// compile the mainmodules and all dependent modules. Link an executable EXECUTABLE, /// compile the mainmodules and link a static library LIBRARY_STATIC, /// compile the mainmodules and link a dynamic library LIBRARY_DYNAMIC; } String type; public void setType( String type ){ this.type = type; } Mode mode = Mode.NOTSET; public void setMode( String mode ){ if( mode.equals( "objects" ) ){ this.mode = Mode.OBJECTS; } else if( mode.equals( "executable" ) ){ this.mode = Mode.EXECUTABLE; } else if( mode.equals( "library-static" ) ){ this.mode = Mode.LIBRARY_STATIC; } else if( mode.equals( "library-dynamic" ) ){ this.mode = Mode.LIBRARY_DYNAMIC; } else { throw new BuildException( String.format("mode can only be set to \"objects\", \"executable\" or \"library\".")); } } private boolean compile = true; public void setCompile( boolean value ){ this.compile = value; } private boolean isWindows(){ return Os.isFamily( "windows" ); } String mapfile; public void setMapFile( File value ){ if( !isWindows() ){ throw new BuildException( "the mapfile option is available only on windows" ); } mapfile= value.getAbsolutePath(); } String deffile; public void setDefFile( File value ){ if( !isWindows() ){ throw new BuildException( "the mapfile option is available only on windows" ); } deffile= value.getAbsolutePath(); } String resfile; public void setResFile( File value ){ if( !isWindows() ){ throw new BuildException( "the mapfile option is available only on windows" ); } resfile= value.getAbsolutePath(); } String compilerdir; public void setCompilerdir( File value ){ compilerdir = value.getAbsolutePath(); } private boolean header = false; public void setHeader( boolean value ){ this.header = value; } private boolean ddoc = false; public void setDdoc( boolean value ){ this.ddoc = value; } boolean debuginfo = false; public void setDebuginfo( boolean value ){ this.debuginfo = value; } boolean debuginfo_c = false; public void setDebuginfo_c( boolean value ){ this.debuginfo_c = value; } private boolean cleanup = true; public void setCleanup( boolean value ){ this.cleanup = value; } boolean warnings = false; public void setWarnings( boolean value ){ this.warnings = value; } boolean unittest = false; public void setUnittest( boolean value ){ this.unittest = value; } boolean stdargs = true; public void setStdargs( boolean value ){ this.stdargs = value; } File destfile; public void setDestfile( File value ){ this.destfile = value; } // --- Nested --- LinkedList linkflags = new LinkedList(); public LinkFlag createLinkflag(){ LinkFlag result = new LinkFlag(); linkflags.add( result ); return result; } LinkedList linkLibs = new LinkedList(); public LinkLib createLinklib(){ LinkLib result = new LinkLib(); linkLibs.add( result ); return result; } LinkedList versionflags = new LinkedList(); public Version createVersion(){ Version result = new Version(); versionflags.add( result ); return result; } LinkedList excludeflags= new LinkedList(); public ExcludePackage createExcludePackage(){ ExcludePackage result = new ExcludePackage(); excludeflags.add( result ); return result; } LinkedList debugflags = new LinkedList(); public Debug createDebug(){ Debug result = new Debug(); debugflags.add( result ); return result; } LinkedList includepaths = new LinkedList(); public IncludePath createIncludepath(){ IncludePath result = new IncludePath(); includepaths.add( result ); return result; } // modules that must be compiled and linked, independent from any dependency calculation. // this is at least the module with the main function. // Later, in case we get reflection, this can hold other modules as well. // In case of a library, all modules shall be compiled and linked, so list them as mainmodules. LinkedList mainModuless = new LinkedList(); public MainModules createMainModules(){ MainModules result = new MainModules(); mainModuless.add( result ); return result; } // modules that are compiled and linked if the dependency calculation detects a dependency from a main module. LinkedList includeModuless = new LinkedList(); public IncludeModules createIncludeModules(){ IncludeModules result = new IncludeModules(); includeModuless.add( result ); return result; } @Override public void execute() throws BuildException { log( String.format("Building %s\n", destfile ), Project.MSG_INFO ); try{ Compiler compiler = null; if( type == null ){ throw new BuildException("type is not set"); } else if( type.equals("gdc")){ compiler = new Gdc(this); } else if( type.equals("dmd-linux")){ compiler = new DmdLinux(this); } else if( type.equals("dmd-windows")){ compiler = new DmdWindows(this); } else{ throw new BuildException("type must be one of dmd/gdc"); } log( String.format("D Task: compile = %s\n", compile ), Project.MSG_VERBOSE ); log( String.format("D Task: compilerdir = %s\n", compilerdir ), Project.MSG_VERBOSE ); log( String.format("D Task: mode = %s\n", mode.toString() ), Project.MSG_VERBOSE ); log( String.format("D Task: header = %s\n", header ), Project.MSG_VERBOSE ); log( String.format("D Task: ddoc = %s\n", ddoc ), Project.MSG_VERBOSE ); log( String.format("D Task: debuginfo = %s\n", debuginfo ), Project.MSG_VERBOSE ); log( String.format("D Task: debuginfo_c = %s\n", debuginfo_c ), Project.MSG_VERBOSE ); log( String.format("D Task: destfile = %s\n", destfile ), Project.MSG_VERBOSE ); log( String.format("D Task: cleanup = %s\n", cleanup ), Project.MSG_VERBOSE ); log( String.format("D Task: warnings = %s\n", warnings ), Project.MSG_VERBOSE ); log( String.format("D Task: stdargs = %s\n", stdargs ), Project.MSG_VERBOSE ); log( String.format("D Task: mapfile = %s\n", mapfile ), Project.MSG_VERBOSE ); log( String.format("D Task: deffile = %s\n", deffile ), Project.MSG_VERBOSE ); log( String.format("D Task: resfile = %s\n", resfile ), Project.MSG_VERBOSE ); for ( ExcludePackage exclude : excludeflags) { log("Excluding: " + exclude.value,Project.MSG_VERBOSE ); } for( MainModules mainModules : mainModuless ){ for( FileSet fileset : mainModules.mods ){ DirectoryScanner scanner = fileset.getDirectoryScanner( getProject() ); scanner.scan(); String[] names = scanner.getIncludedFiles(); for( String name : names ){ File srcFile = new File( scanner.getBasedir(), name ); mMainModules.add(srcFile ); log( String.format("D Task: main module = %s\n", srcFile.getAbsolutePath() ), Project.MSG_VERBOSE ); if( !name.endsWith( ".d" )){ throw new BuildException( String.format( "The source file %s does not end with \".d\".", name )); } } } } for( IncludeModules modules : includeModuless ){ for( DirSet dirset : modules.paths ){ DirectoryScanner scanner = dirset.getDirectoryScanner( getProject() ); scanner.scan(); String[] names = scanner.getIncludedDirectories(); for( String name : names ){ File srcFile = new File( scanner.getBasedir(), name ); mIncludedModules.add(srcFile ); log( String.format("D Task: includepath = %s\n", srcFile.getAbsolutePath() ), Project.MSG_VERBOSE ); if( !srcFile.isDirectory()){ throw new BuildException( String.format( "The include path %s is not an existing directory", srcFile.getAbsolutePath() )); } } } } for( IncludePath modules : includepaths ){ for( DirSet dirset : modules.paths ){ DirectoryScanner scanner = dirset.getDirectoryScanner( getProject() ); scanner.scan(); String[] names = scanner.getIncludedDirectories(); for( String name : names ){ File srcFile = new File( scanner.getBasedir(), name ); mIncludePaths.add(srcFile ); log( String.format("D Task: includepath = %s\n", srcFile.getAbsolutePath() ), Project.MSG_VERBOSE ); if( !srcFile.isDirectory()){ throw new BuildException( String.format( "The include path %s is not an existing directory", srcFile.getAbsolutePath() )); } } } } for( LinkFlag flag : linkflags ){ log( String.format("D Task: linkflag = %s\n", flag.value ), Project.MSG_VERBOSE ); } for( LinkLib linkLib : linkLibs ){ if( linkLib.linkType == null ){ throw new BuildException( "linklib needs a type" ); } switch( linkLib.linkType ){ case STATIC: log( String.format("D Task: link static = %s\n", linkLib.name ), Project.MSG_VERBOSE ); break; case DYNAMIC: log( String.format("D Task: link dynamic= %s\n", linkLib.name ), Project.MSG_VERBOSE ); break; } } try{ compiler.calcDependencies(); compiler.build(); } finally{ if( cleanup ){ compiler.cleanup(); } } } catch( NullPointerException e ){ e.printStackTrace(); throw e; } } private void printFile( File t, Pattern filter ){ try { BufferedReader rd = new BufferedReader( new FileReader( t )); String line; while((line=rd.readLine()) != null ){ Matcher matcher = filter.matcher(line); if( !matcher.matches() ){ log( line, Project.MSG_ERR ); } } rd.close(); } catch (FileNotFoundException e) { } catch (IOException e) { } } File executeCmdCreateOutputFile( LinkedList cmdlist, Pattern errorFilter ){ String[] cmdparts = cmdlist.toArray(new String[0]); try { File t = File.createTempFile("AntMsgs", "log" ); FileOutputStream ostr = new FileOutputStream( t ); log(Commandline.describeCommand(cmdparts), Project.MSG_VERBOSE); Execute exe = new Execute(new PumpStreamHandler(ostr)); exe.setAntRun(getProject()); exe.setCommandline(cmdparts); int retval = exe.execute(); if (Execute.isFailure(retval)) { ostr.close(); printFile( t, errorFilter ); throw new BuildException(cmdparts[0] + " failed with return code " + retval, getLocation()); } ostr.close(); return t; } catch (java.io.IOException exc) { throw new BuildException("Could not launch " + cmdparts[0] + ": " + exc, getLocation()); } } void executeCmd( LinkedList cmdlist ){ Execute.runCommand(this, cmdlist.toArray(new String[0])); } public class Version { String value; public void setValue(String value ){ this.value = value; } } public class ExcludePackage{ String value; public void setValue(String value ){ this.value = value; } } public class Debug { String value = ""; public void setValue(String value ){ this.value = value; } } public class LinkFlag { String value; public void setValue(String value ){ this.value = value; } } public static class LinkLib { enum LinkType{ STATIC, DYNAMIC } String name; public void setName(String value ){ this.name = value; } LinkType linkType; public void setType(String value ){ if( "static".equals(value)){ linkType = LinkType.STATIC; } else if( "dynamic".equals(value)){ linkType = LinkType.DYNAMIC; } else{ throw new BuildException( "only 'static' or 'dynamic' is allowed for linklib type." ); } } } public class MainModules { LinkedList mods = new LinkedList(); public FileSet createFileSet(){ FileSet result = new FileSet(); mods.add( result ); return result; } } public class IncludeModules { LinkedList paths = new LinkedList(); public DirSet createDirSet(){ DirSet result = new DirSet(); paths.add( result ); return result; } } public class IncludePath { LinkedList paths = new LinkedList(); public DirSet createDirSet(){ DirSet result = new DirSet(); paths.add( result ); return result; } } }