diff --git a/jps/.idea/dictionaries/max.xml b/jps/.idea/dictionaries/max.xml index 2109b203ee9e..d02243d14c6a 100644 --- a/jps/.idea/dictionaries/max.xml +++ b/jps/.idea/dictionaries/max.xml @@ -6,6 +6,7 @@ Groovyc Instrumentations Javac + Runtime args chunkey depdends diff --git a/jps/.idea/misc.xml b/jps/.idea/misc.xml index ffae05161609..1f3faa9f133f 100644 --- a/jps/.idea/misc.xml +++ b/jps/.idea/misc.xml @@ -52,7 +52,6 @@ - diff --git a/jps/.idea/modules.xml b/jps/.idea/modules.xml index ae879f386b29..5fbf65bb04ba 100644 --- a/jps/.idea/modules.xml +++ b/jps/.idea/modules.xml @@ -5,6 +5,7 @@ + diff --git a/jps/antLayout/src/jetbrains/antlayout/datatypes/JarContainer.java b/jps/antLayout/src/jetbrains/antlayout/datatypes/JarContainer.java old mode 100755 new mode 100644 index d27918cd69d6..01d490cbb6fb --- a/jps/antLayout/src/jetbrains/antlayout/datatypes/JarContainer.java +++ b/jps/antLayout/src/jetbrains/antlayout/datatypes/JarContainer.java @@ -16,6 +16,7 @@ public class JarContainer extends ZipContainer { protected Zip createTask() { Jar task = new Jar(); task.setTaskName("jar"); + task.setWhenmanifestonly((Zip.WhenEmpty) Zip.WhenEmpty.getInstance(Zip.WhenEmpty.class, "skip")); return task; } diff --git a/jps/antLayout/src/jetbrains/antlayout/datatypes/ZipContainer.java b/jps/antLayout/src/jetbrains/antlayout/datatypes/ZipContainer.java old mode 100755 new mode 100644 index f00fd01fad32..f96476643fd0 --- a/jps/antLayout/src/jetbrains/antlayout/datatypes/ZipContainer.java +++ b/jps/antLayout/src/jetbrains/antlayout/datatypes/ZipContainer.java @@ -18,7 +18,7 @@ public class ZipContainer extends Container { public ZipContainer() { task = createTask(); - task.setCompress(true); // Default compress is false + task.setCompress(false); // Default compress is false } public String getName() { diff --git a/jps/build.gant b/jps/build.gant index b4dd9d837e79..368ecf2307b6 100644 --- a/jps/build.gant +++ b/jps/build.gant @@ -5,22 +5,39 @@ includeTool << Jps def projectHome = "$basedir" def libs = "$projectHome/lib/" -project.targetFolder = "${projectHome}/build" +def gantHome = "/Users/max/libs/gant-1.7.0" +project.targetFolder = "${projectHome}/build" + library("ANT") { classpath "$libs/ant-1.7.1.jar" } +library("gant") { + new File("$gantHome/lib").eachFile { + classpath it + } +} + module("JPS") { + targetLevel ="1.5" classpath antLayout src "${projectHome}/src" } module("antLayout") { + targetLevel = "1.5" classpath ANT src "${projectHome}/antLayout/src" } +module("gantLauncher") { + targetLevel = "1.5" + classpath ANT + classpath gant + src "${projectHome}/gantLauncher/src" +} + target('default' : 'Default target') { project.clean() project.makeAll() @@ -33,5 +50,9 @@ target('default' : 'Default target') { exclude (name: "JDOM*.class") } } + + jar("gant_patches.jar") { + module("gantLauncher") + } } } diff --git a/jps/gantLauncher/gantLauncher.iml b/jps/gantLauncher/gantLauncher.iml new file mode 100644 index 000000000000..e8f81ee6b82d --- /dev/null +++ b/jps/gantLauncher/gantLauncher.iml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/jps/gantLauncher/src/groovy/util/AntBuilder.java b/jps/gantLauncher/src/groovy/util/AntBuilder.java new file mode 100644 index 000000000000..c384116abf37 --- /dev/null +++ b/jps/gantLauncher/src/groovy/util/AntBuilder.java @@ -0,0 +1,351 @@ +/* + * Copyright 2003-2008 the original author or authors. + * + * 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 groovy.util; + +import groovy.xml.QName; +import org.apache.tools.ant.*; +import org.apache.tools.ant.helper.AntXMLContext; +import org.apache.tools.ant.helper.ProjectHelper2; +import org.apache.tools.ant.input.DefaultInputHandler; +import org.codehaus.groovy.ant.FileScanner; +import org.xml.sax.Attributes; +import org.xml.sax.Locator; +import org.xml.sax.SAXParseException; +import org.xml.sax.helpers.AttributesImpl; + +import java.io.InputStream; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Vector; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Allows Ant tasks to + * be used with GroovyMarkup. Requires the ant.jar in your classpath which will + * happen automatically if you are using the Groovy distribution but will be up + * to you to organize if you are embedding Groovy. If you wish to use the + * optional tasks + * you will need to add one or more additional jars from the ant distribution to + * your classpath. + * + * @author James Strachan + * @author Dierk Koenig (dk) + * @author Marc Guillemot + * @version $Revision: 12900 $ + */ +public class AntBuilder extends BuilderSupport { + + private static final Class[] ADD_TASK_PARAM_TYPES = { String.class }; + + private final Logger log = Logger.getLogger(getClass().getName()); + private Project project; + private final AntXMLContext antXmlContext; + private final ProjectHelper2.ElementHandler antElementHandler = new ProjectHelper2.ElementHandler(); + private final ProjectHelper2.TargetHandler antTargetHandler = new ProjectHelper2.TargetHandler(); + private final Target collectorTarget; + private final Target implicitTarget; + private Object lastCompletedNode; + + + + public AntBuilder() { + this(createProject()); + } + + public AntBuilder(final Project project) { + this(project, new Target()); + } + + public AntBuilder(final Project project, final Target owningTarget) { + this.project = project; + + this.project.setInputHandler(new DefaultInputHandler()); + + collectorTarget = owningTarget; + antXmlContext = new AntXMLContext(project); + collectorTarget.setProject(project); + antXmlContext.setCurrentTarget(collectorTarget); + antXmlContext.setLocator(new AntBuilderLocator()); + antXmlContext.setCurrentTargets(new HashMap()); + + implicitTarget = new Target(); + implicitTarget.setProject(project); + implicitTarget.setName(""); + antXmlContext.setImplicitTarget(implicitTarget); + + // FileScanner is a Groovy hack (utility?) + project.addDataTypeDefinition("fileScanner", FileScanner.class); + } + + public AntBuilder(final Task parentTask) { + this(parentTask.getProject(), parentTask.getOwningTarget()); + + // define "owning" task as wrapper to avoid having tasks added to the target + // but it needs to be an UnknownElement and no access is available from + // task to its original UnknownElement + final UnknownElement ue = new UnknownElement(parentTask.getTaskName()); + ue.setProject(parentTask.getProject()); + ue.setTaskType(parentTask.getTaskType()); + ue.setTaskName(parentTask.getTaskName()); + ue.setLocation(parentTask.getLocation()); + ue.setOwningTarget(parentTask.getOwningTarget()); + ue.setRuntimeConfigurableWrapper(parentTask.getRuntimeConfigurableWrapper()); + parentTask.getRuntimeConfigurableWrapper().setProxy(ue); + antXmlContext.pushWrapper(parentTask.getRuntimeConfigurableWrapper()); + } + + /**# + * Gets the Ant project in which the tasks are executed + * @return the project + */ + public Project getProject() { + return project; + } + + /** + * @return Factory method to create new Project instances + */ + protected static Project createProject() { + final Project project = new Project(); + + final ProjectHelper helper = ProjectHelper.getProjectHelper(); + project.addReference(ProjectHelper.PROJECTHELPER_REFERENCE, helper); + helper.getImportStack().addElement("AntBuilder"); // import checks that stack is not empty + + final BuildLogger logger = new NoBannerLogger(); + + logger.setMessageOutputLevel(org.apache.tools.ant.Project.MSG_INFO); + logger.setOutputPrintStream(System.out); + logger.setErrorPrintStream(System.err); + + project.addBuildListener(logger); + + project.init(); + project.getBaseDir(); + return project; + } + + protected void setParent(Object parent, Object child) { + } + + /** + * We don't want to return the node as created in {@link #createNode(Object, Map, Object)} + * but the one made ready by {@link #nodeCompleted(Object, Object)} + * @see groovy.util.BuilderSupport#doInvokeMethod(java.lang.String, java.lang.Object, java.lang.Object) + */ + protected Object doInvokeMethod(String methodName, Object name, Object args) { + super.doInvokeMethod(methodName, name, args); + + + // return the completed node + return lastCompletedNode; + } + + /** + * Determines, when the ANT Task that is represented by the "node" should perform. + * Node must be an ANT Task or no "perform" is called. + * If node is an ANT Task, it performs right after complete contstruction. + * If node is nested in a TaskContainer, calling "perform" is delegated to that + * TaskContainer. + * @param parent note: null when node is root + * @param node the node that now has all its children applied + */ + protected void nodeCompleted(final Object parent, final Object node) { + + antElementHandler.onEndElement(null, null, antXmlContext); + + lastCompletedNode = node; + if (parent != null && !(parent instanceof Target)) { + log.finest("parent is not null: no perform on nodeCompleted"); + return; // parent will care about when children perform + } + + // as in Target.execute() + if (node instanceof Task) { + Task task = (Task) node; + final String taskName = task.getTaskName(); + // save original streams + InputStream savedIn = System.in; + InputStream savedProjectInputStream = project.getDefaultInputStream(); + + if (!(savedIn instanceof DemuxInputStream)) { + project.setDefaultInputStream(savedIn); + System.setIn(new DemuxInputStream(project)); + } + + try { + task.perform(); + } finally { + // restore original streams + project.setDefaultInputStream(savedProjectInputStream); + System.setIn(savedIn); + } + + // "Unwrap" the UnknownElement to return the real task to the calling code + if (node instanceof UnknownElement) { + final UnknownElement unknownElement = (UnknownElement) node; + unknownElement.maybeConfigure(); + lastCompletedNode = unknownElement.getRealThing(); + } + + // restore dummy collector target + if ("import".equals(taskName)) { + antXmlContext.setCurrentTarget(collectorTarget); + } + } + else if (node instanceof Target) { + // restore dummy collector target + antXmlContext.setCurrentTarget(collectorTarget); + } + else { + final RuntimeConfigurable r = (RuntimeConfigurable) node; + r.maybeConfigure(project); + } + } + + protected Object createNode(Object tagName) { + return createNode(tagName, Collections.EMPTY_MAP); + } + + protected Object createNode(Object name, Object value) { + Object task = createNode(name); + setText(task, value.toString()); + return task; + } + + protected Object createNode(Object name, Map attributes, Object value) { + Object task = createNode(name, attributes); + setText(task, value.toString()); + return task; + } + + /** + * Builds an {@link Attributes} from a {@link Map} + * + * @param attributes the attributes to wrap + * @return the wrapped attributes + */ + protected static Attributes buildAttributes(final Map attributes) { + final AttributesImpl attr = new AttributesImpl(); + for (final Iterator iter=attributes.entrySet().iterator(); iter.hasNext(); ) { + final Map.Entry entry = (Map.Entry) iter.next(); + final String attributeName = (String) entry.getKey(); + final String attributeValue = String.valueOf(entry.getValue()); + attr.addAttribute(null, attributeName, attributeName, "CDATA", attributeValue); + } + return attr; + } + + protected Object createNode(final Object name, final Map attributes) { + + final Attributes attrs = buildAttributes(attributes); + String tagName = name.toString(); + String ns = ""; + + if (name instanceof QName) { + QName q = (QName)name; + tagName = q.getLocalPart(); + ns = q.getNamespaceURI(); + } + + // import can be used only as top level element + if ("import".equals(name)) { + antXmlContext.setCurrentTarget(implicitTarget); + } + else if ("target".equals(name)) { + return onStartTarget(attrs, tagName, ns); + } + + try + { + antElementHandler.onStartElement(ns, tagName, tagName, attrs, antXmlContext); + } + catch (final SAXParseException e) + { + log.log(Level.SEVERE, "Caught: " + e, e); + } + + final RuntimeConfigurable wrapper = (RuntimeConfigurable) antXmlContext.getWrapperStack().lastElement(); + return wrapper.getProxy(); + } + + private Target onStartTarget(final Attributes attrs, String tagName, String ns) { + final Target target = new Target(); + target.setProject(project); + target.setLocation(new Location(antXmlContext.getLocator())); + try { + antTargetHandler.onStartElement(ns, tagName, tagName, attrs, antXmlContext); + final Target newTarget = (Target) getProject().getTargets().get(attrs.getValue("name")); + + // execute dependencies (if any) + final Vector targets = new Vector(); + for (final Enumeration deps=newTarget.getDependencies(); deps.hasMoreElements();) + { + final String targetName = (String) deps.nextElement(); + targets.add(project.getTargets().get(targetName)); + } + getProject().executeSortedTargets(targets); + + antXmlContext.setCurrentTarget(newTarget); + return newTarget; + } + catch (final SAXParseException e) { + log.log(Level.SEVERE, "Caught: " + e, e); + } + return null; + } + + protected void setText(Object task, String text) { + final char[] characters = text.toCharArray(); + try { + antElementHandler.characters(characters, 0, characters.length, antXmlContext); + } + catch (final SAXParseException e) { + log.log(Level.WARNING, "SetText failed: " + task + ". Reason: " + e, e); + } + } + + public Project getAntProject() { + return project; + } +} + +/** + * Would be nice to retrieve location information (from AST?). + * In a first time, without info + */ +class AntBuilderLocator implements Locator { + public int getColumnNumber() + { + return 0; + } + public int getLineNumber() + { + return 0; + } + public String getPublicId() + { + return ""; + } + public String getSystemId() + { + return ""; + } +} diff --git a/jps/gantLauncher/src/org/codehaus/gant/ant/Gant.java b/jps/gantLauncher/src/org/codehaus/gant/ant/Gant.java new file mode 100644 index 000000000000..2f07837f39a8 --- /dev/null +++ b/jps/gantLauncher/src/org/codehaus/gant/ant/Gant.java @@ -0,0 +1,180 @@ +// Gant -- A Groovy way of scripting Ant tasks. +// +// Copyright © 2008-9 Russel Winder +// +// 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.codehaus.gant.ant ; + +import java.io.File ; + +import java.util.ArrayList ; +import java.util.HashMap ; +import java.util.List ; +import java.util.Map ; + +import org.apache.tools.ant.AntClassLoader ; +import org.apache.tools.ant.BuildException ; +import org.apache.tools.ant.BuildListener ; +import org.apache.tools.ant.Project ; +import org.apache.tools.ant.Task ; + +import org.codehaus.gant.GantBinding ; +import org.codehaus.gant.GantBuilder ; + +/** + * Execute a Gant script. + * + *

This Ant task provides a Gant calling capability. The original intention behind this was to support + * continuous integration systems that do not directly support Gant but only Ant. However it also allows + * for gradual evolution of an Ant build into a Gant build.

+ * + *

Possible attributes are:

+ * + * + * + *

Both of these are optional. The file 'build.gant' and the default target are used by default. An + * error results if there is no default target and no target is specified.

+ * + *

Definitions, if needed, are specified using nested definition tags, one for each symbol + * to be defined. Each definition tag takes a compulsory name attribute and an + * optional value attribute.

+ * + * @author Russel Winder + */ +public class Gant extends Task { + /** + * The path to the file to use to drive the Gant build. The default is build.gant. This path is + * relative to the basedir of the Ant project if it is set, or the directory in which the job was started + * if the basedir is not set. + */ + private String file = "build.gant" ; + /** + * A class representing a nested definition tag. + */ + public static final class Definition { + private String name ; + private String value ; + public void setName ( final String s ) { name = s ; } + public String getName ( ) { return name ; } + public void setValue ( final String s ) { value = s ; } + public String getValue ( ) { return value ; } + } + /** + * A list of definitions to be set in the Gant instance. + */ + private final List definitions = new ArrayList ( ) ; + /** + * A class representing a nested target tag. + */ + public static final class GantTarget { + private String value ; + public void setValue ( final String s ) { value = s ; } + public String getValue ( ) { return value ; } + } + /** + * A list of targets to be achieved by the Gant instance. + */ + private final List targets = new ArrayList ( ) ; + /** + * Set the name of the build file to use. This path is relative to the basedir of the Ant project if it + * is set, or the directory in which the job was started if the basedir is not set. + * + * @param f The name of the file to be used to drive the build. + */ + public void setFile ( final String f ) { file = f ; } + /** + * Set the target to be achieved. + * + * @param t The target to achieve. + */ + public void setTarget ( final String t ) { + final GantTarget gt = new GantTarget ( ) ; + gt.setValue ( t ) ; + targets.add ( gt ) ; + } + /** + * Create a node to represent a nested gantTarget tag. + * + * @return a new GantTarget instance ready for values to be added. + */ + public GantTarget createGantTarget ( ) { + final GantTarget gt = new GantTarget ( ) ; + targets.add ( gt ) ; + return gt ; + } + /** + * Create a node to represent a nested definition tag. + * + * @return a new Definition instance ready for values to be added. + */ + public Definition createDefinition ( ) { + final Definition definition = new Definition ( ) ; + definitions.add ( definition ) ; + return definition ; + } + /** + * Load the file and then execute it. + */ + @Override public void execute ( ) throws BuildException { + // + // At first it might seem appropriate to use the Project object from the calling Ant instance as the + // Project object used by the AntBuilder object and hence GantBuilder object associated with the Gant + // instance we are going to create here. However, if we just use that Project object directly then + // there are problems with proper annotation of the lines of output, so it isn't really an option. + // Therefore create a new Project instance and set the things appropriately from the original Project + // object. + // + // Issues driving things here are GANT-50 and GANT-80. GANT-50 is about having the correct base + // directory for operations, GANT-80 is about ensuring that all output generation actually generated + // observable output. + // + // NB As this class is called Gant, we have to use fully qualified name to get to the Gant main class. + // + final Project antProject = getOwningTarget ( ).getProject ( ) ; + final Project newProject = new Project ( ) ; + newProject.init ( ) ; + // Deal with GANT-80 by getting all the the loggers from the Ant instance Project object and adding + // them to the new Project Object. This was followed up by GANT-91 so the code was amended to copying + // over all listeners except the class loader if present. + for ( final Object o : antProject.getBuildListeners ( ) ) { + final BuildListener listener = (BuildListener) o ; + if ( ! ( listener instanceof AntClassLoader ) ) { newProject.addBuildListener ( listener ) ; } + } + // Deal with GANT-50 by getting the base directory from the Ant instance Project object and use it for + // the new Project object. + newProject.setBaseDir ( antProject.getBaseDir ( ) ) ; + final File gantFile = newProject.resolveFile( file ) ; + if ( ! gantFile.exists ( ) ) { throw new BuildException ( "Gantfile does not exist." , getLocation ( ) ) ; } + final GantBuilder ant = new GantBuilder ( newProject ) ; + final Map environmentParameter = new HashMap ( ) ; + environmentParameter.put ( "environment" , "environment" ) ; + ant.invokeMethod ( "property" , new Object[] { environmentParameter } ) ; + final GantBinding binding = new GantBinding ( ) ; + binding.forcedSettingOfVariable ( "ant" , ant ) ; + for ( final Definition definition : definitions ) { + final Map definitionParameter = new HashMap ( ) ; + definitionParameter.put ( "name" , definition.getName ( ) ) ; + definitionParameter.put ( "value" , definition.getValue ( ) ) ; + ant.invokeMethod ( "property" , new Object[] { definitionParameter } ) ; + } + final gant.Gant gant = new gant.Gant ( binding ) ; + gant.loadScript ( gantFile ) ; + final List targetsAsStrings = new ArrayList ( ) ; + for ( final GantTarget g : targets ) { targetsAsStrings.add ( g.getValue ( ) ) ; } + final int returnCode = gant.processTargets ( targetsAsStrings ) ; + if ( returnCode != 0 ) { throw new BuildException ( "Gant execution failed with return code " + returnCode + '.' , getLocation ( ) ) ; } + } +} diff --git a/jps/jps.iml b/jps/jps.iml index 3d516e1bcecb..d98f68373730 100644 --- a/jps/jps.iml +++ b/jps/jps.iml @@ -2,7 +2,7 @@ - + @@ -13,8 +13,8 @@ - + diff --git a/jps/lib/javac2-all.jar b/jps/lib/javac2-all.jar index 188a75d7afe5..cc05f8577aea 100644 Binary files a/jps/lib/javac2-all.jar and b/jps/lib/javac2-all.jar differ diff --git a/jps/src/org/jetbrains/jps/Jps.groovy b/jps/src/org/jetbrains/jps/Jps.groovy index b93002f4bb80..2041e4784da9 100644 --- a/jps/src/org/jetbrains/jps/Jps.groovy +++ b/jps/src/org/jetbrains/jps/Jps.groovy @@ -51,7 +51,7 @@ final class Jps { }) - binding.ant.taskdef(name: "layout", classname: "jetbrains.antlayout.tasks.LayoutTask") + project.taskdef(name: "layout", classname: "jetbrains.antlayout.tasks.LayoutTask") } def Jps(GantBinding binding, Map map) { diff --git a/jps/src/org/jetbrains/jps/Module.groovy b/jps/src/org/jetbrains/jps/Module.groovy index 86cd635f052d..ba37cbb50373 100644 --- a/jps/src/org/jetbrains/jps/Module.groovy +++ b/jps/src/org/jetbrains/jps/Module.groovy @@ -13,6 +13,7 @@ class Module extends LazyInitializeableObject implements ClasspathItem { List excludes = [] Map props = [:] + Map sourceRootPrefixes = [:] def Module(project, name, initializer) { this.project = project; @@ -66,6 +67,18 @@ class Module extends LazyInitializeableObject implements ClasspathItem { project.builder.makeModule(this) } + def getOutput() { + make() + } + + List runtimeClasspath() { + project.builder.moduleRuntimeClasspath(this, false) + } + + List testRuntimeClasspath() { + project.builder.moduleRuntimeClasspath(this, true) + } + def makeTests() { project.builder.makeModuleTests(this) } diff --git a/jps/src/org/jetbrains/jps/Project.groovy b/jps/src/org/jetbrains/jps/Project.groovy index a1a1274091b0..05f984f91168 100644 --- a/jps/src/org/jetbrains/jps/Project.groovy +++ b/jps/src/org/jetbrains/jps/Project.groovy @@ -28,8 +28,12 @@ class Project { final Map modules = [:] final Map libraries = [:] + Closure stagePrinter; + String targetFolder = "." + boolean dryRun = false + def Project(GantBinding binding) { builder = new ProjectBuilder(binding, this) this.binding = binding; @@ -105,6 +109,14 @@ class Project { binding.ant.project.log(message, org.apache.tools.ant.Project.MSG_WARN) } + def stage(String message) { + if (stagePrinter != null) { + stagePrinter(message) + } + + info(message) + } + def info(String message) { binding.ant.project.log(message, org.apache.tools.ant.Project.MSG_INFO) } @@ -113,8 +125,19 @@ class Project { builder.buildAll() } + def makeProduction() { + builder.buildProduction() + } + def clean() { - binding.ant.delete(dir: targetFolder) + if (!dryRun) { + stage("Cleaning $targetFolder") + binding.ant.delete(dir: targetFolder) + } + else { + stage("Cleaning $targetFolder skipped as we're running dry") + } + builder.clean() } def ClasspathItem resolve(Object dep) { @@ -122,28 +145,24 @@ class Project { return dep } - if (dep instanceof String) { - String path = dep - List results = [] - resolvers.each { - def resolved = it.resolve(path) - if (resolved != null) results.add(resolved) - } + String path = dep.toString() - if (results.isEmpty()) { - if (new File(path).exists()) return new PathEntry(path: path) - - error("Cannot resolve $path") - } - else if (results.size() > 1) { - error("Ambigous resolve for $path. All of $results match") - } - - return results[0] + List results = [] + resolvers.each { + def resolved = it.resolve(path) + if (resolved != null) results.add(resolved) } - error("cannot resolve $dep") - return null + if (results.isEmpty()) { + if (new File(path).exists()) return new PathEntry(path: path) + + error("Cannot resolve $path") + } + else if (results.size() > 1) { + error("Ambigous resolve for $path. All of $results match") + } + + return results[0] } def getAt(String key) { @@ -159,4 +178,36 @@ class Project { def putAt(String key, Object value) { props[key] = value } + + String getPropertyIfDefined(String name) { + try { + binding[name] + } + catch (MissingPropertyException mpe) { + return null + } + } + + boolean isDefined(String prop) { + try { + binding[prop] + return true + } + catch (MissingPropertyException mpe) { + return false + } + } + + def exportProperty(String name, String value) { + binding.ant.project.setProperty(name, value) + } + + def taskdef(Map args) { + binding.ant.taskdef(name: args.name, classname: args.classname) { + String additionalClasspathId = getPropertyIfDefined("additional.classpath.id") + if (additionalClasspathId != null) { + classpath (refid: additionalClasspathId) + } + } + } } diff --git a/jps/src/org/jetbrains/jps/ProjectBuilder.groovy b/jps/src/org/jetbrains/jps/ProjectBuilder.groovy index d444dce3fa1b..49e53965b679 100644 --- a/jps/src/org/jetbrains/jps/ProjectBuilder.groovy +++ b/jps/src/org/jetbrains/jps/ProjectBuilder.groovy @@ -34,6 +34,13 @@ class ProjectBuilder { } } + public def clean() { + outputs.clear() + testOutputs.clear() + cp.clear() + testCp.clear() + } + public def buildAll() { buildChunks() chunks.each { @@ -42,6 +49,13 @@ class ProjectBuilder { } } + public def buildProduction() { + buildChunks() + chunks.each { + makeChunk(it) + } + } + private ModuleChunk chunkForModule(Module m) { buildChunks(); mapping[m] @@ -55,7 +69,7 @@ class ProjectBuilder { String currentOutput = outputs[chunk] if (currentOutput != null) return currentOutput - project.info("Making module ${chunk.name}") + project.stage("Making module ${chunk.name}") def dst = folderForChunkOutput(chunk, classesDir(binding.project), false) outputs[chunk] = dst compile(chunk, dst, false) @@ -71,7 +85,7 @@ class ProjectBuilder { String currentOutput = testOutputs[chunk] if (currentOutput != null) return currentOutput - project.info("Making tests for ${chunk.name}") + project.stage("Making tests for ${chunk.name}") def dst = folderForChunkOutput(chunk, testClassesDir(binding.project), true) testOutputs[chunk] = dst compile(chunk, dst, true) @@ -80,11 +94,11 @@ class ProjectBuilder { } private String classesDir(Project project) { - return new File(project.targetFolder, "classes").absolutePath + return new File(project.targetFolder, "production").absolutePath } private String testClassesDir(Project project) { - return new File(project.targetFolder, "testClasses").absolutePath + return new File(project.targetFolder, "test").absolutePath } private String folderForChunkOutput(ModuleChunk chunk, String basePath, boolean tests) { @@ -108,25 +122,26 @@ class ProjectBuilder { ( sourceRoots: sources, excludes: chunk.excludes, - classpath: moduleClasspath(chunk, tests), + classpath: moduleCompileClasspath(chunk, tests), targetFolder: dst, tempRootsToDelete: [] ) - state.print() - - project.builders().each { - it.processModule(chunk, state) + if (!project.dryRun) { + project.builders().each { + it.processModule(chunk, state) + } + state.tempRootsToDelete.each { + binding.ant.delete(dir: it) + } } - state.tempRootsToDelete.each { - binding.ant.delete(dir: it) + chunk.modules.each { + project.exportProperty("module.${it.name}.output.${tests ? "test" : "main"}", dst) } - - binding.ant.project.setProperty("module.${chunk.name}.output.${tests ? "test" : "main"}", dst) } - List moduleClasspath(ModuleChunk chunk, boolean test) { + List moduleCompileClasspath(ModuleChunk chunk, boolean test) { Map> map = test ? testCp : cp if (map[chunk] != null) return map[chunk] @@ -143,6 +158,18 @@ class ProjectBuilder { map[chunk] = set.asList() } + List moduleRuntimeClasspath(Module module, boolean test) { + return chunkRuntimeClasspath(chunkForModule(module), test) + } + + List chunkRuntimeClasspath(ModuleChunk chunk, boolean test) { + Set set = new LinkedHashSet() + set.addAll(moduleCompileClasspath(chunk, test)) + set.add(chunkOutput(chunk)) + + return set.asList() + } + private def transitiveClasspath(Object chunkOrModule, boolean test, Set set, Set processed) { if (processed.contains(chunkOrModule)) return processed << chunkOrModule @@ -173,6 +200,9 @@ class ProjectBuilder { } private String zipIfNecessary(String currentOut, ModuleChunk chunk) { + return currentOut + +/* def currentOutAsFile = new File(currentOut) if (currentOutAsFile.isDirectory() && currentOutAsFile.list().length > 0) { @@ -186,6 +216,7 @@ class ProjectBuilder { else { currentOut } +*/ } private String chunkTestOutput(ModuleChunk chunk) { diff --git a/jps/src/org/jetbrains/jps/builders/StandardBuilders.groovy b/jps/src/org/jetbrains/jps/builders/StandardBuilders.groovy index b0f8ff729fb3..c95893d40e03 100644 --- a/jps/src/org/jetbrains/jps/builders/StandardBuilders.groovy +++ b/jps/src/org/jetbrains/jps/builders/StandardBuilders.groovy @@ -23,10 +23,22 @@ class JavacBuilder implements ModuleBuilder { params.destdir = state.targetFolder if (sourceLevel != null) params.source = sourceLevel if (targetLevel != null) params.target = targetLevel - + params.memoryMaximumSize = "512m" params.fork = "true" + params.debug = "on" + + def customJavac = module["javac"] + if (customJavac != null) { + params.executable = customJavac + } + + def customArgs = module["javac_args"] ant.javac (params) { + if (customArgs) { + compilerarg(line: customArgs) + } + state.sourceRoots.each { src(path: it) } @@ -50,26 +62,45 @@ class JavacBuilder implements ModuleBuilder { class ResourceCopier implements ModuleBuilder { - def processModule(ModuleChunk module, ModuleBuildState state) { + def processModule(ModuleChunk chunk, ModuleBuildState state) { if (state.sourceRoots.isEmpty()) return; - def project = module.project + def project = chunk.project def ant = project.binding.ant - ant.copy(todir: state.targetFolder) { - state.sourceRoots.each { root -> - fileset (dir : root) { - patternset (refid: module["compiler.resources.id"]) - type (type: "file") + chunk.modules.each { module -> + def rootProcessor = {String root -> + if (new File(root).exists()) { + def target = state.targetFolder + def prefix = module.sourceRootPrefixes[root] + if (prefix != null) { + if (!(target.endsWith("/") || target.endsWith("\\"))) { + target += "/" + } + target += prefix + } + + ant.copy(todir: target) { + fileset(dir: root) { + patternset(refid: chunk["compiler.resources.id"]) + type(type: "file") + } + } + } + else { + project.warning("$root doesn't exist") } } + + module.sourceRoots.each (rootProcessor) + module.testRoots.each (rootProcessor) } } } class GroovycBuilder implements ModuleBuilder { def GroovycBuilder(Project project) { - project.binding.ant.taskdef (name: "groovyc", classname: "org.codehaus.groovy.ant.Groovyc") + project.taskdef (name: "groovyc", classname: "org.codehaus.groovy.ant.Groovyc") } def processModule(ModuleChunk module, ModuleBuildState state) { @@ -78,15 +109,33 @@ class GroovycBuilder implements ModuleBuilder { def project = module.project def ant = project.binding.ant - ant.groovyc (destdir: state.targetFolder) { + final String destDir = state.targetFolder + + ant.touch(millis: 239) { + fileset(dir: destDir) { + include(name: "**/*.class") + } + } + + ant.groovyc(destdir: destDir) { state.sourceRoots.each { src(path: it) } + include(name: "**/*.groovy") + classpath { state.classpath.each { pathelement(location: it) } + + pathelement(location: destDir) // Includes classes generated there by javac compiler + } + } + + ant.touch() { + fileset(dir: destDir) { + include(name: "**/*.class") } } } @@ -95,7 +144,7 @@ class GroovycBuilder implements ModuleBuilder { class GroovyStubGenerator implements ModuleBuilder { def GroovyStubGenerator(Project project) { - project.binding.ant.taskdef (name: "generatestubs", classname: "org.codehaus.groovy.ant.GenerateStubsTask") + project.taskdef (name: "generatestubs", classname: "org.codehaus.groovy.ant.GenerateStubsTask") } def processModule(ModuleChunk module, ModuleBuildState state) { @@ -133,14 +182,14 @@ class GroovyStubGenerator implements ModuleBuilder { class JetBrainsInstrumentations implements ModuleBuilder { def JetBrainsInstrumentations(Project project) { - project.binding.ant.taskdef(name: "jb_instrumentations", classname: "com.intellij.ant.InstrumentIdeaExtensions") + project.taskdef(name: "jb_instrumentations", classname: "com.intellij.ant.InstrumentIdeaExtensions") } def processModule(ModuleChunk module, ModuleBuildState state) { def project = module.project def ant = project.binding.ant - ant.jb_instrumentations(destdir: state.targetFolder) { + ant.jb_instrumentations(destdir: state.targetFolder, failonerror: "false") { state.sourceRoots.each { src(path: it) } diff --git a/jps/src/org/jetbrains/jps/idea/IdeaProjectLoader.groovy b/jps/src/org/jetbrains/jps/idea/IdeaProjectLoader.groovy index 5abf50707e1f..b78aaa64f714 100644 --- a/jps/src/org/jetbrains/jps/idea/IdeaProjectLoader.groovy +++ b/jps/src/org/jetbrains/jps/idea/IdeaProjectLoader.groovy @@ -147,6 +147,7 @@ public class IdeaProjectLoader { componentTag.content.sourceFolder.each {Node folderTag -> String path = expandMacro(pathFromUrl(attr(folderTag, "url")), projectBasePath, moduleBasePath) + String prefix = attr(folderTag, "packagePrefix") if (folderTag.attribute("isTestSource") == "true") { testSrc path @@ -154,6 +155,10 @@ public class IdeaProjectLoader { else { src path } + + if (prefix != null && prefix != "") { + project.modules[currentModuleName].sourceRootPrefixes[path] = (prefix.replace('.', '/')) + } } componentTag.content.excludeFolder.each {Node exTag ->