| /********************************************************************** |
| * Copyright (c) 2003 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM - Initial API and implementation |
| **********************************************************************/ |
| package org.eclipse.cdt.managedbuilder.core; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.InputStream; |
| import java.io.OutputStreamWriter; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.ListIterator; |
| import java.util.Map; |
| |
| import javax.xml.parsers.DocumentBuilder; |
| import javax.xml.parsers.DocumentBuilderFactory; |
| |
| import org.apache.xerces.dom.DocumentImpl; |
| import org.apache.xml.serialize.Method; |
| import org.apache.xml.serialize.OutputFormat; |
| import org.apache.xml.serialize.Serializer; |
| import org.apache.xml.serialize.SerializerFactory; |
| import org.eclipse.cdt.core.AbstractCExtension; |
| import org.eclipse.cdt.core.parser.*; |
| import org.eclipse.cdt.managedbuilder.internal.core.Configuration; |
| import org.eclipse.cdt.managedbuilder.internal.core.ManagedBuildInfo; |
| import org.eclipse.cdt.managedbuilder.internal.core.Target; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IConfigurationElement; |
| import org.eclipse.core.runtime.IExtension; |
| import org.eclipse.core.runtime.IExtensionPoint; |
| import org.eclipse.core.runtime.QualifiedName; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| |
| /** |
| * This is the main entry point for getting at the build information |
| * for the managed build system. |
| */ |
| public class ManagedBuildManager extends AbstractCExtension implements IScannerInfoProvider { |
| |
| private static final QualifiedName buildInfoProperty = new QualifiedName(ManagedBuilderCorePlugin.getUniqueIdentifier(), "managedBuildInfo"); |
| private static final String ROOT_ELEM_NAME = "ManagedProjectBuildInfo"; //$NON-NLS-1$ |
| private static final String FILE_NAME = ".cdtbuild"; //$NON-NLS-1$ |
| private static final ITarget[] emptyTargets = new ITarget[0]; |
| public static final String INTERFACE_IDENTITY = ManagedBuilderCorePlugin.getUniqueIdentifier() + "." + "ManagedBuildManager"; //$NON-NLS-1$ |
| public static final String EXTENSION_POINT_ID = "ManagedBuildInfo"; //$NON-NLS-1$ |
| |
| // Targets defined by extensions (i.e., not associated with a resource) |
| private static boolean extensionTargetsLoaded = false; |
| private static List extensionTargets; |
| private static Map extensionTargetMap; |
| |
| // Listeners interested in build model changes |
| private static Map buildModelListeners; |
| |
| /** |
| * Returns the list of targets that are defined by this project, |
| * projects referenced by this project, and by the extensions. |
| * |
| * @param project |
| * @return |
| */ |
| public static ITarget[] getDefinedTargets(IProject project) { |
| // Make sure the extensions are loaded |
| loadExtensions(); |
| |
| // Get the targets for this project and all referenced projects |
| List definedTargets = null; |
| // To Do |
| |
| // Create the array and copy the elements over |
| int size = extensionTargets != null ? |
| extensionTargets.size() + (definedTargets != null ? definedTargets.size() : 0) : |
| 0; |
| |
| ITarget[] targets = new ITarget[size]; |
| |
| int n = 0; |
| for (int i = 0; i < extensionTargets.size(); ++i) |
| targets[n++] = (ITarget)extensionTargets.get(i); |
| |
| if (definedTargets != null) |
| for (int i = 0; i < definedTargets.size(); ++i) |
| targets[n++] = (ITarget)definedTargets.get(i); |
| |
| return targets; |
| } |
| |
| /** |
| * @return |
| */ |
| public static Map getExtensionTargetMap() { |
| if (extensionTargetMap == null) { |
| extensionTargetMap = new HashMap(); |
| } |
| return extensionTargetMap; |
| } |
| |
| /** |
| * Returns the targets owned by this project. If none are owned, |
| * an empty array is returned. |
| * |
| * @param project |
| * @return |
| */ |
| public static ITarget[] getTargets(IResource resource) { |
| IManagedBuildInfo buildInfo = getBuildInfo(resource); |
| |
| if (buildInfo != null) { |
| List targets = buildInfo.getTargets(); |
| return (ITarget[])targets.toArray(new ITarget[targets.size()]); |
| } else { |
| return emptyTargets; |
| } |
| } |
| |
| |
| /** |
| * Answers the result of a best-effort search to find a target with the |
| * specified ID, or <code>null</code> if one is not found. |
| * |
| * @param resource |
| * @param id |
| * @return |
| */ |
| public static ITarget getTarget(IResource resource, String id) { |
| ITarget target = null; |
| // Check if the target is spec'd in the build info for the resource |
| if (resource != null) { |
| IManagedBuildInfo buildInfo = getBuildInfo(resource); |
| if (buildInfo != null) |
| target = buildInfo.getTarget(id); |
| } |
| // OK, check the extension map |
| if (target == null) { |
| target = (ITarget)getExtensionTargetMap().get(id); |
| } |
| return target; |
| } |
| |
| /** |
| * Creates a new target for the resource based on the parentTarget. |
| * |
| * @param resource |
| * @param parentTarget |
| * @return new <code>ITarget</code> with settings based on the parent passed in the arguments |
| * @throws BuildException |
| */ |
| public static ITarget createTarget(IResource resource, ITarget parentTarget) |
| throws BuildException |
| { |
| IResource owner = parentTarget.getOwner(); |
| |
| if (owner != null && owner.equals(resource)) |
| // Already added |
| return parentTarget; |
| |
| if (resource instanceof IProject) { |
| // Must be an extension target (why?) |
| if (owner != null) |
| throw new BuildException("addTarget: owner not null"); |
| } else { |
| // Owner must be owned by the project containing this resource |
| if (owner == null) |
| throw new BuildException("addTarget: null owner"); |
| if (!owner.equals(resource.getProject())) |
| throw new BuildException("addTarget: owner not project"); |
| } |
| |
| // Passed validation |
| return new Target(resource, parentTarget); |
| } |
| |
| /** |
| * Sets the default configuration for the project. Note that this will also |
| * update the default target if needed. |
| * |
| * @param project |
| * @param newDefault |
| */ |
| public static void setDefaultConfiguration(IProject project, IConfiguration newDefault) { |
| if (project == null || newDefault == null) { |
| return; |
| } |
| // Set the default in build information for the project |
| IManagedBuildInfo info = getBuildInfo(project); |
| if (info != null) { |
| info.setDefaultConfiguration(newDefault); |
| } |
| } |
| |
| /** |
| * @param config |
| * @param option |
| */ |
| private static void setDirty(IConfiguration config, IOption option) { |
| // Set the build info dirty so builder will rebuild |
| IResource resource = config.getOwner(); |
| IManagedBuildInfo info = getBuildInfo(resource); |
| info.setDirty(true); |
| |
| // Continue if change is something that effect the scanner |
| if (!(option.getValueType() == IOption.INCLUDE_PATH |
| || option.getValueType() == IOption.PREPROCESSOR_SYMBOLS)) { |
| return; |
| } |
| // Figure out if there is a listener for this change |
| List listeners = (List) getBuildModelListeners().get(resource); |
| if (listeners == null) { |
| return; |
| } |
| ListIterator iter = listeners.listIterator(); |
| while (iter.hasNext()) { |
| ((IScannerInfoChangeListener)iter.next()).changeNotification(resource, (IScannerInfo)getBuildInfo(resource, false)); |
| } |
| } |
| |
| /** |
| * Set the string value for an option for a given config. |
| * |
| * @param config The configuration the option belongs to. |
| * @param option The option to set the value for. |
| * @param value The boolean that the option should contain after the change. |
| */ |
| public static void setOption(IConfiguration config, IOption option, boolean value) { |
| try { |
| config.setOption(option, value); |
| setDirty(config, option); |
| } catch (BuildException e) { |
| return; |
| } |
| } |
| |
| /** |
| * Set the string value for an option for a given config. |
| * |
| * @param config The configuration the option belongs to. |
| * @param option The option to set the value for. |
| * @param value The value that the option should contain after the change. |
| */ |
| public static void setOption(IConfiguration config, IOption option, String value) { |
| try { |
| config.setOption(option, value); |
| setDirty(config, option); |
| } catch (BuildException e) { |
| return; |
| } |
| } |
| |
| /** |
| * Set the string array value for an option for a given config. |
| * |
| * @param config The configuration the option belongs to. |
| * @param option The option to set the value for. |
| * @param value The values the option should contain after the change. |
| */ |
| public static void setOption(IConfiguration config, IOption option, String[] value) { |
| try { |
| config.setOption(option, value); |
| setDirty(config, option); |
| } catch (BuildException e) { |
| return; |
| } |
| } |
| |
| /** |
| * Saves the build information associated with a project and all resources |
| * in the project to the build info file. |
| * |
| * @param project |
| */ |
| public static void saveBuildInfo(IProject project) { |
| // Create document |
| Document doc = new DocumentImpl(); |
| Element rootElement = doc.createElement(ROOT_ELEM_NAME); |
| doc.appendChild(rootElement); |
| |
| // Save the build info |
| ManagedBuildInfo buildInfo = (ManagedBuildInfo) getBuildInfo(project); |
| if (buildInfo != null) |
| buildInfo.serialize(doc, rootElement); |
| |
| // Save the document |
| ByteArrayOutputStream s = new ByteArrayOutputStream(); |
| OutputFormat format = new OutputFormat(); |
| format.setIndenting(true); |
| format.setLineSeparator(System.getProperty("line.separator")); //$NON-NLS-1$ |
| String xml = null; |
| try { |
| Serializer serializer |
| = SerializerFactory.getSerializerFactory(Method.XML).makeSerializer(new OutputStreamWriter(s, "UTF8"), format); |
| serializer.asDOMSerializer().serialize(doc); |
| xml = s.toString("UTF8"); //$NON-NLS-1$ |
| IFile rscFile = project.getFile(FILE_NAME); |
| InputStream inputStream = new ByteArrayInputStream(xml.getBytes()); |
| // update the resource content |
| if (rscFile.exists()) { |
| rscFile.setContents(inputStream, IResource.FORCE, null); |
| } else { |
| rscFile.create(inputStream, IResource.FORCE, null); |
| } |
| } catch (Exception e) { |
| return; |
| } |
| } |
| |
| /** |
| * @param resource |
| */ |
| public static void removeBuildInfo(IResource resource) { |
| try { |
| resource.setSessionProperty(buildInfoProperty, null); |
| } catch (CoreException e) { |
| } |
| } |
| |
| /** |
| * Resets the build information for the project and configuration specified in the arguments. |
| * The build information will contain the settings defined in the plugin manifest. |
| * |
| * @param project |
| * @param configuration |
| */ |
| public static void resetConfiguration(IProject project, IConfiguration configuration) { |
| // Make sure the extensions are loaded |
| loadExtensions(); |
| |
| // Find out the parent of the configuration |
| IConfiguration parentConfig = configuration.getParent(); |
| // Find the parent target the configuration |
| ITarget parentTarget = parentConfig.getTarget(); |
| |
| // Get the extension point information |
| IExtensionPoint extensionPoint = ManagedBuilderCorePlugin.getDefault().getDescriptor().getExtensionPoint(EXTENSION_POINT_ID); |
| IExtension[] extensions = extensionPoint.getExtensions(); |
| for (int i = 0; i < extensions.length; ++i) { |
| IExtension extension = extensions[i]; |
| IConfigurationElement[] elements = extension.getConfigurationElements(); |
| for (int j = 0; j < elements.length; ++j) { |
| IConfigurationElement element = elements[j]; |
| if (element.getName().equals(ITarget.TARGET_ELEMENT_NAME) && |
| element.getAttribute(ITarget.ID).equals(parentTarget.getId())) { |
| // We have the parent target so get the definition for the parent config |
| IConfigurationElement[] targetElements = element.getChildren(); |
| for (int k = 0; k < targetElements.length; ++k) { |
| IConfigurationElement targetElement = targetElements[k]; |
| if (targetElement.getName().equals(IConfiguration.CONFIGURATION_ELEMENT_NAME) && |
| targetElement.getAttribute(IConfiguration.ID).equals(parentConfig.getId())) { |
| // We now have the plugin element the target was originally based on |
| ((Configuration)configuration).reset(targetElement); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| // Private stuff |
| |
| public static void addExtensionTarget(Target target) { |
| if (extensionTargets == null) { |
| extensionTargets = new ArrayList(); |
| } |
| |
| extensionTargets.add(target); |
| getExtensionTargetMap().put(target.getId(), target); |
| } |
| |
| private static ManagedBuildInfo loadBuildInfo(IProject project) { |
| ManagedBuildInfo buildInfo = null; |
| IFile file = project.getFile(FILE_NAME); |
| if (!file.exists()) |
| return null; |
| |
| try { |
| InputStream stream = file.getContents(); |
| DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder(); |
| Document document = parser.parse(stream); |
| Node rootElement = document.getFirstChild(); |
| if (rootElement.getNodeName().equals(ROOT_ELEM_NAME)) { |
| buildInfo = new ManagedBuildInfo(project, (Element)rootElement); |
| project.setSessionProperty(buildInfoProperty, buildInfo); |
| } |
| } catch (Exception e) { |
| buildInfo = null; |
| } |
| |
| return buildInfo; |
| } |
| |
| private static void loadExtensions() { |
| if (extensionTargetsLoaded) |
| return; |
| extensionTargetsLoaded = true; |
| |
| IExtensionPoint extensionPoint = ManagedBuilderCorePlugin.getDefault().getDescriptor().getExtensionPoint(EXTENSION_POINT_ID); |
| IExtension[] extensions = extensionPoint.getExtensions(); |
| for (int i = 0; i < extensions.length; ++i) { |
| IExtension extension = extensions[i]; |
| IConfigurationElement[] elements = extension.getConfigurationElements(); |
| for (int j = 0; j < elements.length; ++j) { |
| IConfigurationElement element = elements[j]; |
| if (element.getName().equals(ITarget.TARGET_ELEMENT_NAME)) { |
| new Target(element); |
| } |
| } |
| } |
| } |
| |
| /** |
| * @param project |
| * @return |
| */ |
| public static boolean manages(IResource resource) { |
| // The managed build manager manages build information for the |
| // resource IFF it it is a project and has a build file with the proper |
| // root element |
| IProject project = null; |
| if (resource instanceof IProject){ |
| project = (IProject)resource; |
| } else if (resource instanceof IFile) { |
| project = ((IFile)resource).getProject(); |
| } else { |
| return false; |
| } |
| IFile file = project.getFile(FILE_NAME); |
| if (file.exists()) { |
| try { |
| InputStream stream = file.getContents(); |
| DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder(); |
| Document document = parser.parse(stream); |
| Node rootElement = document.getFirstChild(); |
| if (rootElement.getNodeName().equals(ROOT_ELEM_NAME)) { |
| return true; |
| } |
| } catch (Exception e) { |
| return false; |
| } |
| } |
| return false; |
| } |
| |
| private static ManagedBuildInfo findBuildInfo(IResource resource, boolean create) { |
| // Make sure the extension information is loaded first |
| loadExtensions(); |
| ManagedBuildInfo buildInfo = null; |
| try { |
| buildInfo = (ManagedBuildInfo)resource.getSessionProperty(buildInfoProperty); |
| } catch (CoreException e) { |
| return buildInfo; |
| } |
| |
| if (buildInfo == null && resource instanceof IProject) { |
| buildInfo = loadBuildInfo((IProject)resource); |
| } |
| |
| if (buildInfo == null && create) { |
| try { |
| buildInfo = new ManagedBuildInfo(resource); |
| resource.setSessionProperty(buildInfoProperty, buildInfo); |
| } catch (CoreException e) { |
| buildInfo = null; |
| } |
| } |
| |
| return buildInfo; |
| } |
| |
| public static IManagedBuildInfo getBuildInfo(IResource resource, boolean create) { |
| return (IManagedBuildInfo) findBuildInfo(resource, create); |
| } |
| |
| public static IManagedBuildInfo getBuildInfo(IResource resource) { |
| return (IManagedBuildInfo) findBuildInfo(resource, false); |
| } |
| |
| /* |
| * @return |
| */ |
| private static Map getBuildModelListeners() { |
| if (buildModelListeners == null) { |
| buildModelListeners = new HashMap(); |
| } |
| return buildModelListeners; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.core.parser.IScannerInfoProvider#getScannerInformation(org.eclipse.core.resources.IResource) |
| */ |
| public IScannerInfo getScannerInformation(IResource resource) { |
| return (IScannerInfo) getBuildInfo(resource, false); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.core.parser.IScannerInfoProvider#subscribe(org.eclipse.cdt.core.parser.IScannerInfoChangeListener) |
| */ |
| public synchronized void subscribe(IResource resource, IScannerInfoChangeListener listener) { |
| IResource project = null; |
| if (resource instanceof IProject) { |
| project = resource; |
| } else if (resource instanceof IFile) { |
| project = ((IFile)resource).getProject(); |
| } else { |
| return; |
| } |
| // Get listeners for this resource |
| Map map = getBuildModelListeners(); |
| List list = (List) map.get(project); |
| if (list == null) { |
| // Create a new list |
| list = new ArrayList(); |
| } |
| if (!list.contains(listener)) { |
| // Add the new listener for the resource |
| list.add(listener); |
| map.put(project, list); |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.core.parser.IScannerInfoProvider#unsubscribe(org.eclipse.cdt.core.parser.IScannerInfoChangeListener) |
| */ |
| public synchronized void unsubscribe(IResource resource, IScannerInfoChangeListener listener) { |
| IResource project = null; |
| if (resource instanceof IProject) { |
| project = resource; |
| } else if (resource instanceof IFile) { |
| project = ((IFile)resource).getProject(); |
| } else { |
| return; |
| } |
| // Remove the listener |
| Map map = getBuildModelListeners(); |
| List list = (List) map.get(project); |
| if (list != null && !list.isEmpty()) { |
| // The list is not empty so try to remove listener |
| list.remove(listener); |
| map.put(project, list); |
| } |
| } |
| |
| } |