| /* |
| * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, Germany) and others. |
| * 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://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Eike Stepper - initial API and implementation |
| */ |
| package org.eclipse.emf.cdo.releng.version; |
| |
| import org.eclipse.emf.cdo.releng.version.Release.Element; |
| import org.eclipse.emf.cdo.releng.version.Release.Element.Type; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResourceDelta; |
| import org.eclipse.core.resources.IncrementalProjectBuilder; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.osgi.service.resolver.BundleDescription; |
| import org.eclipse.osgi.service.resolver.BundleSpecification; |
| import org.eclipse.osgi.service.resolver.ExportPackageDescription; |
| import org.eclipse.osgi.service.resolver.ImportPackageSpecification; |
| import org.eclipse.osgi.service.resolver.VersionRange; |
| import org.eclipse.pde.core.plugin.IPluginModelBase; |
| import org.eclipse.pde.core.plugin.PluginRegistry; |
| |
| import org.osgi.framework.Version; |
| |
| import java.io.IOException; |
| import java.util.Map; |
| |
| /** |
| * @author Eike Stepper |
| */ |
| public class VersionBuilder extends IncrementalProjectBuilder |
| { |
| public static final String BUILDER_ID = "org.eclipse.emf.cdo.releng.version.VersionBuilder"; |
| |
| public static final String RELEASE_PATH_ARGUMENT = "release.path"; |
| |
| public static final String VALIDATOR_CLASS_ARGUMENT = "validator.class"; |
| |
| public static boolean DEBUG = false; |
| |
| private static final Path MANIFEST_PATH = new Path("META-INF/MANIFEST.MF"); |
| |
| private Release release; |
| |
| public VersionBuilder() |
| { |
| } |
| |
| @Override |
| protected final IProject[] build(int kind, @SuppressWarnings("rawtypes") Map args, IProgressMonitor monitor) |
| throws CoreException |
| { |
| IProject project = getProject(); |
| IProject[] releaseProject = null; |
| |
| BuildState buildState = Activator.getBuildState(project); |
| boolean fullBuild = buildState.getReleaseTag() == null; |
| VersionValidator validator = null; |
| |
| monitor.beginTask(null, 1); |
| |
| try |
| { |
| Markers.deleteAllMarkers(project); |
| |
| IPluginModelBase pluginModel = PluginRegistry.findModel(getProject()); |
| if (pluginModel == null) |
| { |
| throw new IllegalStateException("Could not locate the plugin model base for project: " + getProject().getName()); |
| } |
| |
| checkDependencyRanges(pluginModel); |
| checkPackageExports(pluginModel); |
| |
| /* |
| * Determine validator to use |
| */ |
| |
| IFile projectDescription = project.getFile(new Path(".project")); |
| String validatorClass = (String)args.get(VALIDATOR_CLASS_ARGUMENT); |
| if (validatorClass == null) |
| { |
| validatorClass = "org.eclipse.emf.cdo.releng.version.digest.DigestValidator$BuildModel"; |
| } |
| |
| try |
| { |
| Class<?> c = Class.forName(validatorClass, true, VersionBuilder.class.getClassLoader()); |
| validator = (VersionValidator)c.newInstance(); |
| } |
| catch (Exception ex) |
| { |
| String msg = ex.getLocalizedMessage() + ": " + validatorClass; |
| Markers.addMarker(projectDescription, msg, IMarker.SEVERITY_ERROR, ".*(" + validatorClass + ").*"); |
| return releaseProject; |
| } |
| |
| trace(validator.getClass().getName() + ": " + project.getName()); |
| |
| /* |
| * Determine release data to validate against |
| */ |
| |
| String releasePath = (String)args.get(RELEASE_PATH_ARGUMENT); |
| if (releasePath == null) |
| { |
| validator.abort(buildState, project, null, monitor); |
| String msg = "Build command argument missing: " + RELEASE_PATH_ARGUMENT; |
| Markers.addMarker(projectDescription, msg, IMarker.SEVERITY_ERROR, ".*(" + BUILDER_ID + ").*"); |
| return releaseProject; |
| } |
| |
| try |
| { |
| IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(releasePath)); |
| releaseProject = new IProject[] { file.getProject() }; |
| |
| Release release = ReleaseManager.INSTANCE.getRelease(file); |
| boolean releaseHasChanged = !release.getTag().equals(buildState.getReleaseTag()); |
| if (releaseHasChanged) |
| { |
| buildState.setReleaseTag(release.getTag()); |
| fullBuild = true; |
| } |
| |
| this.release = release; |
| } |
| catch (CoreException ex) |
| { |
| String msg = "Problem with release spec: " + releasePath; |
| Markers.addMarker(projectDescription, msg, IMarker.SEVERITY_ERROR, releasePath); |
| validator.abort(buildState, project, null, monitor); |
| return releaseProject; |
| } |
| |
| /* |
| * Determine if a validation is needed or if the version has already been increased properly |
| */ |
| |
| Element element = getElement(pluginModel); |
| Element releaseElement = release.getElements().get(element.getName()); |
| if (releaseElement == null) |
| { |
| validator.abort(buildState, project, null, monitor); |
| trace("Project has not been released: " + project.getName()); |
| return releaseProject; |
| } |
| |
| Version elementVersion = element.getVersion(); |
| Version releaseVersion = releaseElement.getVersion(); |
| Version nextVersion = new Version(releaseVersion.getMajor(), releaseVersion.getMinor(), releaseVersion.getMicro() |
| + (release.isIntegration() ? 100 : 1)); |
| int comparison = releaseVersion.compareTo(elementVersion); |
| if (comparison < 0) |
| { |
| if (!nextVersion.equals(elementVersion)) |
| { |
| if (elementVersion.getMajor() == nextVersion.getMajor() |
| && elementVersion.getMinor() == nextVersion.getMinor()) |
| { |
| addVersionMarker("Version should be " + nextVersion); |
| } |
| } |
| |
| validator.abort(buildState, project, null, monitor); |
| return releaseProject; |
| } |
| |
| if (comparison > 0) |
| { |
| validator.abort(buildState, project, null, monitor); |
| addVersionMarker("Version has been decreased after release " + releaseVersion); |
| return releaseProject; |
| } |
| |
| /* |
| * Do the validation |
| */ |
| |
| IResourceDelta delta = null; |
| fullBuild |= kind == FULL_BUILD; |
| if (!fullBuild) |
| { |
| delta = getDelta(project); |
| } |
| |
| validator.updateBuildState(buildState, releasePath, release, project, delta, pluginModel, monitor); |
| |
| try |
| { |
| if (buildState.isChangedSinceRelease()) |
| { |
| addVersionMarker("Version must be increased to " + nextVersion); |
| } |
| } |
| catch (Exception ignore) |
| { |
| Activator.log(ignore); |
| } |
| } |
| catch (Exception ex) |
| { |
| try |
| { |
| if (validator != null) |
| { |
| validator.abort(buildState, project, ex, monitor); |
| } |
| |
| String msg = Activator.log(ex); |
| Markers.addMarker(project, msg); |
| } |
| catch (Exception ignore) |
| { |
| Activator.log(ignore); |
| } |
| } |
| finally |
| { |
| monitor.done(); |
| } |
| |
| return releaseProject; |
| } |
| |
| private Element getElement(IPluginModelBase pluginModel) throws CoreException |
| { |
| BundleDescription description = pluginModel.getBundleDescription(); |
| String name = description.getSymbolicName(); |
| Version version = description.getVersion(); |
| return new Element(name, version, Type.PLUGIN); |
| } |
| |
| private void checkDependencyRanges(IPluginModelBase pluginModel) throws CoreException, IOException |
| { |
| BundleDescription description = pluginModel.getBundleDescription(); |
| for (BundleSpecification requiredBundle : description.getRequiredBundles()) |
| { |
| VersionRange range = requiredBundle.getVersionRange(); |
| if (isUnspecified(range.getMaximum())) |
| { |
| addRequireMarker(requiredBundle.getName(), "dependency must specify a version range"); |
| } |
| else |
| { |
| if (!range.getIncludeMinimum()) |
| { |
| addRequireMarker(requiredBundle.getName(), "dependency range must include the minimum"); |
| } |
| |
| if (range.getIncludeMaximum()) |
| { |
| addRequireMarker(requiredBundle.getName(), "dependency range must not include the maximum"); |
| } |
| } |
| } |
| |
| for (ImportPackageSpecification importPackage : description.getImportPackages()) |
| { |
| VersionRange range = importPackage.getVersionRange(); |
| if (isUnspecified(range.getMaximum())) |
| { |
| addImportMarker(importPackage.getName(), "dependency must specify a version range"); |
| } |
| else |
| { |
| if (!range.getIncludeMinimum()) |
| { |
| addImportMarker(importPackage.getName(), "dependency range must include the minimum"); |
| } |
| |
| if (range.getIncludeMaximum()) |
| { |
| addImportMarker(importPackage.getName(), "dependency range must not include the maximum"); |
| } |
| } |
| } |
| } |
| |
| private boolean isUnspecified(Version version) |
| { |
| if (version.getMajor() != Integer.MAX_VALUE) |
| { |
| return false; |
| } |
| |
| if (version.getMinor() != Integer.MAX_VALUE) |
| { |
| return false; |
| } |
| |
| if (version.getMicro() != Integer.MAX_VALUE) |
| { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| private void checkPackageExports(IPluginModelBase pluginModel) throws CoreException, IOException |
| { |
| BundleDescription description = pluginModel.getBundleDescription(); |
| String bundleName = description.getSymbolicName(); |
| Version bundleVersion = Release.normalizeVersion(description.getVersion()); |
| |
| for (ExportPackageDescription packageExport : description.getExportPackages()) |
| { |
| String packageName = packageExport.getName(); |
| if (isBundlePackage(packageName, bundleName)) |
| { |
| Version packageVersion = packageExport.getVersion(); |
| if (packageVersion != null && !packageVersion.equals(Version.emptyVersion) |
| && !packageVersion.equals(bundleVersion)) |
| { |
| addExportMarker(packageName); |
| } |
| } |
| } |
| } |
| |
| private boolean isBundlePackage(String packageName, String bundleName) |
| { |
| if (packageName.startsWith(bundleName)) |
| { |
| return true; |
| } |
| |
| int lastDot = bundleName.lastIndexOf('.'); |
| if (lastDot != -1) |
| { |
| String bundleStart = bundleName.substring(0, lastDot); |
| String bundleEnd = bundleName.substring(lastDot + 1); |
| if (packageName.startsWith(bundleStart + ".internal." + bundleEnd)) |
| { |
| return true; |
| } |
| |
| if (packageName.startsWith(bundleStart + ".spi." + bundleEnd)) |
| { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| private void addRequireMarker(String name, String message) throws CoreException, IOException |
| { |
| IFile file = getProject().getFile(MANIFEST_PATH); |
| String regex = ".* " + name.replaceAll("\\.", "\\\\.") + ";bundle-version=\"([^\\\"]*)\".*"; |
| Markers.addMarker(file, "'" + name + "' " + message, IMarker.SEVERITY_ERROR, regex); |
| } |
| |
| private void addImportMarker(String name, String message) throws CoreException, IOException |
| { |
| IFile file = getProject().getFile(MANIFEST_PATH); |
| String regex = ".* " + name.replaceAll("\\.", "\\\\.") + ";version=\"([^\\\"]*)\".*"; |
| Markers.addMarker(file, "'" + name + "' " + message, IMarker.SEVERITY_ERROR, regex); |
| } |
| |
| private void addExportMarker(String name) throws CoreException, IOException |
| { |
| IFile file = getProject().getFile(MANIFEST_PATH); |
| String message = "'" + name + "' export has wrong version information"; |
| String regex = ".* " + name.replaceAll("\\.", "\\\\.") + ";version=\"([0123456789\\.]*)\".*"; |
| Markers.addMarker(file, message, IMarker.SEVERITY_ERROR, regex); |
| } |
| |
| private void addVersionMarker(String message) throws CoreException, IOException |
| { |
| IFile file = getProject().getFile(MANIFEST_PATH); |
| String regex = "Bundle-Version: *([^ ]*)"; |
| Markers.addMarker(file, message, IMarker.SEVERITY_ERROR, regex); |
| } |
| |
| public static void trace(String msg) |
| { |
| if (DEBUG) |
| { |
| System.out.println(msg); |
| } |
| } |
| } |