| /******************************************************************** |
| * Copyright (c) 2007 Contributors. All rights reserved. |
| * This program and the accompanying materials are made available |
| * under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution and is available at |
| * http://eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: IBM Corporation - initial API and implementation |
| * Helen Hawkins - initial version (bug 148190) |
| *******************************************************************/ |
| package org.aspectj.ajde.core.internal; |
| |
| import java.io.File; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.StringTokenizer; |
| |
| import org.aspectj.ajde.core.AjCompiler; |
| import org.aspectj.ajde.core.ICompilerConfiguration; |
| import org.aspectj.ajde.core.IOutputLocationManager; |
| import org.aspectj.ajdt.ajc.AjdtCommand; |
| import org.aspectj.ajdt.ajc.BuildArgParser; |
| import org.aspectj.ajdt.ajc.ConfigParser; |
| import org.aspectj.ajdt.internal.core.builder.AjBuildConfig; |
| import org.aspectj.ajdt.internal.core.builder.AjBuildManager; |
| import org.aspectj.ajdt.internal.core.builder.AjState; |
| import org.aspectj.ajdt.internal.core.builder.IncrementalStateManager; |
| import org.aspectj.asm.AsmManager; |
| import org.aspectj.bridge.AbortException; |
| import org.aspectj.bridge.CountingMessageHandler; |
| import org.aspectj.bridge.IMessage; |
| import org.aspectj.bridge.IMessageHandler; |
| import org.aspectj.bridge.ISourceLocation; |
| import org.aspectj.bridge.Message; |
| import org.aspectj.bridge.SourceLocation; |
| import org.aspectj.bridge.context.CompilationAndWeavingContext; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.impl.CompilerOptions; |
| import org.aspectj.util.LangUtil; |
| |
| /** |
| * Build Manager which drives the build for a given AjCompiler. Tools call build on the AjCompiler which drives this. |
| */ |
| public class AjdeCoreBuildManager { |
| |
| private final AjCompiler compiler; |
| private AjdeCoreBuildNotifierAdapter buildEventNotifier = null; |
| private final AjBuildManager ajBuildManager; |
| private final IMessageHandler msgHandlerAdapter; |
| |
| public AjdeCoreBuildManager(AjCompiler compiler) { |
| this.compiler = compiler; |
| this.msgHandlerAdapter = new AjdeCoreMessageHandlerAdapter(compiler.getMessageHandler()); |
| this.ajBuildManager = new AjBuildManager(msgHandlerAdapter); |
| this.ajBuildManager.environmentSupportsIncrementalCompilation(true); |
| |
| // this static information needs to be set to ensure |
| // incremental compilation works correctly |
| IncrementalStateManager.recordIncrementalStates = true; |
| IncrementalStateManager.debugIncrementalStates = false; |
| AsmManager.attemptIncrementalModelRepairs = true; |
| } |
| |
| public AjBuildManager getAjBuildManager() { |
| return ajBuildManager; |
| } |
| |
| /** |
| * Execute a full or incremental build |
| * |
| * @param fullBuild true if requesting a full build, false if requesting to try an incremental build |
| */ |
| public void performBuild(boolean fullBuild) { |
| |
| // If an incremental build is requested, check that we can |
| if (!fullBuild) { |
| AjState existingState = IncrementalStateManager.retrieveStateFor(compiler.getId()); |
| if (existingState == null || existingState.getBuildConfig() == null |
| || ajBuildManager.getState().getBuildConfig() == null) { |
| // No existing state so we must do a full build |
| fullBuild = true; |
| } else { |
| AsmManager.setLastActiveStructureModel(existingState.getStructureModel()); |
| // AsmManager.getDefault().setRelationshipMap(existingState.getRelationshipMap()); |
| // AsmManager.getDefault().setHierarchy(existingState.getStructureModel()); |
| } |
| } |
| try { |
| reportProgressBegin(); |
| |
| // record the options passed to the compiler if INFO turned on |
| if (!msgHandlerAdapter.isIgnoring(IMessage.INFO)) { |
| handleMessage(new Message(getFormattedOptionsString(), IMessage.INFO, null, null)); |
| } |
| |
| CompilationAndWeavingContext.reset(); |
| |
| if (fullBuild) { // FULL BUILD |
| AjBuildConfig buildConfig = generateAjBuildConfig(); |
| if (buildConfig == null) { |
| return; |
| } |
| ajBuildManager.batchBuild(buildConfig, msgHandlerAdapter); |
| } else { // INCREMENTAL BUILD |
| // Only rebuild the config object if the configuration has changed |
| AjBuildConfig buildConfig = null; |
| ICompilerConfiguration compilerConfig = compiler.getCompilerConfiguration(); |
| int changes = compilerConfig.getConfigurationChanges(); |
| if (changes != ICompilerConfiguration.NO_CHANGES) { |
| |
| // What configuration changes can we cope with? And besides just repairing the config object |
| // what does it mean for any existing state that we have? |
| |
| buildConfig = generateAjBuildConfig(); |
| if (buildConfig == null) { |
| return; |
| } |
| } else { |
| buildConfig = ajBuildManager.getState().getBuildConfig(); |
| buildConfig.setChanged(changes); // pass it through for the state to use it when making decisions |
| buildConfig.setModifiedFiles(compilerConfig.getProjectSourceFilesChanged()); |
| buildConfig.setClasspathElementsWithModifiedContents(compilerConfig.getClasspathElementsWithModifiedContents()); |
| compilerConfig.configurationRead(); |
| } |
| ajBuildManager.incrementalBuild(buildConfig, msgHandlerAdapter); |
| } |
| IncrementalStateManager.recordSuccessfulBuild(compiler.getId(), ajBuildManager.getState()); |
| |
| } catch (ConfigParser.ParseException pe) { |
| handleMessage(new Message("Config file entry invalid, file: " + pe.getFile().getPath() + ", line number: " |
| + pe.getLine(), IMessage.WARNING, null, null)); |
| } catch (AbortException e) { |
| final IMessage message = e.getIMessage(); |
| if (message == null) { |
| handleMessage(new Message(LangUtil.unqualifiedClassName(e) + " thrown: " + e.getMessage(), IMessage.ERROR, e, null)); |
| } else { |
| handleMessage(new Message(message.getMessage() + "\n" + CompilationAndWeavingContext.getCurrentContext(), |
| IMessage.ERROR, e, null)); |
| } |
| } catch (Throwable t) { |
| handleMessage(new Message("Compile error: " + LangUtil.unqualifiedClassName(t) + " thrown: " + "" + t.getMessage(), |
| IMessage.ABORT, t, null)); |
| } finally { |
| compiler.getBuildProgressMonitor().finish(ajBuildManager.wasFullBuild()); |
| } |
| } |
| |
| /** |
| * Starts the various notifiers which are interested in the build progress |
| */ |
| private void reportProgressBegin() { |
| compiler.getBuildProgressMonitor().begin(); |
| buildEventNotifier = new AjdeCoreBuildNotifierAdapter(compiler.getBuildProgressMonitor()); |
| ajBuildManager.setProgressListener(buildEventNotifier); |
| } |
| |
| private String getFormattedOptionsString() { |
| ICompilerConfiguration compilerConfig = compiler.getCompilerConfiguration(); |
| return "Building with settings: " + "\n-> output paths: " |
| + formatCollection(compilerConfig.getOutputLocationManager().getAllOutputLocations()) + "\n-> classpath: " |
| + compilerConfig.getClasspath() + "\n-> -inpath " + formatCollection(compilerConfig.getInpath()) + "\n-> -outjar " |
| + formatOptionalString(compilerConfig.getOutJar()) + "\n-> -aspectpath " |
| + formatCollection(compilerConfig.getAspectPath()) + "\n-> -sourcePathResources " |
| + formatMap(compilerConfig.getSourcePathResources()) + "\n-> non-standard options: " |
| + compilerConfig.getNonStandardOptions() + "\n-> javaoptions:" + formatMap(compilerConfig.getJavaOptionsMap()); |
| } |
| |
| private String formatCollection(Collection<?> options) { |
| if (options == null) { |
| return "<default>"; |
| } |
| if (options.isEmpty()) { |
| return "none"; |
| } |
| |
| StringBuffer formattedOptions = new StringBuffer(); |
| Iterator<?> it = options.iterator(); |
| while (it.hasNext()) { |
| String o = it.next().toString(); |
| if (formattedOptions.length() > 0) { |
| formattedOptions.append(", "); |
| } |
| formattedOptions.append(o); |
| } |
| return formattedOptions.toString(); |
| } |
| |
| private String formatMap(Map options) { |
| if (options == null) { |
| return "<default>"; |
| } |
| if (options.isEmpty()) { |
| return "none"; |
| } |
| |
| return options.toString(); |
| } |
| |
| private String formatOptionalString(String s) { |
| if (s == null) { |
| return ""; |
| } else { |
| return s; |
| } |
| } |
| |
| /** |
| * Generate a new AjBuildConfig from the compiler configuration associated with this AjdeCoreBuildManager or from a |
| * configuration file. |
| * |
| * @return null if invalid configuration, corresponding AjBuildConfig otherwise |
| */ |
| public AjBuildConfig generateAjBuildConfig() { |
| File configFile = new File(compiler.getId()); |
| ICompilerConfiguration compilerConfig = compiler.getCompilerConfiguration(); |
| CountingMessageHandler handler = CountingMessageHandler.makeCountingMessageHandler(msgHandlerAdapter); |
| |
| String[] args = null; |
| // Retrieve the set of files from either an arg file (@filename) or the compiler configuration |
| if (configFile.exists() && configFile.isFile()) { |
| args = new String[] { "@" + configFile.getAbsolutePath() }; |
| } else { |
| List<String> l = compilerConfig.getProjectSourceFiles(); |
| if (l == null) { |
| return null; |
| } |
| // If the processor options are specified build the command line options for the JDT compiler to see |
| String processor = compilerConfig.getProcessor(); |
| if (processor != null && processor.length() != 0) { |
| l.add("-processor"); |
| l.add(processor); |
| } |
| String processorPath = compilerConfig.getProcessorPath(); |
| if (processorPath != null && processorPath.length() != 0) { |
| l.add("-processorpath"); |
| l.add(processorPath); |
| } |
| List<String> xmlfiles = compilerConfig.getProjectXmlConfigFiles(); |
| if (xmlfiles != null && !xmlfiles.isEmpty()) { |
| args = new String[l.size() + xmlfiles.size() + 1]; |
| // TODO speedup |
| int p = 0; |
| for (int i = 0; i < l.size(); i++) { |
| args[p++] = (String) l.get(i); |
| } |
| for (int i = 0; i < xmlfiles.size(); i++) { |
| args[p++] = (String) xmlfiles.get(i); |
| } |
| args[p++] = "-xmlConfigured"; |
| } else { |
| args = (String[]) l.toArray(new String[l.size()]); |
| } |
| } |
| |
| BuildArgParser parser = new BuildArgParser(handler); |
| |
| AjBuildConfig config = new AjBuildConfig(parser); |
| |
| parser.populateBuildConfig(config, args, false, configFile); |
| |
| // Process the CLASSPATH |
| String propcp = compilerConfig.getClasspath(); |
| if (propcp != null && propcp.length() != 0) { |
| StringTokenizer st = new StringTokenizer(propcp, File.pathSeparator); |
| List<String> configClasspath = config.getClasspath(); |
| ArrayList<String> toAdd = new ArrayList<String>(); |
| while (st.hasMoreTokens()) { |
| String entry = st.nextToken(); |
| if (!configClasspath.contains(entry)) { |
| toAdd.add(entry); |
| } |
| } |
| if (0 < toAdd.size()) { |
| ArrayList<String> both = new ArrayList<String>(configClasspath.size() + toAdd.size()); |
| both.addAll(configClasspath); |
| both.addAll(toAdd); |
| config.setClasspath(both); |
| } |
| } |
| |
| // Process the OUTJAR |
| if (config.getOutputJar() == null) { |
| String outJar = compilerConfig.getOutJar(); |
| if (outJar != null && outJar.length() != 0) { |
| config.setOutputJar(new File(outJar)); |
| } |
| } |
| |
| // Process the OUTPUT LOCATION MANAGER |
| IOutputLocationManager outputLocationManager = compilerConfig.getOutputLocationManager(); |
| if (config.getCompilationResultDestinationManager() == null && outputLocationManager != null) { |
| config.setCompilationResultDestinationManager(new OutputLocationAdapter(outputLocationManager)); |
| } |
| |
| // Process the INPATH |
| mergeInto(config.getInpath(), compilerConfig.getInpath()); |
| // bug 168840 - calling 'setInPath(..)' creates BinarySourceFiles which |
| // are used to see if there have been changes in classes on the inpath |
| if (config.getInpath() != null) { |
| config.setInPath(config.getInpath()); |
| } |
| |
| // Process the SOURCE PATH RESOURCES |
| config.setSourcePathResources(compilerConfig.getSourcePathResources()); |
| |
| // Process the ASPECTPATH |
| mergeInto(config.getAspectpath(), compilerConfig.getAspectPath()); |
| |
| // Process the JAVA OPTIONS MAP |
| Map jom = compilerConfig.getJavaOptionsMap(); |
| if (jom != null) { |
| String version = (String) jom.get(CompilerOptions.OPTION_Compliance); |
| if (version != null && !version.equals(CompilerOptions.VERSION_1_4)) { |
| config.setBehaveInJava5Way(true); |
| } |
| config.getOptions().set(jom); |
| } |
| |
| // Process the NON-STANDARD COMPILER OPTIONS |
| configureNonStandardOptions(config); |
| |
| compilerConfig.configurationRead(); |
| |
| ISourceLocation location = null; |
| if (config.getConfigFile() != null) { |
| location = new SourceLocation(config.getConfigFile(), 0); |
| } |
| |
| String message = parser.getOtherMessages(true); |
| if (null != message) { |
| IMessage m = new Message(message, IMessage.ERROR, null, location); |
| handler.handleMessage(m); |
| } |
| |
| // always force model generation in AJDE |
| config.setGenerateModelMode(true); |
| // always be in incremental mode in AJDE |
| config.setIncrementalMode(true); |
| // always force proceedOnError in AJDE |
| config.setProceedOnError(true); |
| |
| config.setProjectEncoding(compilerConfig.getProjectEncoding()); |
| config.setProcessor(compilerConfig.getProcessor()); |
| config.setProcessorPath(compilerConfig.getProcessorPath()); |
| return config; |
| } |
| |
| private <T> void mergeInto(Collection<T> target, Collection<T> source) { |
| if ((null == target) || (null == source)) { |
| return; |
| } |
| for (T next : source) { |
| if (!target.contains(next)) { |
| target.add(next); |
| } |
| } |
| } |
| |
| /** |
| * Helper method for configure build options. This reads all command-line options specified in the non-standard options text |
| * entry and sets any corresponding unset values in config. |
| */ |
| private void configureNonStandardOptions(AjBuildConfig config) { |
| |
| String nonStdOptions = compiler.getCompilerConfiguration().getNonStandardOptions(); |
| if (LangUtil.isEmpty(nonStdOptions)) { |
| return; |
| } |
| |
| // Break a string into a string array of non-standard options. |
| // Allows for one option to include a ' '. i.e. assuming it has been quoted, it |
| // won't accidentally get treated as a pair of options (can be needed for xlint props file option) |
| List<String> tokens = new ArrayList<String>(); |
| int ind = nonStdOptions.indexOf('\"'); |
| int ind2 = nonStdOptions.indexOf('\"', ind + 1); |
| if ((ind > -1) && (ind2 > -1)) { // dont tokenize within double quotes |
| String pre = nonStdOptions.substring(0, ind); |
| String quoted = nonStdOptions.substring(ind + 1, ind2); |
| String post = nonStdOptions.substring(ind2 + 1, nonStdOptions.length()); |
| tokens.addAll(tokenizeString(pre)); |
| tokens.add(quoted); |
| tokens.addAll(tokenizeString(post)); |
| } else { |
| tokens.addAll(tokenizeString(nonStdOptions)); |
| } |
| String[] args = (String[]) tokens.toArray(new String[] {}); |
| |
| // set the non-standard options in an alternate build config |
| // (we don't want to lose the settings we already have) |
| CountingMessageHandler counter = CountingMessageHandler.makeCountingMessageHandler(msgHandlerAdapter); |
| AjBuildConfig altConfig = AjdtCommand.genBuildConfig(args, counter); |
| if (counter.hasErrors()) { |
| return; |
| } |
| // copy globals where local is not set |
| config.installGlobals(altConfig); |
| } |
| |
| /** Local helper method for splitting option strings */ |
| private List<String> tokenizeString(String str) { |
| List<String> tokens = new ArrayList<String>(); |
| StringTokenizer tok = new StringTokenizer(str); |
| while (tok.hasMoreTokens()) { |
| tokens.add(tok.nextToken()); |
| } |
| return tokens; |
| } |
| |
| /** |
| * Helper method to ask the messagehandler to handle the given message |
| */ |
| private void handleMessage(Message msg) { |
| compiler.getMessageHandler().handleMessage(msg); |
| } |
| |
| public void setCustomMungerFactory(Object o) { |
| ajBuildManager.setCustomMungerFactory(o); |
| } |
| |
| public Object getCustomMungerFactory() { |
| return ajBuildManager.getCustomMungerFactory(); |
| } |
| |
| public void cleanupEnvironment() { |
| ajBuildManager.cleanupEnvironment(); |
| } |
| |
| public AsmManager getStructureModel() { |
| return ajBuildManager.getStructureModel(); |
| } |
| } |