From ba7f4f26eb8f799d45e40cf84fa68b7857f6df3f Mon Sep 17 00:00:00 2001 From: Jeff Johnston Date: Thu, 8 Feb 2018 14:58:26 -0500 Subject: [PATCH] Add Run ninja command context menu item for Meson projects - add new RunNinjaCommandHandler, RunNinja, AbstractMesonCommandHandler, and RunNinjaPage classes to support running ninja manually with env variables added and options specified - add a new build method to MesonBuildConfiguration which specifies ninja options and environment variables - add SWTImagesFactory for supplying the meson logo image - add WizardMessages class for specifying messages for meson ui wizards - add new MesonUtils class and move stripEnvVars from MesonBuildConfiguration to here - add new constants to IMesonConstants interface Change-Id: I8d635b2bd96792800bb07f4b3f1730be6e41eb24 --- .../meson/core/MesonBuildConfiguration.java | 88 +++---- .../cdt/internal/meson/core/MesonUtils.java | 57 +++++ .../cdt/internal/meson/core/Messages.java | 1 + .../internal/meson/core/messages.properties | 1 + .../cdt/meson/core/IMesonConstants.java | 2 + .../plugin.properties | 2 + build/org.eclipse.cdt.meson.ui/plugin.xml | 58 +++++ .../internal/meson/ui/SWTImagesFactory.java | 103 ++++++++ .../commands/AbstractMesonCommandHandler.java | 242 ++++++++++++++++++ .../ui/commands/RunNinjaCommandHandler.java | 105 ++++++++ .../internal/meson/ui/wizards/RunNinja.java | 56 ++++ .../meson/ui/wizards/RunNinjaPage.java | 102 ++++++++ .../meson/ui/wizards/WizardMessages.java | 33 +++ .../ui/wizards/wizardmessages.properties | 17 ++ 14 files changed, 811 insertions(+), 56 deletions(-) create mode 100644 build/org.eclipse.cdt.meson.core/src/org/eclipse/cdt/internal/meson/core/MesonUtils.java create mode 100644 build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/SWTImagesFactory.java create mode 100644 build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/commands/AbstractMesonCommandHandler.java create mode 100644 build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/commands/RunNinjaCommandHandler.java create mode 100644 build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/wizards/RunNinja.java create mode 100644 build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/wizards/RunNinjaPage.java create mode 100644 build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/wizards/WizardMessages.java create mode 100644 build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/wizards/wizardmessages.properties diff --git a/build/org.eclipse.cdt.meson.core/src/org/eclipse/cdt/internal/meson/core/MesonBuildConfiguration.java b/build/org.eclipse.cdt.meson.core/src/org/eclipse/cdt/internal/meson/core/MesonBuildConfiguration.java index 5be0271507d..8a7891da603 100644 --- a/build/org.eclipse.cdt.meson.core/src/org/eclipse/cdt/internal/meson/core/MesonBuildConfiguration.java +++ b/build/org.eclipse.cdt.meson.core/src/org/eclipse/cdt/internal/meson/core/MesonBuildConfiguration.java @@ -17,8 +17,6 @@ import java.util.Arrays; 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.core.CommandLauncherManager; import org.eclipse.cdt.core.ConsoleOutputStream; @@ -102,8 +100,13 @@ public class MesonBuildConfiguration extends CBuildConfiguration { @Override public IProject[] build(int kind, Map args, IConsole console, IProgressMonitor monitor) throws CoreException { + return build(kind, args, null, null, console, monitor); + } + + public IProject[] build(int kind, Map args, String[] ninjaEnv, String[] ninjaArgs, IConsole console, IProgressMonitor monitor) + throws CoreException { IProject project = getProject(); - + try { project.deleteMarkers(ICModelMarker.C_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); @@ -117,7 +120,7 @@ public class MesonBuildConfiguration extends CBuildConfiguration { if (toolChainFile == null && !isLocal()) { IMesonToolChainManager manager = Activator.getService(IMesonToolChainManager.class); toolChainFile = manager.getToolChainFileFor(getToolChain()); - + if (toolChainFile == null) { // error console.getErrorStream().write(Messages.MesonBuildConfiguration_NoToolchainFile); @@ -133,12 +136,12 @@ public class MesonBuildConfiguration extends CBuildConfiguration { } List argsList = new ArrayList<>(); - + String userArgs = getProperty(IMesonConstants.MESON_ARGUMENTS); if (userArgs != null) { argsList.addAll(Arrays.asList(userArgs.trim().split("\\s+"))); //$NON-NLS-1$ } - + argsList.add(getBuildDirectory().toString()); Map envMap = System.getenv(); @@ -148,25 +151,25 @@ public class MesonBuildConfiguration extends CBuildConfiguration { } String envStr = getProperty(IMesonConstants.MESON_ENV); if (envStr != null) { - envList.addAll(stripEnvVars(envStr)); + envList.addAll(MesonUtils.stripEnvVars(envStr)); } String[] env = envList.toArray(new String[0]); ICommandLauncher launcher = CommandLauncherManager.getInstance().getCommandLauncher(this); - + launcher.setProject(getProject()); if (launcher instanceof ICBuildCommandLauncher) { ((ICBuildCommandLauncher)launcher).setBuildConfiguration(this); } monitor.subTask(Messages.MesonBuildConfiguration_RunningMeson); - + org.eclipse.core.runtime.Path mesonPath = new org.eclipse.core.runtime.Path(path.toString()); outStream.write(String.join(" ", envStr != null ? envStr : "", //$NON-NLS-1$ //$NON-NLS-2$ mesonPath.toString(), userArgs, "\n")); //$NON-NLS-1$ outStream.write(getBuildDirectory() + "\n"); //$NON-NLS-1$ org.eclipse.core.runtime.Path workingDir = new org.eclipse.core.runtime.Path(getBuildDirectory().getParent().getParent().toString()); - + launcher.execute(mesonPath, argsList.toArray(new String[0]), env, workingDir, monitor); if (launcher.waitAndRead(outStream, outStream, SubMonitor.convert(monitor)) != ICommandLauncher.OK) { String errMsg = launcher.getErrorMessage(); @@ -178,28 +181,39 @@ public class MesonBuildConfiguration extends CBuildConfiguration { try (ErrorParserManager epm = new ErrorParserManager(project, getBuildDirectoryURI(), this, getToolChain().getErrorParserIds())) { epm.setOutputStream(console.getOutputStream()); - + String buildCommand = getProperty(IMesonConstants.BUILD_COMMAND); if (buildCommand == null || buildCommand.isEmpty()) { buildCommand = "ninja"; } - String[] env = new String[0]; + Map envMap = System.getenv(); + List envList = new ArrayList<>(); + for (Map.Entry entry : envMap.entrySet()) { + envList.add(entry.getKey() + "=" + entry.getValue()); + } + if (ninjaEnv != null) { + envList.addAll(Arrays.asList(ninjaEnv)); + } + String[] env = envList.toArray(new String[0]); ICommandLauncher launcher = CommandLauncherManager.getInstance().getCommandLauncher(this); - + launcher.setProject(getProject()); if (launcher instanceof ICBuildCommandLauncher) { ((ICBuildCommandLauncher)launcher).setBuildConfiguration(this); } - monitor.subTask(Messages.MesonBuildConfiguration_RunningMeson); - + monitor.subTask(Messages.MesonBuildConfiguration_RunningNinja); + org.eclipse.core.runtime.Path ninjaPath = new org.eclipse.core.runtime.Path(buildCommand); - outStream.write(String.join(" ", ninjaPath.toString() + '\n')); //$NON-NLS-1$ org.eclipse.core.runtime.Path workingDir = new org.eclipse.core.runtime.Path(getBuildDirectory().toString()); - launcher.execute(ninjaPath, new String[] {"-v"}, env, workingDir, monitor); //$NON-NLS-1$ + if (ninjaArgs == null) { + ninjaArgs = new String[] {"-v"}; //$NON-NLS-1$ + } + + launcher.execute(ninjaPath, ninjaArgs, env, workingDir, monitor); if (launcher.waitAndRead(epm.getOutputStream(), epm.getOutputStream(), SubMonitor.convert(monitor)) != ICommandLauncher.OK) { String errMsg = launcher.getErrorMessage(); console.getErrorStream().write(String.format(Messages.MesonBuildConfiguration_RunningNinjaFailure, errMsg)); @@ -300,44 +314,6 @@ public class MesonBuildConfiguration extends CBuildConfiguration { } } - /** - * 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 List stripEnvVars(String envString) { - Pattern p1 = Pattern.compile("(\\w+[=]\\\".*?\\\").*"); //$NON-NLS-1$ - Pattern p2 = Pattern.compile("(\\w+[=]'.*?').*"); //$NON-NLS-1$ - Pattern p3 = Pattern.compile("(\\w+[=][^\\s]+).*"); //$NON-NLS-1$ - boolean finished = false; - List envVars = new ArrayList<>(); - while (!finished) { - Matcher m1 = p1.matcher(envString); - if (m1.matches()) { - envString = envString.replaceFirst("\\w+[=]\\\".*?\\\"","").trim(); //$NON-NLS-1$ //$NON-NLS-2$ - String s = m1.group(1).trim(); - envVars.add(s.replaceAll("\\\"", "")); //$NON-NLS-1$ //$NON-NLS-2$ - } else { - Matcher m2 = p2.matcher(envString); - if (m2.matches()) { - envString = envString.replaceFirst("\\w+[=]'.*?'", "").trim(); //$NON-NLS-1$ //$NON-NLS-2$ - String s = m2.group(1).trim(); - envVars.add(s.replaceAll("'", "")); //$NON-NLS-1$ //$NON-NLS-2$ - } else { - Matcher m3 = p3.matcher(envString); - if (m3.matches()) { - envString = envString.replaceFirst("\\w+[=][^\\s]+", "").trim(); //$NON-NLS-1$ //$NON-NLS-2$ - envVars.add(m3.group(1).trim()); - } else { - finished = true; - } - } - } - } - return envVars; - } + } diff --git a/build/org.eclipse.cdt.meson.core/src/org/eclipse/cdt/internal/meson/core/MesonUtils.java b/build/org.eclipse.cdt.meson.core/src/org/eclipse/cdt/internal/meson/core/MesonUtils.java new file mode 100644 index 00000000000..bcde5ca8692 --- /dev/null +++ b/build/org.eclipse.cdt.meson.core/src/org/eclipse/cdt/internal/meson/core/MesonUtils.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2018 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 + * + * Red Hat Inc. - initial version + *******************************************************************************/ +package org.eclipse.cdt.internal.meson.core; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class MesonUtils { + + /** + * Parse a string containing environment variables into individual VAR=VALUE pairs. + * + * @param envString - String to parse + * @return List of var=value Strings + */ + public static List stripEnvVars(String envString) { + Pattern p1 = Pattern.compile("(\\w+[=]\\\".*?\\\").*"); //$NON-NLS-1$ + Pattern p2 = Pattern.compile("(\\w+[=]'.*?').*"); //$NON-NLS-1$ + Pattern p3 = Pattern.compile("(\\w+[=][^\\s]+).*"); //$NON-NLS-1$ + boolean finished = false; + List envVars = new ArrayList<>(); + while (!finished) { + Matcher m1 = p1.matcher(envString); + if (m1.matches()) { + envString = envString.replaceFirst("\\w+[=]\\\".*?\\\"","").trim(); //$NON-NLS-1$ //$NON-NLS-2$ + String s = m1.group(1).trim(); + envVars.add(s.replaceAll("\\\"", "")); //$NON-NLS-1$ //$NON-NLS-2$ + } else { + Matcher m2 = p2.matcher(envString); + if (m2.matches()) { + envString = envString.replaceFirst("\\w+[=]'.*?'", "").trim(); //$NON-NLS-1$ //$NON-NLS-2$ + String s = m2.group(1).trim(); + envVars.add(s.replaceAll("'", "")); //$NON-NLS-1$ //$NON-NLS-2$ + } else { + Matcher m3 = p3.matcher(envString); + if (m3.matches()) { + envString = envString.replaceFirst("\\w+[=][^\\s]+", "").trim(); //$NON-NLS-1$ //$NON-NLS-2$ + envVars.add(m3.group(1).trim()); + } else { + finished = true; + } + } + } + } + return envVars; + } + +} diff --git a/build/org.eclipse.cdt.meson.core/src/org/eclipse/cdt/internal/meson/core/Messages.java b/build/org.eclipse.cdt.meson.core/src/org/eclipse/cdt/internal/meson/core/Messages.java index e3057822ef4..9af99b24a19 100644 --- a/build/org.eclipse.cdt.meson.core/src/org/eclipse/cdt/internal/meson/core/Messages.java +++ b/build/org.eclipse.cdt.meson.core/src/org/eclipse/cdt/internal/meson/core/Messages.java @@ -19,6 +19,7 @@ public class Messages extends NLS { public static String MesonBuildConfiguration_BuildingComplete; public static String MesonBuildConfiguration_Cleaning; public static String MesonBuildConfiguration_RunningMeson; + public static String MesonBuildConfiguration_RunningNinja; public static String MesonBuildConfiguration_RunningMesonFailure; public static String MesonBuildConfiguration_RunningNinjaFailure; public static String MesonBuildConfiguration_NoToolchainFile; diff --git a/build/org.eclipse.cdt.meson.core/src/org/eclipse/cdt/internal/meson/core/messages.properties b/build/org.eclipse.cdt.meson.core/src/org/eclipse/cdt/internal/meson/core/messages.properties index 44cdce29493..41116f97137 100644 --- a/build/org.eclipse.cdt.meson.core/src/org/eclipse/cdt/internal/meson/core/messages.properties +++ b/build/org.eclipse.cdt.meson.core/src/org/eclipse/cdt/internal/meson/core/messages.properties @@ -12,6 +12,7 @@ MesonBuildConfiguration_BuildingIn=Building in: %s\n MesonBuildConfiguration_BuildingComplete=Build complete: %s\n MesonBuildConfiguration_Cleaning=Cleaning %s MesonBuildConfiguration_RunningMeson=Running meson +MesonBuildConfiguration_RunningNinja=Running ninja MesonBuildConfiguration_ProcCompCmds=Processing compile commands %s MesonBuildConfiguration_ProcCompJson=Processing compile_commands.json MesonBuildConfiguration_NoToolchainFile=No toolchain file found for this target. diff --git a/build/org.eclipse.cdt.meson.core/src/org/eclipse/cdt/meson/core/IMesonConstants.java b/build/org.eclipse.cdt.meson.core/src/org/eclipse/cdt/meson/core/IMesonConstants.java index 696e27f5ee3..e2aa905a9bb 100644 --- a/build/org.eclipse.cdt.meson.core/src/org/eclipse/cdt/meson/core/IMesonConstants.java +++ b/build/org.eclipse.cdt.meson.core/src/org/eclipse/cdt/meson/core/IMesonConstants.java @@ -14,6 +14,8 @@ public interface IMesonConstants { public static final String MESON_ARGUMENTS = "meson.arguments"; //$NON-NLS-1$ public static final String MESON_ENV = "meson.environment"; //$NON-NLS-1$ + public static final String NINJA_ENV = "meson.ninja.environment"; //$NON-NLS-1$ + public static final String NINJA_ARGUMENTS = "meson.ninja.arguments"; //$NON-NLS-1$ public static final String MESON_ENV_SEPARATOR = "|"; //$NON-NLS-1$ String MESON_GENERATOR = "meson.generator"; //$NON-NLS-1$ String BUILD_COMMAND = "meson.command.build"; //$NON-NLS-1$ diff --git a/build/org.eclipse.cdt.meson.ui/plugin.properties b/build/org.eclipse.cdt.meson.ui/plugin.properties index 2987c590423..a42e8b3bc50 100644 --- a/build/org.eclipse.cdt.meson.ui/plugin.properties +++ b/build/org.eclipse.cdt.meson.ui/plugin.properties @@ -7,3 +7,5 @@ ############################################################################### meson.preferences.name = Meson +meson.run.ninja.label=Run ninja +meson.run.ninja.mnemonic=j diff --git a/build/org.eclipse.cdt.meson.ui/plugin.xml b/build/org.eclipse.cdt.meson.ui/plugin.xml index e9ea3f91911..08b35350319 100644 --- a/build/org.eclipse.cdt.meson.ui/plugin.xml +++ b/build/org.eclipse.cdt.meson.ui/plugin.xml @@ -74,5 +74,63 @@ tabClass="org.eclipse.cdt.internal.meson.ui.MesonBuildTab"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/SWTImagesFactory.java b/build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/SWTImagesFactory.java new file mode 100644 index 00000000000..72a7f02911b --- /dev/null +++ b/build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/SWTImagesFactory.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2018 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 Inc. - initial implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.meson.ui; + +import java.net.MalformedURLException; +import java.net.URL; + +import org.eclipse.cdt.meson.ui.Activator; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.resource.ImageRegistry; +import org.eclipse.swt.graphics.Image; + +public class SWTImagesFactory { + // The plug-in registry + private static ImageRegistry imageRegistry = Activator.getPlugin() + .getImageRegistry(); + + // Sub-directory (under the package containing this class) where 16 color + // images are + private static URL fgIconBaseURL; + + static { + try { + fgIconBaseURL = new URL(Activator.getPlugin().getBundle() + .getEntry("/"), "icons/"); //$NON-NLS-1$ //$NON-NLS-2$ + } catch (MalformedURLException e) { + Activator.log(e); + } + } + private static final String NAME_PREFIX = Activator.PLUGIN_ID + '.'; + private static final int NAME_PREFIX_LENGTH = NAME_PREFIX.length(); + public static final String IMG_MESON = NAME_PREFIX + "meson-logo.png"; //$NON-NLS-1$ + public static final ImageDescriptor DESC_MESON = createManaged("", + IMG_MESON); + + private static ImageDescriptor createManaged(String prefix, String name) { + return createManaged(imageRegistry, prefix, name); + } + + private static ImageDescriptor createManaged(ImageRegistry registry, + String prefix, String name) { + ImageDescriptor result = ImageDescriptor.createFromURL(makeIconFileURL( + prefix, name.substring(NAME_PREFIX_LENGTH))); + registry.put(name, result); + return result; + } + + public static Image get(String key) { + return imageRegistry.get(key); + } + + private static ImageDescriptor create(String prefix, String name) { + return ImageDescriptor.createFromURL(makeIconFileURL(prefix, name)); + } + + private static URL makeIconFileURL(String prefix, String name) { + StringBuffer buffer = new StringBuffer(prefix); + buffer.append(name); + try { + return new URL(fgIconBaseURL, buffer.toString()); + } catch (MalformedURLException e) { + Activator.log(e); + return null; + } + } + + /** + * Sets all available image descriptors for the given action. + * + * @param action + * - action + * @param type + * - type of image descriptor + * @param relPath + * - relative path + */ + public static void setImageDescriptors(IAction action, String type, + String relPath) { + if (relPath.startsWith(NAME_PREFIX)) + relPath = relPath.substring(NAME_PREFIX_LENGTH); + action.setDisabledImageDescriptor(create("d" + type, relPath)); //$NON-NLS-1$ + action.setImageDescriptor(create("e" + type, relPath)); //$NON-NLS-1$ + + } + + /** + * Helper method to access the image registry from the CUIPlugin class. + */ + static ImageRegistry getImageRegistry() { + return imageRegistry; + } + +} + diff --git a/build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/commands/AbstractMesonCommandHandler.java b/build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/commands/AbstractMesonCommandHandler.java new file mode 100644 index 00000000000..ea3b887e28f --- /dev/null +++ b/build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/commands/AbstractMesonCommandHandler.java @@ -0,0 +1,242 @@ +/******************************************************************************* + * Copyright (c) 2018 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 + *******************************************************************************/ +package org.eclipse.cdt.internal.meson.ui.commands; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.StringTokenizer; + +import org.eclipse.cdt.core.model.ICContainer; +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.IPath; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.handlers.HandlerUtil; + +public abstract class AbstractMesonCommandHandler extends AbstractHandler { + + private IContainer fContainer; + + protected abstract void run(Shell activeShell); + + protected Object execute1(ExecutionEvent event) { + ISelection k = HandlerUtil.getCurrentSelection(event); + if (!k.isEmpty() && k instanceof IStructuredSelection) { + Object obj = ((IStructuredSelection)k).getFirstElement(); + IContainer container = getContainer(obj); + if (container != null) { + setSelectedContainer(container); + run(HandlerUtil.getActiveShell(event)); + } + } + return null; + } + + @SuppressWarnings("unchecked") + protected IContainer getContainer(Object obj) { + IContainer fContainer = null; + + if (obj instanceof Collection) { + Collection c = (Collection)obj; + Object[] objArray = c.toArray(); + if (objArray.length > 0) + obj = objArray[0]; + } + if (obj instanceof ICElement) { + if ( obj instanceof ICContainer || obj instanceof ICProject) { + fContainer = (IContainer) ((ICElement) obj).getUnderlyingResource(); + } else { + obj = ((ICElement)obj).getResource(); + if ( obj != null) { + fContainer = ((IResource)obj).getParent(); + } + } + } else if (obj instanceof IResource) { + if (obj instanceof IContainer) { + fContainer = (IContainer) obj; + } else { + fContainer = ((IResource)obj).getParent(); + } + } else { + fContainer = null; + } + return fContainer; + } + + public final String SHELL_COMMAND = "sh"; //$NON-NLS-1$ + + protected void showError(String title, String content) { + MessageDialog.openError(new Shell(), title, content); + } + + /** + * Separate targets to array from a string. + * + * @param rawArgList + * @return targets in string[] array. if targets are not formatted properly, + * returns null + */ + protected List separateTargets(String rawArgList) { + + StringTokenizer st = new StringTokenizer(rawArgList, " "); //$NON-NLS-1$ + List targetList = new ArrayList<>(); + + while (st.hasMoreTokens()) { + String currentWord = st.nextToken().trim(); + + if (currentWord.startsWith("'")) { //$NON-NLS-1$ + StringBuilder tmpTarget = new StringBuilder(); + while (!currentWord.endsWith("'")) { //$NON-NLS-1$ + tmpTarget.append(currentWord).append(' '); + if (!st.hasMoreTokens()) { + // quote not closed properly, so return null + return null; + } + currentWord = st.nextToken().trim(); + } + + tmpTarget.append(currentWord); + targetList.add(tmpTarget.toString()); + continue; + } + + if (currentWord.startsWith("\"")) { //$NON-NLS-1$ + StringBuilder tmpTarget = new StringBuilder(); + while (!currentWord.endsWith("\"")) { //$NON-NLS-1$ + tmpTarget.append(currentWord).append(' '); + if (!st.hasMoreTokens()) { + // double quote not closed properly, so return null + return null; + } + currentWord = st.nextToken().trim(); + } + + tmpTarget.append(currentWord); + targetList.add(tmpTarget.toString()); + continue; + } + + // for targets without quote/double quotes. + targetList.add(currentWord); + + } + + return targetList; + } + + protected List separateOptions(String rawArgList) { + List argList = new ArrayList<>(); + // May be multiple user-specified options in which case we + // need to split them up into individual options + rawArgList = rawArgList.trim(); + boolean finished = false; + int lastIndex = rawArgList.indexOf("--"); //$NON-NLS-1$ + if (lastIndex != -1) { + while (!finished) { + int index = rawArgList.indexOf("--", lastIndex + 2); //$NON-NLS-1$ + if (index != -1) { + String previous = rawArgList.substring(lastIndex, index).trim(); + argList.add(previous); + rawArgList = rawArgList.substring(index); + } else { + argList.add(rawArgList); + finished = true; + } + } + } + + return argList; + + } + + protected List simpleParseOptions(String rawArgList) { + List argList = new ArrayList<>(); + int lastArgIndex = -1; + int i = 0; + while (i < rawArgList.length()) { + char ch = rawArgList.charAt(i); + // Skip white-space + while (Character.isWhitespace(ch)) { + ++i; + if (i < rawArgList.length()) + ch = rawArgList.charAt(i); + else // Otherwise we are done + return argList; + } + + // Simplistic parser. We break up into strings delimited + // by blanks. If quotes are used, we ignore blanks within. + // If a backslash is used, we ignore the next character and + // pass it through. + lastArgIndex = i; + boolean inString = false; + while (i < rawArgList.length()) { + ch = rawArgList.charAt(i); + if (ch == '\\') // escape character + ++i; // skip over the next character + else if (ch == '\"') { // double quotes + inString = !inString; + } else if (Character.isWhitespace(ch)) { + if (!inString) { + argList.add(rawArgList.substring(lastArgIndex, i)); + break; + } + } + ++i; + } + // Look for the case where we ran out of chars for the last + // token. + if (i >= rawArgList.length()) + argList.add(rawArgList.substring(lastArgIndex)); + ++i; + } + return argList; + } + + protected IPath getExecDir(IContainer container) { + int type = container.getType(); + IPath execDir = null; + if (type == IResource.FILE) { + execDir = container.getLocation().removeLastSegments(1); + } else { + execDir = container.getLocation(); + } + return execDir; + } + + protected IPath getCWD(IContainer container) { + int type = container.getType(); + IPath cwd = null; + if (type == IResource.FILE) { + cwd = container.getFullPath().removeLastSegments(1); + } else { + cwd = container.getFullPath(); + } + return cwd; + } + + protected IContainer getSelectedContainer() { + return fContainer; + } + + public void setSelectedContainer(IContainer container) { + fContainer = container; + } + +} diff --git a/build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/commands/RunNinjaCommandHandler.java b/build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/commands/RunNinjaCommandHandler.java new file mode 100644 index 00000000000..0b1f09fa8b7 --- /dev/null +++ b/build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/commands/RunNinjaCommandHandler.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2018 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 Inc. - initial implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.meson.ui.commands; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.build.CBuildConfiguration; +import org.eclipse.cdt.core.build.ICBuildConfiguration; +import org.eclipse.cdt.core.resources.IConsole; +import org.eclipse.cdt.internal.meson.core.MesonBuildConfiguration; +import org.eclipse.cdt.internal.meson.core.MesonUtils; +import org.eclipse.cdt.internal.meson.ui.wizards.RunNinja; +import org.eclipse.cdt.meson.core.IMesonConstants; +import org.eclipse.cdt.meson.ui.Activator; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IncrementalProjectBuilder; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.window.Window; +import org.eclipse.jface.wizard.WizardDialog; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +public class RunNinjaCommandHandler extends AbstractMesonCommandHandler { + + @Override + public Object execute(ExecutionEvent event) { + return execute1(event); + } + + @Override + public void run (Shell shell) { + // Set up console + IConsole console = CCorePlugin.getDefault().getConsole(); + IProject project = getSelectedContainer().getAdapter(IProject.class); + console.start(project); + try { + ICBuildConfiguration buildConfig = ((CBuildConfiguration)project.getActiveBuildConfig().getAdapter(ICBuildConfiguration.class)); + + if (buildConfig instanceof MesonBuildConfiguration) { + MesonBuildConfiguration config = (MesonBuildConfiguration)buildConfig; + RunNinja wizard = new RunNinja(buildConfig); + final WizardDialog dialog = new WizardDialog(shell, wizard); + Display.getDefault().syncExec(() -> { + dialog.create(); + dialog.open(); + }); + if (dialog.getReturnCode() == Window.OK) { + // Run ninja command in a Job so user can cancel if it stalls + Job buildJob = new Job("Running Ninja") { + @Override + public IStatus run(final IProgressMonitor monitor) { + + String envString = config.getProperty(IMesonConstants.NINJA_ENV); + String[] ninjaEnv = null; + if (envString != null) { + ninjaEnv = MesonUtils.stripEnvVars(envString).toArray(new String[0]); + } + + String argString = config.getProperty(IMesonConstants.NINJA_ARGUMENTS); + String[] ninjaArgs = null; + if (argString != null) { + List ninjaArgList = new ArrayList<>(); + Matcher m = Pattern.compile("([^\"]\\S*|\".+?\")\\s*").matcher(argString); + while (m.find()) { + ninjaArgList.add(m.group(1)); + } + ninjaArgs = ninjaArgList.toArray(new String[0]); + } + try { + config.build(IncrementalProjectBuilder.FULL_BUILD, null, ninjaEnv, ninjaArgs, console, monitor); + if (monitor.isCanceled()) { + return Status.CANCEL_STATUS; + } + } catch (CoreException e) { + Activator.log(e); + } + return Status.OK_STATUS; + } + }; + buildJob.schedule(); + } + } + } catch (CoreException e) { + Activator.log(e); + } + } + +} diff --git a/build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/wizards/RunNinja.java b/build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/wizards/RunNinja.java new file mode 100644 index 00000000000..a7172f6a497 --- /dev/null +++ b/build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/wizards/RunNinja.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2018 Red Hat Inc. + * 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 Inc. - Initial Contribution + *******************************************************************************/ +package org.eclipse.cdt.internal.meson.ui.wizards; + +import org.eclipse.cdt.core.build.ICBuildConfiguration; +import org.eclipse.cdt.meson.core.IMesonConstants; +import org.eclipse.jface.wizard.Wizard; + +public class RunNinja extends Wizard { + + private RunNinjaPage mainPage; + private ICBuildConfiguration config; + private String envStr; + private String ninjaArgs; + + public RunNinja(ICBuildConfiguration config) { + this.config = config; + } + + @Override + public void addPages() { + mainPage = new RunNinjaPage(config); + addPage(mainPage); + } + + @Override + public boolean canFinish() { + return mainPage.isPageComplete(); + } + + @Override + public boolean performFinish() { + envStr = mainPage.getEnvStr(); + config.setProperty(IMesonConstants.NINJA_ENV, envStr); + ninjaArgs = mainPage.getNinjaArgs(); + config.setProperty(IMesonConstants.NINJA_ARGUMENTS, ninjaArgs); + return true; + } + + public String getEnvStr() { + return envStr; + } + + public String getNinjaArgs() { + return ninjaArgs; + } + +} diff --git a/build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/wizards/RunNinjaPage.java b/build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/wizards/RunNinjaPage.java new file mode 100644 index 00000000000..466bbc7c2bf --- /dev/null +++ b/build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/wizards/RunNinjaPage.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 2018 Red Hat Inc + * 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 Inc. - Initial Contribution + *******************************************************************************/ +package org.eclipse.cdt.internal.meson.ui.wizards; + +import org.eclipse.cdt.core.build.ICBuildConfiguration; +import org.eclipse.cdt.internal.meson.ui.SWTImagesFactory; +import org.eclipse.cdt.meson.core.IMesonConstants; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +/** + * A standard file selection dialog which solicits a list of files from the user. + * The getResult method returns the selected files. + *

+ * This class may be instantiated; it is not intended to be subclassed. + *

+ *

+ * Example: + *

+ *	FileSelectionDialog dialog =
+ *		new FileSelectionDialog(getShell(), rootElement, msg);
+ *	dialog.setInitialSelections(selectedResources);
+ *	dialog.open();
+ *	return dialog.getResult();
+ * 
+ *

+ * @noextend This class is not intended to be subclassed by clients. + */ +public class RunNinjaPage extends WizardPage { + + private ICBuildConfiguration config; + private Text envText; + private Text ninjaArgs; + + public RunNinjaPage(ICBuildConfiguration config) { + super(WizardMessages.RunNinjaPage_name); + setDescription(WizardMessages.RunNinjaPage_description); + setTitle(WizardMessages.RunNinjaPage_title); + setImageDescriptor(SWTImagesFactory.DESC_MESON); + this.config = config; + } + + @Override + public void createControl(Composite parent) { + + Composite composite = new Composite(parent, SWT.NONE); + composite.setLayoutData(new GridData(GridData.FILL_BOTH)); + composite.setLayout(new GridLayout(1, true)); + + Label envLabel = new Label(composite, SWT.NONE); + envLabel.setText(WizardMessages.RunNinjaPage_env_label); + envLabel.setLayoutData(new GridData()); + + envText = new Text(composite, SWT.BORDER); + String lastEnv = config.getProperty(IMesonConstants.NINJA_ENV); + if (lastEnv == null) { + lastEnv = ""; //$NON-NLS-1$ + } + envText.setToolTipText(WizardMessages.RunNinjaPage_env_description); + envText.setText(lastEnv); + GridData gdata = new GridData(SWT.FILL, SWT.FILL, true, false); + envText.setLayoutData(gdata); + + Label argLabel = new Label(composite, SWT.NONE); + argLabel.setText(WizardMessages.RunNinjaPage_options_label); + argLabel.setLayoutData(new GridData()); + + ninjaArgs = new Text(composite, SWT.BORDER); + String lastNinjaArgs = config.getProperty(IMesonConstants.NINJA_ARGUMENTS); + if (lastNinjaArgs == null) { + lastNinjaArgs = ""; //$NON-NLS-1$ + } + ninjaArgs.setToolTipText(WizardMessages.RunNinjaPage_options_description); + ninjaArgs.setText(lastNinjaArgs); + GridData gdata2 = new GridData(SWT.FILL, SWT.FILL, true, false); + ninjaArgs.setLayoutData(gdata2); + + setControl(composite); + } + + public String getEnvStr() { + return envText.getText(); + } + + public String getNinjaArgs() { + return ninjaArgs.getText(); + } + +} diff --git a/build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/wizards/WizardMessages.java b/build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/wizards/WizardMessages.java new file mode 100644 index 00000000000..51c2d3c9880 --- /dev/null +++ b/build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/wizards/WizardMessages.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2018 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 Inc. - initial implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.meson.ui.wizards; + +import org.eclipse.osgi.util.NLS; + +public class WizardMessages extends NLS { + + public static String RunNinjaPage_name; + public static String RunNinjaPage_description; + public static String RunNinjaPage_title; + public static String RunNinjaPage_env_label; + public static String RunNinjaPage_env_description; + public static String RunNinjaPage_options_label; + public static String RunNinjaPage_options_description; + + static { + // initialize resource bundle + NLS.initializeMessages("org.eclipse.cdt.internal.meson.ui.wizards.wizardmessages", WizardMessages.class); //$NON-NLS-1$ + } + + private WizardMessages() { + } +} + diff --git a/build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/wizards/wizardmessages.properties b/build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/wizards/wizardmessages.properties new file mode 100644 index 00000000000..aa21ebd06a5 --- /dev/null +++ b/build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/wizards/wizardmessages.properties @@ -0,0 +1,17 @@ +################################################################################ +# Copyright (c) 2018 Red Hat Inc. +# 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 Inc. - Initial Contribution +################################################################################ +RunNinjaPage_name=Run ninja +RunNinjaPage_description=Run ninja manually for the active build configuration +RunNinjaPage_title=Run ninja manually +RunNinjaPage_env_label=Environment: +RunNinjaPage_env_description=Environment variables for ninja command +RunNinjaPage_options_label=Options: +RunNinjaPage_options_description=Options to pass to ninja command \ No newline at end of file