1 /*******************************************************************************
2  * Copyright (c) 2008, 2012 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.pde.internal.core.exports;
15 
16 import java.util.HashSet;
17 import java.util.LinkedHashMap;
18 import java.util.LinkedHashSet;
19 import java.util.Map;
20 import java.util.Set;
21 import org.eclipse.core.resources.IFile;
22 import org.eclipse.core.resources.IMarker;
23 import org.eclipse.core.resources.IProject;
24 import org.eclipse.core.resources.IResource;
25 import org.eclipse.core.resources.IncrementalProjectBuilder;
26 import org.eclipse.core.runtime.CoreException;
27 import org.eclipse.core.runtime.IPath;
28 import org.eclipse.core.runtime.IProgressMonitor;
29 import org.eclipse.core.runtime.Path;
30 import org.eclipse.debug.core.ILaunch;
31 import org.eclipse.debug.core.ILaunchConfiguration;
32 import org.eclipse.debug.core.model.LaunchConfigurationDelegate;
33 import org.eclipse.jdt.core.IClasspathEntry;
34 import org.eclipse.jdt.core.IJavaModelMarker;
35 import org.eclipse.jdt.core.IJavaProject;
36 import org.eclipse.jdt.core.JavaCore;
37 import org.eclipse.jdt.core.JavaModelException;
38 import org.eclipse.pde.core.build.IBuildEntry;
39 import org.eclipse.pde.core.build.IBuildModel;
40 import org.eclipse.pde.core.plugin.IPluginModelBase;
41 import org.eclipse.pde.internal.build.IBuildPropertiesConstants;
42 import org.eclipse.pde.internal.core.PDECore;
43 import org.eclipse.pde.internal.core.build.WorkspaceBuildModel;
44 import org.eclipse.pde.internal.core.builders.PDEMarkerFactory;
45 import org.eclipse.pde.internal.core.ifeature.IFeatureModel;
46 import org.eclipse.pde.internal.core.ifeature.IFeaturePlugin;
47 import org.eclipse.pde.internal.core.project.PDEProject;
48 
49 /**
50  * Helper class for the various export operation classes, making it easier to export using workspace
51  * compiled files rather than having PDE Build compile everything on its own.  Provides access to
52  * methods in debug that determine what projects need to be built before the operation as well as
53  * checking for errors.
54  *
55  * @see FeatureExportOperation
56  * @see PluginExportOperation
57  */
58 public class WorkspaceExportHelper extends LaunchConfigurationDelegate {
59 
60 	private IProject[] fWorkspaceProjects;
61 
62 	@Override
launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor)63 	public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor) throws CoreException {
64 		// This class is not intended to be launched.
65 	}
66 
67 	/**
68 	 * Builds the workspace projects that are being exported or are required plug-ins
69 	 * of the exported items.  Uses the incremental builder.
70 	 *
71 	 * @param exportedItems The plugins or features being exported
72 	 * @param monitor a progress monitor or <code>null</code> if progress reporting is not desired
73 	 * @throws CoreException
74 	 */
buildBeforeExport(Object[] exportedItems, IProgressMonitor monitor)75 	public void buildBeforeExport(Object[] exportedItems, IProgressMonitor monitor) throws CoreException {
76 		IProject[] projects = getExportedWorkspaceProjects(exportedItems);
77 		for (IProject project : projects) {
78 			project.build(IncrementalProjectBuilder.INCREMENTAL_BUILD, monitor);
79 		}
80 	}
81 
82 	/**
83 	 * Checks the workspace projects that are being exported or are required plug-ins
84 	 * of the exported items for build errors.  A project will be reported as having an
85 	 * error if it has a marker with a severity of error and is of Java model or PDE type.
86 	 *
87 	 * @param exportedItems the plugins or features being exported
88 	 * @return set of IProjects containing errors
89 	 * @throws CoreException
90 	 */
checkForErrors(Object[] exportedItems)91 	public Set<IProject> checkForErrors(Object[] exportedItems) throws CoreException {
92 		IProject[] projects = getExportedWorkspaceProjects(exportedItems);
93 		Set<IProject> projectsWithErrors = new HashSet<>(projects.length);
94 		for (IProject project : projects) {
95 			IMarker[] markers = project.findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE);
96 			if (markers.length > 0) {
97 				for (IMarker marker : markers) {
98 					Integer severity = (Integer) (marker.getAttribute(IMarker.SEVERITY));
99 					if (severity != null && severity.intValue() >= IMarker.SEVERITY_ERROR) {
100 						if (marker.getType().equals(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER) || marker.getType().equals(PDEMarkerFactory.MARKER_ID)) {
101 							projectsWithErrors.add(project);
102 							break;
103 						}
104 					}
105 				}
106 			}
107 		}
108 		return projectsWithErrors;
109 	}
110 
111 	/**
112 	 * Returns a map containing information associating libraries to the output locations the
113 	 * workspace compiles them to.  Uses information in the build.properties and the classpath.
114 	 * The map will be of the following form:
115 	 * String symbolic name > lib output map
116 	 * The lib output map will be of the following form:
117 	 * String lib name > Set of IPath output folders
118 	 *
119 	 * @param exportedItems the plugins or features being exported
120 	 * @return a map of library output folders for each plugin in the workspace
121 	 */
getWorkspaceOutputFolders(Object[] exportedItems)122 	public Map<String, Map<String, Set<IPath>>> getWorkspaceOutputFolders(Object[] exportedItems) throws CoreException {
123 		IProject[] projects = getExportedWorkspaceProjects(exportedItems);
124 		Map<String, Map<String, Set<IPath>>> result = new LinkedHashMap<>(projects.length);
125 		for (IProject project : projects) {
126 			IFile buildFile = PDEProject.getBuildProperties(project);
127 			if (buildFile.exists()) {
128 				IBuildModel buildModel = new WorkspaceBuildModel(buildFile);
129 				buildModel.load();
130 				IJavaProject javaProject = JavaCore.create(project);
131 				if (javaProject.exists()) {
132 					Map<String, Set<IPath>> modelOutput = getPluginOutputFolders(buildModel, javaProject);
133 					if (!modelOutput.isEmpty()) {
134 						IPluginModelBase model = PDECore.getDefault().getModelManager().findModel(project);
135 						if (model != null) {
136 							result.put(model.getBundleDescription().getSymbolicName(), modelOutput);
137 						}
138 					}
139 				}
140 			}
141 		}
142 		return result;
143 	}
144 
getPluginOutputFolders(IBuildModel buildModel, IJavaProject javaProject)145 	private Map<String, Set<IPath>> getPluginOutputFolders(IBuildModel buildModel, IJavaProject javaProject) throws JavaModelException {
146 		Map<String, Set<IPath>> outputEntries = new LinkedHashMap<>();
147 
148 		IBuildEntry[] buildEntries = buildModel.getBuild().getBuildEntries();
149 		for (IBuildEntry buildEntry : buildEntries) {
150 			String name = buildEntry.getName();
151 			if (name.startsWith(IBuildPropertiesConstants.PROPERTY_SOURCE_PREFIX)) {
152 				Set<IPath> outputPaths = new LinkedHashSet<>();
153 
154 				String[] sourceFolders = buildEntry.getTokens();
155 				for (String sourceFolder : sourceFolders) {
156 
157 					IClasspathEntry[] classpathEntries = javaProject.getRawClasspath();
158 					for (IClasspathEntry classpathEntry : classpathEntries) {
159 						if (classpathEntry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
160 							IPath sourcePath = classpathEntry.getPath().removeFirstSegments(1); // Entries include project as first segment
161 							if (sourcePath.equals(new Path(sourceFolder))) {
162 								IPath outputPath = classpathEntry.getOutputLocation();
163 								if (outputPath == null) {
164 									outputPath = javaProject.getOutputLocation();
165 								}
166 								outputPaths.add(outputPath.removeFirstSegments(1)); // Entries include project as first segment
167 							}
168 						}
169 					}
170 				}
171 				if (!outputPaths.isEmpty()) {
172 					outputEntries.put(name.substring(IBuildPropertiesConstants.PROPERTY_SOURCE_PREFIX.length()), outputPaths);
173 				}
174 			}
175 		}
176 		return outputEntries;
177 	}
178 
getExportedWorkspaceProjects(Object[] exportedItems)179 	private IProject[] getExportedWorkspaceProjects(Object[] exportedItems) throws CoreException {
180 		if (fWorkspaceProjects == null) {
181 			// TODO This won't work for nested features either
182 			Set<IProject> projects = new HashSet<>();
183 			for (Object exportedItem : exportedItems) {
184 				if (exportedItem instanceof IPluginModelBase) {
185 					IPath installLocation = new Path(((IPluginModelBase) exportedItem).getInstallLocation());
186 					IProject project = PDECore.getWorkspace().getRoot().getProject(installLocation.lastSegment());
187 					if (project.exists()) {
188 						projects.add(project);
189 					}
190 				} else if (exportedItem instanceof IFeatureModel) {
191 					IFeatureModel feature = (IFeatureModel) exportedItem;
192 					IFeaturePlugin[] plugins = feature.getFeature().getPlugins();
193 					for (IFeaturePlugin plugin : plugins) {
194 						IPluginModelBase model = PDECore.getDefault().getModelManager().findModel(plugin.getId());
195 						if (model != null) {
196 							IPath installLocation = new Path(model.getInstallLocation());
197 							IProject project = PDECore.getWorkspace().getRoot().getProject(installLocation.lastSegment());
198 							if (project.exists()) {
199 								projects.add(project);
200 							}
201 						}
202 					}
203 
204 				}
205 			}
206 			fWorkspaceProjects = computeReferencedBuildOrder(projects.toArray(new IProject[projects.size()]));
207 		}
208 		return fWorkspaceProjects;
209 	}
210 
211 }
212