| /******************************************************************************* |
| * Copyright (c) 2009, 2015 Broadcom Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Broadcom Corporation - initial API and implementation |
| * Clare Richardson (Motorola) - Bug 281397 building specific configs |
| * Dmitry Kozlov (CodeSourcery) - Bug 309909 Headless build import fails |
| * silently with relative pathname |
| * - Bug 300554 Build status not propagated |
| * to exit code |
| * R. Zulliger, C. Walther (Indel AG) - Bug 355609 Disable indexer |
| * John Dallaway - Bug 513763 Save workspace on conclusion |
| * Torbjörn Svensson (STMicroelectronics) - bug #330204 |
| *******************************************************************************/ |
| |
| package org.eclipse.cdt.managedbuilder.internal.core; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.net.URI; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| import java.util.regex.PatternSyntaxException; |
| |
| import org.eclipse.cdt.core.CCorePlugin; |
| import org.eclipse.cdt.core.dom.IPDOMManager; |
| import org.eclipse.cdt.core.envvar.IEnvironmentVariable; |
| import org.eclipse.cdt.core.model.CoreModel; |
| import org.eclipse.cdt.core.model.ICModelMarker; |
| import org.eclipse.cdt.core.resources.ACBuilder; |
| import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; |
| import org.eclipse.cdt.core.settings.model.ICProjectDescription; |
| import org.eclipse.cdt.core.settings.model.util.CDataUtil; |
| import org.eclipse.cdt.internal.core.envvar.EnvironmentVariableManager; |
| import org.eclipse.cdt.internal.core.pdom.PDOMManager; |
| import org.eclipse.cdt.managedbuilder.core.BuildException; |
| import org.eclipse.cdt.managedbuilder.core.IConfiguration; |
| import org.eclipse.cdt.managedbuilder.core.IManagedBuildInfo; |
| import org.eclipse.cdt.managedbuilder.core.IManagedProject; |
| import org.eclipse.cdt.managedbuilder.core.IOption; |
| import org.eclipse.cdt.managedbuilder.core.ITool; |
| import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager; |
| import org.eclipse.cdt.managedbuilder.core.ManagedBuilderCorePlugin; |
| import org.eclipse.core.filesystem.EFS; |
| import org.eclipse.core.filesystem.IFileStore; |
| import org.eclipse.core.filesystem.URIUtil; |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IProjectDescription; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IWorkspaceDescription; |
| import org.eclipse.core.resources.IWorkspaceRoot; |
| import org.eclipse.core.resources.IncrementalProjectBuilder; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.SubProgressMonitor; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.equinox.app.IApplication; |
| import org.eclipse.equinox.app.IApplicationContext; |
| import org.eclipse.osgi.service.datalocation.Location; |
| |
| /** |
| * A headless builder for CDT with additional features. |
| * |
| * IApplication ID: org.eclipse.cdt.managedbuilder.core.headlessbuild |
| * Provides: |
| * - Import projects : -import {[uri:/]/path/to/project} |
| * - Import all projects in the tree : -importAll {[uri:/]/path/to/projectTreeURI} |
| * - Build projects / the workspace : -build {project_name_reg_ex/config_name_reg_ex | all} |
| * - Clean build projects / the workspace : -cleanBuild {project_name_reg_ex/config_name_reg_ex | all} |
| * - Add Include path to build : -I {include_path} |
| * - Add Include file to build : -include {include_file} |
| * - Add preprocessor define to build : -D {prepoc_define} |
| * - Replace environment variable in build : -E {var=value} |
| * - Append environment variable to build : -Ea {var=value} |
| * - Prepend environment variable to build : -Ep {var=value} |
| * - Remove environment variable in build : -Er {var} |
| * - Replace a tool option value: -T {toolid} {optionid=value} |
| * - Append to a tool option value: -Ta {toolid} {optionid=value} |
| * - Prepend to a tool option value: -Tp {toolid} {optionid=value} |
| * - Remove a tool option: -Tr {toolid} {optionid=value} |
| * - Disable indexer: -no-indexer |
| * - Error marker types to consider: -markerType {all | cdt | marker_id} |
| * where: |
| * all is all markers -- default |
| * cdt is C/C++ Problem markers |
| * marker_id, e.g. org.eclipse.cdt.core.problem |
| * - Display all error markers: -printErrorMarkers |
| * |
| * Build output is automatically sent to stdout. |
| * @since 6.0 |
| */ |
| public class HeadlessBuilder implements IApplication { |
| |
| /** |
| * IProgressMonitor to provide printing of task |
| */ |
| public static class PrintingProgressMonitor extends NullProgressMonitor { |
| @Override |
| public void beginTask(String name, int totalWork) { |
| if (name != null && name.length() > 0) |
| System.out.println(name); |
| } |
| } |
| |
| /** |
| * A class representing a new tool option value |
| */ |
| protected static class ToolOption { |
| public static final int REPLACE = 0; |
| public static final int APPEND = 1; |
| public static final int PREPEND = 2; |
| public static final int REMOVE = 3; |
| final String toolId; |
| final String optionId; |
| final String value; |
| final int operation; |
| |
| ToolOption(String toolId, String optionId, String value, int operation) { |
| this.toolId = toolId; |
| this.optionId = optionId; |
| this.value = value; |
| this.operation = operation; |
| } |
| } |
| |
| /** |
| * A class representing a backed-up tool option to restored at the end of the build |
| */ |
| protected static class SavedToolOption { |
| final String toolId; |
| final String optionId; |
| final Object value; |
| |
| SavedToolOption(String toolId, String optionId, Object value) { |
| this.toolId = toolId; |
| this.optionId = optionId; |
| this.value = value; |
| } |
| |
| @Override |
| public int hashCode() { |
| return toolId.hashCode() + optionId.hashCode(); |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| SavedToolOption option = (SavedToolOption) obj; |
| return toolId.equals(option.toolId) && optionId.equals(option.optionId); |
| } |
| } |
| |
| /** Error return status */ |
| public static final Integer ERROR = 1; |
| /** OK return status */ |
| public static final Integer OK = IApplication.EXIT_OK; |
| |
| /** Set of project URIs / paths to import */ |
| protected final Set<String> projectsToImport = new HashSet<>(); |
| /** Tree of projects to recursively import */ |
| protected final Set<String> projectTreeToImport = new HashSet<>(); |
| /** Set of project names to build */ |
| protected final Set<String> projectRegExToBuild = new HashSet<>(); |
| /** Set of project names to clean */ |
| protected final Set<String> projectRegExToClean = new HashSet<>(); |
| protected boolean buildAll = false; |
| protected boolean cleanAll = false; |
| protected boolean disableIndexer = false; |
| |
| /** List of Tool Option values being set */ |
| protected List<ToolOption> toolOptions = new ArrayList<>(); |
| /** Map from configuration ID -> Set of SavedToolOptions */ |
| protected Map<String, Set<SavedToolOption>> savedToolOptions = new HashMap<>(); |
| protected boolean markerTypesDefault = true; |
| protected boolean markerTypesAll = false; |
| protected Set<String> markerTypes = new HashSet<>(); |
| protected boolean printErrorMarkers = false; |
| |
| protected static final String MATCH_ALL_CONFIGS = ".*"; //$NON-NLS-1$ |
| |
| /* |
| * Find all project build configurations that match the regular expression ("project/config") |
| */ |
| protected Map<IProject, Set<ICConfigurationDescription>> matchConfigurations(String regularExpression, |
| IProject[] projectList, Map<IProject, Set<ICConfigurationDescription>> cfgMap) { |
| try { |
| int separatorIndex = regularExpression.indexOf('/'); |
| |
| String projectRegEx; |
| String configRegEx; |
| if (separatorIndex == -1 || separatorIndex == regularExpression.length() - 1) { |
| // build all configurations for this project |
| projectRegEx = regularExpression; |
| configRegEx = MATCH_ALL_CONFIGS; |
| } else { |
| projectRegEx = regularExpression.substring(0, separatorIndex); |
| configRegEx = regularExpression.substring(separatorIndex + 1, regularExpression.length()); |
| } |
| |
| Pattern projectPattern = Pattern.compile(projectRegEx); |
| Pattern configPattern = Pattern.compile(configRegEx); |
| |
| // Find the projects that match the regular expression |
| boolean projectMatched = false; |
| boolean configMatched = false; |
| for (IProject project : projectList) { |
| Matcher projectMatcher = projectPattern.matcher(project.getName()); |
| |
| if (projectMatcher.matches()) { |
| projectMatched = true; |
| // Find the configurations that match the regular expression |
| ICProjectDescription desc = CoreModel.getDefault().getProjectDescription(project, false); |
| if (desc == null) { |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_project + project.getName() |
| + HeadlessBuildMessages.HeadlessBuilder_Not_CDT_Proj); |
| continue; |
| } |
| ICConfigurationDescription[] cfgs = desc.getConfigurations(); |
| |
| for (ICConfigurationDescription cfg : cfgs) { |
| Matcher cfgMatcher = configPattern.matcher(cfg.getName()); |
| |
| if (cfgMatcher.matches()) { |
| configMatched = true; |
| // Build this configuration for this project |
| Set<ICConfigurationDescription> set = cfgMap.get(project); |
| if (set == null) |
| set = new HashSet<>(); |
| set.add(cfg); |
| cfgMap.put(project, set); |
| } |
| } |
| } |
| } |
| if (!projectMatched) |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_NoProjectMatched + regularExpression |
| + HeadlessBuildMessages.HeadlessBuilder_Skipping2); |
| else if (!configMatched) |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_NoConfigMatched + regularExpression |
| + HeadlessBuildMessages.HeadlessBuilder_Skipping2); |
| } catch (PatternSyntaxException e) { |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_RegExSyntaxError + e.toString()); |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_Skipping + regularExpression |
| + HeadlessBuildMessages.HeadlessBuilder_Quote); |
| } |
| return cfgMap; |
| } |
| |
| /* |
| * Build the given configurations using the specified build type (FULL, CLEAN, INCREMENTAL) |
| */ |
| protected void buildConfigurations(Map<IProject, Set<ICConfigurationDescription>> projConfigs, |
| final IProgressMonitor monitor, final int buildType) throws CoreException { |
| for (Map.Entry<IProject, Set<ICConfigurationDescription>> entry : projConfigs.entrySet()) { |
| Set<ICConfigurationDescription> cfgDescs = entry.getValue(); |
| |
| IConfiguration[] configs = new IConfiguration[cfgDescs.size()]; |
| int i = 0; |
| for (ICConfigurationDescription cfgDesc : cfgDescs) |
| configs[i++] = ManagedBuildManager.getConfigurationForDescription(cfgDesc); |
| |
| ManagedBuildManager.buildConfigurations(configs, null, new SubProgressMonitor(monitor, 1), true, buildType); |
| } |
| } |
| |
| /** |
| * Import a project into the workspace |
| * @param projURIStr base URI string |
| * @param recurse should we recurse down the URI importing all projects? |
| * @return int OK / ERROR |
| */ |
| protected int importProject(String projURIStr, boolean recurse) throws CoreException { |
| IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); |
| IProgressMonitor monitor = new PrintingProgressMonitor(); |
| InputStream in = null; |
| try { |
| URI project_uri = null; |
| try { |
| project_uri = URI.create(projURIStr); |
| } catch (Exception e) { |
| // Will be treated as straightforward path in the case below |
| } |
| |
| // Handle local paths as well |
| if (project_uri == null || project_uri.getScheme() == null) { |
| IPath p = new Path(projURIStr).addTrailingSeparator(); |
| project_uri = URIUtil.toURI(p); |
| |
| // Handle relative paths as relative to cwd |
| if (project_uri.getScheme() == null) { |
| String cwd = System.getProperty("user.dir"); //$NON-NLS-1$ |
| p = new Path(cwd).addTrailingSeparator(); |
| p = p.append(projURIStr); |
| project_uri = URIUtil.toURI(p); |
| } |
| if (project_uri.getScheme() == null) { |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_invalid_uri + project_uri); |
| return ERROR; |
| } |
| } |
| |
| if (recurse) { |
| if (!EFS.getStore(project_uri).fetchInfo().exists()) { |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_Directory + project_uri |
| + HeadlessBuildMessages.HeadlessBuilder_cant_be_found); |
| return ERROR; |
| } |
| for (IFileStore info : EFS.getStore(project_uri).childStores(EFS.NONE, monitor)) { |
| if (!info.fetchInfo().isDirectory()) |
| continue; |
| int status = importProject(info.toURI().toString(), recurse); |
| if (status != OK) |
| return status; |
| } |
| } |
| |
| // Load the project description |
| IFileStore fstore = EFS.getStore(project_uri).getChild(".project"); //$NON-NLS-1$ |
| if (!fstore.fetchInfo().exists()) { |
| if (!recurse) { |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_project + project_uri |
| + HeadlessBuildMessages.HeadlessBuilder_cant_be_found); |
| return ERROR; |
| } |
| // .project not found; OK if we're not recursing |
| return OK; |
| } |
| |
| in = fstore.openInputStream(EFS.NONE, monitor); |
| IProjectDescription desc = root.getWorkspace().loadProjectDescription(in); |
| |
| // Check that a project with the same name doesn't already exist in the workspace |
| IProject project = root.getProject(desc.getName()); |
| if (project.exists()) { |
| // It's ok if the project we're importing is the same as one already in the workspace |
| if (URIUtil.equals(project.getLocationURI(), project_uri)) { |
| project.open(monitor); |
| return OK; |
| } |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_project + desc.getName() |
| + HeadlessBuildMessages.HeadlessBuilder_already_exists_in_workspace); |
| return ERROR; |
| } |
| // Create and open the project |
| // Note that if the project exists directly under the workspace root, we can't #setLocationURI(...) |
| if (!URIUtil.equals( |
| org.eclipse.core.runtime.URIUtil.append(ResourcesPlugin.getWorkspace().getRoot().getLocationURI(), |
| org.eclipse.core.runtime.URIUtil.lastSegment(project_uri)), |
| project_uri)) |
| desc.setLocationURI(project_uri); |
| else |
| project_uri = null; |
| // Check the URI is valid for a project in this workspace |
| if (!root.getWorkspace().validateProjectLocationURI(project, project_uri).equals(Status.OK_STATUS)) { |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_URI + project_uri |
| + HeadlessBuildMessages.HeadlessBuilder_is_not_valid_in_workspace); |
| return ERROR; |
| } |
| |
| project.create(desc, monitor); |
| project.open(monitor); |
| } finally { |
| try { |
| if (in != null) |
| in.close(); |
| } catch (IOException e2) { |
| /* don't care */ } |
| } |
| return OK; |
| } |
| |
| protected boolean isProjectSuccesfullyBuild(IProject project) { |
| try { |
| for (String markerType : markerTypes) { |
| int findMaxProblemSeverity = project.findMaxProblemSeverity(markerType, true, IResource.DEPTH_INFINITE); |
| if (findMaxProblemSeverity >= IMarker.SEVERITY_ERROR) { |
| return false; |
| } |
| } |
| } catch (CoreException e) { |
| ManagedBuilderCorePlugin.log(e); |
| return false; |
| } |
| return true; |
| } |
| |
| protected void accumulateErrorMarkers(IProject project, List<String> allBuildErrors) { |
| try { |
| for (String markerType : markerTypes) { |
| IMarker[] findMarkers = project.findMarkers(markerType, true, IResource.DEPTH_INFINITE); |
| for (IMarker marker : findMarkers) { |
| int severity = marker.getAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO); |
| if (severity >= IMarker.SEVERITY_ERROR) { |
| // the string format of Markers is not API and recommended only for debugging, but |
| // it suits our purpose here. |
| allBuildErrors.add(marker.toString()); |
| } |
| } |
| } |
| } catch (CoreException e) { |
| ManagedBuilderCorePlugin.log(e); |
| } |
| } |
| |
| @Override |
| public Object start(IApplicationContext context) throws Exception { |
| // Build result: whether projects were built successfully |
| boolean buildSuccessful = true; |
| List<String> allBuildErrors = new ArrayList<>(); |
| |
| // Check its OK to use this workspace as IDEApplication does |
| if (!checkInstanceLocation()) |
| return ERROR; |
| |
| IProgressMonitor monitor = new PrintingProgressMonitor(); |
| IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); |
| |
| final boolean isAutoBuilding = root.getWorkspace().isAutoBuilding(); |
| try { |
| { |
| // Turn off workspace auto-build |
| IWorkspaceDescription desc = root.getWorkspace().getDescription(); |
| desc.setAutoBuilding(false); |
| root.getWorkspace().setDescription(desc); |
| } |
| |
| if (!root.isAccessible()) { |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_Workspace + root.getLocationURI().toString() |
| + HeadlessBuildMessages.HeadlessBuilder_is_not_accessible); |
| return ERROR; |
| } |
| |
| // Handle user provided arguments |
| if (!getArguments((String[]) context.getArguments().get(IApplicationContext.APPLICATION_ARGS))) |
| return ERROR; |
| |
| if (markerTypesDefault || markerTypesAll) { |
| markerTypes.clear(); |
| markerTypes.add(IMarker.PROBLEM); |
| } |
| |
| if (disableIndexer) { |
| CCorePlugin.getIndexManager().setDefaultIndexerId(IPDOMManager.ID_NO_INDEXER); |
| // yes, this is ugly - but I haven't found a way to "officially" |
| // stop the indexer. |
| // The problem is that the "workspace job" for the |
| // "index manager" (PDOMIndexerJob) is created and scheduled |
| // during plugin activation (CCorePlugin.start(BundleContext)) |
| // and therefore, it may already be running when we first reach |
| // this line of code. Therefore, we do not just (temporarily) |
| // disable the indexer but also stop it. |
| if (CCorePlugin.getIndexManager() instanceof PDOMManager) { |
| PDOMManager man = (PDOMManager) CCorePlugin.getIndexManager(); |
| man.shutdown(); |
| } |
| } |
| |
| // Set the console environment so build output is echo'd to stdout |
| if (System.getProperty("org.eclipse.cdt.core.console") == null) //$NON-NLS-1$ |
| System.setProperty("org.eclipse.cdt.core.console", "org.eclipse.cdt.core.systemConsole"); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| /* |
| * Perform the project import |
| */ |
| // Import any projects that need importing |
| for (String projURIStr : projectsToImport) { |
| int status = importProject(projURIStr, false); |
| if (status != OK) |
| return status; |
| } |
| for (String projURIStr : projectTreeToImport) { |
| int status = importProject(projURIStr, true); |
| if (status != OK) |
| return status; |
| } |
| |
| // Hook in our external settings to the build |
| HeadlessBuilderExternalSettingsProvider.hookExternalSettingsProvider(); |
| |
| IProject[] allProjects = root.getProjects(); |
| // Map from Project -> Configurations to build. We also Build all projects which are clean'd |
| Map<IProject, Set<ICConfigurationDescription>> configsToBuild = new HashMap<>(); |
| |
| /* |
| * Perform the Clean / Build |
| */ |
| final boolean buildAllConfigs = ACBuilder.needAllConfigBuild(); |
| try { |
| // Set the tool options for all project configurations |
| // (This can't be done just for the projects being built, as they |
| // may cause other projects to be built via references) |
| if (!toolOptions.isEmpty()) |
| for (IProject project : allProjects) { |
| IManagedBuildInfo info = ManagedBuildManager.getBuildInfo(project); |
| if (info == null) |
| continue; |
| IManagedProject mProj = info.getManagedProject(); |
| IConfiguration[] cfgs = mProj.getConfigurations(); |
| for (IConfiguration cfg : cfgs) |
| setToolOptions(cfg); |
| ManagedBuildManager.saveBuildInfo(project, true); |
| } |
| |
| // Clean the projects |
| if (cleanAll) { |
| // Ensure we clean all the configurations |
| ACBuilder.setAllConfigBuild(true); |
| |
| System.out.println(HeadlessBuildMessages.HeadlessBuilder_cleaning_all_projects); |
| root.getWorkspace().build(IncrementalProjectBuilder.CLEAN_BUILD, monitor); |
| |
| // Reset the build_all_configs preference value to its previous state |
| ACBuilder.setAllConfigBuild(buildAllConfigs); |
| } else { |
| // Resolve the regular expression project names to build configurations |
| for (String regEx : projectRegExToClean) |
| matchConfigurations(regEx, allProjects, configsToBuild); |
| // Clean the list of configurations |
| buildConfigurations(configsToBuild, monitor, IncrementalProjectBuilder.CLEAN_BUILD); |
| } |
| |
| // Build the projects the user wants building |
| if (buildAll) { |
| // Ensure we build all the configurations |
| ACBuilder.setAllConfigBuild(true); |
| |
| System.out.println(HeadlessBuildMessages.HeadlessBuilder_building_all); |
| root.getWorkspace().build(IncrementalProjectBuilder.FULL_BUILD, monitor); |
| for (IProject p : root.getProjects()) { |
| buildSuccessful = buildSuccessful && isProjectSuccesfullyBuild(p); |
| if (printErrorMarkers) { |
| accumulateErrorMarkers(p, allBuildErrors); |
| } |
| } |
| } else { |
| // Resolve the regular expression project names to build configurations |
| for (String regEx : projectRegExToBuild) |
| matchConfigurations(regEx, allProjects, configsToBuild); |
| // Build the list of configurations |
| buildConfigurations(configsToBuild, monitor, IncrementalProjectBuilder.FULL_BUILD); |
| for (IProject p : configsToBuild.keySet()) { |
| buildSuccessful = buildSuccessful && isProjectSuccesfullyBuild(p); |
| if (printErrorMarkers) { |
| accumulateErrorMarkers(p, allBuildErrors); |
| } |
| } |
| } |
| } finally { |
| // Reset the tool options |
| if (!savedToolOptions.isEmpty()) |
| for (IProject project : allProjects) { |
| IManagedBuildInfo info = ManagedBuildManager.getBuildInfo(project); |
| if (info == null) |
| continue; |
| IManagedProject mProj = info.getManagedProject(); |
| IConfiguration[] cfgs = mProj.getConfigurations(); |
| for (IConfiguration cfg : cfgs) |
| resetToolOptions(cfg); |
| ManagedBuildManager.saveBuildInfo(project, true); |
| } |
| // Reset the build_all_configs preference value to its previous state |
| ACBuilder.setAllConfigBuild(buildAllConfigs); |
| // Unhook the external settings provider |
| HeadlessBuilderExternalSettingsProvider.unhookExternalSettingsProvider(); |
| } |
| } finally { |
| // Wait for any outstanding jobs to finish |
| while (!Job.getJobManager().isIdle()) |
| Thread.sleep(10); |
| |
| // Reset workspace auto-build preference |
| IWorkspaceDescription desc = root.getWorkspace().getDescription(); |
| desc.setAutoBuilding(isAutoBuilding); |
| root.getWorkspace().setDescription(desc); |
| |
| // Save modified workspace (bug 513763) |
| root.getWorkspace().save(true, monitor); |
| } |
| if (printErrorMarkers) { |
| if (buildSuccessful) { |
| System.out.println(HeadlessBuildMessages.HeadlessBuilder_completed_successfully); |
| } else { |
| System.out.println(HeadlessBuildMessages.HeadlessBuilder_encountered_errors); |
| for (String buildError : allBuildErrors) { |
| System.err.println(buildError); |
| } |
| } |
| } |
| return buildSuccessful ? OK : ERROR; |
| } |
| |
| /** |
| * Verify that it's safe to use the specified workspace. i.e. that we can write to it and that it's not |
| * already locked / in-use. |
| * |
| * Return true if a valid workspace path has been set and false otherwise. |
| * |
| * @return true if a valid instance location has been set and false otherwise |
| */ |
| protected boolean checkInstanceLocation() { |
| // -data @none was specified but an ide requires workspace |
| Location instanceLoc = Platform.getInstanceLocation(); |
| if (instanceLoc == null || !instanceLoc.isSet()) { |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_MustSpecifyWorkspace); |
| return false; |
| } |
| |
| // -data "/valid/path", workspace already set |
| try { |
| // at this point its valid, so try to lock it to prevent concurrent use |
| if (!instanceLoc.lock()) { |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_WorkspaceInUse); |
| return false; |
| } |
| return true; |
| } catch (IOException e) { |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_CouldntLockWorkspace); |
| } |
| return false; |
| } |
| |
| /** |
| * Helper method to process expected arguments |
| * |
| * Arguments |
| * -import {[uri:/]/path/to/project} |
| * -importAll {[uri:/]/path/to/projectTreeURI} Import all projects in the tree |
| * -build {project_name_reg_ex/config_name_reg_ex | all} |
| * -cleanBuild {project_name_reg_ex/config_name_reg_ex | all} |
| * -I {include_path} additional include_path to add to tools |
| * -include {include_file} additional include_file to pass to tools |
| * -D {prepoc_define} addition preprocessor defines to pass to the tools |
| * -E {var=value} replace/add value to environment variable when running all tools |
| * -Ea {var=value} append value to environment variable when running all tools |
| * -Ep {var=value} prepend value to environment variable when running all tools |
| * -Er {var} remove/unset the given environment variable |
| * -T {toolid} {optionid=value} replace a tool option value |
| * -Ta {toolid} {optionid=value} append to a tool option value |
| * -Tp {toolid} {optionid=value} prepend to a tool option value |
| * -Tr {toolid} {optionid=value} remove a tool option value |
| * -no-indexer Disable indexer |
| * -markerType Which markers to consider |
| * -printErrorMarkers Print all error markers that caused build to fail |
| * |
| * Each argument may be specified more than once |
| * @param args String[] of arguments to parse |
| * @return boolean indicating success |
| */ |
| public boolean getArguments(String[] args) { |
| try { |
| if (args == null || args.length == 0) |
| throw new Exception(HeadlessBuildMessages.HeadlessBuilder_no_arguments); |
| for (int i = 0; i < args.length; i++) { |
| if ("-help".equals(args[i])) { //$NON-NLS-1$ |
| throw new Exception(HeadlessBuildMessages.HeadlessBuilder_help_requested); |
| } else if ("-import".equals(args[i])) { //$NON-NLS-1$ |
| projectsToImport.add(args[++i]); |
| } else if ("-importAll".equals(args[i])) { //$NON-NLS-1$ |
| projectTreeToImport.add(args[++i]); |
| } else if ("-build".equals(args[i])) { //$NON-NLS-1$ |
| projectRegExToBuild.add(args[++i]); |
| } else if ("-cleanBuild".equals(args[i])) { //$NON-NLS-1$ |
| projectRegExToClean.add(args[++i]); |
| } else if ("-D".equals(args[i])) { //$NON-NLS-1$ |
| String macro = args[++i]; |
| String macroVal = ""; //$NON-NLS-1$ |
| if (macro.indexOf('=') != -1) { |
| macroVal = macro.substring(macro.indexOf('=') + 1); |
| macro = macro.substring(0, macro.indexOf('=')); |
| } |
| HeadlessBuilderExternalSettingsProvider.additionalSettings |
| .add(CDataUtil.createCMacroEntry(macro, macroVal, 0)); |
| } else if ("-I".equals(args[i])) { //$NON-NLS-1$ |
| HeadlessBuilderExternalSettingsProvider.additionalSettings |
| .add(CDataUtil.createCIncludePathEntry(args[++i], 0)); |
| } else if ("-include".equals(args[i])) { //$NON-NLS-1$ |
| HeadlessBuilderExternalSettingsProvider.additionalSettings |
| .add(CDataUtil.createCIncludeFileEntry(args[++i], 0)); |
| } else if ("-E".equals(args[i])) { //$NON-NLS-1$ |
| addEnvironmentVariable(args[++i], IEnvironmentVariable.ENVVAR_REPLACE); |
| } else if ("-Ea".equals(args[i])) { //$NON-NLS-1$ |
| addEnvironmentVariable(args[++i], IEnvironmentVariable.ENVVAR_APPEND); |
| } else if ("-Ep".equals(args[i])) { //$NON-NLS-1$ |
| addEnvironmentVariable(args[++i], IEnvironmentVariable.ENVVAR_PREPEND); |
| } else if ("-Er".equals(args[i])) { //$NON-NLS-1$ |
| addEnvironmentVariable(args[++i], IEnvironmentVariable.ENVVAR_REMOVE); |
| } else if ("-T".equals(args[i])) { //$NON-NLS-1$ |
| String toolId = args[++i]; |
| String option = args[++i]; |
| addToolOption(toolId, option, ToolOption.REPLACE); |
| } else if ("-Ta".equals(args[i])) { //$NON-NLS-1$ |
| String toolId = args[++i]; |
| String option = args[++i]; |
| addToolOption(toolId, option, ToolOption.APPEND); |
| } else if ("-Tp".equals(args[i])) { //$NON-NLS-1$ |
| String toolId = args[++i]; |
| String option = args[++i]; |
| addToolOption(toolId, option, ToolOption.PREPEND); |
| } else if ("-Tr".equals(args[i])) { //$NON-NLS-1$ |
| String toolId = args[++i]; |
| String option = args[++i]; |
| addToolOption(toolId, option, ToolOption.REMOVE); |
| } else if ("-no-indexer".equals(args[i])) { //$NON-NLS-1$ |
| disableIndexer = true; |
| } else if ("-markerType".equals(args[i])) { //$NON-NLS-1$ |
| addMarkerType(args[++i]); |
| } else if ("-printErrorMarkers".equals(args[i])) { //$NON-NLS-1$ |
| printErrorMarkers = true; |
| } else { |
| throw new Exception(HeadlessBuildMessages.HeadlessBuilder_unknown_argument + args[i]); |
| } |
| } |
| } catch (Exception e) { |
| // Print usage |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_invalid_argument + Arrays.toString(args)); |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_Error + e.getMessage()); |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_usage); |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_usage_import); |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_importAll); |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_usage_build); |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_usage_clean_build); |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_usage_no_indexer); |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_usage_marker_type); |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_usage_print_all_error_markers); |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_InlucdePath); |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_IncludeFile); |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_PreprocessorDefine); |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_EnvVar_Replace); |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_EnvVar_Append); |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_EnvVar_Prepend); |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_EnvVar_Remove); |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_ToolOption_Replace); |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_ToolOption_Append); |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_ToolOption_Prepend); |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_ToolOption_Remove); |
| System.err.println(HeadlessBuildMessages.HeadlessBuilder_ToolOption_Types); |
| return false; |
| } |
| |
| if (projectRegExToClean.contains("all") || projectRegExToClean.contains("*")) { //$NON-NLS-1$ //$NON-NLS-2$ |
| cleanAll = true; |
| buildAll = true; |
| projectRegExToClean.remove("all"); //$NON-NLS-1$ |
| projectRegExToClean.remove("*"); //$NON-NLS-1$ |
| } |
| if (projectRegExToBuild.contains("all") || projectRegExToBuild.contains("*")) { //$NON-NLS-1$ //$NON-NLS-2$ |
| buildAll = true; |
| projectRegExToBuild.remove("all"); //$NON-NLS-1$ |
| projectRegExToBuild.remove("*"); //$NON-NLS-1$ |
| } |
| |
| return true; |
| } |
| |
| protected void addEnvironmentVariable(String string, int op) throws Exception { |
| String[] parts = string.split("=", 2); //$NON-NLS-1$ |
| String name = parts[0]; |
| String value = ""; //$NON-NLS-1$ |
| if (parts.length > 1) |
| value = parts[1]; |
| EnvironmentVariableManager.fUserSupplier.createOverrideVariable(name, value, op, null); |
| } |
| |
| protected void addToolOption(String toolId, String option, int operation) { |
| String optionId = option; |
| String value = ""; //$NON-NLS-1$ |
| if (option.indexOf('=') != -1) { |
| value = option.substring(option.indexOf('=') + 1); |
| optionId = option.substring(0, option.indexOf('=')); |
| } |
| toolOptions.add(new ToolOption(toolId, optionId, value, operation)); |
| } |
| |
| protected void addMarkerType(String markerType) { |
| markerTypesDefault = false; |
| if ("all".equals(markerType)) { //$NON-NLS-1$ |
| markerTypesAll = true; |
| } else if ("cdt".equals(markerType)) { //$NON-NLS-1$ |
| markerTypes.add(ICModelMarker.C_MODEL_PROBLEM_MARKER); |
| } else { |
| markerTypes.add(markerType); |
| } |
| } |
| |
| /** |
| * Set the tool options in a configuration, and saves the current values so that |
| * they can be restored at the end of the build. These are reset after the build |
| * by calls to {@link #resetToolOptions(IConfiguration)}. |
| */ |
| @SuppressWarnings("unchecked") |
| protected void setToolOptions(IConfiguration configuration) throws BuildException { |
| if (!savedToolOptions.containsKey(configuration.getId())) |
| savedToolOptions.put(configuration.getId(), new HashSet<SavedToolOption>()); |
| Set<SavedToolOption> savedToolOptionsSet = savedToolOptions.get(configuration.getId()); |
| for (ToolOption toolOption : toolOptions) { |
| ITool[] tools = configuration.getToolsBySuperClassId(toolOption.toolId); |
| for (ITool tool : tools) { |
| IOption option = tool.getOptionBySuperClassId(toolOption.optionId); |
| if (option != null) { |
| // Save the tool option so that it can be reset later (does not overwrite existing |
| // saved options, so if an option is specified multiple times it will be reset to the |
| // correct value) |
| savedToolOptionsSet.add(new SavedToolOption(tool.getId(), option.getId(), option.getValue())); |
| // Update the value of the tool option in a type-dependent manner |
| switch (option.getValueType()) { |
| case IOption.BOOLEAN: |
| boolean booleanValue = (Boolean) option.getDefaultValue(); |
| if (toolOption.operation != ToolOption.REMOVE) |
| booleanValue = Boolean.parseBoolean(toolOption.value); |
| ManagedBuildManager.setOption(configuration, tool, option, booleanValue); |
| break; |
| case IOption.STRING_LIST: |
| case IOption.INCLUDE_PATH: |
| case IOption.PREPROCESSOR_SYMBOLS: |
| case IOption.LIBRARIES: |
| case IOption.OBJECTS: |
| case IOption.INCLUDE_FILES: |
| case IOption.LIBRARY_PATHS: |
| case IOption.LIBRARY_FILES: |
| case IOption.MACRO_FILES: |
| case IOption.UNDEF_INCLUDE_PATH: |
| case IOption.UNDEF_PREPROCESSOR_SYMBOLS: |
| case IOption.UNDEF_INCLUDE_FILES: |
| case IOption.UNDEF_LIBRARY_PATHS: |
| case IOption.UNDEF_LIBRARY_FILES: |
| case IOption.UNDEF_MACRO_FILES: |
| List<String> listValue = new ArrayList<>(); |
| switch (toolOption.operation) { |
| case ToolOption.APPEND: |
| listValue.addAll((List<String>) option.getValue()); |
| listValue.addAll(Arrays.asList(toolOption.value.split(","))); //$NON-NLS-1$ |
| break; |
| case ToolOption.PREPEND: |
| listValue.addAll(Arrays.asList(toolOption.value.split(","))); //$NON-NLS-1$ |
| listValue.addAll((List<String>) option.getValue()); |
| break; |
| case ToolOption.REMOVE: |
| listValue = (List<String>) option.getDefaultValue(); |
| break; |
| default: |
| listValue = Arrays.asList(toolOption.value.split(",")); //$NON-NLS-1$ |
| break; |
| } |
| ManagedBuildManager.setOption(configuration, tool, option, |
| listValue == null ? new String[0] : listValue.toArray(new String[listValue.size()])); |
| break; |
| default: // IOption.ENUMERATED, IOption.STRING |
| String stringValue = toolOption.value; |
| switch (toolOption.operation) { |
| case ToolOption.APPEND: |
| stringValue = option.getValue() + stringValue; |
| break; |
| case ToolOption.PREPEND: |
| stringValue = stringValue + option.getValue(); |
| break; |
| case ToolOption.REMOVE: |
| stringValue = (String) option.getDefaultValue(); |
| break; |
| } |
| ManagedBuildManager.setOption(configuration, tool, option, stringValue); |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Reset the tool options that were set using {@link #setToolOptions(IConfiguration)} |
| */ |
| protected void resetToolOptions(IConfiguration configuration) throws BuildException { |
| for (SavedToolOption toolOption : savedToolOptions.get(configuration.getId())) { |
| IOption option = configuration.getTool(toolOption.toolId).getOptionById(toolOption.optionId); |
| option.setValue(toolOption.value); |
| } |
| } |
| |
| @Override |
| public void stop() { |
| } |
| } |