/******************************************************************************* * Copyright (c) 2009, 2016 Red Hat Inc. 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: * Red Hat Incorporated - initial API and implementation * Anna Dushistova (MontaVista)- [375007] [autotools] allow absolute paths for configure scripts *******************************************************************************/ package org.eclipse.cdt.internal.autotools.core; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.cdt.autotools.core.AutotoolsPlugin; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.ConsoleOutputStream; import org.eclipse.cdt.core.ICDescriptor; import org.eclipse.cdt.core.ICommandLauncher; import org.eclipse.cdt.core.envvar.IEnvironmentVariable; import org.eclipse.cdt.core.resources.IConsole; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.core.settings.model.ICProjectDescription; import org.eclipse.cdt.core.settings.model.ICStorageElement; import org.eclipse.cdt.internal.autotools.core.configure.AutotoolsConfigurationManager; import org.eclipse.cdt.internal.autotools.core.configure.IAConfiguration; import org.eclipse.cdt.internal.autotools.core.configure.IConfigureOption; import org.eclipse.cdt.make.core.IMakeBuilderInfo; import org.eclipse.cdt.make.core.IMakeTarget; import org.eclipse.cdt.make.core.IMakeTargetManager; import org.eclipse.cdt.make.core.MakeCorePlugin; import org.eclipse.cdt.make.core.makefile.IMakefile; import org.eclipse.cdt.make.core.makefile.ITarget; import org.eclipse.cdt.make.core.makefile.ITargetRule; import org.eclipse.cdt.managedbuilder.core.IBuilder; import org.eclipse.cdt.managedbuilder.core.IConfiguration; import org.eclipse.cdt.managedbuilder.core.IManagedBuildInfo; import org.eclipse.cdt.managedbuilder.core.IMultiConfiguration; import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager; import org.eclipse.cdt.managedbuilder.core.ManagedBuilderCorePlugin; import org.eclipse.cdt.managedbuilder.macros.BuildMacroException; import org.eclipse.cdt.managedbuilder.macros.IBuildMacroProvider; import org.eclipse.cdt.newmake.core.IMakeCommonBuildInfo; import org.eclipse.cdt.remote.core.RemoteCommandLauncher; import org.eclipse.cdt.ui.CUIPlugin; import org.eclipse.core.filesystem.URIUtil; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceStatus; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.remote.core.IRemoteConnection; import org.eclipse.remote.core.IRemoteConnectionType; import org.eclipse.remote.core.IRemoteResource; import org.eclipse.remote.core.IRemoteServicesManager; import org.eclipse.remote.core.exception.RemoteConnectionException; @SuppressWarnings("deprecation") public class AutotoolsNewMakeGenerator extends MarkerGenerator { public static final String CONFIG_STATUS = "config.status"; //$NON-NLS-1$ public static final String MAKEFILE = "Makefile"; //$NON-NLS-1$ public static final String MAKEFILE_CVS = "Makefile.cvs"; //$NON-NLS-1$ public static final String SETTINGS_FILE_NAME = ".cdtconfigure"; //$NON-NLS-1$ public static final String SHELL_COMMAND = "sh"; //$NON-NLS-1$ public static final String AUTOGEN_TOOL_ID = "autogen"; //$NON-NLS-1$ public static final String CONFIGURE_TOOL_ID = "configure"; //$NON-NLS-1$ public static final String GENERATED_TARGET = AutotoolsPlugin.PLUGIN_ID + ".generated.MakeTarget"; //$NON-NLS-1$ private static final String MAKE_TARGET_KEY = MakeCorePlugin.getUniqueIdentifier() + ".buildtargets"; //$NON-NLS-1$ private static final String BUILD_TARGET_ELEMENT = "buildTargets"; //$NON-NLS-1$ private static final String TARGET_ELEMENT = "target"; //$NON-NLS-1$ private static final String TARGET_ATTR_ID = "targetID"; //$NON-NLS-1$ private static final String TARGET_ATTR_PATH = "path"; //$NON-NLS-1$ private static final String TARGET_ATTR_NAME = "name"; //$NON-NLS-1$ private static final String TARGET_STOP_ON_ERROR = "stopOnError"; //$NON-NLS-1$ private static final String TARGET_USE_DEFAULT_CMD = "useDefaultCommand"; //$NON-NLS-1$ private static final String TARGET_ARGUMENTS = "buildArguments"; //$NON-NLS-1$ private static final String TARGET_COMMAND = "buildCommand"; //$NON-NLS-1$ private static final String TARGET_RUN_ALL_BUILDERS = "runAllBuilders"; private static final String TARGET = "buildTarget"; //$NON-NLS-1$ private static final String DEFAULT_AUTORECONF = "autoreconf"; //$NON-NLS-1$ private IProject project; private IProgressMonitor monitor; private IPath buildLocation; private String buildDir; private String srcDir; private String winOSType = ""; private IConfiguration cfg; private ICConfigurationDescription cdesc; private IAConfiguration toolsCfg; private IBuilder builder; /** * @since 2.0 */ public MultiStatus generateMakefiles() throws CoreException { return regenerateMakefiles(false); } private void initializeBuildConfigDirs(IConfiguration c, IAConfiguration a) { IBuilder b = c.getBuilder(); IPath buildDirectory = b.getBuildLocation(); if (buildDirectory == null || buildDirectory.isEmpty()) { // default build directory to project directory buildDirectory = getProjectLocation(); } buildLocation = buildDirectory; buildDir = buildDirectory.toString(); srcDir = a.getConfigToolDirectory(); try { String resolved = ManagedBuildManager.getBuildMacroProvider().resolveValue(srcDir, "", null, IBuildMacroProvider.CONTEXT_CONFIGURATION, c); srcDir = resolved; } catch (BuildMacroException e) { // do nothing } } public void initialize(IProject project, IManagedBuildInfo info, IProgressMonitor monitor) { this.project = project; ICProjectDescription pdesc = CCorePlugin.getDefault().getProjectDescription(project); this.cdesc = pdesc.getActiveConfiguration(); this.cfg = info.getDefaultConfiguration(); this.builder = cfg.getBuilder(); this.monitor = monitor; CUIPlugin.getDefault().getPreferenceStore().getString("dummy"); } @Override public IProject getProject() { return project; } /** * @since 2.0 */ public boolean isGeneratedResource() { return false; } /** * @since 2.0 */ public void regenerateDependencies() { } /** * Check whether the build has been cancelled. Cancellation requests * propagated to the caller by throwing * <code>OperationCanceledException</code>. * * @see org.eclipse.core.runtime.OperationCanceledException#OperationCanceledException() */ protected void checkCancel() { if (monitor != null && monitor.isCanceled()) { throw new OperationCanceledException(); } } /** * Return or create the makefile needed for the build. If we are creating * the resource, set the derived bit to true so the CM system ignores the * contents. If the resource exists, respect the existing derived setting. * * @param makefilePath * @return IFile */ protected IFile createFile(IPath makefilePath) throws CoreException { // Create or get the handle for the makefile IWorkspaceRoot root = CCorePlugin.getWorkspace().getRoot(); IFile newFile = root.getFileForLocation(makefilePath); if (newFile == null) { newFile = root.getFile(makefilePath); } // Create the file if it does not exist ByteArrayInputStream contents = new ByteArrayInputStream(new byte[0]); try { newFile.create(contents, false, SubMonitor.convert(monitor, 1)); // Make sure the new file is marked as derived if (!newFile.isDerived()) { newFile.setDerived(true, SubMonitor.convert(monitor, 1)); } // if successful, refresh any remote projects to notify them of the new file refresh(); } catch (CoreException e) { // If the file already existed locally, just refresh to get contents if (e.getStatus().getCode() == IResourceStatus.PATH_OCCUPIED) newFile.refreshLocal(IResource.DEPTH_ZERO, null); else throw e; } return newFile; } /** * Create a directory. * * @param boolean * @return whether the directory was created */ private boolean createDirectory(String dirName) throws CoreException { // Create or get the handle for the build directory IPath path = new Path(dirName); boolean rc = true; if (dirName.length() == 0 || dirName.equals(".")) path = getProjectLocation().append(dirName); File f = path.toFile(); if (!f.exists()) { rc = f.mkdirs(); if (rc) { // if successful, refresh any remote projects to notify them of the new directory refresh(); } } return rc; } private void refresh() throws CoreException{ IRemoteResource remRes = getProject().getAdapter(IRemoteResource.class); if (remRes != null) { remRes.refresh(SubMonitor.convert(monitor)); } } /** * @see org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderMakefileGenerator#getMakefileName() */ public String getMakefileName() { return MAKEFILE; } /** * Reconfigure the project. * @return MultiStatus status of regeneration operation * @throws CoreException */ public MultiStatus reconfigure() throws CoreException { return regenerateMakefiles(true); } public MultiStatus regenerateMakefiles(boolean reconfigure) throws CoreException { MultiStatus status = null; if (cfg instanceof IMultiConfiguration) { IMultiConfiguration mfcg = (IMultiConfiguration)cfg; Object[] objs = mfcg.getItems(); for (int i = 0; i < objs.length; ++i) { IConfiguration icfg = (IConfiguration)objs[i]; Status rc = regenerateMakefiles(icfg, reconfigure); if (!rc.isOK()) { if(status == null){ status = new MultiStatus( AutotoolsPlugin.getUniqueIdentifier(), IStatus.ERROR, "", null); } status.add(rc); } } } else { Status rc = regenerateMakefiles(cfg, reconfigure); if (!rc.isOK()) { if(status == null){ status = new MultiStatus( AutotoolsPlugin.getUniqueIdentifier(), IStatus.ERROR, "", null); } status.add(rc); } } if(status == null){ status = new MultiStatus( ManagedBuilderCorePlugin.getUniqueIdentifier(), IStatus.OK, "", null); } return status; } private Status regenerateMakefiles(IConfiguration icfg, boolean reconfigure) throws CoreException { MultiStatus status; int rc = IStatus.OK; String errMsg = ""; boolean needFullConfigure = false; // See if the user has cancelled the build checkCancel(); // Synchronize the Autotools configurations with the Project Description AutotoolsConfigurationManager.getInstance().syncConfigurations(getProject()); toolsCfg = AutotoolsConfigurationManager.getInstance().getConfiguration(getProject(), icfg.getId()); initializeBuildConfigDirs(icfg, toolsCfg); // Create the top-level directory for the build output if (!createDirectory(buildDir)) { rc = IStatus.ERROR; errMsg = AutotoolsPlugin.getFormattedString("MakeGenerator.createdir.error", //$NON-NLS-1$ new String[] {buildDir}); status = new MultiStatus(AutotoolsPlugin .getUniqueIdentifier(), rc, errMsg, null); } checkCancel(); // // How did we do // if (!getInvalidDirList().isEmpty()) { // status = new MultiStatus ( // ManagedBuilderCorePlugin.getUniqueIdentifier(), // IStatus.WARNING, // "", // null); // // Add a new status for each of the bad folders // iter = getInvalidDirList().iterator(); // while (iter.hasNext()) { // status.add(new Status ( // IStatus.WARNING, // ManagedBuilderCorePlugin.getUniqueIdentifier(), // SPACES_IN_PATH, // ((IContainer)iter.next()).getFullPath().toString(), // null)); // } // } else { // status = new MultiStatus( // ManagedBuilderCorePlugin.getUniqueIdentifier(), // IStatus.OK, // "", // null); // } // Get a build console for the project IConsole console = CCorePlugin.getDefault().getConsole("org.eclipse.cdt.autotools.ui.configureConsole"); //$NON-NLS-1$ boolean consoleStart = true; // Make sure there's a monitor to cancel the build if (monitor == null) { monitor = new NullProgressMonitor(); } try { // If a config.status file exists in the build directory, we call it // to // regenerate the makefile IPath configfile = buildLocation.append(CONFIG_STATUS); IPath topConfigFile = getProjectLocation().append(CONFIG_STATUS); IPath makefilePath = buildLocation.append(MAKEFILE); IPath topMakefilePath = getProjectLocation().append(MAKEFILE); File configStatus = configfile.toFile(); File topConfigStatus = topConfigFile.toFile(); File makefile = makefilePath.toFile(); File topMakefile = topMakefilePath.toFile(); // Check if a configure has been done in the top-level source directory if (!(configfile.equals(topConfigFile)) && topConfigStatus.exists()) { // Must perform distclean on source directory because 2nd configuration // cannot occur otherwise // There is a make target for cleaning. if (topMakefile != null && topMakefile.exists()) { String[] makeargs = new String[1]; IPath makeCmd = builder.getBuildCommand(); String target = null; try { target = getProject().getPersistentProperty(AutotoolsPropertyConstants.CLEAN_MAKE_TARGET); } catch (CoreException ce) { // do nothing } if (target == null) target = AutotoolsPropertyConstants.CLEAN_MAKE_TARGET_DEFAULT; String args = builder.getBuildArguments(); if (args != null && !(args = args.trim()).isEmpty()) { String[] newArgs = makeArray(args); makeargs = new String[newArgs.length + 1]; System.arraycopy(newArgs, 0, makeargs, 0, newArgs.length); } makeargs[makeargs.length - 1] = target; rc = runCommand(makeCmd, getProjectLocation(), makeargs, AutotoolsPlugin.getResourceString("MakeGenerator.clean.topdir"), //$NON-NLS-1$ errMsg, console, consoleStart); consoleStart = false; } } // If the active configuration is dirty, then we need to do a full // reconfigure. if (toolsCfg.isDirty() || reconfigure) { needFullConfigure = true; // If we are going to do a full reconfigure, then if the current // build directory exists, we should clean it out first. This is // because the reconfiguration could change compile flags, etc.. // and the Makefile might not detect a rebuild is required. In // addition, the build directory itself could have been changed and // we should remove the previous build. if (buildLocation != null && buildLocation.toFile().exists()) { // See what type of cleaning the user has set up in the // build properties dialog. String cleanDelete = null; try { cleanDelete = getProject().getPersistentProperty(AutotoolsPropertyConstants.CLEAN_DELETE); } catch (CoreException ce) { // do nothing } if (cleanDelete != null && cleanDelete.equals(AutotoolsPropertyConstants.TRUE)) buildLocation.toFile().delete(); else { // There is a make target for cleaning. if (makefile != null && makefile.exists()) { String[] makeargs = new String[1]; IPath makeCmd = builder.getBuildCommand(); String target = null; try { target = getProject().getPersistentProperty(AutotoolsPropertyConstants.CLEAN_MAKE_TARGET); } catch (CoreException ce) { // do nothing } if (target == null) target = AutotoolsPropertyConstants.CLEAN_MAKE_TARGET_DEFAULT; String args = builder.getBuildArguments(); if (args != null && !(args = args.trim()).isEmpty()) { String[] newArgs = makeArray(args); makeargs = new String[newArgs.length + 1]; System.arraycopy(newArgs, 0, makeargs, 0, newArgs.length); } makeargs[makeargs.length - 1] = target; rc = runCommand(makeCmd, buildLocation, makeargs, AutotoolsPlugin.getFormattedString("MakeGenerator.clean.builddir", new String[]{buildDir}), //$NON-NLS-1$ errMsg, console, consoleStart); consoleStart = false; } } } // Mark the scanner info as dirty. try { project.setSessionProperty(AutotoolsPropertyConstants.SCANNER_INFO_DIRTY, Boolean.TRUE); } catch (CoreException ce) { // do nothing } } List<String> configureEnvs = new ArrayList<>(); List<String> configureCmdParms = new ArrayList<>(); IPath configurePath = getConfigurePath(configureEnvs, configureCmdParms); String[] configArgs = getConfigArgs(configureCmdParms); List<String> autogenEnvs = new ArrayList<>(); List<String> autogenCmdParms = new ArrayList<>(); IPath autogenPath = getAutogenPath(autogenEnvs, autogenCmdParms); // Check if we have a config.status (meaning configure has already run). if (!needFullConfigure && configStatus != null && configStatus.exists()) { // If no corresponding Makefile in the same build location, then we // can simply run config.status again to ensure the top level Makefile has been // created. if (makefile == null || !makefile.exists()) { rc = runScript(configfile, buildLocation, null, AutotoolsPlugin.getFormattedString("MakeGenerator.run.config.status", new String[]{buildDir}), //$NON-NLS-1$ errMsg, console, null, consoleStart); consoleStart = false; } } // Look for configure and configure from scratch else if (configurePath.toFile().exists()) { rc = runScript(configurePath, buildLocation, configArgs, AutotoolsPlugin.getFormattedString("MakeGenerator.gen.makefile", new String[]{buildDir}), //$NON-NLS-1$ errMsg, console, configureEnvs, consoleStart); consoleStart = false; if (rc != IStatus.ERROR) { File makefileFile = buildLocation.append(MAKEFILE).toFile(); addMakeTargetsToManager(makefileFile); // TODO: should we do something special if configure doesn't // return ok? toolsCfg.setDirty(false); } } // If no configure, look for autogen.sh which may create configure and // possibly even run it. else if (autogenPath.toFile().exists()) { // Remove the existing config.status file since we use it // to figure out if configure was run. if (configStatus.exists()) configStatus.delete(); // Get any user-specified arguments for autogen. String[] autogenArgs = getAutogenArgs(autogenCmdParms); rc = runScript(autogenPath, autogenPath.removeLastSegments(1), autogenArgs, AutotoolsPlugin.getFormattedString("MakeGenerator.autogen.sh", new String[]{buildDir}), //$NON-NLS-1$ errMsg, console, autogenEnvs, consoleStart); consoleStart = false; if (rc != IStatus.ERROR) { refresh(); configStatus = configfile.toFile(); // Check for config.status. If it is created, then // autogen.sh ran configure and we should not run it // ourselves. if (configStatus == null || !configStatus.exists()) { if (!configurePath.toFile().exists()) { // no configure script either...try running autoreconf String[] reconfArgs = new String[1]; String reconfCmd = project.getPersistentProperty(AutotoolsPropertyConstants.AUTORECONF_TOOL); if (reconfCmd == null) reconfCmd = DEFAULT_AUTORECONF; IPath reconfCmdPath = new Path(reconfCmd); reconfArgs[0] = "-i"; //$NON-NLS-1$ rc = runScript(reconfCmdPath, getSourcePath(), reconfArgs, AutotoolsPlugin.getFormattedString("MakeGenerator.autoreconf", new String[]{buildDir}), //$NON-NLS-1$ errMsg, console, null, consoleStart); consoleStart = false; refresh(); } // Check if configure generated and if yes, run it. if (rc != IStatus.ERROR && configurePath.toFile().exists()) { rc = runScript(configurePath, buildLocation, configArgs, AutotoolsPlugin.getFormattedString("MakeGenerator.gen.makefile", new String[]{buildDir}), //$NON-NLS-1$ errMsg, console, configureEnvs, false); if (rc != IStatus.ERROR) { File makefileFile = buildLocation.append(MAKEFILE).toFile(); addMakeTargetsToManager(makefileFile); toolsCfg.setDirty(false); } } } else { File makefileFile = buildLocation.append(MAKEFILE).toFile(); addMakeTargetsToManager(makefileFile); toolsCfg.setDirty(false); } } } // If nothing this far, look for a Makefile.cvs file which needs to be run. else if (makefileCvsExists()) { String[] makeargs = new String[1]; IPath makeCmd = builder.getBuildCommand(); makeargs[0] = "-f" + getMakefileCVSPath().toOSString(); //$NON-NLS-1$ rc = runCommand(makeCmd, getProjectLocation().append(buildDir), makeargs, AutotoolsPlugin.getFormattedString("MakeGenerator.makefile.cvs", new String[]{buildDir}), //$NON-NLS-1$ errMsg, console, consoleStart); consoleStart = false; if (rc != IStatus.ERROR) { File makefileFile = getProjectLocation().append(buildDir) .append(MAKEFILE).toFile(); addMakeTargetsToManager(makefileFile); toolsCfg.setDirty(false); } } // If nothing this far, try running autoreconf -i else { String[] reconfArgs = new String[1]; String reconfCmd = project.getPersistentProperty(AutotoolsPropertyConstants.AUTORECONF_TOOL); if (reconfCmd == null) reconfCmd = DEFAULT_AUTORECONF; IPath reconfCmdPath = new Path(reconfCmd); reconfArgs[0] = "-i"; //$NON-NLS-1$ rc = runScript(reconfCmdPath, getSourcePath(), reconfArgs, AutotoolsPlugin.getFormattedString("MakeGenerator.autoreconf", new String[]{buildDir}), //$NON-NLS-1$ errMsg, console, null, consoleStart); consoleStart = false; // Check if configure generated and if yes, run it. if (rc != IStatus.ERROR) { refresh(); if (configurePath.toFile().exists()) { rc = runScript(configurePath, buildLocation, configArgs, AutotoolsPlugin.getFormattedString("MakeGenerator.gen.makefile", new String[]{buildDir}), //$NON-NLS-1$ errMsg, console, configureEnvs, false); if (rc != IStatus.ERROR) { File makefileFile = buildLocation.append(MAKEFILE).toFile(); addMakeTargetsToManager(makefileFile); // TODO: should we do something special if configure doesn't // return ok? toolsCfg.setDirty(false); } } } } // If we didn't create a Makefile, consider that an error. if (makefile == null || !makefile.exists()) { rc = IStatus.ERROR; errMsg = AutotoolsPlugin.getResourceString("MakeGenerator.didnt.generate"); //$NON-NLS-1$ } } catch (IOException e) { e.printStackTrace(); // forgetLastBuiltState(); rc = IStatus.ERROR; } finally { // getGenerationProblems().clear(); status = new MultiStatus(AutotoolsPlugin .getUniqueIdentifier(), rc, errMsg, null); if (rc != IStatus.OK) status.add(new Status ( rc, AutotoolsPlugin.getUniqueIdentifier(), 0, errMsg, null)); } return status; } /** * Strip a command of VAR=VALUE pairs that appear ahead or behind the command and add * them to a list of environment variables. * * @param command - command to strip * @param envVars - ArrayList to add environment variables to * @return stripped command */ public static String stripEnvVars(String command, List<String> envVars) { Pattern p1 = Pattern.compile("(\\w+[=]\\\".*?\\\"\\s+)\\w+.*"); Pattern p2 = Pattern.compile("(\\w+[=]'.*?'\\s+)\\w+.*"); Pattern p3 = Pattern.compile("(\\w+[=][^\\s]+\\s+)\\w+.*"); Pattern p4 = Pattern.compile("\\w+\\s+(\\w+[=]\\\".*?\\\"\\s*)+.*"); Pattern p5 = Pattern.compile("\\w+\\s+(\\w+[=]'.*?'\\s*)+.*"); Pattern p6 = Pattern.compile("\\w+\\s+(\\w+[=][^\\s]+).*"); boolean finished = false; while (!finished) { Matcher m1 = p1.matcher(command); if (m1.matches()) { command = command.replaceFirst("\\w+[=]\\\".*?\\\"","").trim(); String s = m1.group(1).trim(); envVars.add(s.replaceAll("\\\"", "")); } else { Matcher m2 = p2.matcher(command); if (m2.matches()) { command = command.replaceFirst("\\w+[=]'.*?'", "").trim(); String s = m2.group(1).trim(); envVars.add(s.replaceAll("'", "")); } else { Matcher m3 = p3.matcher(command); if (m3.matches()) { command = command.replaceFirst("\\w+[=][^\\s]+", "").trim(); envVars.add(m3.group(1).trim()); } else { Matcher m4 = p4.matcher(command); if (m4.matches()) { command = command.replaceFirst("\\w+[=]\\\".*?\\\"","").trim(); String s = m4.group(1).trim(); envVars.add(s.replaceAll("\\\"", "")); } else { Matcher m5 = p5.matcher(command); if (m5.matches()) { command = command.replaceFirst("\\w+[=]'.*?'", "").trim(); String s = m5.group(1).trim(); envVars.add(s.replaceAll("'", "")); } else { Matcher m6 = p6.matcher(command); if (m6.matches()) { command = command.replaceFirst("\\w+[=][^\\s+]+", "").trim(); envVars.add(m6.group(1).trim()); } else { finished = true; } } } } } } } return command; } /** * Strip a configure option of VAR=VALUE pairs and add * them to a list of environment variables. * * @param str - string to strip * @param envVars - ArrayList to add environment variables to * @return stripped option */ public static String stripEnvVarsFromOption(String str, List<String> envVars) { Pattern p1 = Pattern.compile("(\\w+[=]\\\".*?\\\"\\s*).*"); Pattern p2 = Pattern.compile("(\\w+[=]'.*?'\\s*).*"); Pattern p3 = Pattern.compile("(\\w+[=][^\\s]+).*"); boolean finished = false; while (!finished) { Matcher m1 = p1.matcher(str); if (m1.matches()) { str = str.replaceFirst("\\w+[=]\\\".*?\\\"","").trim(); String s = m1.group(1).trim(); envVars.add(s.replaceAll("\\\"", "")); } else { Matcher m2 = p2.matcher(str); if (m2.matches()) { str = str.replaceFirst("\\w+[=]'.*?'", "").trim(); String s = m2.group(1).trim(); envVars.add(s.replaceAll("'", "")); } else { Matcher m3 = p3.matcher(str); if (m3.matches()) { str = str.replaceFirst("\\w+[=][^\\s]+", "").trim(); envVars.add(m3.group(1).trim()); } else { finished = true; } } } } return str; } private IPath getProjectLocation() { return project.getLocation(); } private IPath getBuildPath(){ return new Path(this.buildDir); } private IPath getSourcePath(){ IPath sourcePath; if (srcDir.isEmpty()) sourcePath = getProjectLocation(); else { // find location of source directory which may be a virtual folder IResource sourceResource = project.findMember(srcDir); if (sourceResource.exists() && sourceResource.getType() == IResource.FOLDER) sourcePath = sourceResource.getLocation(); else // punt and let configure fail if directory is not found by then sourcePath = getProjectLocation().append(srcDir); } return sourcePath; } protected IPath getConfigurePath(List<String> envVars, List<String> cmdParms) { IPath configPath; IConfigureOption configOption = toolsCfg.getOption(CONFIGURE_TOOL_ID); String command = "configure"; //$NON-NLS-1$ if (configOption != null) command = stripEnvVars(configOption.getValue().trim(), envVars); String[] tokens = command.split("\\s"); if (tokens.length > 1) { command = tokens[0]; for (int i = 1; i < tokens.length; ++i) cmdParms.add(tokens[i]); } if (Path.fromOSString(command).isAbsolute()) { configPath = new Path(command); } else { configPath = getSourcePath().append(command); } return configPath; } protected IPath getMakefileCVSPath() { IPath makefileCVSPath; makefileCVSPath = getSourcePath().append(MAKEFILE_CVS); return makefileCVSPath; } protected boolean makefileCvsExists() { IPath makefileCVSPath = getMakefileCVSPath(); return makefileCVSPath.toFile().exists(); } protected IPath getAutogenPath(List<String> envVars, List<String> cmdParms) { IPath autogenPath; IConfigureOption autogenOption = toolsCfg.getOption(AUTOGEN_TOOL_ID); String command = "autogen.sh"; //$NON-NLS-1$ if (autogenOption != null) command = stripEnvVars(autogenOption.getValue().trim(), envVars); String[] tokens = command.split("\\s"); if (tokens.length > 1) { command = tokens[0]; for (int i = 1; i < tokens.length; ++i) cmdParms.add(tokens[i]); } autogenPath = getSourcePath().append(command); return autogenPath; } private String[] getAutogenArgs(List<String> cmdParms) { // Get the arguments to be passed to config from build model List<String> autogenArgs = toolsCfg.getToolArgs(AUTOGEN_TOOL_ID); cmdParms.addAll(autogenArgs); return cmdParms.toArray(new String[cmdParms.size()]); } private String[] getConfigArgs(List<String> cmdParms) { // Get the arguments to be passed to config from build model List<String> configArgs = toolsCfg.getToolArgs(CONFIGURE_TOOL_ID); cmdParms.addAll(configArgs); return cmdParms.toArray(new String[cmdParms.size()]); } // Run a command or executable (e.g. make). private int runCommand(IPath commandPath, IPath runPath, String[] args, String jobDescription, String errMsg, IConsole console, boolean consoleStart) throws CoreException, NullPointerException, IOException { int rc = IStatus.OK; removeAllMarkers(project); String[] configTargets = args; if (args == null) configTargets = new String[0]; for (int i = 0; i < configTargets.length; ++i) { // try to resolve the build macros in any argument try{ String resolved = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat( configTargets[i], "", //$NON-NLS-1$ " ", //$NON-NLS-1$ IBuildMacroProvider.CONTEXT_CONFIGURATION, cfg); configTargets[i] = resolved; } catch (BuildMacroException e) { } } String[] msgs = new String[2]; msgs[0] = commandPath.toString(); msgs[1] = project.getName(); monitor.subTask(AutotoolsPlugin.getFormattedString( "MakeGenerator.make.message", msgs)); //$NON-NLS-1$ StringBuilder buf = new StringBuilder(); // Launch command - main invocation if (consoleStart) console.start(project); try (ConsoleOutputStream consoleOutStream = console.getOutputStream()) { String[] consoleHeader = new String[3]; consoleHeader[0] = jobDescription; consoleHeader[1] = toolsCfg.getId(); consoleHeader[2] = project.getName(); buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$ //$NON-NLS-2$ buf.append(jobDescription); buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$ //$NON-NLS-2$ buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$ //$NON-NLS-2$ consoleOutStream.write(buf.toString().getBytes()); consoleOutStream.flush(); // Get a launcher for the config command RemoteCommandLauncher launcher = new RemoteCommandLauncher(); launcher.setProject(project); // Set the environment IEnvironmentVariable variables[] = CCorePlugin.getDefault().getBuildEnvironmentManager().getVariables(cdesc, true); String[] env = null; ArrayList<String> envList = new ArrayList<>(); if (variables != null) { for (int i = 0; i < variables.length; i++) { envList.add(variables[i].getName() + "=" + variables[i].getValue()); //$NON-NLS-1$ } env = envList.toArray(new String[envList.size()]); } // Hook up an error parser manager URI uri = URIUtil.toURI(runPath); try (ErrorParserManager epm = new ErrorParserManager(project, uri, this)) { epm.setOutputStream(consoleOutStream); epm.addErrorParser(ErrorParser.ID, new ErrorParser(getSourcePath(), getBuildPath())); OutputStream stdout = epm.getOutputStream(); OutputStream stderr = stdout; launcher.showCommand(true); Process proc = launcher.execute(commandPath, configTargets, env, runPath, SubMonitor.convert(monitor)); int exitValue = 0; if (proc != null) { try { // Close the input of the process since we will never // write to // it proc.getOutputStream().close(); } catch (IOException e) { } if (launcher.waitAndRead(stdout, stderr, SubMonitor.convert(monitor)) != ICommandLauncher.OK) { errMsg = launcher.getErrorMessage(); } // There can be a problem looking at the process exit value, // so prevent an exception from occurring. if (errMsg == null || errMsg.length() == 0) { try { exitValue = proc.exitValue(); } catch (IllegalThreadStateException e) { try { proc.waitFor(); } catch (InterruptedException e1) { e1.printStackTrace(); } exitValue = proc.exitValue(); } } // Force a resync of the projects without allowing the user // to // cancel. // This is probably unkind, but short of this there is no // way to // ensure // the UI is up-to-date with the build results // monitor.subTask(ManagedMakeMessages // .getResourceString(REFRESH)); monitor.subTask(AutotoolsPlugin.getResourceString("MakeGenerator.refresh")); //$NON-NLS-1$ try { project.refreshLocal(IResource.DEPTH_INFINITE, null); } catch (CoreException e) { monitor.subTask(AutotoolsPlugin.getResourceString("MakeGenerator.refresh.error")); //$NON-NLS-1$ } } else { errMsg = launcher.getErrorMessage(); } // Report either the success or failure of our mission buf = new StringBuilder(); if (errMsg != null && errMsg.length() > 0) { String errorDesc = AutotoolsPlugin.getResourceString("MakeGenerator.generation.error"); //$NON-NLS-1$ buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$//$NON-NLS-2$ buf.append(errorDesc); buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$//$NON-NLS-2$ buf.append("(").append(errMsg).append(")"); //$NON-NLS-1$ //$NON-NLS-2$ rc = IStatus.ERROR; } else if (exitValue >= 1 || exitValue < 0) { // We have an invalid return code from configuration. String[] errArg = new String[2]; errArg[0] = Integer.toString(proc.exitValue()); errArg[1] = commandPath.toString(); errMsg = AutotoolsPlugin.getFormattedString("MakeGenerator.config.error", errArg); //$NON-NLS-1$ buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$//$NON-NLS-2$ buf.append(AutotoolsPlugin.getResourceString("MakeGenerator.generation.error")); //$NON-NLS-1$ buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$//$NON-NLS-2$ if (proc.exitValue() == 1) rc = IStatus.WARNING; else rc = IStatus.ERROR; } else { // Report a successful build String successMsg = AutotoolsPlugin.getResourceString("MakeGenerator.success"); //$NON-NLS-1$ buf.append(successMsg); buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$//$NON-NLS-2$ rc = IStatus.OK; } } // Write message on the console consoleOutStream.write(buf.toString().getBytes()); consoleOutStream.flush(); // // Generate any error markers that the build has discovered // monitor.subTask(ManagedMakeMessages // .getResourceString(MARKERS)); // epm.reportProblems(); } // If we have an error and no specific error markers, use the default error marker. if (rc == IStatus.ERROR && !hasMarkers(project)) { addMarker(project, -1, errMsg, SEVERITY_ERROR_BUILD, null); } return rc; } // Method to get the Win OS Type to distinguish between Cygwin and MingW private String getWinOSType() { if (winOSType.isEmpty()) { try { RemoteCommandLauncher launcher = new RemoteCommandLauncher(); launcher.setProject(getProject()); ByteArrayOutputStream out = new ByteArrayOutputStream(); // Fix Bug 423192 - use environment variables when checking the Win OS Type using // a shell command as the path to sh may be specified there IEnvironmentVariable variables[] = CCorePlugin.getDefault().getBuildEnvironmentManager().getVariables(cdesc, true); String[] env = new String[0]; ArrayList<String> envList = new ArrayList<>(); if (variables != null) { for (int i = 0; i < variables.length; i++) { envList.add(variables[i].getName() + "=" + variables[i].getValue()); //$NON-NLS-1$ } env = envList.toArray(new String[envList.size()]); } launcher.execute( new Path(SHELL_COMMAND), //$NON-NLS-1$ new String[] { "-c", "echo $OSTYPE" }, //$NON-NLS-1$ //$NON-NLS-2$ env, new Path("."), //$NON-NLS-1$ SubMonitor.convert(monitor)); if (launcher.waitAndRead(out, out) == ICommandLauncher.OK) winOSType = out.toString().trim(); } catch (CoreException e) { // do nothing } } return winOSType; } // Get OS name either remotely or locally, depending on the project private String getOSName() { IRemoteResource remRes = getProject().getAdapter(IRemoteResource.class); if (remRes != null) { URI uri = remRes.getActiveLocationURI(); IRemoteServicesManager remoteServiceManager = AutotoolsPlugin.getService(IRemoteServicesManager.class); IRemoteConnectionType connectionType = remoteServiceManager.getConnectionType(uri); if (connectionType != null) { IRemoteConnection conn = connectionType.getConnection(uri); if (conn != null) { if (!conn.isOpen()) { try { conn.open(SubMonitor.convert(monitor)); } catch (RemoteConnectionException e) { // Ignore and return platform OS } } if (conn.isOpen()) { return conn.getProperty(IRemoteConnection.OS_NAME_PROPERTY); } } } } return Platform.getOS(); } // Get the path string. We add a Win check to handle MingW. // For MingW, we would rather represent C:\a\b as /C/a/b which // doesn't cause Makefile to choke. For Cygwin we use /cygdrive/C/a/b private String getPathString(IPath path) { String s = path.toString(); if (getOSName().equals(Platform.OS_WIN32)) { if (getWinOSType().equals("cygwin")) { s = s.replaceAll("^([a-zA-Z]):", "/cygdrive/$1"); } else { s = s.replaceAll("^([a-zA-Z]):", "/$1"); } } return s; } // Fix any escape characters in sh -c command arguments private String fixEscapeChars(String s) { s = s.replaceAll("\\\\", "\\\\\\\\"); s = s.replaceAll("\\(", "\\\\("); s = s.replaceAll("\\)", "\\\\)"); return s; } // Run an autotools script (e.g. configure, autogen.sh, config.status). private int runScript(IPath commandPath, IPath runPath, String[] args, String jobDescription, String errMsg, IConsole console, List<String> additionalEnvs, boolean consoleStart) throws CoreException, NullPointerException, IOException { int rc = IStatus.OK; boolean removePWD = false; removeAllMarkers(project); // We want to run the script via the shell command. So, we add the command // script as the first argument and expect "sh" to be on the runtime path. // Any other arguments are placed after the script name. String[] configTargets = null; if (args == null) configTargets = new String[1]; else { configTargets = new String[args.length+1]; System.arraycopy(args, 0, configTargets, 1, args.length); } configTargets[0] = getPathString(commandPath); // Fix for bug #343879 String osName = getOSName(); if (osName.equals(Platform.OS_WIN32) || osName.equals(Platform.OS_MACOSX)) removePWD = true; // Fix for bug #343731 and bug #371277 // Always use sh -c for executing autotool scripts which should // work on all Linux POSIX compliant shells including bash, dash, as // well as Windows and Mac OSX. String command = null; for (String arg : configTargets) { // TODO check for spaces in args if (command == null) command = arg; else command += " " + arg; } configTargets = new String[] { "-c", command }; for (int i = 0; i < configTargets.length; ++i) { // try to resolve the build macros in any argument try{ String resolved = ManagedBuildManager.getBuildMacroProvider().resolveValueToMakefileFormat( configTargets[i], "", //$NON-NLS-1$ " ", //$NON-NLS-1$ IBuildMacroProvider.CONTEXT_CONFIGURATION, cfg); // strip any env-var settings from options // fix for bug #356278 if (resolved.length() > 0 && resolved.charAt(0) != '-') resolved = stripEnvVarsFromOption(resolved, additionalEnvs); configTargets[i] = fixEscapeChars(resolved); } catch (BuildMacroException e) { } } String[] msgs = new String[2]; msgs[0] = commandPath.toString(); msgs[1] = project.getName(); monitor.subTask(AutotoolsPlugin.getFormattedString( "MakeGenerator.make.message", msgs)); //$NON-NLS-1$ StringBuilder buf = new StringBuilder(); // Launch command - main invocation if (consoleStart) console.start(project); try (ConsoleOutputStream consoleOutStream = console.getOutputStream()) { String[] consoleHeader = new String[3]; consoleHeader[0] = jobDescription; consoleHeader[1] = toolsCfg.getId(); consoleHeader[2] = project.getName(); buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$ //$NON-NLS-2$ buf.append(jobDescription); buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$ //$NON-NLS-2$ buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$ //$NON-NLS-2$ // Display command-line environment variables that have been stripped by us // because launch showCommand won't do this. if (additionalEnvs != null && additionalEnvs.size() > 0) { buf.append(AutotoolsPlugin .getResourceString("MakeGenerator.commandline.envvars")); buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$ //$NON-NLS-2$ buf.append("\t"); for (int i = 0; i < additionalEnvs.size(); ++i) { String envvar = additionalEnvs.get(i); buf.append(envvar.replaceFirst("(\\w+=)(.*)"," $1\"$2\"")); } buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$ //$NON-NLS-2$ buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$ //$NON-NLS-2$ } consoleOutStream.write(buf.toString().getBytes()); consoleOutStream.flush(); // Get a launcher for the config command RemoteCommandLauncher launcher = new RemoteCommandLauncher(); launcher.setProject(project); // Set the environment IEnvironmentVariable variables[] = CCorePlugin.getDefault().getBuildEnvironmentManager().getVariables(cdesc, true); String[] env = null; ArrayList<String> envList = new ArrayList<>(); if (variables != null) { for (int i = 0; i < variables.length; i++) { // For Windows/Mac, check for PWD environment variable being passed. // Remove it for now as it is causing errors in configuration. // Fix for bug #343879 if (!removePWD || !variables[i].getName().equals("PWD")) { //$NON-NLS-1$ String value = variables[i].getValue(); // The following is a work-around for bug #407580. Configure doesn't recognize // a directory with a trailing separator at the end is equivalent to the same // directory without that trailing separator. This problem can cause // configure to try and link a file to itself (e.g. projects with a GnuMakefile) and // obliterate the contents. Thus, we remove the trailing separator to be safe. if (variables[i].getName().equals("PWD")) { //$NON-NLS-1$ if (value.charAt(value.length()-1) == IPath.SEPARATOR) value = value.substring(0, value.length() - 1); } envList.add(variables[i].getName() + "=" + value); //$NON-NLS-1$ } } if (additionalEnvs != null) envList.addAll(additionalEnvs); // add any additional environment variables specified ahead of script env = envList.toArray(new String[envList.size()]); } // Hook up an error parser manager URI uri = URIUtil.toURI(runPath); try (ErrorParserManager epm = new ErrorParserManager(project, uri, this)) { epm.setOutputStream(consoleOutStream); epm.addErrorParser(ErrorParser.ID, new ErrorParser(getSourcePath(), getBuildPath())); OutputStream stdout = epm.getOutputStream(); OutputStream stderr = stdout; launcher.showCommand(true); // Run the shell script via shell command. Process proc = launcher.execute(new Path(SHELL_COMMAND), configTargets, env, runPath, SubMonitor.convert(monitor)); int exitValue = 0; if (proc != null) { try { // Close the input of the process since we will never // write to // it proc.getOutputStream().close(); } catch (IOException e) { } if (launcher.waitAndRead(stdout, stderr, SubMonitor.convert(monitor)) != ICommandLauncher.OK) { errMsg = launcher.getErrorMessage(); } // There can be a problem looking at the process exit value, // so prevent an exception from occurring. if (errMsg == null || errMsg.length() == 0) { try { exitValue = proc.exitValue(); } catch (IllegalThreadStateException e) { try { proc.waitFor(); } catch (InterruptedException e1) { e1.printStackTrace(); } exitValue = proc.exitValue(); } } // Force a resync of the projects without allowing the user // to // cancel. // This is probably unkind, but short of this there is no // way to // ensure // the UI is up-to-date with the build results // monitor.subTask(ManagedMakeMessages // .getResourceString(REFRESH)); monitor.subTask(AutotoolsPlugin.getResourceString("MakeGenerator.refresh")); //$NON-NLS-1$ try { project.refreshLocal(IResource.DEPTH_INFINITE, null); } catch (CoreException e) { monitor.subTask(AutotoolsPlugin.getResourceString("MakeGenerator.refresh.error")); //$NON-NLS-1$ } } else { errMsg = launcher.getErrorMessage(); } // Report either the success or failure of our mission buf = new StringBuilder(); if (errMsg != null && errMsg.length() > 0) { String errorDesc = AutotoolsPlugin.getResourceString("MakeGenerator.generation.error"); //$NON-NLS-1$ buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$//$NON-NLS-2$ buf.append(errorDesc); buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$//$NON-NLS-2$ buf.append("(").append(errMsg).append(")"); //$NON-NLS-1$ //$NON-NLS-2$ rc = IStatus.ERROR; } else if (exitValue >= 1 || exitValue < 0) { // We have an invalid return code from configuration. String[] errArg = new String[2]; errArg[0] = Integer.toString(proc.exitValue()); errArg[1] = commandPath.toString(); errMsg = AutotoolsPlugin.getFormattedString("MakeGenerator.config.error", errArg); //$NON-NLS-1$ buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$//$NON-NLS-2$ buf.append(AutotoolsPlugin.getResourceString("MakeGenerator.generation.error")); //$NON-NLS-1$ buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$//$NON-NLS-2$ if (proc.exitValue() == 1) rc = IStatus.WARNING; else rc = IStatus.ERROR; } else { // Report a successful build String successMsg = AutotoolsPlugin.getResourceString("MakeGenerator.success"); //$NON-NLS-1$ buf.append(successMsg); buf.append(System.getProperty("line.separator", "\n")); //$NON-NLS-1$//$NON-NLS-2$ rc = IStatus.OK; } } // Write message on the console consoleOutStream.write(buf.toString().getBytes()); consoleOutStream.flush(); // // Generate any error markers that the build has discovered // monitor.subTask(ManagedMakeMessages // .getResourceString(MARKERS)); // epm.reportProblems(); } // If we have an error and no specific error markers, use the default error marker. if (rc == IStatus.ERROR && !hasMarkers(project)) { addMarker(project, -1, errMsg, SEVERITY_ERROR_BUILD, null); } return rc; } private ICStorageElement createTargetElement(ICStorageElement parent, IMakeTarget target) { ICStorageElement targetElem = parent.createChild(TARGET_ELEMENT); targetElem.setAttribute(TARGET_ATTR_NAME, target.getName()); targetElem.setAttribute(TARGET_ATTR_ID, target.getTargetBuilderID()); targetElem.setAttribute(TARGET_ATTR_PATH, target.getContainer().getProjectRelativePath().toString()); ICStorageElement elem = targetElem.createChild(TARGET_COMMAND); elem.setValue(target.getBuildAttribute(IMakeCommonBuildInfo.BUILD_COMMAND, builder.getBuildCommand().toOSString())); String targetAttr = target.getBuildAttribute(IMakeCommonBuildInfo.BUILD_ARGUMENTS, null); if ( targetAttr != null) { elem = targetElem.createChild(TARGET_ARGUMENTS); elem.setValue(targetAttr); } targetAttr = target.getBuildAttribute(IMakeTarget.BUILD_TARGET, null); if (targetAttr != null) { elem = targetElem.createChild(TARGET); elem.setValue(targetAttr); } elem = targetElem.createChild(TARGET_STOP_ON_ERROR); elem.setValue(String.valueOf(target.isStopOnError())); elem = targetElem.createChild(TARGET_USE_DEFAULT_CMD); elem.setValue(String.valueOf(target.isDefaultBuildCmd())); elem = targetElem.createChild(TARGET_RUN_ALL_BUILDERS); elem.setValue(String.valueOf(target.runAllBuilders())); return targetElem; } /** * This output method saves the information into the .cdtproject metadata file. * * @param doc * @throws CoreException */ private void saveTargets(IMakeTarget[] makeTargets) throws CoreException { // FIXME: fix this when MakeTargetManager fixes its code. ICDescriptor descriptor = CCorePlugin.getDefault().getCProjectDescription(getProject(), true); ICStorageElement rootElement = descriptor.getProjectStorageElement(MAKE_TARGET_KEY); //Nuke the children since we are going to write out new ones rootElement.clear(); // Fetch the ProjectTargets as ICStorageElements rootElement = rootElement.createChild(BUILD_TARGET_ELEMENT); for (int i = 0; i < makeTargets.length; ++i) createTargetElement(rootElement, makeTargets[i]); //Save the results descriptor.saveProjectData(); } protected static class MakeTargetComparator implements Comparator<Object> { @Override public int compare(Object a, Object b) { IMakeTarget make1 = (IMakeTarget)a; IMakeTarget make2 = (IMakeTarget)b; return make1.getName().compareToIgnoreCase(make2.getName()); } } /** * This method parses the given Makefile and produces MakeTargets for all targets so the * end-user can access them from the MakeTargets popup-menu. * * @param makefileFile the Makefile to parse * @throws CoreException */ private void addMakeTargetsToManager(File makefileFile) throws CoreException { // We don't bother if the Makefile wasn't created. if (makefileFile == null || !makefileFile.exists()) return; checkCancel(); if (monitor == null) monitor = new NullProgressMonitor(); String statusMsg = AutotoolsPlugin.getResourceString("MakeGenerator.refresh.MakeTargets"); //$NON-NLS-1$ monitor.subTask(statusMsg); IMakeTargetManager makeTargetManager = MakeCorePlugin.getDefault().getTargetManager(); IMakefile makefile = MakeCorePlugin.createMakefile(makefileFile.toURI(), false, null); ITargetRule[] targets = makefile.getTargetRules(); ITarget target = null; Map<String, IMakeTarget> makeTargets = new HashMap<>(); // use a HashMap // so duplicate // names are // handled String[] id = makeTargetManager.getTargetBuilders(getProject()); if (id.length == 0) { return; } String targetBuildID = id[0]; IMakeBuilderInfo buildInfo = MakeCorePlugin.createBuildInfo(getProject(), makeTargetManager.getBuilderID(targetBuildID)); boolean isStopOnError = buildInfo.isStopOnError(); IPath buildCommand = buildInfo.getBuildCommand(); String defaultBuildCommand = buildCommand.toString(); String buildArguments = buildInfo.getBuildArguments(); // Bug #351660 - reset targets to a single dummy target so that // we will never be able to find any of the new targets we are about to // create and thus avoid an extraneous event notification on a change to // the MakeTarget. The dummy target should have an invalid name for // a normal make target. IMakeTarget dummyTarget = makeTargetManager.createTarget( project, "\ndummyTarget\n", targetBuildID); //$NON-NLS-1$ makeTargetManager.setTargets(project, new IMakeTarget[]{dummyTarget}); for (int i = 0; i < targets.length; i++) { target = targets[i].getTarget(); String targetName = target.toString(); if (!isValidTarget(targetName)) continue; try { // Bug #351660 - always create a new MakeTarget because an // existing MakeTarget will cause events to occur on every // modification whereas a new MakeTarget not yet added will // not cause this delay. IMakeTarget makeTarget = makeTargetManager.createTarget( project, targetName, targetBuildID); makeTarget.setContainer(project); makeTarget.setStopOnError(isStopOnError); makeTarget.setRunAllBuilders(false); makeTarget.setUseDefaultBuildCmd(true); makeTarget.setBuildAttribute(IMakeCommonBuildInfo.BUILD_ARGUMENTS, buildArguments); makeTarget.setBuildAttribute(IMakeCommonBuildInfo.BUILD_COMMAND, defaultBuildCommand); makeTarget.setBuildAttribute(GENERATED_TARGET, "true"); //$NON-NLS-1$ makeTarget.setBuildAttribute(IMakeTarget.BUILD_TARGET, targetName); //TODO: should this be raw build directory in macro form? makeTarget.setBuildAttribute(IMakeCommonBuildInfo.BUILD_LOCATION, buildDir); makeTargets.put(makeTarget.getName(), makeTarget); } catch (CoreException e) { // Duplicate target. Ignore. } } IMakeTarget[] makeTargetArray = new IMakeTarget[makeTargets.size()]; Collection<IMakeTarget> values = makeTargets.values(); List<IMakeTarget> valueList = new ArrayList<>(values); valueList.toArray(makeTargetArray); MakeTargetComparator compareMakeTargets = new MakeTargetComparator(); Arrays.sort(makeTargetArray, compareMakeTargets); // Check if we have MakeTargetManager patch which adds the ability // to save multiple targets at once. If yes, use it as it updates // the MakeTargets now. Otherwise, fall back to old method which // saves the targets externally..requiring closing the project and // reopening to see them. Class<? extends IMakeTargetManager> c = makeTargetManager.getClass(); boolean targetsAdded = false; try { Method m = c.getMethod("setTargets", IContainer.class, IMakeTarget[].class); m.invoke(makeTargetManager, project, makeTargetArray); targetsAdded = true; } catch (NoSuchMethodException | IllegalArgumentException | IllegalAccessException | InvocationTargetException e) { // ignore and use fail-safe saveTargets method } if (!targetsAdded) saveTargets(makeTargetArray); } private boolean isValidTarget(String targetName) { return !(targetName.endsWith("-am") //$NON-NLS-1$ || targetName.endsWith("PROGRAMS") //$NON-NLS-1$ || targetName.endsWith("-generic") //$NON-NLS-1$ || (targetName.indexOf('$') >= 0) || (targetName.charAt(0) == '.') || targetName.equals(targetName.toUpperCase())); } // Turn the string into an array. private String[] makeArray(String string) { string = string.trim(); char[] array = string.toCharArray(); ArrayList<String> aList = new ArrayList<>(); StringBuilder buffer = new StringBuilder(); boolean inComment = false; for (int i = 0; i < array.length; i++) { char c = array[i]; boolean needsToAdd = true; if (array[i] == '"' || array[i] == '\'') { if (i > 0 && array[i - 1] == '\\') { inComment = false; } else { inComment = !inComment; needsToAdd = false; // skip it } } if (c == ' ' && !inComment) { if (buffer.length() > 0){ String str = buffer.toString().trim(); if(str.length() > 0){ aList.add(str); } } buffer = new StringBuilder(); } else { if (needsToAdd) buffer.append(c); } } if (buffer.length() > 0){ String str = buffer.toString().trim(); if(str.length() > 0){ aList.add(str); } } return aList.toArray(new String[aList.size()]); } }