From c96d126b86382356caec9b8ee961e37b84313f6b Mon Sep 17 00:00:00 2001 From: Jeff Johnston Date: Thu, 9 Feb 2017 18:43:05 -0500 Subject: [PATCH] Bug 513589 - Add support to build CDT projects in a Docker Container - add IOptionalBuildObjectPropertiesContainer interface to use for objects that supply optional build properties - add new IOptionalBuildProperties interface that defines optional build properties donated by external plug-ins - add new - change IConfiguration to an IOptionalBuildObjectPropertiesContainer - change IManagedProject to be an IOptionalBuildObjectPropertiesContainer - fix ProcessClosure to ensure that readers are not null before accessing them - fix Container launch delegate to look at project optional build properties for active configuration to fetch connection and image info and use said info to find a matching launch or create a new one - have Container launch delegate use the image name as part of the launch config name - have Container launch short-cut also use the project's optional build properties for the active config to get connection and image information before any defaulting - change AutotoolsNewMarkerGenerator to store the command launcher as an ICommandLauncher - add new CommandLauncherFactory extension to cdt.core that allows plug-ins to specify a CommandLauncherFactory that will return an ICommandLauncher based on the project - add macros for new extension to CCorePlugin - add new CommandLauncherManager class that loads CommandLauncherFactory extensions and is used to give an ICommandLauncher wrapper that will go through the list of CommandLauncherFactory extensions until one returns non-null ICommandLauncher - add code to RemoteCommandLauncher so it will use the CommandLauncherManager to get the local launcher - also change RemoteCommandLauncher to check at execution time whether the command is local and in that case use the local command launcher - add new ICommandLauncherFactory interface - add new ContainerCommandLauncher to launch - add new ContainerCommandLauncherFactory class for returning a ContainerCommandLauncher instance to launch commands in a Docker Container - change MakeBuilder to use CommandLauncherManager to get its ICommandLauncher - change CommandBuilder to use CommandLauncherManager too - ditto for Builder and AbstractBuiltinSpecsDetector and ExternalToolInvoker - change Configuration to load/store optional build properties as well as return the properties to get/set - ditto for MultiConfiguration - change ManagedProject to implement IOptionalBuildOptionProperties interface - ditto for ProjectType - create new OptionalBuildProperties class to store optional build properties for a configuration - bump cdt.docker.launcher to 1.1.0 - use CommandLauncherFactory extension to define ContainerCommandLauncherFactory - add optional ContainerPropertyTab which allows the end-user to optionally choose to build a C/C++ project in a Container and specify the connection/image to use - in LanguageSettingsSerializableSettings class, call the CommandLauncherManager getLanguageSettingEntries method to get the massaged language setting entries based on the current list - in LanguageSettingsProviderSerializer, try and get the pooled entries using the cfg description so that it will have the project and can use the CommandLauncherManager to get entries from image - in ContainerCommandLauncherFactory move cached headers under a HEADERS directory in the plug-in area - create a sub-directory for the connection and a sub-directory for the image based on cleansed names - store the real names of the connection and image to use later in the DockerHeaderPreferencePage - modify LanguageSettingsEntriesTab to force the horizontal scroll bar to appear (this is a bug in SWT SashForm support and the fix here isn't quite correct, but is better) - add new DockerHeaderPreferencePage that allows user to remove cached headers from images - change C/C++ Docker preferences to be titled: Docker Container - fix LanguageSettingsWorkspaceProvider.getSettingEntries method to use the CommandLauncherManager so entries will be transformed to use cached headers - add BaseDatabindingModel class - add DataVolumeModel class to model a volume mount - add ContainerPropertyVolumes model to model volume specification and selected volumes - add properties to ContainerCommandLauncher to represent volumes and selected volumes for a configuration - add ContainerDataVolumeDialog for specifying a volume mount by the end-user - add a null detector for cfgDescription in LanguageSettingsSerializableProvider - fix AutotoolsNewMakeGenerator.getWinOSType to not specify "." for working dir - fix GCCBuiltinSpecsDetectorCygwin to not map paths to Cygwin if the current configuration is enabled for container build - add logic to ContainerCommandLauncher to look for Windows file formats and change them to unix format and map any "." working dir to be /tmp - fix ContainerLauncherConfigurationDelegate similarly - fix AbstractBuiltinSpecsDetector to pass in the current configuration description when getting the CommandLauncher since the current configuration may not be the active configuration - change ContainerPropertyTab to add Elf and GNU Elf binary parsers when build in Container is chosen so that output executables are treated as Binaries by the CDT project - add documentationl for the ContainerPropertyTab in Build Settings and the Data Volume dialog pop-up it brings up - change CommandBuilder to accept a project as an argument to its constructor and to pass this as an argument to the CommandLauncherManager - have StepBuilder pass project when creating a CommandBuilder Change-Id: Ia78488b93056e6ec7ca83a6c87b3a9d2b9424943 --- .../core/AutotoolsNewMakeGenerator.java | 6 +- .../cdt/autotools/tests/ProjectTools.java | 5 +- .../eclipse/cdt/make/core/MakeBuilder.java | 4 +- .../scannerconfig2/DefaultRunSIProvider.java | 4 +- .../IOptionalBuildProperties.java | 30 + .../core/IBuildObjectPropertiesContainer.java | 1 - .../managedbuilder/core/IConfiguration.java | 6 +- .../managedbuilder/core/IManagedProject.java | 6 +- ...ptionalBuildObjectPropertiesContainer.java | 21 + .../cdt/managedbuilder/core/IProjectType.java | 2 +- .../internal/buildmodel/CommandBuilder.java | 10 +- .../internal/buildmodel/StepBuilder.java | 6 +- .../managedbuilder/internal/core/Builder.java | 4 +- .../internal/core/Configuration.java | 43 + .../internal/core/ManagedProject.java | 28 + .../internal/core/MultiConfiguration.java | 9 + .../core/OptionalBuildProperties.java | 104 ++ .../internal/core/ProjectType.java | 24 + .../GCCBuiltinSpecsDetectorCygwin.java | 16 +- .../AbstractBuiltinSpecsDetector.java | 7 +- .../providers/GCCBuiltinSpecsDetector.java | 2 +- .../ui/tests/util/TestConfiguration.java | 8 + .../ui/tests/util/TestProjectType.java | 3 + .../externaltool/ExternalToolInvoker.java | 4 +- .../cdt/core/testplugin/CModelMock.java | 4 +- .../LanguageSettingsSerializableProvider.java | 8 + .../LanguageSettingsProvidersSerializer.java | 9 + core/org.eclipse.cdt.core/plugin.xml | 1 + .../schema/CommandLauncherFactory.exsd | 128 ++ .../src/org/eclipse/cdt/core/CCorePlugin.java | 11 + .../cdt/core/CommandLauncherManager.java | 288 +++++ .../cdt/core/ICommandLauncherFactory.java | 54 + .../cdt/internal/core/ProcessClosure.java | 16 +- .../providers/LanguageSettingsEntriesTab.java | 11 +- .../images/prop_container.png | Bin 0 -> 41949 bytes .../images/prop_datavolumes.png | Bin 0 -> 22638 bytes .../reference/cdt_u_prop_build.htm | 1 + .../cdt_u_prop_build_settings_artifact.htm | 1 + .../cdt_u_prop_build_settings_binparser.htm | 1 + .../cdt_u_prop_build_settings_container.htm | 166 +++ .../cdt_u_prop_build_settings_errparser.htm | 1 + .../cdt_u_prop_build_settings_steps.htm | 1 + .../cdt_u_prop_build_settings_tool.htm | 1 + .../reference/cdt_u_prop_data_volumes.htm | 149 +++ .../reference/cdt_u_properties.htm | 1 + .../META-INF/MANIFEST.MF | 11 +- .../icons/error_obj.gif | Bin 0 -> 339 bytes .../icons/file_obj.gif | Bin 0 -> 349 bytes .../icons/folder_closed.gif | Bin 0 -> 219 bytes .../icons/warning_obj.gif | Bin 0 -> 337 bytes .../plugin.properties | 10 +- .../plugin.xml | 27 +- .../ContainerCommandLauncherFactory.java | 265 +++++ .../docker/launcher/BaseDatabindingModel.java | 44 + .../launcher/ContainerCommandLauncher.java | 429 +++++++ .../launcher/ContainerDataVolumeDialog.java | 466 ++++++++ .../ContainerLaunchConfigurationDelegate.java | 67 +- .../docker/launcher/ContainerPropertyTab.java | 1053 +++++++++++++++++ .../ContainerPropertyVolumesModel.java | 142 +++ .../docker/launcher/ContainerTab.java | 7 +- .../docker/launcher/DataVolumeModel.java | 347 ++++++ .../docker/launcher/LaunchShortcut.java | 102 +- .../internal/docker/launcher/Messages.java | 15 + .../docker/launcher/SWTImagesFactory.java | 13 + .../docker/launcher/messages.properties | 15 + .../DockerHeaderPreferencePage.java | 356 ++++++ .../remote/core/RemoteCommandLauncher.java | 35 +- 67 files changed, 4543 insertions(+), 66 deletions(-) create mode 100644 build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/buildproperties/IOptionalBuildProperties.java create mode 100644 build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/IOptionalBuildObjectPropertiesContainer.java create mode 100644 build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/OptionalBuildProperties.java create mode 100644 core/org.eclipse.cdt.core/schema/CommandLauncherFactory.exsd create mode 100644 core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CommandLauncherManager.java create mode 100644 core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ICommandLauncherFactory.java create mode 100644 doc/org.eclipse.cdt.doc.user/images/prop_container.png create mode 100644 doc/org.eclipse.cdt.doc.user/images/prop_datavolumes.png create mode 100644 doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_build_settings_container.htm create mode 100644 doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_data_volumes.htm create mode 100644 launch/org.eclipse.cdt.docker.launcher/icons/error_obj.gif create mode 100644 launch/org.eclipse.cdt.docker.launcher/icons/file_obj.gif create mode 100644 launch/org.eclipse.cdt.docker.launcher/icons/folder_closed.gif create mode 100644 launch/org.eclipse.cdt.docker.launcher/icons/warning_obj.gif create mode 100644 launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/docker/launcher/ContainerCommandLauncherFactory.java create mode 100644 launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/BaseDatabindingModel.java create mode 100644 launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerCommandLauncher.java create mode 100644 launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerDataVolumeDialog.java create mode 100644 launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerPropertyTab.java create mode 100644 launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerPropertyVolumesModel.java create mode 100644 launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/DataVolumeModel.java create mode 100644 launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ui/preferences/DockerHeaderPreferencePage.java diff --git a/build/org.eclipse.cdt.autotools.core/src/org/eclipse/cdt/internal/autotools/core/AutotoolsNewMakeGenerator.java b/build/org.eclipse.cdt.autotools.core/src/org/eclipse/cdt/internal/autotools/core/AutotoolsNewMakeGenerator.java index 813fd3b4b50..b94eab734ec 100644 --- a/build/org.eclipse.cdt.autotools.core/src/org/eclipse/cdt/internal/autotools/core/AutotoolsNewMakeGenerator.java +++ b/build/org.eclipse.cdt.autotools.core/src/org/eclipse/cdt/internal/autotools/core/AutotoolsNewMakeGenerator.java @@ -893,7 +893,7 @@ public class AutotoolsNewMakeGenerator extends MarkerGenerator { consoleOutStream.flush(); // Get a launcher for the config command - RemoteCommandLauncher launcher = new RemoteCommandLauncher(); + ICommandLauncher launcher = new RemoteCommandLauncher(); launcher.setProject(project); // Set the environment IEnvironmentVariable variables[] = @@ -1042,7 +1042,7 @@ public class AutotoolsNewMakeGenerator extends MarkerGenerator { new Path(SHELL_COMMAND), //$NON-NLS-1$ new String[] { "-c", "echo $OSTYPE" }, //$NON-NLS-1$ //$NON-NLS-2$ env, - new Path("."), //$NON-NLS-1$ + buildLocation, SubMonitor.convert(monitor)); if (launcher.waitAndRead(out, out) == ICommandLauncher.OK) winOSType = out.toString().trim(); @@ -1207,7 +1207,7 @@ public class AutotoolsNewMakeGenerator extends MarkerGenerator { consoleOutStream.flush(); // Get a launcher for the config command - RemoteCommandLauncher launcher = new RemoteCommandLauncher(); + ICommandLauncher launcher = new RemoteCommandLauncher(); launcher.setProject(project); // Set the environment IEnvironmentVariable variables[] = diff --git a/build/org.eclipse.cdt.autotools.tests/src/org/eclipse/cdt/autotools/tests/ProjectTools.java b/build/org.eclipse.cdt.autotools.tests/src/org/eclipse/cdt/autotools/tests/ProjectTools.java index ad5aad0d3bd..8cbe618ac7d 100644 --- a/build/org.eclipse.cdt.autotools.tests/src/org/eclipse/cdt/autotools/tests/ProjectTools.java +++ b/build/org.eclipse.cdt.autotools.tests/src/org/eclipse/cdt/autotools/tests/ProjectTools.java @@ -18,6 +18,8 @@ import java.util.zip.ZipFile; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.CommandLauncher; +import org.eclipse.cdt.core.CommandLauncherManager; +import org.eclipse.cdt.core.ICommandLauncher; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.internal.autotools.core.configure.AutotoolsConfigurationManager; import org.eclipse.cdt.internal.autotools.core.configure.IAConfiguration; @@ -129,7 +131,8 @@ public class ProjectTools { */ public static boolean markExecutable(IProject project, String filePath) { // Get a launcher for the config command - CommandLauncher launcher = new CommandLauncher(); + ICommandLauncher launcher = CommandLauncherManager.getInstance().getCommandLauncher(); + launcher.setProject(project); OutputStream stdout = new ByteArrayOutputStream(); OutputStream stderr = new ByteArrayOutputStream(); diff --git a/build/org.eclipse.cdt.make.core/src/org/eclipse/cdt/make/core/MakeBuilder.java b/build/org.eclipse.cdt.make.core/src/org/eclipse/cdt/make/core/MakeBuilder.java index 4d9b9ad51c1..50a9e61e5e8 100644 --- a/build/org.eclipse.cdt.make.core/src/org/eclipse/cdt/make/core/MakeBuilder.java +++ b/build/org.eclipse.cdt.make.core/src/org/eclipse/cdt/make/core/MakeBuilder.java @@ -23,7 +23,7 @@ import java.util.List; import java.util.Map; import org.eclipse.cdt.core.CCorePlugin; -import org.eclipse.cdt.core.CommandLauncher; +import org.eclipse.cdt.core.CommandLauncherManager; import org.eclipse.cdt.core.ErrorParserManager; import org.eclipse.cdt.core.ICommandLauncher; import org.eclipse.cdt.core.IConsoleParser; @@ -180,7 +180,7 @@ public class MakeBuilder extends ACBuilder { console.start(project); // Prepare launch parameters for BuildRunnerHelper - ICommandLauncher launcher = new CommandLauncher(); + ICommandLauncher launcher = CommandLauncherManager.getInstance().getCommandLauncher(); String[] targets = getTargets(kind, info); if (targets.length != 0 && targets[targets.length - 1].equals(info.getCleanBuildTarget())) diff --git a/build/org.eclipse.cdt.make.core/src/org/eclipse/cdt/make/internal/core/scannerconfig2/DefaultRunSIProvider.java b/build/org.eclipse.cdt.make.core/src/org/eclipse/cdt/make/internal/core/scannerconfig2/DefaultRunSIProvider.java index cd8b03563be..8a2c47fab19 100644 --- a/build/org.eclipse.cdt.make.core/src/org/eclipse/cdt/make/internal/core/scannerconfig2/DefaultRunSIProvider.java +++ b/build/org.eclipse.cdt.make.core/src/org/eclipse/cdt/make/internal/core/scannerconfig2/DefaultRunSIProvider.java @@ -19,7 +19,7 @@ import java.util.List; import java.util.Properties; import org.eclipse.cdt.core.CCorePlugin; -import org.eclipse.cdt.core.CommandLauncher; +import org.eclipse.cdt.core.CommandLauncherManager; import org.eclipse.cdt.core.ErrorParserManager; import org.eclipse.cdt.core.ICommandLauncher; import org.eclipse.cdt.core.IConsoleParser; @@ -120,7 +120,7 @@ public class DefaultRunSIProvider implements IExternalScannerInfoProvider { } console.start(project); - ICommandLauncher launcher = new CommandLauncher(); + ICommandLauncher launcher = CommandLauncherManager.getInstance().getCommandLauncher(); launcher.setProject(project); IPath program = getCommandToLaunch(); diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/buildproperties/IOptionalBuildProperties.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/buildproperties/IOptionalBuildProperties.java new file mode 100644 index 00000000000..23043aed061 --- /dev/null +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/buildproperties/IOptionalBuildProperties.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2017 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 API and implementation + *******************************************************************************/ +package org.eclipse.cdt.managedbuilder.buildproperties; + +/** + * @noextend This class is not intended to be subclassed by clients. + * @noimplement This interface is not intended to be implemented by clients. + * @since 8.5 + */ +public interface IOptionalBuildProperties extends Cloneable { + String[] getProperties(); + + String getProperty(String id); + + void setProperty(String propertyId, String propertyValue); + + void removeProperty(String id); + + void clear(); + + Object clone(); +} \ No newline at end of file diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/IBuildObjectPropertiesContainer.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/IBuildObjectPropertiesContainer.java index 3bc55876f69..a3704a24f05 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/IBuildObjectPropertiesContainer.java +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/IBuildObjectPropertiesContainer.java @@ -10,7 +10,6 @@ *******************************************************************************/ package org.eclipse.cdt.managedbuilder.core; - /** * @noextend This class is not intended to be subclassed by clients. * @noimplement This interface is not intended to be implemented by clients. diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/IConfiguration.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/IConfiguration.java index f04149faefa..81d1f28bbb1 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/IConfiguration.java +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/IConfiguration.java @@ -36,7 +36,7 @@ import org.eclipse.core.runtime.IPath; * @noextend This class is not intended to be subclassed by clients. * @noimplement This interface is not intended to be implemented by clients. */ -public interface IConfiguration extends IBuildObject, IBuildObjectPropertiesContainer { +public interface IConfiguration extends IBuildObject, IBuildObjectPropertiesContainer, IOptionalBuildObjectPropertiesContainer { public static final String ARTIFACT_NAME = "artifactName"; //$NON-NLS-1$ public static final String CLEAN_COMMAND = "cleanCommand"; //$NON-NLS-1$ public static final String PREBUILD_STEP = "prebuildStep"; //$NON-NLS-1$ @@ -54,6 +54,10 @@ public interface IConfiguration extends IBuildObject, IBuildObjectPropertiesCont public static final String DESCRIPTION = "description"; //$NON-NLS-1$ public static final String BUILD_PROPERTIES = "buildProperties"; //$NON-NLS-1$ + /** + * @since 8.5 + */ + public static final String OPTIONAL_BUILD_PROPERTIES = "optionalBuildProperties"; //$NON-NLS-1$ public static final String BUILD_ARTEFACT_TYPE = "buildArtefactType"; //$NON-NLS-1$ public static final String IS_SYSTEM = "isSystem"; //$NON-NLS-1$ diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/IManagedProject.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/IManagedProject.java index e0d527e9392..8e5a0d7857f 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/IManagedProject.java +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/IManagedProject.java @@ -38,10 +38,14 @@ import org.eclipse.core.resources.IResource; * @noextend This class is not intended to be subclassed by clients. * @noimplement This interface is not intended to be implemented by clients. */ -public interface IManagedProject extends IBuildObject, IBuildObjectPropertiesContainer { +public interface IManagedProject extends IBuildObject, IBuildObjectPropertiesContainer, IOptionalBuildObjectPropertiesContainer { public static final String MANAGED_PROJECT_ELEMENT_NAME = "project"; //$NON-NLS-1$ public static final String PROJECTTYPE = "projectType"; //$NON-NLS-1$ public static final String BUILD_PROPERTIES = "buildProperties"; //$NON-NLS-1$ + /** + * @since 8.5 + */ + public static final String OPTIONAL_BUILD_PROPERTIES = "optionalBuildProperties"; //$NON-NLS-1$ public static final String BUILD_ARTEFACT_TYPE = "buildArtefactType"; //$NON-NLS-1$ diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/IOptionalBuildObjectPropertiesContainer.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/IOptionalBuildObjectPropertiesContainer.java new file mode 100644 index 00000000000..68cfa186bcb --- /dev/null +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/IOptionalBuildObjectPropertiesContainer.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2017 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 API and implementation + *******************************************************************************/ +package org.eclipse.cdt.managedbuilder.core; + +import org.eclipse.cdt.managedbuilder.buildproperties.IOptionalBuildProperties; + +/** + * @since 8.5 + */ +public interface IOptionalBuildObjectPropertiesContainer { + IOptionalBuildProperties getOptionalBuildProperties(); + +} diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/IProjectType.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/IProjectType.java index 1eea850c358..6d810e7110d 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/IProjectType.java +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/IProjectType.java @@ -44,7 +44,7 @@ import org.eclipse.cdt.managedbuilder.macros.IProjectBuildMacroSupplier; * @noextend This class is not intended to be subclassed by clients. * @noimplement This interface is not intended to be implemented by clients. */ -public interface IProjectType extends IBuildObject, IBuildObjectPropertiesContainer { +public interface IProjectType extends IBuildObject, IBuildObjectPropertiesContainer, IOptionalBuildObjectPropertiesContainer { public static final String PROJECTTYPE_ELEMENT_NAME = "projectType"; //$NON-NLS-1$ public static final String SUPERCLASS = "superClass"; //$NON-NLS-1$ public static final String IS_ABSTRACT = "isAbstract"; //$NON-NLS-1$ diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/buildmodel/CommandBuilder.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/buildmodel/CommandBuilder.java index 4fe8091783a..9f7c979b64d 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/buildmodel/CommandBuilder.java +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/buildmodel/CommandBuilder.java @@ -18,11 +18,12 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import org.eclipse.cdt.core.CommandLauncher; +import org.eclipse.cdt.core.CommandLauncherManager; import org.eclipse.cdt.core.ICommandLauncher; import org.eclipse.cdt.managedbuilder.buildmodel.IBuildCommand; import org.eclipse.cdt.managedbuilder.core.ManagedBuilderCorePlugin; import org.eclipse.cdt.managedbuilder.internal.core.ManagedMakeMessages; +import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; @@ -47,6 +48,8 @@ public class CommandBuilder implements IBuildModelBuilder { private Process fProcess; private String fErrMsg; + private IProject fProject; + protected class OutputStreamWrapper extends OutputStream { private OutputStream fOut; @@ -80,8 +83,9 @@ public class CommandBuilder implements IBuildModelBuilder { } - public CommandBuilder(IBuildCommand cmd, IResourceRebuildStateContainer cr){ + public CommandBuilder(IBuildCommand cmd, IResourceRebuildStateContainer cr, IProject project){ fCmd = cmd; + fProject = project; } protected OutputStream wrap(OutputStream out){ @@ -143,7 +147,7 @@ public class CommandBuilder implements IBuildModelBuilder { } protected ICommandLauncher createLauncher() { - return new CommandLauncher(); + return CommandLauncherManager.getInstance().getCommandLauncher(fProject); } public String getErrMsg() { diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/buildmodel/StepBuilder.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/buildmodel/StepBuilder.java index 9773bec0aac..b40ae1ccc68 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/buildmodel/StepBuilder.java +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/buildmodel/StepBuilder.java @@ -17,7 +17,9 @@ import org.eclipse.cdt.managedbuilder.buildmodel.IBuildCommand; import org.eclipse.cdt.managedbuilder.buildmodel.IBuildDescription; import org.eclipse.cdt.managedbuilder.buildmodel.IBuildResource; import org.eclipse.cdt.managedbuilder.buildmodel.IBuildStep; +import org.eclipse.cdt.managedbuilder.core.IConfiguration; import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; @@ -223,8 +225,10 @@ public class StepBuilder implements IBuildModelBuilder { fCommandBuilders = new CommandBuilder[0]; else { fCommandBuilders = new CommandBuilder[cmds.length]; + IConfiguration cfg = fStep.getBuildDescription().getConfiguration(); + IProject project = (IProject)cfg.getOwner(); for(int i = 0; i < cmds.length; i++){ - fCommandBuilders[i] = new CommandBuilder(cmds[i], fRebuildStateContainer); + fCommandBuilders[i] = new CommandBuilder(cmds[i], fRebuildStateContainer, project); } } } diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/Builder.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/Builder.java index 2b61d3e6cac..aa3427dce55 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/Builder.java +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/Builder.java @@ -25,7 +25,7 @@ import java.util.SortedMap; import java.util.StringTokenizer; import org.eclipse.cdt.core.CCorePlugin; -import org.eclipse.cdt.core.CommandLauncher; +import org.eclipse.cdt.core.CommandLauncherManager; import org.eclipse.cdt.core.ErrorParserManager; import org.eclipse.cdt.core.ICommandLauncher; import org.eclipse.cdt.core.cdtvariables.CdtVariableException; @@ -2858,7 +2858,7 @@ public class Builder extends HoldsOptions implements IBuilder, IMatchKeyProvider return getSuperClass().getCommandLauncher(); else if(fCommandLauncher == null) // catch all for backwards compatibility - fCommandLauncher = new CommandLauncher(); + fCommandLauncher = CommandLauncherManager.getInstance().getCommandLauncher(); return fCommandLauncher; } diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/Configuration.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/Configuration.java index bba687d8168..03f14a6256c 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/Configuration.java +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/Configuration.java @@ -45,6 +45,7 @@ import org.eclipse.cdt.internal.core.SafeStringInterner; import org.eclipse.cdt.managedbuilder.buildproperties.IBuildProperty; import org.eclipse.cdt.managedbuilder.buildproperties.IBuildPropertyType; import org.eclipse.cdt.managedbuilder.buildproperties.IBuildPropertyValue; +import org.eclipse.cdt.managedbuilder.buildproperties.IOptionalBuildProperties; import org.eclipse.cdt.managedbuilder.core.BuildException; import org.eclipse.cdt.managedbuilder.core.IBuildObject; import org.eclipse.cdt.managedbuilder.core.IBuildObjectProperties; @@ -114,6 +115,7 @@ public class Configuration extends BuildObject implements IConfiguration, IBuild private String description; private ICSourceEntry[] sourceEntries; private BuildObjectProperties buildProperties; + private OptionalBuildProperties optionalBuildProperties; private boolean isTest; private SupportedProperties supportedProperties; @@ -261,6 +263,10 @@ public class Configuration extends BuildObject implements IConfiguration, IBuild if(props != null) buildProperties = new BuildObjectProperties(props, this, this); + String optionalProps = SafeStringInterner.safeIntern(element.getAttribute(OPTIONAL_BUILD_PROPERTIES)); + if(optionalProps != null) + optionalBuildProperties = new OptionalBuildProperties(optionalProps); + String artType = SafeStringInterner.safeIntern(element.getAttribute(BUILD_ARTEFACT_TYPE)); if(artType != null){ if(buildProperties == null) @@ -482,6 +488,9 @@ public class Configuration extends BuildObject implements IConfiguration, IBuild if(baseCfg.buildProperties != null) this.buildProperties = new BuildObjectProperties(baseCfg.buildProperties, this, this); + if (baseCfg.optionalBuildProperties != null) + this.optionalBuildProperties = new OptionalBuildProperties(baseCfg.optionalBuildProperties); + // set managedBuildRevision setManagedBuildRevision(baseCfg.getManagedBuildRevision()); @@ -625,6 +634,10 @@ public class Configuration extends BuildObject implements IConfiguration, IBuild if(cloneConfig.buildProperties != null) { this.buildProperties = new BuildObjectProperties(cloneConfig.buildProperties, this, this); } + + if (cloneConfig.optionalBuildProperties != null) { + this.optionalBuildProperties = new OptionalBuildProperties(cloneConfig.optionalBuildProperties); + } this.description = cloneConfig.getDescription(); @@ -819,6 +832,10 @@ public class Configuration extends BuildObject implements IConfiguration, IBuild if(props != null) buildProperties = new BuildObjectProperties(props, this, this); + String optionalProps = element.getAttribute(OPTIONAL_BUILD_PROPERTIES); + if (optionalProps != null) + optionalBuildProperties = new OptionalBuildProperties(optionalProps); + String artType = SafeStringInterner.safeIntern(element.getAttribute(BUILD_ARTEFACT_TYPE)); if(artType != null){ if(buildProperties == null) @@ -908,6 +925,10 @@ public class Configuration extends BuildObject implements IConfiguration, IBuild } } + if(optionalBuildProperties != null){ + element.setAttribute(OPTIONAL_BUILD_PROPERTIES, optionalBuildProperties.toString()); + } + if (parent != null) element.setAttribute(IConfiguration.PARENT, parent.getId()); @@ -2398,6 +2419,18 @@ public class Configuration extends BuildObject implements IConfiguration, IBuild return buildProperties; } + @Override + public IOptionalBuildProperties getOptionalBuildProperties() { + if (optionalBuildProperties == null){ + OptionalBuildProperties parentProps = findOptionalBuildProperties(); + if(parentProps != null) + optionalBuildProperties = new OptionalBuildProperties(parentProps); + else + optionalBuildProperties = new OptionalBuildProperties(); + } + return optionalBuildProperties; + } + private BuildObjectProperties findBuildProperties(){ if(buildProperties == null){ if(parent != null){ @@ -2407,6 +2440,16 @@ public class Configuration extends BuildObject implements IConfiguration, IBuild } return buildProperties; } + + private OptionalBuildProperties findOptionalBuildProperties(){ + if (optionalBuildProperties == null){ + if (parent != null){ + return ((Configuration)parent).findOptionalBuildProperties(); + } + return null; + } + return optionalBuildProperties; + } public boolean supportsType(IBuildPropertyType type) { return supportsType(type.getId()); diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/ManagedProject.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/ManagedProject.java index 22bbaa7cde2..68d59d2f271 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/ManagedProject.java +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/ManagedProject.java @@ -24,6 +24,7 @@ import org.eclipse.cdt.core.settings.model.ICStorageElement; import org.eclipse.cdt.internal.core.cdtvariables.StorableCdtVariables; import org.eclipse.cdt.managedbuilder.buildproperties.IBuildPropertyType; import org.eclipse.cdt.managedbuilder.buildproperties.IBuildPropertyValue; +import org.eclipse.cdt.managedbuilder.buildproperties.IOptionalBuildProperties; import org.eclipse.cdt.managedbuilder.core.IBuildObject; import org.eclipse.cdt.managedbuilder.core.IBuildObjectProperties; import org.eclipse.cdt.managedbuilder.core.IBuildPropertiesRestriction; @@ -57,6 +58,7 @@ public class ManagedProject extends BuildObject implements IManagedProject, IBui // private StorableEnvironment userDefinedEnvironment; private BuildObjectProperties buildProperties; + private OptionalBuildProperties optionalBuildProperties; /* * C O N S T R U C T O R S @@ -191,6 +193,10 @@ public class ManagedProject extends BuildObject implements IManagedProject, IBui String props = element.getAttribute(BUILD_PROPERTIES); if(props != null && props.length() != 0) buildProperties = new BuildObjectProperties(props, this, this); + + String optionalProps = element.getAttribute(OPTIONAL_BUILD_PROPERTIES); + if (optionalProps != null && optionalProps.length() != 0) + optionalBuildProperties = new OptionalBuildProperties(optionalProps); String artType = element.getAttribute(BUILD_ARTEFACT_TYPE); if(artType != null){ @@ -576,6 +582,18 @@ public class ManagedProject extends BuildObject implements IManagedProject, IBui return buildProperties; } + @Override + public IOptionalBuildProperties getOptionalBuildProperties() { + if(optionalBuildProperties == null){ + OptionalBuildProperties parentProps = findOptionalBuildProperties(); + if(parentProps != null) + optionalBuildProperties = new OptionalBuildProperties(parentProps); + else + optionalBuildProperties = new OptionalBuildProperties(); + } + return optionalBuildProperties; + } + private BuildObjectProperties findBuildProperties(){ if(buildProperties == null){ if(projectType != null){ @@ -586,6 +604,16 @@ public class ManagedProject extends BuildObject implements IManagedProject, IBui return buildProperties; } + private OptionalBuildProperties findOptionalBuildProperties(){ + if(optionalBuildProperties == null){ + if(projectType != null){ + return ((ProjectType)projectType).findOptionalBuildProperties(); + } + return null; + } + return optionalBuildProperties; + } + @Override public void propertiesChanged() { IConfiguration cfgs[] = getConfigurations(); diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/MultiConfiguration.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/MultiConfiguration.java index f3e9bda6d7f..0a7b5a0e4b6 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/MultiConfiguration.java +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/MultiConfiguration.java @@ -22,6 +22,7 @@ import org.eclipse.cdt.core.settings.model.extension.CBuildData; import org.eclipse.cdt.core.settings.model.extension.CConfigurationData; import org.eclipse.cdt.managedbuilder.buildproperties.IBuildProperty; import org.eclipse.cdt.managedbuilder.buildproperties.IBuildPropertyValue; +import org.eclipse.cdt.managedbuilder.buildproperties.IOptionalBuildProperties; import org.eclipse.cdt.managedbuilder.core.BuildException; import org.eclipse.cdt.managedbuilder.core.IBuildObjectProperties; import org.eclipse.cdt.managedbuilder.core.IBuilder; @@ -1204,6 +1205,14 @@ public class MultiConfiguration extends MultiItemsHolder implements return curr().getBuildProperties(); } + /* (non-Javadoc) + * @see org.eclipse.cdt.managedbuilder.core.IBuildObjectPropertiesContainer#getOptionalBuildProperties() + */ + @Override + public IOptionalBuildProperties getOptionalBuildProperties() { + return curr().getOptionalBuildProperties(); + } + @Override public boolean getParallelDef() { for (IConfiguration cfg : fCfgs) { diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/OptionalBuildProperties.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/OptionalBuildProperties.java new file mode 100644 index 00000000000..8ec750feba4 --- /dev/null +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/OptionalBuildProperties.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (c) 2017 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 contribution + *******************************************************************************/ +package org.eclipse.cdt.managedbuilder.internal.core; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.StringTokenizer; + +import org.eclipse.cdt.internal.core.SafeStringInterner; +import org.eclipse.cdt.managedbuilder.buildproperties.IOptionalBuildProperties; + +public class OptionalBuildProperties implements IOptionalBuildProperties { + + public static final String PROPERTY_VALUE_SEPARATOR = "="; //$NON-NLS-1$ + public static final String PROPERTIES_SEPARATOR = ","; //$NON-NLS-1$ + + private Map fProperties = new HashMap<>(); + + public OptionalBuildProperties() { + } + + public OptionalBuildProperties(String properties) { + StringTokenizer t = new StringTokenizer(properties, PROPERTIES_SEPARATOR); + while(t.hasMoreTokens()){ + String property = t.nextToken(); + int index = property.indexOf(PROPERTY_VALUE_SEPARATOR); + String id, value; + if(index != -1){ + id = SafeStringInterner.safeIntern(property.substring(0, index)); + value = SafeStringInterner.safeIntern(property.substring(index + 1)); + } else { + id = SafeStringInterner.safeIntern(property); + value = null; + } + fProperties.put(id, value); + } + } + + public OptionalBuildProperties(OptionalBuildProperties properties) { + fProperties.putAll(properties.fProperties); + } + + @Override + public String getProperty(String id) { + return fProperties.get(id); + } + + @Override + public void setProperty(String id, String value) { + fProperties.put(id, value); + } + + @Override + public String[] getProperties(){ + return fProperties.values().toArray(new String[fProperties.size()]); + } + + @Override + public void removeProperty(String id) { + fProperties.remove(id); + } + + @Override + public String toString(){ + int size = fProperties.size(); + Set> entries = fProperties.entrySet(); + if(size == 0) + return ""; //$NON-NLS-1$ + + StringBuilder buf = new StringBuilder(); + Iterator> iterator = entries.iterator(); + Entry entry = iterator.next(); + buf.append(entry.getKey() + PROPERTY_VALUE_SEPARATOR + entry.getValue()); + + while (iterator.hasNext()) { + buf.append(PROPERTIES_SEPARATOR); + entry = iterator.next(); + buf.append(entry.getKey() + PROPERTY_VALUE_SEPARATOR + entry.getValue()); + } + return buf.toString(); + } + + @Override + public Object clone() { + return new OptionalBuildProperties(this); + } + + @Override + public void clear() { + fProperties.clear(); + } + + +} diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/ProjectType.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/ProjectType.java index 780b7f374e7..c95d833b50d 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/ProjectType.java +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/ProjectType.java @@ -22,6 +22,7 @@ import org.eclipse.cdt.internal.core.SafeStringInterner; import org.eclipse.cdt.managedbuilder.buildproperties.IBuildProperty; import org.eclipse.cdt.managedbuilder.buildproperties.IBuildPropertyType; import org.eclipse.cdt.managedbuilder.buildproperties.IBuildPropertyValue; +import org.eclipse.cdt.managedbuilder.buildproperties.IOptionalBuildProperties; import org.eclipse.cdt.managedbuilder.core.IBuildObjectProperties; import org.eclipse.cdt.managedbuilder.core.IBuildPropertiesRestriction; import org.eclipse.cdt.managedbuilder.core.IConfiguration; @@ -66,6 +67,7 @@ public class ProjectType extends BuildObject implements IProjectType, IBuildProp private IProjectBuildMacroSupplier buildMacroSupplier = null; BuildObjectProperties buildProperties; + OptionalBuildProperties optionalBuildProperties; // Miscellaneous @@ -707,6 +709,28 @@ public class ProjectType extends BuildObject implements IProjectType, IBuildProp return buildProperties; } + @Override + public IOptionalBuildProperties getOptionalBuildProperties() { + if(optionalBuildProperties == null){ + OptionalBuildProperties parentProps = findOptionalBuildProperties(); + if(parentProps != null) + optionalBuildProperties = new OptionalBuildProperties(parentProps); + else + optionalBuildProperties = new OptionalBuildProperties(); + } + return optionalBuildProperties; + } + + OptionalBuildProperties findOptionalBuildProperties(){ + if(optionalBuildProperties == null){ + if(superClass != null){ + return ((ProjectType)superClass).findOptionalBuildProperties(); + } + return null; + } + return optionalBuildProperties; + } + @Override public void propertiesChanged() { List list = getConfigurationList(); diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/language/settings/providers/GCCBuiltinSpecsDetectorCygwin.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/language/settings/providers/GCCBuiltinSpecsDetectorCygwin.java index d2a4b86ed7b..1c63fd73c84 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/language/settings/providers/GCCBuiltinSpecsDetectorCygwin.java +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/language/settings/providers/GCCBuiltinSpecsDetectorCygwin.java @@ -12,11 +12,17 @@ package org.eclipse.cdt.managedbuilder.internal.language.settings.providers; import java.net.URI; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.eclipse.cdt.core.EFSExtensionProvider; import org.eclipse.cdt.internal.core.Cygwin; +import org.eclipse.cdt.managedbuilder.buildproperties.IOptionalBuildProperties; +import org.eclipse.cdt.managedbuilder.core.IConfiguration; +import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager; import org.eclipse.cdt.managedbuilder.core.ManagedBuilderCorePlugin; import org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector; +import org.eclipse.core.runtime.Platform; /** * Class to detect built-in compiler settings for Cygwin toolchain. @@ -26,6 +32,7 @@ public class GCCBuiltinSpecsDetectorCygwin extends GCCBuiltinSpecsDetector { // ID must match the tool-chain definition in org.eclipse.cdt.managedbuilder.core.buildDefinitions extension point private static final String GCC_TOOLCHAIN_ID_CYGWIN = "cdt.managedbuild.toolchain.gnu.cygwin.base"; //$NON-NLS-1$ private static final String ENV_PATH = "PATH"; //$NON-NLS-1$ + public static final String CONTAINER_ENABLEMENT_PROPERTY = "org.eclipse.cdt.docker.launcher.containerbuild.property.enablement"; //$NON-NLS-1$ /** * EFSExtensionProvider for Cygwin translations @@ -46,7 +53,14 @@ public class GCCBuiltinSpecsDetectorCygwin extends GCCBuiltinSpecsDetector { String windowsPath = null; try { String cygwinPath = getPathFromURI(locationURI); - windowsPath = Cygwin.cygwinToWindowsPath(cygwinPath, envPathValue); + IConfiguration cfg = ManagedBuildManager.getConfigurationForDescription(currentCfgDescription); + if (cfg != null) { + IOptionalBuildProperties bp = cfg.getOptionalBuildProperties(); + String ep = bp.getProperty(CONTAINER_ENABLEMENT_PROPERTY); + if (ep == null || !Boolean.parseBoolean(ep)) { + windowsPath = Cygwin.cygwinToWindowsPath(cygwinPath, envPathValue); + } + } } catch (Exception e) { ManagedBuilderCorePlugin.log(e); } diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/language/settings/providers/AbstractBuiltinSpecsDetector.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/language/settings/providers/AbstractBuiltinSpecsDetector.java index 3ed6c149970..ca31fcc4d65 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/language/settings/providers/AbstractBuiltinSpecsDetector.java +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/language/settings/providers/AbstractBuiltinSpecsDetector.java @@ -26,7 +26,7 @@ import java.util.Map.Entry; import java.util.Set; import org.eclipse.cdt.core.CCorePlugin; -import org.eclipse.cdt.core.CommandLauncher; +import org.eclipse.cdt.core.CommandLauncherManager; import org.eclipse.cdt.core.ErrorParserManager; import org.eclipse.cdt.core.ICommandLauncher; import org.eclipse.cdt.core.IConsoleParser; @@ -542,6 +542,7 @@ public abstract class AbstractBuiltinSpecsDetector extends AbstractLanguageSetti boolean isChanged = false; List languageIds = getLanguageScope(); + if (languageIds != null) { monitor.beginTask(ManagedMakeMessages.getResourceString("AbstractBuiltinSpecsDetector.ScannerDiscoveryTaskTitle"), //$NON-NLS-1$ TICKS_REMOVE_MARKERS + languageIds.size()*TICKS_RUN_FOR_ONE_LANGUAGE + TICKS_SERIALIZATION); @@ -656,7 +657,7 @@ public abstract class AbstractBuiltinSpecsDetector extends AbstractLanguageSetti } console.start(currentProject); - ICommandLauncher launcher = new CommandLauncher(); + ICommandLauncher launcher = CommandLauncherManager.getInstance().getCommandLauncher(currentCfgDescription); launcher.setProject(currentProject); IPath program = new Path(""); //$NON-NLS-1$ @@ -765,7 +766,7 @@ public abstract class AbstractBuiltinSpecsDetector extends AbstractLanguageSetti // so collect them to save later when output finishes if (entries != null) { for (ICLanguageSettingEntry entry : entries) { - if (!detectedSettingEntries.contains(entry)) { + if (detectedSettingEntries != null && !detectedSettingEntries.contains(entry)) { detectedSettingEntries.add(entry); } } diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/language/settings/providers/GCCBuiltinSpecsDetector.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/language/settings/providers/GCCBuiltinSpecsDetector.java index 163e22a088d..d07d225b8cc 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/language/settings/providers/GCCBuiltinSpecsDetector.java +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/language/settings/providers/GCCBuiltinSpecsDetector.java @@ -74,7 +74,7 @@ public class GCCBuiltinSpecsDetector extends ToolchainBuiltinSpecsDetector imple @Override protected List parseOptions(String line) { line = line.trim(); - + // contribution of -dD option if (line.startsWith("#define")) { return makeList(line); diff --git a/build/org.eclipse.cdt.managedbuilder.ui.tests/src/org/eclipse/cdt/managedbuilder/ui/tests/util/TestConfiguration.java b/build/org.eclipse.cdt.managedbuilder.ui.tests/src/org/eclipse/cdt/managedbuilder/ui/tests/util/TestConfiguration.java index 6bb61e82998..d3c139e359a 100644 --- a/build/org.eclipse.cdt.managedbuilder.ui.tests/src/org/eclipse/cdt/managedbuilder/ui/tests/util/TestConfiguration.java +++ b/build/org.eclipse.cdt.managedbuilder.ui.tests/src/org/eclipse/cdt/managedbuilder/ui/tests/util/TestConfiguration.java @@ -14,6 +14,7 @@ import org.eclipse.cdt.core.settings.model.ICSourceEntry; import org.eclipse.cdt.core.settings.model.extension.CBuildData; import org.eclipse.cdt.core.settings.model.extension.CConfigurationData; import org.eclipse.cdt.managedbuilder.buildproperties.IBuildPropertyValue; +import org.eclipse.cdt.managedbuilder.buildproperties.IOptionalBuildProperties; import org.eclipse.cdt.managedbuilder.core.BuildException; import org.eclipse.cdt.managedbuilder.core.IBuildObjectProperties; import org.eclipse.cdt.managedbuilder.core.IBuilder; @@ -632,6 +633,13 @@ public class TestConfiguration implements IConfiguration { // TODO Auto-generated method stub return null; } + + @Override + public IOptionalBuildProperties getOptionalBuildProperties() { + // TODO Auto-generated method stub + return null; + } + @Override public IResource getOwner() { return null; } diff --git a/build/org.eclipse.cdt.managedbuilder.ui.tests/src/org/eclipse/cdt/managedbuilder/ui/tests/util/TestProjectType.java b/build/org.eclipse.cdt.managedbuilder.ui.tests/src/org/eclipse/cdt/managedbuilder/ui/tests/util/TestProjectType.java index d22f0878a6b..0727d3bdc73 100644 --- a/build/org.eclipse.cdt.managedbuilder.ui.tests/src/org/eclipse/cdt/managedbuilder/ui/tests/util/TestProjectType.java +++ b/build/org.eclipse.cdt.managedbuilder.ui.tests/src/org/eclipse/cdt/managedbuilder/ui/tests/util/TestProjectType.java @@ -11,6 +11,7 @@ package org.eclipse.cdt.managedbuilder.ui.tests.util; import org.eclipse.cdt.managedbuilder.buildproperties.IBuildPropertyValue; +import org.eclipse.cdt.managedbuilder.buildproperties.IOptionalBuildProperties; import org.eclipse.cdt.managedbuilder.core.IBuildObjectProperties; import org.eclipse.cdt.managedbuilder.core.IConfiguration; import org.eclipse.cdt.managedbuilder.core.IConfigurationNameProvider; @@ -76,6 +77,8 @@ public class TestProjectType implements IProjectType { public void setVersion(Version version) {} @Override public IBuildObjectProperties getBuildProperties() { return null; } + @Override + public IOptionalBuildProperties getOptionalBuildProperties() { return null; } @Override public IBuildPropertyValue getBuildArtefactType() { diff --git a/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/internal/externaltool/ExternalToolInvoker.java b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/internal/externaltool/ExternalToolInvoker.java index 8343815c423..4e02aa476f6 100644 --- a/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/internal/externaltool/ExternalToolInvoker.java +++ b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/internal/externaltool/ExternalToolInvoker.java @@ -18,7 +18,7 @@ import org.eclipse.cdt.codan.core.cxx.externaltool.ConfigurationSettings; import org.eclipse.cdt.codan.core.cxx.externaltool.InvocationFailure; import org.eclipse.cdt.codan.core.cxx.externaltool.InvocationParameters; import org.eclipse.cdt.core.CCorePlugin; -import org.eclipse.cdt.core.CommandLauncher; +import org.eclipse.cdt.core.CommandLauncherManager; import org.eclipse.cdt.core.ICommandLauncher; import org.eclipse.cdt.core.IConsoleParser; import org.eclipse.cdt.core.resources.IConsole; @@ -85,7 +85,7 @@ public class ExternalToolInvoker { final OutputStream out = sniffer.getOutputStream(); final OutputStream err = sniffer.getErrorStream(); try { - ICommandLauncher launcher = new CommandLauncher(); + ICommandLauncher launcher = CommandLauncherManager.getInstance().getCommandLauncher(); launcher.showCommand(true); launcher.setProject(project); Process p = launcher.execute(commandPath, commandArgs, commandEnv, workingDirectory, new SubProgressMonitor(monitor, 50)); diff --git a/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/testplugin/CModelMock.java b/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/testplugin/CModelMock.java index a6c0a5d2551..db644fd1d62 100644 --- a/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/testplugin/CModelMock.java +++ b/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/testplugin/CModelMock.java @@ -221,9 +221,11 @@ public class CModelMock { */ public static class DummyCConfigurationDescription implements ICConfigurationDescription { private String id; + private ICProjectDescription projectDescription; public DummyCConfigurationDescription(String id) { this.id = id; + this.projectDescription = new DummyCProjectDescription(); } @Override @@ -302,7 +304,7 @@ public class CModelMock { @Override public ICProjectDescription getProjectDescription() { - return null; + return projectDescription; } @Override diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsSerializableProvider.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsSerializableProvider.java index 839f80e4c60..f14594d452d 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsSerializableProvider.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsSerializableProvider.java @@ -17,6 +17,7 @@ import java.util.Map; import java.util.Map.Entry; import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.CommandLauncherManager; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; import org.eclipse.cdt.internal.core.XmlUtil; @@ -146,6 +147,9 @@ public class LanguageSettingsSerializableProvider extends LanguageSettingsBasePr List entries) { String rcProjectPath = rc!=null ? rc.getProjectRelativePath().toString() : null; fStorage.setSettingEntries(rcProjectPath, languageId, entries); + if (cfgDescription != null) { + CommandLauncherManager.getInstance().setLanguageSettingEntries(cfgDescription.getProjectDescription().getProject(), entries); + } } /** @@ -167,6 +171,10 @@ public class LanguageSettingsSerializableProvider extends LanguageSettingsBasePr entries = fStorage.getSettingEntries(rcProjectPath, null); } } + + if (cfgDescription != null) { + entries = CommandLauncherManager.getInstance().getLanguageSettingEntries(cfgDescription.getProjectDescription().getProject(), entries); + } return entries; } diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsProvidersSerializer.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsProvidersSerializer.java index 1338d2b704d..cbb1001c69f 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsProvidersSerializer.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsProvidersSerializer.java @@ -20,6 +20,7 @@ import java.util.Map; import java.util.Set; import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.CommandLauncherManager; import org.eclipse.cdt.core.language.settings.providers.ICListenerAgent; import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsBroadcastingProvider; import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsChangeEvent; @@ -178,6 +179,10 @@ public class LanguageSettingsProvidersSerializer { public List getSettingEntries(ICConfigurationDescription cfgDescription, IResource rc, String languageId) { ILanguageSettingsProvider rawProvider = getRawProvider(); List entries = rawProvider!=null ? rawProvider.getSettingEntries(cfgDescription, rc, languageId) : null; + if (cfgDescription != null) { + IProject project = cfgDescription.getProjectDescription().getProject(); + entries = CommandLauncherManager.getInstance().getLanguageSettingEntries(project, entries); + } return entries; } @@ -1456,6 +1461,10 @@ public class LanguageSettingsProvidersSerializer { return getSettingEntriesUpResourceTree(provider, cfgDescription, parentFolder, languageId); } // if out of parent resources - get default entries + entries = getSettingEntriesPooled(provider, cfgDescription, null, languageId); + if (entries != null) { + return entries; + } entries = getSettingEntriesPooled(provider, null, null, languageId); if (entries != null) { return entries; diff --git a/core/org.eclipse.cdt.core/plugin.xml b/core/org.eclipse.cdt.core/plugin.xml index 81354a3bcb3..086b08d5efe 100644 --- a/core/org.eclipse.cdt.core/plugin.xml +++ b/core/org.eclipse.cdt.core/plugin.xml @@ -692,6 +692,7 @@ + diff --git a/core/org.eclipse.cdt.core/schema/CommandLauncherFactory.exsd b/core/org.eclipse.cdt.core/schema/CommandLauncherFactory.exsd new file mode 100644 index 00000000000..a4b0564eb18 --- /dev/null +++ b/core/org.eclipse.cdt.core/schema/CommandLauncherFactory.exsd @@ -0,0 +1,128 @@ + + + + + + + + + This extension point is used to contribute a Command Launcher factory to CDT. A Command Launcher factory creates a Command Launcher for running commands. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Integer priority of factory. Default is 0. Use higher values if you want to override an existing factory. + + + + + + + Command Launcher factory class that implements org.eclipse.cdt.core.ICommandLauncherFactory. + + + + + + + + + + + + + + + CDT 9.4.0 + + + + + + + + + [Enter extension point usage example here.] + + + + + + + + + Plug-ins that want to extend this extension point must implement <samp>org.eclipse.cdt.core.ICommandLauncherFactory</samp> interface. + + + + + + + + + [Enter information about supplied implementation of this extension point.] + + + + + + + + + Copyright (c) 2017 Red Hat Inc. and others.<br/> +All rights reserved. This program and the accompanying materials<br/> +are made available under the terms of the Eclipse Public License v1.0<br/> +which accompanies this distribution, and is available at<br/> +http://www.eclipse.org/legal/epl-v10.html<br/> + + + + diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePlugin.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePlugin.java index b639dee57cf..ab980ee9876 100644 --- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePlugin.java +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePlugin.java @@ -156,6 +156,17 @@ public class CCorePlugin extends Plugin { */ public static final String ERROR_PARSER_UNIQ_ID = PLUGIN_ID + "." + ERROR_PARSER_SIMPLE_ID; //$NON-NLS-1$ + /** + * Name of the extension point for contributing a Command Launcher factory + * @since 6.3 + */ + public static final String COMMAND_LAUNCHER_FACTORY_SIMPLE_ID = "CommandLauncherFactory"; //$NON-NLS-1$ + /** + * Full unique name of the extension point for contributing a Command Launcher factory + * @since 6.3 + */ + public static final String COMMAND_LAUNCHER_FACTORY_UNIQ_ID = PLUGIN_ID + "." + COMMAND_LAUNCHER_FACTORY_SIMPLE_ID; //$NON-NLS-1$ + // default store for pathentry public static final String DEFAULT_PATHENTRY_STORE_ID = PLUGIN_ID + ".cdtPathEntryStore"; //$NON-NLS-1$ diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CommandLauncherManager.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CommandLauncherManager.java new file mode 100644 index 00000000000..ad70f104cc8 --- /dev/null +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CommandLauncherManager.java @@ -0,0 +1,288 @@ +/******************************************************************************* + * Copyright (c) 2017 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 API and implementation + *******************************************************************************/ +package org.eclipse.cdt.core; + +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtension; +import org.eclipse.core.runtime.IExtensionPoint; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.Platform; + +/** + * @since 6.3 + */ +public class CommandLauncherManager { + + private static CommandLauncherManager instance; + + private List factories = new ArrayList<>(); + private Map priorityMapping = new HashMap<>(); + + private CommandLauncherManager() { + loadCommandLauncherFactoryExtensions(); + } + + public static synchronized CommandLauncherManager getInstance() { + if (instance == null) { + instance = new CommandLauncherManager(); + } + return instance; + } + + public ICommandLauncher getCommandLauncher() { + return new CommandLauncherWrapper(this); + } + + + private class CommandLauncherWrapper implements ICommandLauncher { + + private ICommandLauncher launcher; + private IProject fProject; + private boolean fShowCommand; + private String fErrorMessage; + private CommandLauncherManager manager; + + public CommandLauncherWrapper(CommandLauncherManager manager) { + this.manager = manager; + } + + @Override + public void setProject(IProject project) { + if (launcher != null) { + launcher.setProject(project); + } else { + fProject = project; + } + } + + @Override + public IProject getProject() { + if (launcher != null) { + return launcher.getProject(); + } + return fProject; + } + + @Override + public void showCommand(boolean show) { + if (launcher != null) { + launcher.showCommand(show); + } else { + fShowCommand = show; + } + } + + @Override + public String getErrorMessage() { + if (launcher != null) { + return launcher.getErrorMessage(); + } + return fErrorMessage; + } + + @Override + public void setErrorMessage(String error) { + if (launcher != null) { + launcher.setErrorMessage(error); + } else { + fErrorMessage = error; + } + } + + @Override + public String[] getCommandArgs() { + if (launcher != null) { + return launcher.getCommandArgs(); + } + return new String[0]; + } + + @Override + public Properties getEnvironment() { + if (launcher != null) { + return launcher.getEnvironment(); + } + return null; + } + + @Override + public String getCommandLine() { + if (launcher != null) { + return launcher.getCommandLine(); + } + return null; + } + + @Override + public Process execute(IPath commandPath, String[] args, String[] env, IPath workingDirectory, + IProgressMonitor monitor) throws CoreException { + if (launcher == null) { + launcher = manager.getCommandLauncher(fProject); + launcher.setProject(fProject); + launcher.showCommand(fShowCommand); + launcher.setErrorMessage(fErrorMessage); + } + return launcher.execute(commandPath, args, env, workingDirectory, monitor); + } + + @SuppressWarnings("deprecation") + @Override + public int waitAndRead(OutputStream out, OutputStream err) { + if (launcher != null) { + return launcher.waitAndRead(out, err); + } + return 0; + } + + @Override + public int waitAndRead(OutputStream output, OutputStream err, IProgressMonitor monitor) { + if (launcher != null) { + return launcher.waitAndRead(output, err, monitor); + } + return 0; + } + + } + + + /** + * Get a command launcher. + * + * @param project - IProject to determine launcher for. + * @return an ICommandLauncher for running commands + */ + public ICommandLauncher getCommandLauncher(IProject project) { + // loop through list of factories and return launcher returned with + // highest priority + int highestPriority = -1; + ICommandLauncher bestLauncher = null; + for (ICommandLauncherFactory factory : factories) { + ICommandLauncher launcher = factory.getCommandLauncher(project); + if (launcher != null) { + if (priorityMapping.get(factory) > highestPriority) { + bestLauncher = launcher; + } + } + } + if (bestLauncher != null) { + return bestLauncher; + } + // default to local CommandLauncher + return new CommandLauncher(); + } + + /** + * Get a command launcher. + * + * @param cfgd - ICConfigurationDescription to get command launcher for. + * @return an ICommandLauncher for running commands + */ + public ICommandLauncher getCommandLauncher(ICConfigurationDescription cfgd) { + // loop through list of factories and return launcher returned with + // highest priority + int highestPriority = -1; + ICommandLauncher bestLauncher = null; + for (ICommandLauncherFactory factory : factories) { + ICommandLauncher launcher = factory.getCommandLauncher(cfgd); + if (launcher != null) { + if (priorityMapping.get(factory) > highestPriority) { + bestLauncher = launcher; + } + } + } + if (bestLauncher != null) { + return bestLauncher; + } + // default to local CommandLauncher + return new CommandLauncher(); + } + + /** + * Load command launcher factory contributed extensions from extension registry. + * + */ + private void loadCommandLauncherFactoryExtensions() { + IExtensionRegistry registry = Platform.getExtensionRegistry(); + IExtensionPoint extension = registry.getExtensionPoint(CCorePlugin.PLUGIN_ID, CCorePlugin.COMMAND_LAUNCHER_FACTORY_SIMPLE_ID); + if (extension != null) { + IExtension[] extensions = extension.getExtensions(); + for (IExtension ext : extensions) { + try { + IConfigurationElement element[] = ext.getConfigurationElements(); + for (IConfigurationElement element2 : element) { + if (element2.getName().equalsIgnoreCase("factory")) { //$NON-NLS-1$ + ICommandLauncherFactory factory = (ICommandLauncherFactory) element2.createExecutableExtension("class"); //$NON-NLS-1$ + String priorityAttr = element2.getAttribute("priority"); //$NON-NLS-1$ + int priority = Integer.valueOf(0); + if (priorityAttr != null) { + try { + priority = Integer.valueOf(priorityAttr); + } catch (NumberFormatException e) { + CCorePlugin.log(e); + } + } + factories.add(factory); + priorityMapping.put(factory, priority); + } + } + } catch (Exception e) { + CCorePlugin.log("Cannot load CommandLauncherFactory extension " + ext.getUniqueIdentifier(), e); //$NON-NLS-1$ + } + } + } + } + + private ICommandLauncherFactory getBestFactory(IProject project) { + // loop through list of factories and return launcher returned with + // highest priority + int highestPriority = -1; + ICommandLauncherFactory bestLauncherFactory = null; + for (ICommandLauncherFactory factory : factories) { + ICommandLauncher launcher = factory.getCommandLauncher(project); + if (launcher != null) { + if (priorityMapping.get(factory) > highestPriority) { + bestLauncherFactory = factory; + } + } + } + return bestLauncherFactory; + } + + public void setLanguageSettingEntries(IProject project, List entries) { + ICommandLauncherFactory factory = getBestFactory(project); + if (factory != null) { + factory.registerLanguageSettingEntries(project, entries); + } + } + + public List getLanguageSettingEntries(IProject project, List entries) { + List verifiedEntries = entries; + ICommandLauncherFactory factory = getBestFactory(project); + if (factory != null) { + verifiedEntries = factory.verifyLanguageSettingEntries(project, entries); + } + return verifiedEntries; + } + +} diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ICommandLauncherFactory.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ICommandLauncherFactory.java new file mode 100644 index 00000000000..7da2397e784 --- /dev/null +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ICommandLauncherFactory.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2017 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 API and implementation + *******************************************************************************/ +package org.eclipse.cdt.core; + +import java.util.List; + +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; +import org.eclipse.core.resources.IProject; + +/** + * @since 6.3 + */ +public interface ICommandLauncherFactory { + + /** + * Get a Command Launcher for a project (based on active configuration) + * @param project - IProject to get command launcher for + * @return ICommandLauncher + */ + public ICommandLauncher getCommandLauncher(IProject project); + + /** + * Get a Command Launcher for a build configuration descriptor + * @param cfgd - ICConfigurationDescription to get command launcher for + * @return ICommandLauncher + */ + public ICommandLauncher getCommandLauncher(ICConfigurationDescription cfgd); + + /** + * Register language setting entries for a project + * @param project - IProject used in obtaining language setting entries + * @param entries - List of language setting entries + */ + public void registerLanguageSettingEntries(IProject project, List entries); + + /** + * Verify language setting entries for a project and change any entries that + * have been copied to a local location + * @param project - IProject used in obtaining language setting entries + * @param entries - List of language setting entries + * @return modified List of language setting entries + */ + public List verifyLanguageSettingEntries(IProject project, List entries); + +} diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/ProcessClosure.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/ProcessClosure.java index 771cb6e4b0b..668457e1d75 100644 --- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/ProcessClosure.java +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/ProcessClosure.java @@ -217,14 +217,18 @@ public class ProcessClosure { fProcess.destroy(); fProcess = null; } - if (!fOutputReader.finished()) { - fOutputReader.waitFor(); + if (fOutputReader != null) { + if (!fOutputReader.finished()) { + fOutputReader.waitFor(); + } + fOutputReader.close(); } - if (!fErrorReader.finished()) { - fErrorReader.waitFor(); + if (fErrorReader != null) { + if (!fErrorReader.finished()) { + fErrorReader.waitFor(); + } + fErrorReader.close(); } - fOutputReader.close(); - fErrorReader.close(); fOutputReader = null; fErrorReader = null; } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/language/settings/providers/LanguageSettingsEntriesTab.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/language/settings/providers/LanguageSettingsEntriesTab.java index 91851f784b4..a7c5be0c706 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/language/settings/providers/LanguageSettingsEntriesTab.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/language/settings/providers/LanguageSettingsEntriesTab.java @@ -28,6 +28,7 @@ import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; +import org.eclipse.jface.layout.PixelConverter; import org.eclipse.jface.viewers.IDecoration; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.TreeViewer; @@ -39,6 +40,7 @@ import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; @@ -375,7 +377,7 @@ public class LanguageSettingsEntriesTab extends AbstractCPropertyTab { */ private void createTreeForEntries(Composite parent) { treeEntries = new Tree(parent, SWT.BORDER | SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL); - treeEntries.setLayoutData(new GridData(GridData.FILL_VERTICAL)); + treeEntries.setLayoutData(new GridData(GridData.FILL_VERTICAL | GridData.GRAB_HORIZONTAL)); treeEntries.setHeaderVisible(true); treeEntries.setLinesVisible(true); @@ -383,13 +385,14 @@ public class LanguageSettingsEntriesTab extends AbstractCPropertyTab { treeEntries.addPaintListener(new PaintListener() { @Override public void paintControl(PaintEvent e) { - int x = treeEntries.getClientArea().width; - if (treeCol.getWidth() != x) - treeCol.setWidth(x); + Point p = treeEntries.computeSize(SWT.DEFAULT, SWT.DEFAULT); + if (treeCol.getWidth() != p.x) + treeCol.setWidth(p.x); } }); treeCol.setText(Messages.LanguageSettingsProviderTab_SettingEntries); + treeCol.setWidth(200); treeCol.setResizable(false); treeCol.setToolTipText(Messages.LanguageSettingsProviderTab_SettingEntriesTooltip); diff --git a/doc/org.eclipse.cdt.doc.user/images/prop_container.png b/doc/org.eclipse.cdt.doc.user/images/prop_container.png new file mode 100644 index 0000000000000000000000000000000000000000..e6e47bbf8b53a1e2b4b07d655282d0e6c8914d21 GIT binary patch literal 41949 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 Z{@*e?`^QXI7#UOT;PdbXW7_+-1Z>84D(Gy~ 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 YZnU3#^;z0^_^M=U?RcvCy9){b0XO9%LjV8( literal 0 HcmV?d00001 diff --git a/doc/org.eclipse.cdt.doc.user/images/prop_datavolumes.png b/doc/org.eclipse.cdt.doc.user/images/prop_datavolumes.png new file mode 100644 index 0000000000000000000000000000000000000000..1dea453d4bd4d4e224f980b8b2eedf7b7f627902 GIT binary patch literal 22638 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%0Aj=a;YzU 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 literal 0 HcmV?d00001 diff --git a/doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_build.htm b/doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_build.htm index e446b5934fa..5e7b923b63d 100644 --- a/doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_build.htm +++ b/doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_build.htm @@ -178,6 +178,7 @@ and, moreover, change the visibility of other property pages.
  • Settings page
    • Tool Settings tab +
    • Container Settings tab
    • Build Steps tab
    • Build Artifact tab
    • Binary Parsers tab diff --git a/doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_build_settings_artifact.htm b/doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_build_settings_artifact.htm index 94b36ef1be3..9a1f7ea59dc 100644 --- a/doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_build_settings_artifact.htm +++ b/doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_build_settings_artifact.htm @@ -75,6 +75,7 @@
    • Settings page
      • Tool Settings tab +
      • Container Settings tab
      • Build Steps tab
      • Build Artifact tab
      • Binary Parsers tab diff --git a/doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_build_settings_binparser.htm b/doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_build_settings_binparser.htm index 2fc03874cad..06f58a92e1a 100644 --- a/doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_build_settings_binparser.htm +++ b/doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_build_settings_binparser.htm @@ -85,6 +85,7 @@ symbols of the object file using the C/C++ Projects view.

      • Settings page
        • Tool Settings tab +
        • Container Settings tab
        • Build Steps tab
        • Build Artifact tab
        • Binary Parsers tab diff --git a/doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_build_settings_container.htm b/doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_build_settings_container.htm new file mode 100644 index 00000000000..983542a1a90 --- /dev/null +++ b/doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_build_settings_container.htm @@ -0,0 +1,166 @@ + + + + + + C/C++ Project Properties, Build, Settings, Container Settings + + + +

          C/C++ Project Properties, Build, Settings, Container Settings

          +

          Use the Container Settings properties tab to +specify options for building the project in a Docker Container. This properties tab is +provided with the optional Docker C/C++ Launch feature. By default, +project configurations will build locally, however, if you want to have a particular +configuration build the project in a Container, use the enablement check-box. +After enabling building in a Container, you need to select an active Docker Connection and +a down-loaded Image in that Connection to run the build in. The Image must have whatever +tools are necessary to perform the build already installed. Docker Images will be linux-based so the build +will use linux appropriate toolsets and create a linux-compatible executable.

          +

          By default, the C/C++ project and all dependent project contents will be mounted +into the Container where the build is occurring. This allows the build to find project source and +header files. If additional host directories are needed to +build the project (e.g. a local header file directory that is not part of an +installed package in the Docker Image), such directories can be added to the Data Volumes table and they +will be mounted in the Container before the build occurs. If the Docker daemon is running locally, this +will be a simple mount. If the Docker daemon is remote, +mounting will involve automatically copying the directory contents over to the Container since system mounting cannot +occur across machines.

          +

          To accommodate indexing, build-specs discovery will be performed in the Docker Container. Directories found +by specs detection will be copied locally for use by the indexer. This copying process also occurs for any +header file directories discovered while building. The +header files are stored within the workspace and are only uploaded once per Docker Image. These header files +can be removed from the Preferences page if, for example, the project no longer needs to build/index for that +particular Docker Image.

          + +

          C/C++ Project Properties, Build, Settings, Container Settings

          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          Container settings options
          GroupDescription
          ConfigurationClick here for a description.
          Manage Configurations...Click here for a description.
          Build inside Docker Image (check-box)Enables the build to occur inside a Container.
          ConnectionPull-down that contains a list of known Docker daemon connections to run the build in if enablement button is checked.
          ImagePull-down that contains Docker Image names to use to create the Container the build will run in. This list is enabled by + a Docker Connection selection and the enablement check-box.
          Data VolumesOptional list of directories to mount into the Container prior to building. By default, the C/C++ project and any refererenced + projects are mounted automatically in the Container. Directories can be added, edited, or removed using the buttons to the + right of the list.
          Add...Button to bring up the Data Volume dialog for specifying a + data volume to mount in the Docker Container. This usually involves selecting a directory from the local system. Once finished, + the mount will be shown in the Data Volumes list.
          Edit...Button to edit the currently selected data volume in the Data Volumes list.
          RemoveButton to remove the currently selected data volume(s) in the Data Volumes list.
          +
          +

          Available for: CDT projects.

          + +

          Related tasks +
          +

          + + +

          Related reference
          + + +

          +

          +
          +

          Intel Copyright Statement

          + +
          + diff --git a/doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_build_settings_errparser.htm b/doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_build_settings_errparser.htm index 566c8c54473..9de52de6876 100644 --- a/doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_build_settings_errparser.htm +++ b/doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_build_settings_errparser.htm @@ -88,6 +88,7 @@ build output log.

        • Settings page
          • Tool Settings tab +
          • Container Settings tab
          • Build Steps tab
          • Build Artifact tab
          • Binary Parsers tab diff --git a/doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_build_settings_steps.htm b/doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_build_settings_steps.htm index 39c3a44e902..3ed28ad752e 100644 --- a/doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_build_settings_steps.htm +++ b/doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_build_settings_steps.htm @@ -99,6 +99,7 @@ including pre-build or post-build steps.
          • Settings page
            • Tool Settings tab +
            • Container Settings tab
            • Build Steps tab
            • Build Artifact tab
            • Binary Parsers tab diff --git a/doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_build_settings_tool.htm b/doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_build_settings_tool.htm index 7d0a6d548a2..d999fa31f02 100644 --- a/doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_build_settings_tool.htm +++ b/doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_build_settings_tool.htm @@ -65,6 +65,7 @@ Use the Tool Settings properties tab to customize the tools and
            • Settings page
              • Tool Settings tab +
              • Container Settings tab
              • Build Steps tab
              • Build Artifact tab
              • Binary Parsers tab diff --git a/doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_data_volumes.htm b/doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_data_volumes.htm new file mode 100644 index 00000000000..8515ae8e1e4 --- /dev/null +++ b/doc/org.eclipse.cdt.doc.user/reference/cdt_u_prop_data_volumes.htm @@ -0,0 +1,149 @@ + + + + + + C/C++ Project Properties, Build, Settings, Data Volume Dialog + + + +

                C/C++ Project Properties, Build, Settings, Data Volume Dialog

                +

                The Data Volume dialog is brought up when a user selects the +Add... button from the Container Settings Data Volumes table. Like the properties tab, +this dialog is only available if the optional Docker C/C++ Launch feature is installed.

                +

                Data volumes can be specified from the local file system, another Container, or created +as empty, in the Docker Container where the build will be performed.

                + +

                C/C++ Project Properties, Build, Settings, Data Volume Dialog

                + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                Data Volume Dialog
                GroupDescription
                Container pathThis defines the path that the data volume will be mounted as within the Docker Container.
                No external mountUse this option to create a new empty directory in the Docker Container that is not mounted + to any external directory.
                Mount a host directory or host fileUse this to specify a local system path that will be mounted in the Docker Container with + the given Container path.
                PathThis is the local path of the directory or file to mount in the Docker Container. This + can be specified manually or via the Directory/File buttons.
                DirectoryUse this button to bring up a local directory browser to fill in the Path text field + when mounting a local directory in the Docker Container.
                FileUse this button to bring up a local file browser to fill in the Path text field + when mounting a local file in the Docker Container.
                Read-only accessCheck this button if mounting a local directory or file and you do not wish the + Container to modify it.
                Mount a data volume containerCheck this button if mounting the volumes of another Container.
                ContainerThis is a pull-down containing actively running Containers to use data volumes from if + the option above is chosen.
                +
                +

                Available for: CDT projects.

                + +

                Related tasks +
                +

                + + +

                Related reference
                + + +

                +

                +
                +

                Intel Copyright Statement

                + +
                + diff --git a/doc/org.eclipse.cdt.doc.user/reference/cdt_u_properties.htm b/doc/org.eclipse.cdt.doc.user/reference/cdt_u_properties.htm index 06b57c23857..23611f6a4c1 100644 --- a/doc/org.eclipse.cdt.doc.user/reference/cdt_u_properties.htm +++ b/doc/org.eclipse.cdt.doc.user/reference/cdt_u_properties.htm @@ -39,6 +39,7 @@ To select object properties, right click on object in view and select Pr
              • Settings page
                • Tool Settings tab +
                • Container Settings tab
                • Build Steps tab
                • Build Artifact tab
                • Binary Parsers tab diff --git a/launch/org.eclipse.cdt.docker.launcher/META-INF/MANIFEST.MF b/launch/org.eclipse.cdt.docker.launcher/META-INF/MANIFEST.MF index c24f2f49390..8eb9ae3603f 100644 --- a/launch/org.eclipse.cdt.docker.launcher/META-INF/MANIFEST.MF +++ b/launch/org.eclipse.cdt.docker.launcher/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Plugin.name Bundle-SymbolicName: org.eclipse.cdt.docker.launcher;singleton:=true -Bundle-Version: 1.0.0.qualifier +Bundle-Version: 1.1.0.qualifier Bundle-Activator: org.eclipse.cdt.docker.launcher.DockerLaunchUIPlugin Bundle-Vendor: %Plugin.vendor Bundle-Localization: plugin @@ -20,7 +20,14 @@ Require-Bundle: org.eclipse.ui, org.eclipse.cdt.debug.ui;bundle-version="7.5.0", org.eclipse.cdt.dsf.gdb;bundle-version="4.6.0", org.eclipse.cdt.dsf.gdb.ui;bundle-version="2.4.0", - org.eclipse.core.variables + org.eclipse.core.variables, + org.eclipse.cdt.managedbuilder.ui, + org.eclipse.cdt.managedbuilder.core, + org.eclipse.core.databinding.observable, + org.eclipse.core.databinding.beans, + org.eclipse.jface.databinding, + org.eclipse.core.databinding;bundle-version="1.6.0", + org.eclipse.core.databinding.property;bundle-version="1.6.0" Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy Export-Package: org.eclipse.cdt.docker.launcher;x-internal:=true, diff --git a/launch/org.eclipse.cdt.docker.launcher/icons/error_obj.gif b/launch/org.eclipse.cdt.docker.launcher/icons/error_obj.gif new file mode 100644 index 0000000000000000000000000000000000000000..0bc60689c6dcbfb71770789a3c2e98f51f6eecd7 GIT binary patch literal 339 zcmZ?wbhEHb6krfwxXQrrHNWU4 z&?^&f5)>{J;LlXy?-rr-`SJ62+pK*(^AAm4c5K#~(~CBr zU$Nuznmt$VKL7sY&CloWe!l(m>+`qYcb#j0_BB3_2hs zAU`p%RXNNq@X!&8J)v?*qeO7tg_+%)w^_KZF|7S&w8(_>$ialnizzFTas*DgFiiZR z5E_#x!R=bn6DS%fAi&hzBB0``Dq2yM#3ridsxHnyWh%e8x~mdjcQqTIva5o~lBG)( hUFC%5_6p0p$_NSZZrLg%<0`vr_a0fF{mzaI)&RuOq0ayS literal 0 HcmV?d00001 diff --git a/launch/org.eclipse.cdt.docker.launcher/icons/folder_closed.gif b/launch/org.eclipse.cdt.docker.launcher/icons/folder_closed.gif new file mode 100644 index 0000000000000000000000000000000000000000..42e027c933103acb0bc3a6889b1ee253bdfa4657 GIT binary patch literal 219 zcmZ?wbhEHb6krfwIKsg2=fjTwU(ft}x$*zUL!a)i`2TwI|EEj--|GB-f7+YNbN*i} z{eQLg>4}NkYYq1|o9u5f+1+4#yxaU}r`gdi^F!@s8_V^#R2r-=)!$xYw6WY^bAj4_ zFkpZI#h)yU3=A?1Iv@d%oeZoI3hI3+ne)<@xV>1B`(fEuK?V+&Lu+13upU?-aN>~% z+leJYmV$v?7gmUP30m^pSR?jZz)|4A1_>U)M7|eWr0xntihbB2lP8!e@?(!&ykzXZ OhQ@l`mR1!-25SJE4`R#! literal 0 HcmV?d00001 diff --git a/launch/org.eclipse.cdt.docker.launcher/icons/warning_obj.gif b/launch/org.eclipse.cdt.docker.launcher/icons/warning_obj.gif new file mode 100644 index 0000000000000000000000000000000000000000..1e5f5eb367c8afd38524d2ef4bda50a7d3f2bf41 GIT binary patch literal 337 zcmZ?wbhEHb6krfwxXQrr|8~*;dyW4e_5Od<|NqIf|Bt8te>(U7vw8pD9sGYH>i^m7 z{}+n?Uupb*wdw!W=Kpu5|Gzi)|MMOH&*W4G3O9s`Hie5dhKhAYOSDHz^u$R`Ns&F; ztb4Lu|5%IOu~xma-9{(d4NrF&pY1liGsFJ=Y^MivoF6U?J>712zT5O-ui5SC4)5DtO}Hh?yJlt(pV!feMjNLpIXs?}$PiGTC(p>)%E_o$maD)d w#4jPm&&8~sqc6k4Eg&W#z|AT%O;(qUec3Wyxkl+VYuA)}`E2s@b7Zgv0ExwgEdT%j literal 0 HcmV?d00001 diff --git a/launch/org.eclipse.cdt.docker.launcher/plugin.properties b/launch/org.eclipse.cdt.docker.launcher/plugin.properties index 0669aa80aad..245288bb62e 100644 --- a/launch/org.eclipse.cdt.docker.launcher/plugin.properties +++ b/launch/org.eclipse.cdt.docker.launcher/plugin.properties @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2015 Red Hat Inc. and others. +# Copyright (c) 2015,2017 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 @@ -15,4 +15,10 @@ Delegate.desc=This launcher runs C/C++ applications in a specified Docker Contai must be set-up to supply the C/C++ application what it needs to run. LaunchConfigurationType.name=C/C++ Container Launcher Shortcut.label=C/C++ Container Application -DockerLaunchPreferencePage.name=Docker Container Launch +DockerLaunchPreferencePage.name=Docker Container +DockerHeaderPreferencePage.name=Cached Headers +ContainerCommandLauncherFactory.name=Container Command Launcher Factory +Container.settings=Container Settings +ContainerBuild.property.enablement=Container Build Enablement +ContainerBuild.property.connection=Container Build Connection +ContainerBuild.property.image=Container Build Image diff --git a/launch/org.eclipse.cdt.docker.launcher/plugin.xml b/launch/org.eclipse.cdt.docker.launcher/plugin.xml index a8e0a2cdb82..a9c3f67e105 100644 --- a/launch/org.eclipse.cdt.docker.launcher/plugin.xml +++ b/launch/org.eclipse.cdt.docker.launcher/plugin.xml @@ -65,6 +65,12 @@ id="org.eclipse.cdt.docker.launcher.page1" name="%DockerLaunchPreferencePage.name"> + + @@ -88,5 +94,24 @@ id="org.eclipse.cdt.docker.launcher.launchConfigurationTypeImage1"> - + + + + + + + + + diff --git a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/docker/launcher/ContainerCommandLauncherFactory.java b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/docker/launcher/ContainerCommandLauncherFactory.java new file mode 100644 index 00000000000..271f173beb0 --- /dev/null +++ b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/docker/launcher/ContainerCommandLauncherFactory.java @@ -0,0 +1,265 @@ +/******************************************************************************* + * Copyright (c) 2017 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 API and implementation + *******************************************************************************/ +package org.eclipse.cdt.docker.launcher; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import org.eclipse.cdt.core.ICommandLauncher; +import org.eclipse.cdt.core.ICommandLauncherFactory; +import org.eclipse.cdt.core.model.CoreModel; +import org.eclipse.cdt.core.settings.model.CIncludePathEntry; +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.core.settings.model.ICIncludePathEntry; +import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; +import org.eclipse.cdt.internal.docker.launcher.ContainerCommandLauncher; +import org.eclipse.cdt.internal.docker.launcher.Messages; +import org.eclipse.cdt.managedbuilder.buildproperties.IOptionalBuildProperties; +import org.eclipse.cdt.managedbuilder.core.IConfiguration; +import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Platform; +import org.eclipse.linuxtools.docker.ui.launch.ContainerLauncher; + +public class ContainerCommandLauncherFactory + implements ICommandLauncherFactory { + + @Override + public ICommandLauncher getCommandLauncher(IProject project) { + // check if container build enablement has been checked + ICConfigurationDescription cfgd = CoreModel.getDefault() + .getProjectDescription(project) + .getActiveConfiguration(); + IConfiguration cfg = ManagedBuildManager + .getConfigurationForDescription(cfgd); + // TODO: figure out why this occurs + if (cfg == null) { + return null; + } + IOptionalBuildProperties props = cfg.getOptionalBuildProperties(); + if (props != null) { + String enablementProperty = props.getProperty( + ContainerCommandLauncher.CONTAINER_BUILD_ENABLED); + if (enablementProperty != null) { + boolean enableContainer = Boolean + .parseBoolean(enablementProperty); + // enablement has occurred, we can return a + // ContainerCommandLauncher + if (enableContainer) { + return new ContainerCommandLauncher(); + } + } + } + return null; + } + + @Override + public ICommandLauncher getCommandLauncher( + ICConfigurationDescription cfgd) { + // check if container build enablement has been checked + IConfiguration cfg = ManagedBuildManager + .getConfigurationForDescription(cfgd); + // TODO: figure out why this occurs + if (cfg == null) { + return null; + } + IOptionalBuildProperties props = cfg.getOptionalBuildProperties(); + if (props != null) { + String enablementProperty = props.getProperty( + ContainerCommandLauncher.CONTAINER_BUILD_ENABLED); + if (enablementProperty != null) { + boolean enableContainer = Boolean + .parseBoolean(enablementProperty); + // enablement has occurred, we can return a + // ContainerCommandLauncher + if (enableContainer) { + return new ContainerCommandLauncher(); + } + } + } + return null; + } + + @Override + public void registerLanguageSettingEntries(IProject project, + List langEntries) { + @SuppressWarnings("unchecked") + List entries = (List) langEntries; + ICConfigurationDescription cfgd = CoreModel.getDefault() + .getProjectDescription(project).getActiveConfiguration(); + IConfiguration cfg = ManagedBuildManager + .getConfigurationForDescription(cfgd); + IOptionalBuildProperties props = cfg.getOptionalBuildProperties(); + if (props != null) { + String enablementProperty = props.getProperty( + ContainerCommandLauncher.CONTAINER_BUILD_ENABLED); + if (enablementProperty != null) { + boolean enableContainer = Boolean + .parseBoolean(enablementProperty); + if (enableContainer) { + String connectionName = props.getProperty( + ContainerCommandLauncher.CONNECTION_ID); + String imageName = props + .getProperty(ContainerCommandLauncher.IMAGE_ID); + if (connectionName == null || connectionName.isEmpty() + || imageName == null || imageName.isEmpty()) { + DockerLaunchUIPlugin.logErrorMessage( + Messages.ContainerCommandLauncher_invalid_values); + return; + } + ContainerLauncher launcher = new ContainerLauncher(); + List paths = new ArrayList<>(); + for (ICLanguageSettingEntry entry : entries) { + if (entry instanceof ICIncludePathEntry) { + paths.add(entry.getValue()); + } + } + if (paths.size() > 0) { + // Create a directory to put the header files for + // the image. Use the connection name to form + // the directory name as the connection may be + // connected to a different repo using the same + // image name. + IPath pluginPath = Platform.getStateLocation(Platform + .getBundle(DockerLaunchUIPlugin.PLUGIN_ID)) + .append("HEADERS"); //$NON-NLS-1$ + pluginPath.toFile().mkdir(); + pluginPath = pluginPath + .append(getCleanName(connectionName)); + pluginPath.toFile().mkdir(); + // To allow the user to later manage the headers, store + // the + // real connection name in a file. + IPath connectionNamePath = pluginPath.append(".name"); //$NON-NLS-1$ + File f = connectionNamePath.toFile(); + try { + f.createNewFile(); + try (FileWriter writer = new FileWriter(f); + BufferedWriter bufferedWriter = new BufferedWriter( + writer);) { + bufferedWriter.write(connectionName); + bufferedWriter.newLine(); + } catch (IOException e) { + DockerLaunchUIPlugin.log(e); + return; + } + pluginPath = pluginPath + .append(getCleanName(imageName)); + pluginPath.toFile().mkdir(); + // To allow the user to later manage the headers, + // store the + // real image name in a file. + IPath imageNamePath = pluginPath.append(".name"); //$NON-NLS-1$ + f = imageNamePath.toFile(); + f.createNewFile(); + try (FileWriter writer = new FileWriter(f); + BufferedWriter bufferedWriter = new BufferedWriter( + writer);) { + bufferedWriter.write(imageName); + bufferedWriter.newLine(); + } catch (IOException e) { + DockerLaunchUIPlugin.log(e); + return; + } + } catch (IOException e) { + DockerLaunchUIPlugin.log(e); + return; + } + IPath hostDir = pluginPath; + @SuppressWarnings("unused") + int status = launcher.fetchContainerDirs(connectionName, + imageName, + paths, hostDir); + } + } + } + } + + } + + @Override + public List verifyLanguageSettingEntries( + IProject project, List entries) { + if (entries == null) { + return null; + } + ICConfigurationDescription cfgd = CoreModel.getDefault() + .getProjectDescription(project).getActiveConfiguration(); + IConfiguration cfg = ManagedBuildManager + .getConfigurationForDescription(cfgd); + IOptionalBuildProperties props = cfg.getOptionalBuildProperties(); + if (props != null) { + String enablementProperty = props.getProperty( + ContainerCommandLauncher.CONTAINER_BUILD_ENABLED); + if (enablementProperty != null) { + boolean enableContainer = Boolean + .parseBoolean(enablementProperty); + if (enableContainer) { + String connectionName = props.getProperty( + ContainerCommandLauncher.CONNECTION_ID); + String imageName = props + .getProperty(ContainerCommandLauncher.IMAGE_ID); + if (connectionName == null || connectionName.isEmpty() + || imageName == null || imageName.isEmpty()) { + DockerLaunchUIPlugin.logErrorMessage( + Messages.ContainerCommandLauncher_invalid_values); + return entries; + } + + ContainerLauncher launcher = new ContainerLauncher(); + Set copiedVolumes = launcher + .getCopiedVolumes(connectionName, imageName); + List newEntries = new ArrayList<>(); + IPath pluginPath = Platform.getStateLocation( + Platform.getBundle(DockerLaunchUIPlugin.PLUGIN_ID)); + IPath hostDir = pluginPath.append("HEADERS") //$NON-NLS-1$ + .append(getCleanName(connectionName)) + .append(getCleanName(imageName)); + + for (ICLanguageSettingEntry entry : entries) { + if (entry instanceof ICIncludePathEntry) { + if (copiedVolumes + .contains(((ICIncludePathEntry) entry) + .getName().toString())) { + // //$NON-NLS-2$ + IPath newPath = hostDir.append(entry.getName()); + CIncludePathEntry newEntry = new CIncludePathEntry( + newPath.toString(), + entry.getFlags()); + newEntries.add(newEntry); + continue; + } else { + newEntries.add(entry); + } + } else { + newEntries.add(entry); + } + } + return newEntries; + } + } + } + return entries; + } + + private String getCleanName(String name) { + String cleanName = name.replace("unix:///", "unix_"); //$NON-NLS-1$ //$NON-NLS-2$ + cleanName = cleanName.replace("tcp://", "tcp_"); //$NON-NLS-1$ //$NON-NLS-2$ + return cleanName.replaceAll("[:/.]", "_"); //$NON-NLS-1$ //$NON-NLS-2$ + } + +} diff --git a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/BaseDatabindingModel.java b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/BaseDatabindingModel.java new file mode 100644 index 00000000000..b17d89701ac --- /dev/null +++ b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/BaseDatabindingModel.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2014, 2015 Red Hat. + * 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 - Initial Contribution + *******************************************************************************/ + +package org.eclipse.cdt.internal.docker.launcher; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; + +/** + * Base class for all model classes that need Databinding support + * + */ +public abstract class BaseDatabindingModel { + + private PropertyChangeSupport changeSupport = new PropertyChangeSupport(this); + + public void addPropertyChangeListener(PropertyChangeListener listener) { + changeSupport.addPropertyChangeListener(listener); + } + + public void removePropertyChangeListener(PropertyChangeListener listener) { + changeSupport.removePropertyChangeListener(listener); + } + + public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { + changeSupport.addPropertyChangeListener(propertyName, listener); + } + + public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { + changeSupport.removePropertyChangeListener(propertyName, listener); + } + + protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { + changeSupport.firePropertyChange(propertyName, oldValue, newValue); + } +} diff --git a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerCommandLauncher.java b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerCommandLauncher.java new file mode 100644 index 00000000000..79db65bd631 --- /dev/null +++ b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerCommandLauncher.java @@ -0,0 +1,429 @@ +package org.eclipse.cdt.internal.docker.launcher; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.eclipse.cdt.core.ICommandLauncher; +import org.eclipse.cdt.core.model.CoreModel; +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.docker.launcher.DockerLaunchUIPlugin; +import org.eclipse.cdt.internal.core.ProcessClosure; +import org.eclipse.cdt.managedbuilder.buildproperties.IOptionalBuildProperties; +import org.eclipse.cdt.managedbuilder.core.IConfiguration; +import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.preferences.InstanceScope; +import org.eclipse.core.variables.VariablesPlugin; +import org.eclipse.linuxtools.docker.ui.launch.ContainerLauncher; +import org.eclipse.linuxtools.docker.ui.launch.IErrorMessageHolder; +import org.eclipse.linuxtools.internal.docker.ui.launch.ContainerCommandProcess; +import org.eclipse.osgi.util.NLS; +import org.osgi.service.prefs.Preferences; + +@SuppressWarnings("restriction") +public class ContainerCommandLauncher + implements ICommandLauncher, IErrorMessageHolder { + + public final static String CONTAINER_BUILD_ENABLED = DockerLaunchUIPlugin.PLUGIN_ID + + ".containerbuild.property.enablement"; //$NON-NLS-1$ + public final static String CONNECTION_ID = DockerLaunchUIPlugin.PLUGIN_ID + + ".containerbuild.property.connection"; //$NON-NLS-1$ + public final static String IMAGE_ID = DockerLaunchUIPlugin.PLUGIN_ID + + ".containerbuild.property.image"; //$NON-NLS-1$ + public final static String VOLUMES_ID = DockerLaunchUIPlugin.PLUGIN_ID + + ".containerbuild.property.volumes"; //$NON-NLS-1$ + public final static String SELECTED_VOLUMES_ID = DockerLaunchUIPlugin.PLUGIN_ID + + ".containerbuild.property.selectedvolumes"; //$NON-NLS-1$ + + public final static String VOLUME_SEPARATOR_REGEX = "[|]"; //$NON-NLS-1$ + + private IProject fProject; + private Process fProcess; + private boolean fShowCommand; + private String fErrorMessage; + private Properties fEnvironment; + + private String[] commandArgs; + private String fImageName = ""; //$NON-NLS-1$ + + public final static int COMMAND_CANCELED = ICommandLauncher.COMMAND_CANCELED; + public final static int ILLEGAL_COMMAND = ICommandLauncher.ILLEGAL_COMMAND; + public final static int OK = ICommandLauncher.OK; + + private static final String NEWLINE = System.getProperty("line.separator", //$NON-NLS-1$ + "\n"); //$NON-NLS-1$ + + /** + * The number of milliseconds to pause between polling. + */ + protected static final long DELAY = 50L; + + @Override + public void setProject(IProject project) { + this.fProject = project; + } + + @Override + public IProject getProject() { + return fProject; + } + + private String getImageName() { + return fImageName; + } + + private void setImageName(String imageName) { + fImageName = imageName; + } + + @Override + public void showCommand(boolean show) { + this.fShowCommand = show; + } + + @Override + public String getErrorMessage() { + return fErrorMessage; + } + + @Override + public void setErrorMessage(String error) { + fErrorMessage = error; + } + + @Override + public String[] getCommandArgs() { + return commandArgs; + } + + @Override + public Properties getEnvironment() { + return fEnvironment; + } + + @Override + public String getCommandLine() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Process execute(IPath commandPath, String[] args, String[] env, + IPath workingDirectory, IProgressMonitor monitor) + throws CoreException { + + HashMap labels = new HashMap<>(); + labels.put("org.eclipse.cdt.container-command", ""); //$NON-NLS-1$ //$NON-NLS-2$ + String projectName = fProject.getName(); + labels.put("org.eclipse.cdt.project-name", projectName); //$NON-NLS-1$ + + List additionalDirs = new ArrayList<>(); + + // + IPath projectLocation = fProject.getLocation(); + String projectPath = projectLocation.toPortableString(); + if (projectLocation.getDevice() != null) { + projectPath = "/" + projectPath.replace(':', '/'); //$NON-NLS-1$ + } + additionalDirs.add(projectPath); + + ArrayList commandSegments = new ArrayList<>(); + + StringBuilder b = new StringBuilder(); + String commandString = commandPath.toPortableString(); + if (commandPath.getDevice() != null) { + commandString = "/" + commandString.replace(':', '/'); //$NON-NLS-1$ + } + b.append(commandString); + commandSegments.add(commandString); + for (String arg : args) { + b.append(" "); //$NON-NLS-1$ + String realArg = VariablesPlugin.getDefault() + .getStringVariableManager().performStringSubstitution(arg); + if (Platform.getOS().equals(Platform.OS_WIN32)) { + // check if file exists and if so, add an additional directory + IPath p = new Path(realArg); + if (p.isValidPath(realArg) && p.getDevice() != null) { + File f = p.toFile(); + String modifiedArg = realArg; + // if the directory of the arg as a file exists, we mount it + // and modify the argument to be unix-style + if (f.isFile()) { + f = f.getParentFile(); + modifiedArg = "/" //$NON-NLS-1$ + + p.toPortableString().replace(':', '/'); + p = p.removeLastSegments(1); + } + if (f != null && f.exists()) { + additionalDirs.add( + "/" + p.toPortableString().replace(':', '/')); //$NON-NLS-1$ + realArg = modifiedArg; + } + } + } else if (realArg.startsWith("/")) { //$NON-NLS-1$ + // check if file directory exists and if so, add an additional + // directory + IPath p = new Path(realArg); + if (p.isValidPath(realArg)) { + File f = p.toFile(); + if (f.isFile()) { + f = f.getParentFile(); + } + if (f != null && f.exists()) { + additionalDirs.add(f.getAbsolutePath()); + } + } + } + b.append(realArg); + commandSegments.add(realArg); + } + + commandArgs = commandSegments.toArray(new String[0]); + + String commandDir = commandPath.removeLastSegments(1).toString(); + if (commandDir.isEmpty()) { + commandDir = null; + } else if (commandPath.getDevice() != null) { + commandDir = "/" + commandDir.replace(':', '/'); //$NON-NLS-1$ + } + + IProject[] referencedProjects = fProject.getReferencedProjects(); + for (IProject referencedProject : referencedProjects) { + String referencedProjectPath = referencedProject.getLocation() + .toPortableString(); + if (referencedProject.getLocation().getDevice() != null) { + referencedProjectPath = "/" //$NON-NLS-1$ + + referencedProjectPath.replace(':', '/'); + } + additionalDirs + .add(referencedProjectPath); + } + + String command = b.toString(); + + String workingDir = workingDirectory.makeAbsolute().toPortableString(); + if (workingDirectory.toPortableString().equals(".")) { //$NON-NLS-1$ + workingDir = "/tmp"; //$NON-NLS-1$ + } else if (workingDirectory.getDevice() != null) { + workingDir = "/" + workingDir.replace(':', '/'); //$NON-NLS-1$ + } + parseEnvironment(env); + Map origEnv = null; + + boolean supportStdin = false; + + boolean privilegedMode = false; + + ContainerLauncher launcher = new ContainerLauncher(); + + Preferences prefs = InstanceScope.INSTANCE + .getNode(DockerLaunchUIPlugin.PLUGIN_ID); + + boolean keepContainer = prefs.getBoolean( + PreferenceConstants.KEEP_CONTAINER_AFTER_LAUNCH, false); + + ICConfigurationDescription cfgd = CoreModel.getDefault() + .getProjectDescription(fProject).getActiveConfiguration(); + IConfiguration cfg = ManagedBuildManager + .getConfigurationForDescription(cfgd); + if (cfg == null) { + return null; + } + IOptionalBuildProperties props = cfg.getOptionalBuildProperties(); + + // Add any specified volumes to additional dir list + String selectedVolumeString = props.getProperty(SELECTED_VOLUMES_ID); + if (selectedVolumeString != null && !selectedVolumeString.isEmpty()) { + String[] selectedVolumes = selectedVolumeString + .split(VOLUME_SEPARATOR_REGEX); + if (Platform.getOS().equals(Platform.OS_WIN32)) { + for (String selectedVolume : selectedVolumes) { + IPath path = new Path(selectedVolume); + String selectedPath = path.toPortableString(); + if (path.getDevice() != null) { + selectedPath = "/" + selectedPath.replace(':', '/'); //$NON-NLS-1$ + } + additionalDirs.add(selectedPath); + } + } else { + additionalDirs.addAll(Arrays.asList(selectedVolumes)); + } + } + + String connectionName = props + .getProperty(ContainerCommandLauncher.CONNECTION_ID); + if (connectionName == null) { + return null; + } + String imageName = props + .getProperty(ContainerCommandLauncher.IMAGE_ID); + if (imageName == null) { + return null; + } + setImageName(imageName); + + fProcess = launcher.runCommand(connectionName, imageName, fProject, + this, + command, + commandDir, + workingDir, + additionalDirs, + origEnv, fEnvironment, supportStdin, privilegedMode, + labels, keepContainer); + + return fProcess; + } + + /** + * Parse array of "ENV=value" pairs to Properties. + */ + private void parseEnvironment(String[] env) { + fEnvironment = null; + if (env != null) { + fEnvironment = new Properties(); + for (String envStr : env) { + // Split "ENV=value" and put in Properties + int pos = envStr.indexOf('='); // $NON-NLS-1$ + if (pos < 0) + pos = envStr.length(); + String key = envStr.substring(0, pos); + String value = envStr.substring(pos + 1); + fEnvironment.put(key, value); + } + } + } + + @Override + public int waitAndRead(OutputStream out, OutputStream err) { + printImageHeader(out); + + if (fShowCommand) { + printCommandLine(out); + } + + if (fProcess == null) { + return ILLEGAL_COMMAND; + } + ProcessClosure closure = new ProcessClosure(fProcess, out, err); + closure.runBlocking(); // a blocking call + return OK; + } + + @Override + public int waitAndRead(OutputStream output, OutputStream err, + IProgressMonitor monitor) { + printImageHeader(output); + + if (fShowCommand) { + printCommandLine(output); + } + + if (fProcess == null) { + return ILLEGAL_COMMAND; + } + + ProcessClosure closure = new ProcessClosure(fProcess, output, err); + closure.runNonBlocking(); + Runnable watchProcess = () -> { + try { + fProcess.waitFor(); + } catch (InterruptedException e) { + // ignore + } + closure.terminate(); + }; + Thread t = new Thread(watchProcess); + t.start(); + while (!monitor.isCanceled() && closure.isAlive()) { + try { + Thread.sleep(DELAY); + } catch (InterruptedException ie) { + break; + } + } + try { + t.join(500); + } catch (InterruptedException e1) { + // ignore + } + int state = OK; + + // Operation canceled by the user, terminate abnormally. + if (monitor.isCanceled()) { + closure.terminate(); + state = COMMAND_CANCELED; + setErrorMessage(Messages.CommandLauncher_CommandCancelled); + } + try { + fProcess.waitFor(); + } catch (InterruptedException e) { + // ignore + } + + monitor.done(); + return state; + } + + protected void printImageHeader(OutputStream os) { + if (os != null) { + try { + os.write(NLS + .bind(Messages.ContainerCommandLauncher_image_msg, + ((ContainerCommandProcess) fProcess).getImage()) + .getBytes()); + os.write(NEWLINE.getBytes()); + os.flush(); + } catch (IOException e) { + // ignore + } + } + } + + protected void printCommandLine(OutputStream os) { + if (os != null) { + try { + os.write(getCommandLineQuoted(getCommandArgs(), true) + .getBytes()); + os.flush(); + } catch (IOException e) { + // ignore; + } + } + } + + @SuppressWarnings("nls") + private String getCommandLineQuoted(String[] commandArgs, boolean quote) { + StringBuilder buf = new StringBuilder(); + if (commandArgs != null) { + for (String commandArg : commandArgs) { + if (quote && (commandArg.contains(" ") + || commandArg.contains("\"") + || commandArg.contains("\\"))) { + commandArg = '"' + commandArg.replaceAll("\\\\", "\\\\\\\\") + .replaceAll("\"", "\\\\\"") + '"'; + } + buf.append(commandArg); + buf.append(' '); + } + buf.append(NEWLINE); + } + return buf.toString(); + } + + protected String getCommandLine(String[] commandArgs) { + return getCommandLineQuoted(commandArgs, false); + } + +} diff --git a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerDataVolumeDialog.java b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerDataVolumeDialog.java new file mode 100644 index 00000000000..8e8af909e81 --- /dev/null +++ b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerDataVolumeDialog.java @@ -0,0 +1,466 @@ +/******************************************************************************* + * Copyright (c) 2015, 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 - Initial Contribution + *******************************************************************************/ + +package org.eclipse.cdt.internal.docker.launcher; + +import java.io.File; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.cdt.internal.docker.launcher.ContainerPropertyVolumesModel.MountType; +import org.eclipse.core.databinding.Binding; +import org.eclipse.core.databinding.DataBindingContext; +import org.eclipse.core.databinding.UpdateValueStrategy; +import org.eclipse.core.databinding.beans.BeanProperties; +import org.eclipse.core.databinding.observable.IChangeListener; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.core.databinding.validation.ValidationStatus; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.jface.databinding.swt.ISWTObservableValue; +import org.eclipse.jface.databinding.swt.WidgetProperties; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.fieldassist.ComboContentAdapter; +import org.eclipse.jface.fieldassist.ContentProposal; +import org.eclipse.jface.fieldassist.ContentProposalAdapter; +import org.eclipse.jface.fieldassist.ControlDecoration; +import org.eclipse.jface.fieldassist.IContentProposal; +import org.eclipse.jface.fieldassist.IContentProposalProvider; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.layout.GridLayoutFactory; +import org.eclipse.jface.viewers.ArrayContentProvider; +import org.eclipse.jface.viewers.ComboViewer; +import org.eclipse.linuxtools.docker.core.IDockerConnection; +import org.eclipse.linuxtools.docker.core.IDockerContainer; +import org.eclipse.linuxtools.docker.core.IDockerContainerInfo; +import org.eclipse.linuxtools.internal.docker.ui.wizards.WizardMessages; +import org.eclipse.linuxtools.internal.docker.ui.wizards.WizardUtils; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.DirectoryDialog; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +/** + * @author xcoulon + * + */ +public class ContainerDataVolumeDialog extends Dialog { + + private final DataVolumeModel model; + + private final DataBindingContext dbc = new DataBindingContext(); + + private final List containerNames; + + private final IDockerConnection connection; + + public ContainerDataVolumeDialog(final Shell parentShell, + final IDockerConnection connection, + final DataVolumeModel selectedDataVolume) { + super(parentShell); + this.connection = connection; + this.model = new DataVolumeModel(selectedDataVolume); + this.containerNames = WizardUtils.getContainerNames(connection); + } + + public ContainerDataVolumeDialog(final Shell parentShell, + final IDockerConnection connection) { + super(parentShell); + this.connection = connection; + this.model = new DataVolumeModel(); + this.containerNames = WizardUtils.getContainerNames(connection); + } + + public DataVolumeModel getDataVolume() { + return model; + } + + @Override + protected void configureShell(final Shell shell) { + super.configureShell(shell); + setShellStyle(getShellStyle() | SWT.RESIZE); + shell.setText( + WizardMessages.getString("ContainerDataVolumeDialog.title")); //$NON-NLS-1$ + } + + /** + * Disable the 'OK' button by default + */ + @Override + protected Button createButton(Composite parent, int id, String label, + boolean defaultButton) { + final Button button = super.createButton(parent, id, label, + defaultButton); + if (id == IDialogConstants.OK_ID) { + button.setEnabled(false); + } + return button; + } + + @Override + protected Point getInitialSize() { + return new Point(450, super.getInitialSize().y); + } + + @Override + protected Control createDialogArea(final Composite parent) { + final Composite container = new Composite(parent, SWT.NONE); + final int COLUMNS = 3; + GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).span(1, 1) + .grab(true, true).applyTo(container); + GridLayoutFactory.fillDefaults().margins(10, 10).numColumns(COLUMNS) + .applyTo(container); + + // Container path + final Label containerPathLabel = new Label(container, SWT.NONE); + containerPathLabel.setText(WizardMessages + .getString("ContainerDataVolumeDialog.containerPathLabel")); //$NON-NLS-1$ + GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER) + .grab(false, false).applyTo(containerPathLabel); + final Text containerPathText = new Text(container, SWT.BORDER); + GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER) + .grab(true, false).applyTo(containerPathText); + final IObservableValue containerPathObservable = BeanProperties + .value(DataVolumeModel.class, DataVolumeModel.CONTAINER_PATH) + .observe(model); + dbc.bindValue( + WidgetProperties.text(SWT.Modify).observe(containerPathText), + containerPathObservable); + // mount type + final Label explanationLabel = new Label(container, SWT.NONE); + explanationLabel.setText(WizardMessages + .getString("ContainerDataVolumeDialog.explanationLabel")); //$NON-NLS-1$ + GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER) + .span(COLUMNS, 1).grab(true, false).applyTo(explanationLabel); + final int INDENT = 20; + // No mount + final Button noMountButton = new Button(container, SWT.RADIO); + noMountButton.setText(WizardMessages + .getString("ContainerDataVolumeDialog.noMountButton")); //$NON-NLS-1$ + GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER) + .indent(INDENT, 0).span(COLUMNS, 1).grab(true, false) + .applyTo(noMountButton); + bindButton(noMountButton, MountType.NONE); + // File System mount + final Button fileSystemMountButton = new Button(container, SWT.RADIO); + fileSystemMountButton.setText(WizardMessages + .getString("ContainerDataVolumeDialog.fileSystemMountButton")); //$NON-NLS-1$ + GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER) + .indent(INDENT, 0).span(COLUMNS, 1).grab(true, false) + .applyTo(fileSystemMountButton); + final Label hostPathLabel = new Label(container, SWT.NONE); + hostPathLabel.setText(WizardMessages + .getString("ContainerDataVolumeDialog.hostPathLabel")); //$NON-NLS-1$ + GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER) + .indent(2 * INDENT, SWT.DEFAULT).grab(false, false) + .applyTo(hostPathLabel); + final Text hostPathText = new Text(container, SWT.BORDER); + GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER) + .grab(true, false).applyTo(hostPathText); + final IObservableValue hostPathObservable = BeanProperties + .value(DataVolumeModel.class, DataVolumeModel.HOST_PATH_MOUNT) + .observe(model); + dbc.bindValue(WidgetProperties.text(SWT.Modify).observe(hostPathText), + hostPathObservable); + // browse for directory + final Button hostPathDirectoryButton = new Button(container, SWT.NONE); + hostPathDirectoryButton.setText(WizardMessages + .getString("ContainerDataVolumeDialog.directoryButton")); //$NON-NLS-1$ + GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER) + .grab(false, false).applyTo(hostPathDirectoryButton); + hostPathDirectoryButton.addSelectionListener(onHostDirectoryPath()); + GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER) + .grab(false, false).applyTo(new Label(container, SWT.NONE)); + // optional read-only access + final Button readOnlyButton = new Button(container, SWT.CHECK); + readOnlyButton.setText(WizardMessages + .getString("ContainerDataVolumeDialog.readOnlyButton")); //$NON-NLS-1$ + readOnlyButton.setToolTipText(WizardMessages + .getString("ContainerDataVolumeDialog.readOnlyButtonTooltip")); //$NON-NLS-1$ + GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER) + .span(COLUMNS - 2, 1).grab(true, false).applyTo(readOnlyButton); + final ISWTObservableValue readOnlyButtonObservable = WidgetProperties + .selection().observe(readOnlyButton); + dbc.bindValue(readOnlyButtonObservable, + BeanProperties + .value(DataVolumeModel.class, + DataVolumeModel.READ_ONLY_VOLUME) + .observe(model)); + // browse for file + final Button hostPathFileButton = new Button(container, SWT.NONE); + hostPathFileButton.setText(WizardMessages + .getString("ContainerDataVolumeDialog.fileButton")); //$NON-NLS-1$ + GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER) + .grab(false, false).applyTo(hostPathFileButton); + hostPathFileButton.addSelectionListener(onHostFilePath()); + bindButton(fileSystemMountButton, MountType.HOST_FILE_SYSTEM, + hostPathText, hostPathDirectoryButton, hostPathFileButton, + readOnlyButton); + + // Container mount + final Button containerMountButton = new Button(container, SWT.RADIO); + containerMountButton.setText(WizardMessages + .getString("ContainerDataVolumeDialog.containerMountButton")); //$NON-NLS-1$ + GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER) + .indent(INDENT, 0).span(COLUMNS, 1).grab(true, false) + .applyTo(containerMountButton); + final Label containerSelectionLabel = new Label(container, SWT.NONE); + containerSelectionLabel.setText(WizardMessages.getString( + "ContainerDataVolumeDialog.containerSelectionLabel")); //$NON-NLS-1$ + GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER) + .indent(2 * INDENT, SWT.DEFAULT) + .applyTo(containerSelectionLabel); + final Combo containerSelectionCombo = new Combo(container, SWT.BORDER); + GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER) + .grab(true, false).span(1, 1).applyTo(containerSelectionCombo); + new ControlDecoration(containerSelectionCombo, SWT.TOP | SWT.LEFT); + GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER) + .grab(false, false).applyTo(new Label(container, SWT.NONE)); + bindButton(containerMountButton, MountType.CONTAINER, + containerSelectionCombo); + final ComboViewer containerSelectionComboViewer = new ComboViewer( + containerSelectionCombo); + containerSelectionComboViewer + .setContentProvider(new ArrayContentProvider()); + containerSelectionComboViewer.setInput(this.containerNames); + final IObservableValue selectedContainerObservable = BeanProperties + .value(DataVolumeModel.class, DataVolumeModel.CONTAINER_MOUNT) + .observe(model); + dbc.bindValue( + WidgetProperties.selection().observe(containerSelectionCombo), + selectedContainerObservable); + new ContentProposalAdapter(containerSelectionCombo, + new ComboContentAdapter() { + @Override + public void insertControlContents(Control control, + String text, int cursorPosition) { + final Combo combo = (Combo) control; + final Point selection = combo.getSelection(); + combo.setText(text); + selection.x = text.length(); + selection.y = selection.x; + combo.setSelection(selection); + } + }, getContainerNameContentProposalProvider( + containerSelectionCombo), + null, null); + + // error message + final Composite errorContainer = new Composite(container, SWT.NONE); + GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL) + .span(COLUMNS, 1).grab(true, true).applyTo(errorContainer); + GridLayoutFactory.fillDefaults().margins(6, 6).numColumns(2) + .applyTo(errorContainer); + + final Label errorMessageIcon = new Label(errorContainer, SWT.NONE); + GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER) + .hint(20, SWT.DEFAULT) + .applyTo(errorMessageIcon); + final Label errorMessageLabel = new Label(errorContainer, SWT.NONE); + GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER) + .grab(true, false) + .applyTo(errorMessageLabel); + setupValidationSupport(errorMessageIcon, errorMessageLabel); + return container; + } + + private void setupValidationSupport(final Label errorMessageIcon, + final Label errorMessageLabel) { + for (@SuppressWarnings("unchecked") + Iterator iterator = dbc.getBindings().iterator(); iterator + .hasNext();) { + final Binding binding = iterator.next(); + binding.getModel().addChangeListener(onDataVolumeSettingsChanged( + errorMessageIcon, errorMessageLabel)); + } + } + + /** + * Binds the given {@link MountType} to the given {@link Button} when it is + * selected, and set the enablement of the associated {@link Control} at the + * same time (ie: the {@link Control} are only enabled when the given + * {@link Button} is selected. + * + * @param button + * the {@link Button} to bind + * @param mountType + * the {@link MountType} to bind to the {@link Button} + * @param controls + * the {@link Control}s to enable or disable when the Button is + * selected/unselected. + * @return + */ + private Binding bindButton(final Button button, final MountType mountType, + final Control... controls) { + return dbc.bindValue(WidgetProperties.selection().observe(button), + BeanProperties.value(DataVolumeModel.class, + DataVolumeModel.MOUNT_TYPE).observe(model), + new UpdateValueStrategy() { + @Override + public Object convert(Object value) { + if (value.equals(Boolean.TRUE)) { + setEnabled(controls, true); + return mountType; + } + setEnabled(controls, false); + return null; + } + + private void setEnabled(final Control[] controls, + final boolean enabled) { + for (Control control : controls) { + control.setEnabled(enabled); + } + } + }, new UpdateValueStrategy() { + @Override + public Object convert(final Object value) { + if (mountType.equals(value)) { + button.setEnabled(true); + } + return mountType.equals(value); + } + }); + } + + private SelectionListener onHostDirectoryPath() { + return SelectionListener.widgetSelectedAdapter(e -> { + final DirectoryDialog directoryDialog = new DirectoryDialog( + getShell()); + final String selectedPath = directoryDialog.open(); + if (selectedPath != null) { + model.setHostPathMount(selectedPath); + } + }); + } + + private SelectionListener onHostFilePath() { + return SelectionListener.widgetSelectedAdapter(e -> { + final FileDialog fileDialog = new FileDialog(getShell()); + final String selectedPath = fileDialog.open(); + if (selectedPath != null) { + model.setHostPathMount(selectedPath); + } + }); + } + + /** + * Creates an {@link IContentProposalProvider} to propose + * {@link IDockerContainer} names based on the current text. + * + * @param items + * @return + */ + private IContentProposalProvider getContainerNameContentProposalProvider( + final Combo containerSelectionCombo) { + return (contents, position) -> { + final List proposals = new ArrayList<>(); + for (String containerName : containerSelectionCombo.getItems()) { + if (containerName.contains(contents)) { + proposals.add(new ContentProposal(containerName, + containerName, containerName, position)); + } + } + return proposals.toArray(new IContentProposal[0]); + }; + } + + private IChangeListener onDataVolumeSettingsChanged( + final Label errorMessageIcon, final Label errorMessageLabel) { + + return event -> { + // skip if dialog has been closed + if (Display.getCurrent() == null || getShell().isDisposed()) { + return; + } + final IStatus status = validateInput(); + Display.getCurrent().syncExec(() -> { + if (status.isOK()) { + errorMessageIcon.setVisible(false); + errorMessageLabel.setVisible(false); + setOkButtonEnabled(true); + } else if (status.matches(IStatus.WARNING)) { + errorMessageIcon.setVisible(true); + errorMessageIcon.setImage( + SWTImagesFactory.DESC_WARNING.createImage()); + errorMessageLabel.setVisible(true); + errorMessageLabel.setText(status.getMessage()); + setOkButtonEnabled(true); + } else if (status.matches(IStatus.ERROR)) { + if (status.getMessage() != null + && !status.getMessage().isEmpty()) { + errorMessageIcon.setVisible(true); + errorMessageIcon.setImage( + SWTImagesFactory.DESC_ERROR.createImage()); + errorMessageLabel.setVisible(true); + errorMessageLabel.setText(status.getMessage()); + } + setOkButtonEnabled(false); + } + }); + }; + } + + private IStatus validateInput() { + final String containerPath = model.getContainerPath(); + final MountType mountType = model.getMountType(); + final String hostPath = model.getHostPathMount(); + if (containerPath == null || containerPath.isEmpty()) { + return ValidationStatus.error(null); + } else if (mountType == null) { + return ValidationStatus.error(null); + } else if (mountType == MountType.HOST_FILE_SYSTEM + && (hostPath == null || hostPath.isEmpty())) { + return ValidationStatus.error(null); + } else if (mountType == MountType.HOST_FILE_SYSTEM + && !new File(hostPath).exists()) { + return ValidationStatus + .warning("The specified path does not exist on the host."); //$NON-NLS-1$ + } else if (mountType == MountType.CONTAINER) { + final IDockerContainer container = WizardUtils + .getContainer(connection, model.getContainerMount()); + if (container == null) { + // just make sure that the dialog cannot complete + return ValidationStatus.error(null); + } + final IDockerContainerInfo selectedContainerInfo = container.info(); + if (selectedContainerInfo != null + && selectedContainerInfo.volumes() != null + && !selectedContainerInfo.volumes() + .containsKey(model.getContainerPath())) { + return ValidationStatus + .warning(WizardMessages.getFormattedString( + "ContainerDataVolumeDialog.volumeWarning", //$NON-NLS-1$ + model.getContainerPath())); + } + } + return ValidationStatus.ok(); + } + + private void setOkButtonEnabled(final boolean enabled) { + final Button okButton = getButton(IDialogConstants.OK_ID); + // skip if 'OK' button does not exist yet. + if (okButton != null) { + okButton.setEnabled(enabled); + } + } + +} diff --git a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerLaunchConfigurationDelegate.java b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerLaunchConfigurationDelegate.java index ae11435556f..096db2fbed7 100644 --- a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerLaunchConfigurationDelegate.java +++ b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerLaunchConfigurationDelegate.java @@ -10,6 +10,7 @@ *******************************************************************************/ package org.eclipse.cdt.internal.docker.launcher; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -80,7 +81,8 @@ public class ContainerLaunchConfigurationDelegate extends GdbLaunchDelegate @Override public void newOutput(String output) { - if (output.contains(Messages.Gdbserver_up)) { + if (output.contains(Messages.Gdbserver_up) + || output.contains("gdbserver:")) { //$NON-NLS-1$ started = true; } @@ -137,10 +139,16 @@ public class ContainerLaunchConfigurationDelegate extends GdbLaunchDelegate labels.put("org.eclipse.cdt.project-name", projectName); //$NON-NLS-1$ if (mode.equals(ILaunchManager.RUN_MODE)) { String commandDir = commandPath.removeLastSegments(1) - .toString(); + .toPortableString(); + String commandString = commandPath.toPortableString(); + + if (commandPath.getDevice() != null) { + commandDir = "/" + commandDir.replace(':', '/'); //$NON-NLS-1$ + commandString = "/" + commandString.replace(':', '/'); //$NON-NLS-1$ + } StringBuilder b = new StringBuilder(); - b.append(commandPath.toString().trim()); + b.append(commandString); String arguments = getProgramArguments(configuration); if (arguments.trim().length() > 0) { @@ -154,6 +162,13 @@ public class ContainerLaunchConfigurationDelegate extends GdbLaunchDelegate .getAttribute( ICDTLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY, (String) null); + if (workingDir != null) { + IPath workingPath = new Path(workingDir); + if (workingPath.getDevice() != null) { + workingDir = "/" + workingPath.toPortableString() //$NON-NLS-1$ + .replace(':', '/'); + } + } Map envMap = configuration.getAttribute( ILaunchManager.ATTR_ENVIRONMENT_VARIABLES, (Map) null); @@ -168,6 +183,18 @@ public class ContainerLaunchConfigurationDelegate extends GdbLaunchDelegate List additionalDirs = configuration.getAttribute( ILaunchConstants.ATTR_ADDITIONAL_DIRS, (List) null); + if (additionalDirs != null) { + List dirs = new ArrayList<>(); + for (String additionalDir : additionalDirs) { + IPath path = new Path(additionalDir); + String dir = path.toPortableString(); + if (path.getDevice() != null) { + dir = "/" + dir.replace(':', '/'); + } + dirs.add(dir); + } + additionalDirs = dirs; + } String image = configuration.getAttribute( ILaunchConstants.ATTR_IMAGE, (String) null); String connectionUri = configuration.getAttribute( @@ -196,11 +223,18 @@ public class ContainerLaunchConfigurationDelegate extends GdbLaunchDelegate String gdbserverCommand = configuration.getAttribute( ILaunchConstants.ATTR_GDBSERVER_COMMAND, ILaunchConstants.ATTR_GDBSERVER_COMMAND_DEFAULT); - String commandArguments = ":" + gdbserverPortNumber + " " //$NON-NLS-1$ //$NON-NLS-2$ - + spaceEscapify(commandPath.toString()); + String commandString = commandPath.toPortableString(); String commandDir = commandPath.removeLastSegments(1) - .toString(); + .toPortableString(); + + if (commandPath.getDevice() != null) { + commandDir = "/" + commandDir.replace(':', '/'); //$NON-NLS-1$ + commandString = "/" + commandString.replace(':', '/'); //$NON-NLS-1$ + } + + String commandArguments = ":" + gdbserverPortNumber + " " //$NON-NLS-1$ //$NON-NLS-2$ + + spaceEscapify(commandString); StringBuilder b = new StringBuilder(); @@ -217,6 +251,14 @@ public class ContainerLaunchConfigurationDelegate extends GdbLaunchDelegate .getAttribute( ICDTLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY, (String) null); + if (workingDir != null) { + IPath workingPath = new Path(workingDir); + if (workingPath.getDevice() != null) { + workingDir = "/" + workingPath.toPortableString() //$NON-NLS-1$ + .replace(':', '/'); + } + } + Map envMap = configuration.getAttribute( ILaunchManager.ATTR_ENVIRONMENT_VARIABLES, (Map) null); @@ -231,6 +273,19 @@ public class ContainerLaunchConfigurationDelegate extends GdbLaunchDelegate List additionalDirs = configuration.getAttribute( ILaunchConstants.ATTR_ADDITIONAL_DIRS, (List) null); + if (additionalDirs != null) { + List dirs = new ArrayList<>(); + for (String additionalDir : additionalDirs) { + IPath path = new Path(additionalDir); + String dir = path.toPortableString(); + if (path.getDevice() != null) { + dir = "/" + dir.replace(':', '/'); + } + dirs.add(dir); + } + additionalDirs = dirs; + } + String image = configuration.getAttribute( ILaunchConstants.ATTR_IMAGE, (String) null); String connectionUri = configuration.getAttribute( diff --git a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerPropertyTab.java b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerPropertyTab.java new file mode 100644 index 00000000000..cb892ea4526 --- /dev/null +++ b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerPropertyTab.java @@ -0,0 +1,1053 @@ +/******************************************************************************* + * Copyright (c) 2017 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 API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.docker.launcher; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsProvider; +import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsProvidersKeeper; +import org.eclipse.cdt.core.model.CoreModelUtil; +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.core.settings.model.ICMultiConfigDescription; +import org.eclipse.cdt.core.settings.model.ICResourceDescription; +import org.eclipse.cdt.core.settings.model.ICTargetPlatformSetting; +import org.eclipse.cdt.internal.docker.launcher.ContainerPropertyVolumesModel.MountType; +import org.eclipse.cdt.managedbuilder.buildproperties.IOptionalBuildProperties; +import org.eclipse.cdt.managedbuilder.core.IConfiguration; +import org.eclipse.cdt.managedbuilder.core.IMultiConfiguration; +import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager; +import org.eclipse.cdt.managedbuilder.internal.core.Configuration; +import org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector; +import org.eclipse.cdt.managedbuilder.ui.properties.AbstractCBuildPropertyTab; +import org.eclipse.core.databinding.DataBindingContext; +import org.eclipse.core.databinding.beans.BeanProperties; +import org.eclipse.core.databinding.beans.IBeanValueProperty; +import org.eclipse.core.databinding.observable.list.IObservableList; +import org.eclipse.core.databinding.observable.map.IObservableMap; +import org.eclipse.core.databinding.property.Properties; +import org.eclipse.jface.databinding.viewers.ObservableListContentProvider; +import org.eclipse.jface.databinding.viewers.ObservableMapLabelProvider; +import org.eclipse.jface.databinding.viewers.ViewerSupport; +import org.eclipse.jface.databinding.viewers.ViewersObservables; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.layout.GridLayoutFactory; +import org.eclipse.jface.viewers.CheckboxTableViewer; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.StructuredViewer; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TableViewerColumn; +import org.eclipse.linuxtools.docker.core.DockerConnectionManager; +import org.eclipse.linuxtools.docker.core.IDockerConnection; +import org.eclipse.linuxtools.docker.core.IDockerConnectionManagerListener; +import org.eclipse.linuxtools.docker.core.IDockerImage; +import org.eclipse.linuxtools.docker.core.IDockerImageListener; +import org.eclipse.linuxtools.internal.docker.ui.wizards.WizardMessages; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.events.VerifyEvent; +import org.eclipse.swt.events.VerifyListener; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; + +@SuppressWarnings("restriction") +public class ContainerPropertyTab extends AbstractCBuildPropertyTab + implements IDockerConnectionManagerListener, IDockerImageListener { + + public final static String VOLUME_SEPARATOR = "|"; //$NON-NLS-1$ + + private final static String GNU_ELF_PARSER_ID = "org.eclipse.cdt.core.GNU_ELF"; //$NON-NLS-1$ + private final static String ELF_PARSER_ID = "org.eclipse.cdt.core.ELF"; //$NON-NLS-1$ + + private Combo imageCombo; + private Combo connectionSelector; + private Button enableButton; + private Button addButton; + private IDockerConnection connection; + private IDockerConnection[] connections; + private IDockerImageListener containerTab; + + private String connectionName; + private String connectionUri = ""; //$NON-NLS-1$ + + private boolean initialEnabled; + private String initialConnection; + private String initialImageId; + private String initialVolumes; + private String initialSelectedVolumes; + + private boolean multiChange; + + private List displayedImages = new ArrayList<>(); + + private IConfiguration iCfg; + private ICConfigurationDescription iCfgd; + + private final DataBindingContext dbc = new DataBindingContext(); + private final ContainerPropertyVolumesModel model; + + private ModifyListener connectionModifyListener = new ModifyListener() { + + @Override + public void modifyText(ModifyEvent e) { + int index = connectionSelector.getSelectionIndex(); + if (index < 0) { + connection = null; + connectionName = ""; + return; + } + if (connection != null) + connection.removeImageListener(containerTab); + connection = connections[index]; + connectionUri = connection.getUri(); + if (!connectionName.equals(connection.getName())) { + imageCombo.setText(""); + initialImageId = null; + refreshImages(); + setVolumeControlsEnabled(new Button[] { addButton }, false); + } + connectionName = connection.getName(); + setConnection(connectionUri); + model.setConnection(connection); + } + + }; + + public ContainerPropertyTab() { + this.containerTab = this; + this.model = new ContainerPropertyVolumesModel( + (IDockerConnection) null); + } + + @Override + public void createControls(Composite parent) { + super.createControls(parent); + + usercomp.setLayout(new GridLayout(5, false)); + usercomp.setFont(parent.getFont()); + GridData gd = new GridData(GridData.FILL_HORIZONTAL); + gd.horizontalSpan = 1; + + usercomp.setLayoutData(gd); + + enableButton = new Button(usercomp, SWT.CHECK); + enableButton.setText(Messages.ContainerPropertyTab_Enable_Msg); + + iCfg = getCfg(); + iCfgd = getResDesc().getConfiguration(); + + gd = new GridData(GridData.FILL_HORIZONTAL); + gd.horizontalSpan = 5; + enableButton.setLayoutData(gd); + + Label connectionSelectorLabel = new Label(usercomp, SWT.NULL); + connectionSelectorLabel + .setText(Messages.ContainerTab_Connection_Selector_Label); + gd = new GridData(GridData.FILL_HORIZONTAL); + gd.horizontalSpan = 1; + gd.grabExcessHorizontalSpace = false; + connectionSelectorLabel.setLayoutData(gd); + + connectionSelector = new Combo(usercomp, SWT.BORDER | SWT.READ_ONLY); + initializeConnectionSelector(); + connectionSelector.addModifyListener(connectionModifyListener); + // Following is a kludge so that on Linux the Combo is read-only but + // has a white background. + connectionSelector.addVerifyListener(new VerifyListener() { + @Override + public void verifyText(VerifyEvent e) { + e.doit = false; + } + }); + gd = new GridData(GridData.FILL_HORIZONTAL); + gd.horizontalSpan = 3; + gd.grabExcessHorizontalSpace = true; + connectionSelector.setLayoutData(gd); + + Label label1 = new Label(usercomp, SWT.NULL); + gd = new GridData(); + gd.horizontalSpan = 1; + gd.grabExcessHorizontalSpace = false; + label1.setLayoutData(gd); + + Label imageSelectorLabel = new Label(usercomp, SWT.NULL); + imageSelectorLabel.setText(Messages.ContainerTab_Image_Selector_Label); + gd = new GridData(GridData.FILL_HORIZONTAL); + gd.horizontalSpan = 1; + connectionSelectorLabel.setLayoutData(gd); + + imageCombo = new Combo(usercomp, SWT.DROP_DOWN); + gd = new GridData(GridData.FILL_HORIZONTAL); + gd.horizontalSpan = 3; + gd.grabExcessHorizontalSpace = true; + imageCombo.setLayoutData(gd); + + Label label2 = new Label(usercomp, SWT.NULL); + gd = new GridData(); + gd.horizontalSpan = 1; + gd.grabExcessHorizontalSpace = false; + label2.setLayoutData(gd); + + initializeImageCombo(); + + imageCombo.addSelectionListener(new SelectionListener() { + + @Override + public void widgetSelected(SelectionEvent e) { + setImageId(imageCombo.getText()); + model.setSelectedImage( + displayedImages.get(imageCombo.getSelectionIndex())); + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + } + + }); + + initializeEnablementButton(); + enableButton.addSelectionListener(new SelectionListener() { + + @Override + public void widgetSelected(SelectionEvent e) { + setControlsEnabled(enableButton.getSelection()); + setEnablement(enableButton.getSelection()); + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + // ignore + } + + }); + + createVolumeSettingsContainer(usercomp); + } + + private void createVolumeSettingsContainer(final Composite container) { + final Label volumesLabel = new Label(container, SWT.NONE); + volumesLabel.setText(WizardMessages + .getString("ImageRunResourceVolVarPage.dataVolumesLabel")); //$NON-NLS-1$ + GridDataFactory.fillDefaults().grab(false, false).applyTo(volumesLabel); + final CheckboxTableViewer dataVolumesTableViewer = createVolumesTable( + container); + GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP) + .grab(true, false).hint(400, 200) + .applyTo(dataVolumesTableViewer.getTable()); + // buttons + final Composite buttonsContainers = new Composite(container, SWT.NONE); + GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP) + .grab(false, false).applyTo(buttonsContainers); + GridLayoutFactory.fillDefaults().numColumns(1).margins(0, 0) + .spacing(SWT.DEFAULT, 0).applyTo(buttonsContainers); + + addButton = new Button(buttonsContainers, SWT.NONE); + GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP) + .grab(true, false).applyTo(addButton); + addButton.setText(WizardMessages + .getString("ImageRunResourceVolVarPage.addButton")); //$NON-NLS-1$ + addButton.addSelectionListener(onAddDataVolume(dataVolumesTableViewer)); + if (imageCombo.getText() != null && !imageCombo.getText().equals("")) { + setVolumeControlsEnabled(new Button[] { addButton }, true); + } + final Button editButton = new Button(buttonsContainers, SWT.NONE); + GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP) + .grab(true, false).applyTo(editButton); + editButton.setText(WizardMessages + .getString("ImageRunResourceVolVarPage.editButton")); //$NON-NLS-1$ + editButton + .addSelectionListener(onEditDataVolume(dataVolumesTableViewer)); + editButton.setEnabled(false); + final Button removeButton = new Button(buttonsContainers, SWT.NONE); + GridDataFactory.fillDefaults().align(SWT.FILL, SWT.TOP) + .grab(true, false).applyTo(removeButton); + removeButton.setText(WizardMessages + .getString("ImageRunResourceVolVarPage.removeButton")); //$NON-NLS-1$ + removeButton.addSelectionListener( + onRemoveDataVolumes(dataVolumesTableViewer)); + removeButton.setEnabled(false); + // update table content when selected image changes + bind(dataVolumesTableViewer, model.getDataVolumes(), + BeanProperties.values(DataVolumeModel.class, + DataVolumeModel.CONTAINER_PATH, DataVolumeModel.MOUNT, + DataVolumeModel.READ_ONLY_VOLUME)); + dbc.bindSet( + ViewersObservables.observeCheckedElements( + dataVolumesTableViewer, DataVolumeModel.class), + BeanProperties.set( + ContainerPropertyVolumesModel.SELECTED_DATA_VOLUMES) + .observe(model)); + // disable the edit and removeButton if the table is empty + dataVolumesTableViewer.addSelectionChangedListener( + onSelectionChanged(editButton, removeButton)); + + initializeVolumesTable(); + } + + /** + * Same as + * {@link ViewerSupport#bind(StructuredViewer, IObservableList, org.eclipse.core.databinding.property.value.IValueProperty[]) + * but with a custom LabelProvider, DataVolumesLabelProvider + * + * @param viewer + * @param input + * @param labelProperties + */ + private void bind(final StructuredViewer viewer, + final IObservableList input, + final IBeanValueProperty[] labelProperties) { + final ObservableListContentProvider contentProvider = new ObservableListContentProvider(); + if (viewer.getInput() != null) { + viewer.setInput(null); + } + viewer.setContentProvider(contentProvider); + viewer.setLabelProvider( + new DataVolumesLabelProvider(Properties.observeEach( + contentProvider.getKnownElements(), labelProperties))); + if (input != null) { + viewer.setInput(input); + } + + } + + private ISelectionChangedListener onSelectionChanged( + final Button... targetButtons) { + return e -> { + if (e.getSelection().isEmpty()) { + setVolumeControlsEnabled(targetButtons, false); + } else { + setVolumeControlsEnabled(targetButtons, true); + } + }; + } + + private static void setVolumeControlsEnabled(final Control[] controls, + final boolean enabled) { + for (Control control : controls) { + if (control != null) { + control.setEnabled(enabled); + } + } + } + + private SelectionListener onAddDataVolume( + final CheckboxTableViewer dataVolumesTableViewer) { + return SelectionListener.widgetSelectedAdapter(e -> { + final ContainerDataVolumeDialog dialog = new ContainerDataVolumeDialog( + Display.getDefault().getActiveShell(), + model.getConnection()); + dialog.create(); + if (dialog.open() == IDialogConstants.OK_ID) { + final DataVolumeModel dataVolume = dialog.getDataVolume(); + dataVolume.setSelected(true); + model.getDataVolumes().add(dataVolume); + model.getSelectedDataVolumes().add(dataVolume); + dataVolumesTableViewer.setChecked(dataVolume, true); + setVolumes(); + } + }); + } + + private SelectionListener onEditDataVolume( + final CheckboxTableViewer dataVolumesTableViewer) { + return SelectionListener.widgetSelectedAdapter(e -> { + final IStructuredSelection selection = (IStructuredSelection) dataVolumesTableViewer + .getSelection(); + if (selection.isEmpty()) { + return; + } + final DataVolumeModel selectedDataVolume = (DataVolumeModel) selection + .getFirstElement(); + final ContainerDataVolumeDialog dialog = new ContainerDataVolumeDialog( + Display.getDefault().getActiveShell(), + model.getConnection(), selectedDataVolume); + dialog.create(); + if (dialog.open() == IDialogConstants.OK_ID) { + final DataVolumeModel dialogDataVolume = dialog.getDataVolume(); + selectedDataVolume.setContainerMount( + dialogDataVolume.getContainerMount()); + selectedDataVolume + .setMountType(dialogDataVolume.getMountType()); + selectedDataVolume + .setHostPathMount(dialogDataVolume.getHostPathMount()); + selectedDataVolume.setContainerMount( + dialogDataVolume.getContainerMount()); + selectedDataVolume.setReadOnly(dialogDataVolume.isReadOnly()); + model.getSelectedDataVolumes().add(selectedDataVolume); + dataVolumesTableViewer.setChecked(selectedDataVolume, true); + setVolumes(); + } + }); + } + + private SelectionListener onRemoveDataVolumes( + final TableViewer dataVolumesTableViewer) { + return SelectionListener.widgetSelectedAdapter(e -> { + final IStructuredSelection selection = dataVolumesTableViewer + .getStructuredSelection(); + for (@SuppressWarnings("unchecked") + Iterator iterator = selection.iterator(); iterator + .hasNext();) { + final DataVolumeModel volume = iterator.next(); + model.removeDataVolume(volume); + model.getSelectedDataVolumes().remove(volume); + } + setVolumes(); + }); + } + + private CheckboxTableViewer createVolumesTable(final Composite container) { + final Table table = new Table(container, SWT.CHECK | SWT.BORDER + | SWT.FULL_SELECTION | SWT.V_SCROLL | SWT.H_SCROLL); + final CheckboxTableViewer tableViewer = new CheckboxTableViewer(table); + table.setHeaderVisible(true); + table.setLinesVisible(true); + dbc.bindSet( + ViewersObservables.observeCheckedElements(tableViewer, + DataVolumeModel.class), + BeanProperties.set( + ContainerPropertyVolumesModel.SELECTED_DATA_VOLUMES) + .observe(model)); + addTableViewerColumn(tableViewer, + WizardMessages.getString( + "ImageRunResourceVolVarPage.containerPathColumn"), //$NON-NLS-1$ + 180); + addTableViewerColumn(tableViewer, + WizardMessages + .getString("ImageRunResourceVolVarPage.mountColumn"), //$NON-NLS-1$ + 180); + addTableViewerColumn(tableViewer, + WizardMessages + .getString("ImageRunResourceVolVarPage.readonlyColumn"), //$NON-NLS-1$ + 60); + return tableViewer; + } + + private TableViewerColumn addTableViewerColumn( + final TableViewer tableViewer, + final String title, final int width) { + final TableViewerColumn viewerColumn = new TableViewerColumn( + tableViewer, SWT.NONE); + final TableColumn column = viewerColumn.getColumn(); + if (title != null) { + column.setText(title); + } + column.setWidth(width); + return viewerColumn; + } + + private static final class DataVolumesLabelProvider + extends ObservableMapLabelProvider { + + private Image CONTAINER_IMAGE = SWTImagesFactory.DESC_CONTAINER + .createImage(); + private Image FOLDER_CLOSED_IMAGE = SWTImagesFactory.DESC_FOLDER_CLOSED + .createImage(); + private Image FILE_IMAGE = SWTImagesFactory.DESC_FILE.createImage(); + + public DataVolumesLabelProvider(final IObservableMap[] attributeMaps) { + super(attributeMaps); + } + + @Override + public void dispose() { + CONTAINER_IMAGE.dispose(); + FOLDER_CLOSED_IMAGE.dispose(); + FILE_IMAGE.dispose(); + super.dispose(); + } + + @Override + public Image getColumnImage(Object element, int columnIndex) { + final DataVolumeModel dataVolume = ((DataVolumeModel) element); + if (dataVolume.getMountType() != null && columnIndex == 1) { + switch (dataVolume.getMountType()) { + case CONTAINER: + return CONTAINER_IMAGE; + case HOST_FILE_SYSTEM: + final File hostFile = new File(dataVolume.getMount()); + if (!hostFile.exists() || hostFile.isDirectory()) { + return FOLDER_CLOSED_IMAGE; + } else { + return FILE_IMAGE; + } + default: + return null; + } + } + return null; + } + + @Override + public String getColumnText(Object element, int columnIndex) { + final DataVolumeModel dataVolume = ((DataVolumeModel) element); + switch (columnIndex) { + case 0: + return dataVolume.getContainerPath(); + case 1: + return dataVolume.getMount(); + case 2: + if (dataVolume.getMountType() != MountType.HOST_FILE_SYSTEM) { + return null; + } else if (dataVolume.isReadOnly()) { + return WizardMessages + .getString("ImageRunResourceVolVarPage.true"); //$NON-NLS-1$ + } + return WizardMessages + .getString("ImageRunResourceVolVarPage.false"); //$NON-NLS-1$ + default: + return null; + } + } + } + + private void setVolumes() { + StringBuffer buffer = new StringBuffer(); + String separator = ""; //$NON-NLS-1$ + for (DataVolumeModel volume : model.getDataVolumes()) { + buffer.append(separator); + buffer.append(volume.toString()); + separator = VOLUME_SEPARATOR; + } + StringBuffer selectedBuffer = new StringBuffer(); + separator = ""; //$NON-NLS-1$ + for (DataVolumeModel volume : model.getSelectedDataVolumes()) { + selectedBuffer.append(separator); + selectedBuffer.append(volume.toString()); + separator = VOLUME_SEPARATOR; + } + if (iCfg instanceof IMultiConfiguration) { + IConfiguration[] cfs = (IConfiguration[]) ((IMultiConfiguration) iCfg) + .getItems(); + for (int i = 0; i < cfs.length; i++) { + IConfiguration cfg = cfs[i]; + IOptionalBuildProperties p = cfg.getOptionalBuildProperties(); + p.setProperty(ContainerCommandLauncher.VOLUMES_ID, + buffer.toString()); + p.setProperty(ContainerCommandLauncher.SELECTED_VOLUMES_ID, + selectedBuffer.toString()); + } + } else { + IOptionalBuildProperties p = iCfg.getOptionalBuildProperties(); + p.setProperty(ContainerCommandLauncher.VOLUMES_ID, + buffer.toString()); + p.setProperty(ContainerCommandLauncher.SELECTED_VOLUMES_ID, + selectedBuffer.toString()); + } + } + + private void setEnablement(boolean enabled) { + if (iCfg instanceof IMultiConfiguration) { + IConfiguration[] cfs = (IConfiguration[]) ((IMultiConfiguration) iCfg) + .getItems(); + for (int i = 0; i < cfs.length; i++) { + IConfiguration cfg = cfs[i]; + IOptionalBuildProperties p = cfg.getOptionalBuildProperties(); + p.setProperty(ContainerCommandLauncher.CONTAINER_BUILD_ENABLED, + Boolean.toString(enableButton.getSelection())); + } + } else { + IOptionalBuildProperties p = iCfg.getOptionalBuildProperties(); + p.setProperty(ContainerCommandLauncher.CONTAINER_BUILD_ENABLED, + Boolean.toString(enableButton.getSelection())); + } + // if enabled, make sure we have ELF binary parsers specified + if (enabled) { + String[] ids = CoreModelUtil + .getBinaryParserIds(page.getCfgsEditable()); + List idList = new ArrayList<>(Arrays.asList(ids)); + if (!idList.contains(GNU_ELF_PARSER_ID)) { + idList.add(GNU_ELF_PARSER_ID); + } + if (!idList.contains(ELF_PARSER_ID)) { + idList.add(ELF_PARSER_ID); + } + CoreModelUtil.setBinaryParserIds(page.getCfgsEditable(), + idList.toArray(new String[0])); + } + } + + private void setImageId(String imageId) { + if (iCfg instanceof IMultiConfiguration) { + IConfiguration[] cfs = (IConfiguration[]) ((IMultiConfiguration) iCfg) + .getItems(); + for (int i = 0; i < cfs.length; i++) { + IConfiguration cfg = cfs[i]; + IOptionalBuildProperties p = cfg.getOptionalBuildProperties(); + p.setProperty(ContainerCommandLauncher.IMAGE_ID, imageId); + } + } else { + IOptionalBuildProperties p = iCfg.getOptionalBuildProperties(); + p.setProperty(ContainerCommandLauncher.IMAGE_ID, imageId); + } + } + + private void setConnection(String uri) { + if (iCfg instanceof IMultiConfiguration) { + IConfiguration[] cfs = (IConfiguration[]) ((IMultiConfiguration) iCfg) + .getItems(); + for (int i = 0; i < cfs.length; i++) { + IConfiguration cfg = cfs[i]; + IOptionalBuildProperties p = cfg.getOptionalBuildProperties(); + p.setProperty(ContainerCommandLauncher.CONNECTION_ID, uri); + } + } else { + IOptionalBuildProperties p = iCfg.getOptionalBuildProperties(); + p.setProperty(ContainerCommandLauncher.CONNECTION_ID, uri); + } + } + + private void setControlsEnabled(boolean enabled) { + imageCombo.setEnabled(enabled); + connectionSelector.setEnabled(enabled); + setVolumeControlsEnabled(new Button[] { addButton }, enabled); + } + + private void initializeEnablementButton() { + initialEnabled = false; + IOptionalBuildProperties properties = iCfg.getOptionalBuildProperties(); + String savedEnabled = properties + .getProperty(ContainerCommandLauncher.CONTAINER_BUILD_ENABLED); + if (savedEnabled != null) { + initialEnabled = Boolean + .parseBoolean(savedEnabled); + } + enableButton.setSelection(initialEnabled); + setControlsEnabled(initialEnabled); + } + + private void initializeConnectionSelector() { + int defaultIndex = -1; + initialConnection = null; + IOptionalBuildProperties properties = iCfg.getOptionalBuildProperties(); + String id = properties + .getProperty(ContainerCommandLauncher.CONNECTION_ID); + if (id != null) { + initialConnection = id; + } + connections = DockerConnectionManager.getInstance().getConnections(); + if (connections.length == 0) { + // setErrorMessage(Messages.ContainerTab_Error_No_Connections); + return; + } + String[] connectionNames = new String[connections.length]; + for (int i = 0; i < connections.length; ++i) { + connectionNames[i] = connections[i].getName(); + if (connections[i].getUri().equals(initialConnection)) + defaultIndex = i; + } + if (defaultIndex < 0) { + initialEnabled = false; + defaultIndex = 0; + } + connectionSelector.setItems(connectionNames); + if (connections.length > 0) { + connectionSelector.select(defaultIndex); + connection = connections[defaultIndex]; + connectionName = connection.getName(); + connectionUri = connection.getUri(); + initialConnection = connectionUri; + model.setConnection(connection); + } + } + + private void refreshImages() { + if (connection != null) { + java.util.List images = connection.getImages(); + if (images == null || images.size() == 0) { + // setsetErrorMessage(Messages.ContainerTab_Error_No_Images); + return; + } + connection.removeImageListener(containerTab); + ArrayList imageNames = new ArrayList(); + displayedImages = new ArrayList<>(); + for (IDockerImage image : images) { + java.util.List tags = image.repoTags(); + if (tags != null) { + for (String tag : tags) { + if (!tag.equals(":")) { //$NON-NLS-1$ + imageNames.add(tag); + displayedImages.add(image); + } + } + } + } + imageCombo.setItems(imageNames.toArray(new String[0])); + if (initialImageId != null) { + int index = imageCombo.indexOf(initialImageId); + if (index > -1) { + imageCombo.select(index); + model.setSelectedImage(displayedImages.get(index)); + setVolumeControlsEnabled(new Button[] { addButton }, true); + } else { + } + } + connection.addImageListener(containerTab); + } + + } + + private void initializeImageCombo() { + initialImageId = null; + IOptionalBuildProperties properties = iCfg.getOptionalBuildProperties(); + String id = properties.getProperty(ContainerCommandLauncher.IMAGE_ID); + if (id != null) { + initialImageId = id; + } + refreshImages(); + } + + private void initializeVolumesTable() { + model.clearDataVolumes(); + int imageSelectionIndex = imageCombo.getSelectionIndex(); + if (imageSelectionIndex >= 0 + && imageSelectionIndex < displayedImages.size()) { + model.setSelectedImage(displayedImages.get(imageSelectionIndex)); + } + + IOptionalBuildProperties properties = iCfg.getOptionalBuildProperties(); + initialVolumes = properties + .getProperty(ContainerCommandLauncher.VOLUMES_ID); + Map volumeMap = parseVolumes(initialVolumes); + initialSelectedVolumes = properties + .getProperty(ContainerCommandLauncher.SELECTED_VOLUMES_ID); + Map selectedVolumeMap = parseVolumes( + initialSelectedVolumes); + Set selectedVolumes = new HashSet<>(); + for (DataVolumeModel dvm : selectedVolumeMap.values()) { + // we need selected volumes to be volumes that are in the volumes + // collection, so just replace them in the volumes Map so they will + // be + // the same objects + volumeMap.put(dvm.getContainerPath(), dvm); + selectedVolumes.add(dvm); + } + model.setDataVolumes(volumeMap.values()); + model.setSelectedDataVolumes(selectedVolumes); + } + + private Map parseVolumes(String volumesString) { + Map volumeMap = new HashMap<>(); + if (volumesString != null && !volumesString.equals("")) { //$NON-NLS-1$ + String[] volumes = volumesString + .split("[" + VOLUME_SEPARATOR + "]"); //$NON-NLS-1$ //$NON-NLS-2$ + for (String volume : volumes) { + if (volume != null && !volume.equals("")) { //$NON-NLS-1$ + DataVolumeModel dataVolume = DataVolumeModel + .parseString(volume); + volumeMap.put(dataVolume.getContainerPath(), dataVolume); + } + } + } + return volumeMap; + } + + @Override + protected void performApply(ICResourceDescription src, + ICResourceDescription dst) { + setVolumes(); + boolean needToRecalculate = false; + ICConfigurationDescription defaultCfg = null; + if (page.isMultiCfg()) { + ICMultiConfigDescription mc1 = (ICMultiConfigDescription) src + .getConfiguration(); + ICMultiConfigDescription mc2 = (ICMultiConfigDescription) dst + .getConfiguration(); + ICConfigurationDescription[] cds1 = (ICConfigurationDescription[]) mc1 + .getItems(); + ICConfigurationDescription[] cds2 = (ICConfigurationDescription[]) mc2 + .getItems(); + defaultCfg = cds1[0]; + for (int i = 0; i < cds1.length; i++) + needToRecalculate |= applyToCfg(cds1[i], cds2[i]); + } else { + defaultCfg = src.getConfiguration(); + needToRecalculate = applyToCfg(src.getConfiguration(), + dst.getConfiguration()); + } + if (needToRecalculate) { + recalculateSpecs(defaultCfg, true); + } + } + + private boolean applyToCfg(ICConfigurationDescription c1, + ICConfigurationDescription c2) { + Configuration cfg01 = (Configuration) getCfg(c1); + Configuration cfg02 = (Configuration) getCfg(c2); + IOptionalBuildProperties prop1 = cfg01.getOptionalBuildProperties(); + IOptionalBuildProperties prop2 = cfg02.getOptionalBuildProperties(); + boolean needToRecalculate = false; + + ICTargetPlatformSetting tps = c1.getTargetPlatformSetting(); + String[] pids = tps.getBinaryParserIds(); + ICTargetPlatformSetting tps2 = c2.getTargetPlatformSetting(); + tps2.setBinaryParserIds(pids); + + String enablementProperty = prop1 + .getProperty(ContainerCommandLauncher.CONTAINER_BUILD_ENABLED); + String enablementProperty2 = prop2 + .getProperty(ContainerCommandLauncher.CONTAINER_BUILD_ENABLED); + if (enablementProperty != null + && !enablementProperty.equals(enablementProperty2)) { + needToRecalculate = true; + } + prop2.setProperty(ContainerCommandLauncher.CONTAINER_BUILD_ENABLED, + enablementProperty); + + String connectionProperty = prop1 + .getProperty(ContainerCommandLauncher.CONNECTION_ID); + String connectionProperty2 = prop2 + .getProperty(ContainerCommandLauncher.CONNECTION_ID); + if (connectionProperty != null + && !connectionProperty.equals(connectionProperty2)) { + needToRecalculate = true; + } + prop2.setProperty(ContainerCommandLauncher.CONNECTION_ID, + connectionProperty); + + String imageProperty = prop1 + .getProperty(ContainerCommandLauncher.IMAGE_ID); + String imageProperty2 = prop2 + .getProperty(ContainerCommandLauncher.IMAGE_ID); + if (imageProperty != null && !imageProperty.equals(imageProperty2)) { + needToRecalculate = true; + } + prop2.setProperty(ContainerCommandLauncher.IMAGE_ID, imageProperty); + + String volumesProperty = prop1 + .getProperty(ContainerCommandLauncher.VOLUMES_ID); + prop2.setProperty(ContainerCommandLauncher.VOLUMES_ID, volumesProperty); + + String selectedVolumesProperty = prop1 + .getProperty(ContainerCommandLauncher.SELECTED_VOLUMES_ID); + prop2.setProperty(ContainerCommandLauncher.SELECTED_VOLUMES_ID, + selectedVolumesProperty); + + return needToRecalculate; + } + + + protected void recalculateSpecs(ICConfigurationDescription cfgd, + boolean performingApply) { + IConfiguration cfg = getCfg(cfgd); + IOptionalBuildProperties properties = cfg.getOptionalBuildProperties(); + initialEnabled = Boolean.parseBoolean(properties + .getProperty(ContainerCommandLauncher.CONTAINER_BUILD_ENABLED)); + initialConnection = properties + .getProperty(ContainerCommandLauncher.CONNECTION_ID); + initialImageId = properties + .getProperty(ContainerCommandLauncher.IMAGE_ID); + initialVolumes = properties + .getProperty(ContainerCommandLauncher.VOLUMES_ID); + initialSelectedVolumes = properties + .getProperty(ContainerCommandLauncher.SELECTED_VOLUMES_ID); + List providers = ((ILanguageSettingsProvidersKeeper) cfgd) + .getLanguageSettingProviders(); + for (ILanguageSettingsProvider provider : providers) { + if (provider instanceof GCCBuiltinSpecsDetector) { + GCCBuiltinSpecsDetector d = (GCCBuiltinSpecsDetector) provider; + // force recalculation of gcc include path + d.clear(); + if (performingApply) { + d.handleEvent(null); + } + // final IProject project = getProject(); + // CCorePlugin.getIndexManager().reindex(CoreModel.getDefault().create(project)); + } + } + } + + + @Override + protected void performOK() { + boolean needToRecalculate = false; + setVolumes(); + if (iCfg instanceof IMultiConfiguration) { + needToRecalculate = multiChange; + } else { + IOptionalBuildProperties p = iCfg.getOptionalBuildProperties(); + if (initialEnabled != Boolean.parseBoolean(p.getProperty( + ContainerCommandLauncher.CONTAINER_BUILD_ENABLED))) { + needToRecalculate = true; + } else if (initialEnabled == true) { + if (!initialConnection.equals( + p.getProperty(ContainerCommandLauncher.CONNECTION_ID)) + || !initialImageId.equals(p.getProperty( + ContainerCommandLauncher.IMAGE_ID))) { + needToRecalculate = true; + } + } + } + if (needToRecalculate) { + recalculateSpecs( + ManagedBuildManager.getDescriptionForConfiguration(iCfg), + false); + } + } + + @Override + protected void performDefaults() { + if (iCfg instanceof IMultiConfiguration) { + IConfiguration[] cfs = (IConfiguration[]) ((IMultiConfiguration) iCfg) + .getItems(); + for (int i = 0; i < cfs.length; i++) { + IOptionalBuildProperties props = cfs[i] + .getOptionalBuildProperties(); + props.setProperty( + ContainerCommandLauncher.CONTAINER_BUILD_ENABLED, + Boolean.toString(false)); + if (connections.length > 0) { + props.setProperty(ContainerCommandLauncher.CONNECTION_ID, + connections[0].getUri()); + } else { + props.setProperty(ContainerCommandLauncher.CONNECTION_ID, + null); + } + props.setProperty(ContainerCommandLauncher.IMAGE_ID, null); + props.setProperty(ContainerCommandLauncher.VOLUMES_ID, null); + props.setProperty(ContainerCommandLauncher.SELECTED_VOLUMES_ID, + null); + } + } else { + IOptionalBuildProperties props = iCfg.getOptionalBuildProperties(); + props.setProperty(ContainerCommandLauncher.CONTAINER_BUILD_ENABLED, + Boolean.toString(false)); + if (connections.length > 0) { + props.setProperty(ContainerCommandLauncher.CONNECTION_ID, + connections[0].getUri()); + } else { + props.setProperty(ContainerCommandLauncher.CONNECTION_ID, null); + } + props.setProperty(ContainerCommandLauncher.IMAGE_ID, null); + } + initialEnabled = false; + initialConnection = null; + initialImageId = null; + initialVolumes = null; + initialSelectedVolumes = null; + if (connections.length > 0) { + connectionSelector.select(0); + } + imageCombo.setText(""); //$NON-NLS-1$ + model.setDataVolumes(null); + model.setSelectedDataVolumes(null); + enableButton.setSelection(false); + setControlsEnabled(false); + } + + @Override + public void updateData(ICResourceDescription cfgd) { + if (cfgd == null) + return; + iCfg = getCfg(cfgd.getConfiguration()); + iCfgd = cfgd.getConfiguration(); + + multiChange = false; + + initializeConnectionSelector(); + initializeImageCombo(); + initializeEnablementButton(); + initializeVolumesTable(); + } + + @Override + protected void updateButtons() { + // TODO Auto-generated method stub + + } + + @Override + public void changeEvent(IDockerConnection changedConnection, int type) { + String currUri = null; + int currIndex = 0; + connections = DockerConnectionManager.getInstance().getConnections(); + if (connection != null) { + currUri = connection.getUri(); + currIndex = connectionSelector.getSelectionIndex(); + } + String[] connectionNames = new String[connections.length]; + int index = 0; + for (int i = 0; i < connections.length; ++i) { + connectionNames[i] = connections[i].getName(); + if (connections[i].getUri().equals(currUri)) + index = i; + } + if (type == IDockerConnectionManagerListener.RENAME_EVENT) { + index = currIndex; // no change in connection displayed + } + connectionSelector.removeModifyListener(connectionModifyListener); + connectionSelector.setItems(connectionNames); + if (connectionNames.length > 0) { + connectionSelector.setText(connectionNames[index]); + connection = connections[index]; + model.setConnection(connection); + connectionUri = connection.getUri(); + } else { + connection = null; + model.setConnection(null); + model.setSelectedImage(null); + connectionUri = ""; + connectionSelector.setText(""); + } + connectionSelector.addModifyListener(connectionModifyListener); + } + + @Override + public void listChanged(IDockerConnection c, + java.util.List list) { + final IDockerImage[] finalList = list.toArray(new IDockerImage[0]); + if (c.getName().equals(connection.getName())) { + Display.getDefault().syncExec(new Runnable() { + @Override + public void run() { + connection.removeImageListener(containerTab); + ArrayList imageNames = new ArrayList(); + displayedImages = new ArrayList<>(); + for (IDockerImage image : finalList) { + java.util.List tags = image.repoTags(); + if (tags != null) { + for (String tag : tags) { + imageNames.add(tag); + displayedImages.add(image); + } + } + } + if (!imageCombo.isDisposed()) + imageCombo.setItems(imageNames.toArray(new String[0])); + connection.addImageListener(containerTab); + } + + }); + } + } + +} diff --git a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerPropertyVolumesModel.java b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerPropertyVolumesModel.java new file mode 100644 index 00000000000..ad4ccbf6cba --- /dev/null +++ b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerPropertyVolumesModel.java @@ -0,0 +1,142 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Red Hat. + * 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 - Initial Contribution + *******************************************************************************/ +package org.eclipse.cdt.internal.docker.launcher; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.core.databinding.observable.list.WritableList; +import org.eclipse.linuxtools.docker.core.DockerException; +import org.eclipse.linuxtools.docker.core.IDockerConnection; +import org.eclipse.linuxtools.docker.core.IDockerImage; +import org.eclipse.linuxtools.docker.core.IDockerImageInfo; + +/** + * Databinding model for the {@link ContainerPropertyTab} + * + */ +public class ContainerPropertyVolumesModel + extends BaseDatabindingModel { + + public enum MountType { + NONE, HOST_FILE_SYSTEM, CONTAINER; + } + + + public static final String DATA_VOLUMES = "dataVolumes"; //$NON-NLS-1$ + + public static final String SELECTED_DATA_VOLUMES = "selectedDataVolumes"; //$NON-NLS-1$ + + private IDockerConnection connection; + + private IDockerImageInfo imageInfo = null; + + private Set selectedDataVolumes = new HashSet<>(); + + private WritableList dataVolumes = new WritableList<>(); + + private List previousVolumes = new ArrayList<>(); + + private IDockerImage selectedImage; + + public ContainerPropertyVolumesModel( + final IDockerConnection connection) { + this.connection = connection; + } + + public ContainerPropertyVolumesModel( + final IDockerImage selectedImage) throws DockerException { + this(selectedImage.getConnection()); + this.selectedImage = selectedImage; + } + + public void setConnection(IDockerConnection connection) { + this.connection = connection; + setSelectedImage(null); + } + + public IDockerConnection getConnection() { + return connection; + } + + /** + * Refreshes the list of Volumes to display in the for the given + * + * @param selectedImage + */ + public void setSelectedImage(final IDockerImage selectedImage) { + if (this.selectedImage == null + || !this.selectedImage.equals(selectedImage)) { + this.selectedImage = selectedImage; + if (selectedImage != null) { + this.imageInfo = selectedImage.getConnection() + .getImageInfo(selectedImage.id()); + if (this.imageInfo.config() != null + && this.imageInfo.config().volumes() != null) { + for (DataVolumeModel dvm : previousVolumes) { + removeDataVolume(dvm); + selectedDataVolumes.remove(dvm); + } + final List volumes = new ArrayList<>(); + for (String volume : this.imageInfo.config().volumes()) { + volumes.add(new DataVolumeModel(volume)); + } + setDataVolumes(volumes); + previousVolumes = volumes; + } + } else { + setDataVolumes(Collections. emptyList()); + } + } + + } + + public IDockerImage getSelectedImage() { + return selectedImage; + } + + public IDockerImageInfo getSelectedImageInfo() { + return imageInfo; + } + + public WritableList getDataVolumes() { + return dataVolumes; + } + + public void setDataVolumes(final Collection volumes) { + if (volumes != null) { + this.dataVolumes.addAll(volumes); + } + } + + public void clearDataVolumes() { + this.dataVolumes.clear(); + } + + public void removeDataVolume(final DataVolumeModel dataVolume) { + this.dataVolumes.remove(dataVolume); + } + + public Set getSelectedDataVolumes() { + return selectedDataVolumes; + } + + public void setSelectedDataVolumes( + final Set selectedDataVolumes) { + firePropertyChange(SELECTED_DATA_VOLUMES, this.selectedDataVolumes, + this.selectedDataVolumes = selectedDataVolumes); + } + +} diff --git a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerTab.java b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerTab.java index 1786646846f..0d69db41fdd 100644 --- a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerTab.java +++ b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ContainerTab.java @@ -517,7 +517,8 @@ public class ContainerTab extends AbstractLaunchConfigurationTab implements return SWTImagesFactory.get(SWTImagesFactory.IMG_CONTAINER); } - public void changeEvent(int type) { + @Override + public void changeEvent(IDockerConnection changedConnection, int type) { String currUri = null; int currIndex = 0; connections = DockerConnectionManager.getInstance().getConnections(); @@ -549,10 +550,6 @@ public class ContainerTab extends AbstractLaunchConfigurationTab implements connectionSelector.addModifyListener(connectionModifyListener); } - public void changeEvent(IDockerConnection connection, int event) { - changeEvent(event); - } - public void listChanged(IDockerConnection c, java.util.List list) { final IDockerImage[] finalList = list.toArray(new IDockerImage[0]); diff --git a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/DataVolumeModel.java b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/DataVolumeModel.java new file mode 100644 index 00000000000..d3e5816d18b --- /dev/null +++ b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/DataVolumeModel.java @@ -0,0 +1,347 @@ +/******************************************************************************* + * Copyright (c) 2015 Red Hat. + * 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 - Initial Contribution + *******************************************************************************/ +package org.eclipse.cdt.internal.docker.launcher; + +import java.util.UUID; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.cdt.internal.docker.launcher.ContainerPropertyVolumesModel.MountType; +import org.eclipse.core.runtime.Platform; + +/** + * Data binding model for container data volumes + * + */ +public class DataVolumeModel extends BaseDatabindingModel + implements Comparable { + + private static final String SEPARATOR = ":"; //$NON-NLS-1$ + + public static final String CONTAINER_PATH = "containerPath"; //$NON-NLS-1$ + + public static final String MOUNT_TYPE = "mountType"; //$NON-NLS-1$ + + public static final String MOUNT = "mount"; //$NON-NLS-1$ + + public static final String HOST_PATH_MOUNT = "hostPathMount"; //$NON-NLS-1$ + + public static final String READ_ONLY_VOLUME = "readOnly"; //$NON-NLS-1$ + + public static final String CONTAINER_MOUNT = "containerMount"; //$NON-NLS-1$ + + public static final String SELECTED = "selected"; //$NON-NLS-1$ + + private final String id = UUID.randomUUID().toString(); + + private String containerPath; + + private MountType mountType; + + private String mount; + + private String hostPathMount; + + private String containerMount; + + private boolean readOnly = false; + + private boolean selected; + + + /** + * Default constructor + */ + public DataVolumeModel() { + } + + /** + * Constructor + * + * @param containerPath + * the container path + */ + public DataVolumeModel(final String containerPath) { + this.containerPath = containerPath; + this.mountType = MountType.NONE; + } + + public DataVolumeModel(final String containerPath, final String hostPath, + final boolean readOnly) { + this.containerPath = containerPath; + this.mountType = MountType.HOST_FILE_SYSTEM; + this.hostPathMount = hostPath; + this.mount = this.hostPathMount; + this.readOnly = readOnly; + } + + public DataVolumeModel(final DataVolumeModel selectedDataVolume) { + this.containerPath = selectedDataVolume.getContainerPath(); + this.mountType = selectedDataVolume.getMountType(); + if (this.mountType != null) { + switch (this.mountType) { + case CONTAINER: + this.containerMount = selectedDataVolume.getMount(); + break; + case HOST_FILE_SYSTEM: + this.hostPathMount = selectedDataVolume.getMount(); + this.readOnly = selectedDataVolume.isReadOnly(); + break; + case NONE: + break; + } + } else { + this.mountType = MountType.NONE; + } + } + + /** + * Create a DataVolumeModel from a toString() output. + * + * @param fromString + * @return DataVolumeModel + */ + public static DataVolumeModel parseString( + final String fromString) { + final DataVolumeModel model = new DataVolumeModel(); + final String[] items = fromString.split(SEPARATOR); // $NON-NLS-1$ + model.containerPath = items[0]; + model.mountType = MountType.valueOf(items[1]); + switch (model.mountType) { + case CONTAINER: + model.setContainerMount(items[2]); + model.setSelected(Boolean.valueOf(items[3])); + break; + case HOST_FILE_SYSTEM: + // For Windows, there are multiple formats. If a user has specified + // a windows drive using the : separator, we have to form the + // host path by merging the path back together. If the user + // has specified an alternate format, we don't do this. + if (Platform.OS_WIN32.equals(Platform.getOS()) + && items.length > 5) { + model.setHostPathMount(items[2] + SEPARATOR + items[3]); + model.setReadOnly(Boolean.valueOf(items[4])); + model.setSelected(Boolean.valueOf(items[5])); + } else { + model.setHostPathMount(items[2]); + model.setReadOnly(Boolean.valueOf(items[3])); + model.setSelected(Boolean.valueOf(items[4])); + } + break; + case NONE: + model.setSelected(Boolean.valueOf(items[2])); + break; + } + return model; + } + + /** + * creates a {@link DataVolumeModel} from the 'volumeFrom' container info + * + * @param volumeFrom + * the value to parse. + * + * Format: <containerName> + * + * @See + * https://docs.docker.com/engine/userguide/dockervolumes/ + */ + public static DataVolumeModel parseVolumeFrom(String volumeFrom) { + final DataVolumeModel model = new DataVolumeModel(); + model.mountType = MountType.CONTAINER; + model.containerMount = volumeFrom; + model.selected = true; + return model; + } + + /** + * creates a {@link DataVolumeModel} from the 'volumeFrom' container info + * + * @param volumeFrom + * the value to parse. Format: + * <host_path>:<container_path>:<label_suffix_flag> + * + * @See + * https://docs.docker.com/engine/userguide/dockervolumes/ + */ + public static DataVolumeModel parseHostBinding(String volumeFrom) { + final DataVolumeModel model = new DataVolumeModel(); + final String[] items = volumeFrom.split(SEPARATOR); // $NON-NLS-1$ + // converts the host path to a valid Win32 path if Platform OS is Win32 + model.setHostPathMount(convertToWin32Path(Platform.getOS(), items[0])); + model.containerPath = items[1]; + model.mountType = MountType.HOST_FILE_SYSTEM; + if (items[2].equals("ro")) { + model.setReadOnly(true); + } else { + model.setReadOnly(false); + } + model.selected = true; + return model; + } + + /** + * Converts the given path to a portable form, replacing all "\" and ": " + * with "/" if the given os is {@link Platform#OS_WIN32}. + * + * @param os + * the current OS + * @param path + * the path to convert + * @return the converted path or the given path + * @see {@link Platform#getOS()} + */ + public static String convertToWin32Path(final String os, + final String path) { + if (os != null && os.equals(Platform.OS_WIN32)) { + // replace all "/" with "\" and then drive info (eg "/c/" to "C:/") + final Matcher m = Pattern.compile("^/([a-zA-Z])/").matcher(path); //$NON-NLS-1$ + if (m.find()) { + final StringBuffer b = new StringBuffer(); + m.appendReplacement(b, m.group(1).toUpperCase()); + b.append(":\\"); //$NON-NLS-1$ + m.appendTail(b); + return b.toString().replace('/', '\\'); // $NON-NLS-1$ + // //$NON-NLS-2$ + } + } + return path; + } + + public String getContainerPath() { + return this.containerPath; + } + + public void setContainerPath(final String containerPath) { + firePropertyChange(CONTAINER_PATH, this.containerPath, + this.containerPath = containerPath); + } + + public String getMount() { + return mount; + } + + public void setMount(final String mount) { + firePropertyChange(MOUNT, this.mount, this.mount = mount); + } + + public MountType getMountType() { + return mountType; + } + + public void setMountType(final MountType mountType) { + // ignore 'null' assignments that may come from the UpdateStrategy + // in + // the EditDataVolumePage when a radion button is unselected. + if (mountType == null) { + return; + } + firePropertyChange(MOUNT_TYPE, this.mountType, + this.mountType = mountType); + if (this.mountType == MountType.NONE) { + setMount(""); + } + + } + + public String getHostPathMount() { + return hostPathMount; + } + + public void setHostPathMount(final String hostPathMount) { + firePropertyChange(HOST_PATH_MOUNT, this.hostPathMount, + this.hostPathMount = hostPathMount); + if (this.mountType == MountType.HOST_FILE_SYSTEM) { + setMount(this.hostPathMount); + } + } + + public boolean isReadOnly() { + return readOnly; + } + + public void setReadOnly(final boolean readOnly) { + firePropertyChange(READ_ONLY_VOLUME, this.readOnly, + this.readOnly = readOnly); + } + + public String getContainerMount() { + return this.containerMount; + } + + public void setContainerMount(final String containerMount) { + firePropertyChange(CONTAINER_MOUNT, this.containerMount, + this.containerMount = containerMount); + if (this.mountType == MountType.CONTAINER) { + setMount(this.containerMount); + } + } + + public boolean getSelected() { + return selected; + } + + public void setSelected(final boolean selected) { + firePropertyChange(SELECTED, this.selected, this.selected = selected); + } + + @Override + public int compareTo(final DataVolumeModel other) { + return this.getContainerPath().compareTo(other.getContainerPath()); + } + + // FIXME we should have a dedicated method to serialize the bean + @Override + public String toString() { + final StringBuffer buffer = new StringBuffer(); + buffer.append( + this.containerPath + SEPARATOR + getMountType() + SEPARATOR); + switch (getMountType()) { + case CONTAINER: + buffer.append(getContainerMount()); + break; + case HOST_FILE_SYSTEM: + buffer.append(getHostPathMount() + SEPARATOR); // $NON-NLS-1$ + buffer.append(isReadOnly()); + break; + case NONE: + break; + } + buffer.append(SEPARATOR).append(this.selected); + return buffer.toString(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((id == null) ? 0 : id.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + DataVolumeModel other = (DataVolumeModel) obj; + if (id == null) { + if (other.id != null) + return false; + } else if (!id.equals(other.id)) + return false; + return true; + } + +} diff --git a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/LaunchShortcut.java b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/LaunchShortcut.java index ed716c3361f..5ad81d2dcde 100644 --- a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/LaunchShortcut.java +++ b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/LaunchShortcut.java @@ -19,13 +19,18 @@ import org.eclipse.cdt.core.model.CModelException; import org.eclipse.cdt.core.model.CoreModel; import org.eclipse.cdt.core.model.IBinary; import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.debug.core.CDebugUtils; import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; import org.eclipse.cdt.docker.launcher.DockerLaunchUIPlugin; import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; import org.eclipse.cdt.dsf.gdb.IGdbDebugPreferenceConstants; import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin; +import org.eclipse.cdt.managedbuilder.buildproperties.IOptionalBuildProperties; +import org.eclipse.cdt.managedbuilder.core.IConfiguration; +import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager; import org.eclipse.cdt.ui.CElementLabelProvider; +import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; @@ -253,6 +258,31 @@ public class LaunchShortcut implements ILaunchShortcut { ILaunchConfiguration configuration = null; ILaunchConfigurationType configType = getLaunchConfigType(); List candidateConfigs = Collections.emptyList(); + IProject project = bin.getCProject().getProject(); + ICConfigurationDescription cfgd = CoreModel.getDefault() + .getProjectDescription(project).getActiveConfiguration(); + String connectionUri = null; + String imageName = null; + if (cfgd != null) { + IConfiguration cfg = ManagedBuildManager + .getConfigurationForDescription(cfgd); + if (cfg != null) { + IOptionalBuildProperties props = cfg + .getOptionalBuildProperties(); + String containerBuild = props.getProperty( + ContainerCommandLauncher.CONTAINER_BUILD_ENABLED); + if (containerBuild != null) { + boolean containerBuildEnabled = Boolean + .parseBoolean(containerBuild); + if (containerBuildEnabled) { + connectionUri = props.getProperty( + ContainerCommandLauncher.CONNECTION_ID); + imageName = props + .getProperty(ContainerCommandLauncher.IMAGE_ID); + } + } + } + } try { ILaunchConfiguration[] configs = DebugPlugin.getDefault() .getLaunchManager().getLaunchConfigurations(configType); @@ -265,7 +295,17 @@ public class LaunchShortcut implements ILaunchShortcut { if (projectName != null && projectName.equals(bin.getCProject() .getProject().getName())) { - candidateConfigs.add(config); + // if we have an active configuration with container + // build properties, make sure they match, otherwise + // add the launch config as a candidate + if (connectionUri.equals(config.getAttribute( + ILaunchConstants.ATTR_CONNECTION_URI, + connectionUri))) { + if (imageName.equals(config.getAttribute( + ILaunchConstants.ATTR_IMAGE, imageName))) { + candidateConfigs.add(config); + } + } } } } @@ -312,11 +352,40 @@ public class LaunchShortcut implements ILaunchShortcut { String binaryPath = bin.getResource().getProjectRelativePath() .toString(); + IProject project = bin.getResource().getProject(); + + ICConfigurationDescription cfgd = CoreModel.getDefault() + .getProjectDescription(project).getActiveConfiguration(); + IConfiguration cfg = ManagedBuildManager + .getConfigurationForDescription(cfgd); + + IOptionalBuildProperties options = cfg.getOptionalBuildProperties(); + boolean containerBuild = false; + String connectionId = null; + String imageName = null; + + if (options != null) { + String containerBuildString = options.getProperty( + ContainerCommandLauncher.CONTAINER_BUILD_ENABLED); + if (containerBuildString != null) { + containerBuild = Boolean.parseBoolean(options.getProperty( + ContainerCommandLauncher.CONTAINER_BUILD_ENABLED)); + } + if (containerBuild) { + connectionId = options.getProperty( + ContainerCommandLauncher.CONNECTION_ID); + imageName = options + .getProperty(ContainerCommandLauncher.IMAGE_ID); + } + } + ILaunchConfigurationType configType = getLaunchConfigType(); ILaunchConfigurationWorkingCopy wc = configType.newInstance( null, getLaunchManager().generateLaunchConfigurationName( - bin.getElementName())); + bin.getResource().getName() + (imageName != null + ? ("[" + imageName + "]") //$NON-NLS-1$ //$NON-NLS-2$ + : ""))); //$NON-NLS-1$ // DSF settings...use GdbUIPlugin preference store for defaults IPreferenceStore preferenceStore = GdbUIPlugin.getDefault() @@ -357,19 +426,25 @@ public class LaunchShortcut implements ILaunchShortcut { Preferences prefs = InstanceScope.INSTANCE .getNode(DockerLaunchUIPlugin.PLUGIN_ID); - // get the connection from the ConnectionListener which waits for - // any activity - // from the DockerExplorerView - IDockerConnection connection = ConnectionListener.getInstance() + // get the connection using following order: + // 1. connection used in build of project + // 2. current connection + // 3. first connection + IDockerConnection connection = null; + if (connectionId != null) { + connection = DockerConnectionManager.getInstance() + .getConnectionByUri(connectionId); + } + if (connection == null) { + connection = ConnectionListener.getInstance() .getCurrentConnection(); + } if (connection == null) { IDockerConnection[] connections = DockerConnectionManager .getInstance().getConnections(); if (connections != null && connections.length > 0) - connection = DockerConnectionManager.getInstance() - .getConnections()[0]; + connection = connections[0]; } - // issue error message if no connections exist if (connection == null) { Display.getDefault().syncExec(new Runnable() { @@ -389,9 +464,14 @@ public class LaunchShortcut implements ILaunchShortcut { wc.setAttribute(ILaunchConstants.ATTR_CONNECTION_URI, connection.getUri()); - // get any default image if specified, otherwise use first + // use build image if one is specified, otherwise, see if a default + // image is set in preferences, otherwise find first image in image + // list // image in image list for connection - String image = prefs.get(PreferenceConstants.DEFAULT_IMAGE, null); + String image = imageName; + if (image == null) { + image = prefs.get(PreferenceConstants.DEFAULT_IMAGE, null); + } if (image == null) { List images = connection.getImages(); if (images != null && images.size() > 0) diff --git a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/Messages.java b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/Messages.java index f3d6003d8a1..dc90a4e3144 100644 --- a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/Messages.java +++ b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/Messages.java @@ -47,6 +47,13 @@ public class Messages extends NLS { public static String ContainerTab_Warning_Connection_Not_Found; public static String ContainerTab_Warning_Image_Not_Found; + public static String HeaderPreferencePage_Connection_Label; + public static String HeaderPreferencePage_Image_Label; + public static String HeaderPreferencePage_Remove_Label; + public static String HeaderPreferencePage_Remove_Tooltip; + public static String HeaderPreferencePage_Confirm_Removal_Title; + public static String HeaderPreferencePage_Confirm_Removal_Msg; + public static String Remote_GDB_Debugger_Options; public static String Gdbserver_Settings_Tab_Name; public static String Gdbserver_name_textfield_label; @@ -90,6 +97,14 @@ public class Messages extends NLS { public static String StandardGDBDebuggerPage14; + public static String ContainerPropertyTab_Title; + public static String ContainerPropertyTab_Enable_Msg; + + public static String ContainerCommandLauncher_image_msg; + public static String CommandLauncher_CommandCancelled; + + public static String ContainerCommandLauncher_invalid_values; + static { // initialize resource bundle NLS.initializeMessages(BUNDLE_NAME, Messages.class); diff --git a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/SWTImagesFactory.java b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/SWTImagesFactory.java index b886f2c6427..d08edf77d55 100644 --- a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/SWTImagesFactory.java +++ b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/SWTImagesFactory.java @@ -44,8 +44,21 @@ public class SWTImagesFactory { private static final int NAME_PREFIX_LENGTH = NAME_PREFIX.length(); public static final String IMG_CONTAINER = NAME_PREFIX + "repository-middle.gif"; //$NON-NLS-1$ + public static final String IMG_FOLDER_CLOSED = NAME_PREFIX + + "folder_closed.gif"; //$NON-NLS-1$ + public static final String IMG_FILE = NAME_PREFIX + "file_obj.gif"; //$NON-NLS-1$ + public static final String IMG_WARNING = NAME_PREFIX + "warning_obj.gif"; //$NON-NLS-1$ + public static final String IMG_ERROR = NAME_PREFIX + "error_obj.gif"; //$NON-NLS-1$ + public static final ImageDescriptor DESC_CONTAINER = createManaged("", IMG_CONTAINER); + public static final ImageDescriptor DESC_FOLDER_CLOSED = createManaged("", + IMG_FOLDER_CLOSED); + public static final ImageDescriptor DESC_FILE = createManaged("", IMG_FILE); + public static final ImageDescriptor DESC_WARNING = createManaged("", + IMG_WARNING); + public static final ImageDescriptor DESC_ERROR = createManaged("", + IMG_ERROR); private static ImageDescriptor createManaged(String prefix, String name) { return createManaged(imageRegistry, prefix, name); diff --git a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/messages.properties b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/messages.properties index 15e1b19a0c7..06609d611cf 100644 --- a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/messages.properties +++ b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/messages.properties @@ -40,6 +40,21 @@ ContainerTab_Error_No_Images=No Docker Images exist ContainerTab_Warning_Connection_Not_Found=Docker Connection: {0} for Launch Configuration not found: defaulting to {1} ContainerTab_Warning_Image_Not_Found=Docker Image: {0} is not a valid pulled image in current Connection: {1} +ContainerPropertyTab_Title=Container Settings +ContainerPropertyTab_Enable_Msg=Build inside Docker Image + +HeaderPreferencePage_Connection_Label=Connection +HeaderPreferencePage_Image_Label=Image +HeaderPreferencePage_Remove_Label=Remove +HeaderPreferencePage_Remove_Tooltip=Remove headers cached from Docker image +HeaderPreferencePage_Confirm_Removal_Title=Confirm Header File Removal +HeaderPreferencePage_Confirm_Removal_Msg=Confirm removal of specified cached header files + +ContainerCommandLauncher_image_msg=[Running in image <{0}>] +ContainerCommandLauncher_invalid_values=Invalid values for Connection and/or Image name + +CommandLauncher_CommandCancelled=Command cancelled + Remote_GDB_Debugger_Options=Docker Container GDB Debugger Options Gdbserver_Settings_Tab_Name=Gdbserver Settings Gdbserver_name_textfield_label=Gdbserver path: diff --git a/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ui/preferences/DockerHeaderPreferencePage.java b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ui/preferences/DockerHeaderPreferencePage.java new file mode 100644 index 00000000000..0e0269ab90d --- /dev/null +++ b/launch/org.eclipse.cdt.docker.launcher/src/org/eclipse/cdt/internal/docker/launcher/ui/preferences/DockerHeaderPreferencePage.java @@ -0,0 +1,356 @@ +package org.eclipse.cdt.internal.docker.launcher.ui.preferences; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.cdt.docker.launcher.DockerLaunchUIPlugin; +import org.eclipse.cdt.internal.docker.launcher.Messages; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Platform; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.preference.PreferencePage; +import org.eclipse.jface.viewers.ColumnWeightData; +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.ITableLabelProvider; +import org.eclipse.jface.viewers.TableLayout; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; + +public class DockerHeaderPreferencePage extends PreferencePage + implements IWorkbenchPreferencePage, Listener { + + // SWT Widgets and content providers + private Table hdrTable; + private TableViewer hdrTableViewer; + private HeaderContentProvider provider; + private Button removeButton; + private List directories; + + private final class HeaderContentProvider + implements IStructuredContentProvider, ITableLabelProvider { + + /** + * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(Object) + */ + @Override + public Object[] getElements(Object inputElement) { + return directories.toArray(); + } + + /** + * @see org.eclipse.jface.viewers.IContentProvider#dispose() + */ + @Override + public void dispose() { + } + + /** + * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(Viewer, + * Object, Object) + */ + @Override + public void inputChanged(Viewer viewer, Object oldInput, + Object newInput) { + } + + /** + * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnImage(Object, + * int) + */ + @Override + public Image getColumnImage(Object element, int columnIndex) { + return null; + } + + private String readNameFile(IPath path) { + // try and read real name from special .name file found in + // directory. + IPath namePath = path.append(".name"); //$NON-NLS-1$ + // default to use last directory segment if any problems occur. + String name = path.lastSegment(); + if (namePath.toFile().exists()) { + try (FileReader reader = new FileReader(namePath.toFile()); + BufferedReader bufferReader = new BufferedReader( + reader);) { + name = bufferReader.readLine(); + } catch (IOException e) { + // ignore + } + } + return name; + } + + /** + * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnText(Object, + * int) + */ + @Override + public String getColumnText(Object element, int columnIndex) { + IPath path = (IPath) element; + if (columnIndex == 0) { + IPath connectionPath = path.removeLastSegments(1); + String connectionName = readNameFile(connectionPath); + return connectionName; + } + String imageName = readNameFile(path); + return imageName; + } + + /** + * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(ILabelProviderListener) + */ + @Override + public void addListener(ILabelProviderListener listener) { + } + + /** + * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(Object, + * String) + */ + @Override + public boolean isLabelProperty(Object element, String property) { + return false; + } + + /** + * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(ILabelProviderListener) + */ + @Override + public void removeListener(ILabelProviderListener listener) { + } + + } + + public DockerHeaderPreferencePage() { + noDefaultAndApplyButton(); + provider = new HeaderContentProvider(); + } + + @Override + public void init(IWorkbench workbench) { + directories = new ArrayList<>(); + IPath pluginPath = Platform + .getStateLocation( + Platform.getBundle(DockerLaunchUIPlugin.PLUGIN_ID)) + .append("HEADERS"); //$NON-NLS-1$ + File d = pluginPath.toFile(); + + if (d.exists() && d.isDirectory()) { + File[] connections = d.listFiles(); + for (File connection : connections) { + if (connection.isDirectory()) { + File[] images = connection.listFiles(); + for (File image : images) { + if (image.isDirectory()) { + directories + .add(pluginPath.append(connection.getName()) + .append(image.getName())); + } + } + } + } + } + } + + @Override + protected Control createContents(Composite parent) { + Composite page = createComposite(parent, 1, 2, false, null, -1, -1, + GridData.FILL); + GridData gd = (GridData) page.getLayoutData(); + gd.grabExcessHorizontalSpace = true; + gd.grabExcessVerticalSpace = true; + + // SystemWidgetHelpers.createLabel(page, + // SystemResources.RESID_PREF_SIGNON_DESCRIPTION, 2); + + // Header table + hdrTable = new Table(page, SWT.FULL_SELECTION | SWT.MULTI | SWT.V_SCROLL + | SWT.H_SCROLL | SWT.BORDER); + hdrTable.setLinesVisible(true); + hdrTable.setHeaderVisible(true); + hdrTable.addListener(SWT.Selection, this); + + TableLayout tableLayout = new TableLayout(); + tableLayout.addColumnData(new ColumnWeightData(60, true)); + tableLayout.addColumnData(new ColumnWeightData(40, true)); + hdrTable.setLayout(tableLayout); + + gd = new GridData(GridData.FILL_BOTH); + gd.grabExcessHorizontalSpace = true; + gd.grabExcessVerticalSpace = true; + + hdrTable.setLayoutData(gd); + + // Connection column + TableColumn connectionColumn = new TableColumn(hdrTable, SWT.NONE); + connectionColumn + .setText(Messages.HeaderPreferencePage_Connection_Label); + + // Image column + TableColumn imageColumn = new TableColumn(hdrTable, SWT.NONE); + imageColumn.setText(Messages.HeaderPreferencePage_Image_Label); + + hdrTableViewer = new TableViewer(hdrTable); + hdrTableViewer.setContentProvider(provider); + hdrTableViewer.setLabelProvider(provider); + hdrTableViewer.setInput(directories); + + // Create the Button bar for add, change and remove + Composite buttonBar = createComposite(page, 1, 1, false, null, -1, -1, + GridData.FILL); + gd = (GridData) buttonBar.getLayoutData(); + gd.grabExcessHorizontalSpace = false; + gd.grabExcessVerticalSpace = true; + + removeButton = createPushButton(buttonBar, this, + Messages.HeaderPreferencePage_Remove_Label, + Messages.HeaderPreferencePage_Remove_Tooltip); + + removeButton.setEnabled(false); + return parent; + } + + private static Composite createComposite(Composite parent, int parentSpan, + int numColumns, boolean border, String label, int marginSize, + int spacingSize, int verticalAlignment) { + // border = true; + boolean borderNeeded = border; + if (label != null) + borderNeeded = true; // force the case + int style = SWT.NULL; + if (borderNeeded) + style |= SWT.SHADOW_ETCHED_IN; + Composite composite = null; + if (borderNeeded) { + composite = new Group(parent, style); + if (label != null) + ((Group) composite).setText(label); + } else { + composite = new Composite(parent, style); + } + // GridLayout + GridLayout layout = new GridLayout(); + layout.numColumns = numColumns; + if (marginSize != -1) { + layout.marginWidth = 0; + layout.marginHeight = 0; + } + if (spacingSize != -1) { + layout.horizontalSpacing = 0; + layout.verticalSpacing = 0; + } + composite.setLayout(layout); + // GridData + GridData data = new GridData(); + data.horizontalSpan = parentSpan; + data.horizontalAlignment = GridData.FILL; + data.grabExcessHorizontalSpace = true; + + data.verticalAlignment = verticalAlignment; + data.grabExcessVerticalSpace = false; + + composite.setLayoutData(data); + return composite; + } + + public static Button createPushButton(Composite group, Listener listener, + String label, String tooltip) { + Button button = new Button(group, SWT.PUSH); + button.setText(label); + if (listener != null) + button.addListener(SWT.Selection, listener); + GridData data = new GridData(); + data.horizontalAlignment = GridData.FILL; + data.grabExcessHorizontalSpace = true; + button.setLayoutData(data); + if (tooltip != null) + button.setToolTipText(tooltip); + return button; + } + + private class DialogStatus { + private boolean status; + + public DialogStatus(boolean status) { + this.status = status; + } + + public void setStatus(boolean status) { + this.status = status; + } + + public boolean getStatus() { + return status; + } + } + + /** + * @see org.eclipse.swt.widgets.Listener#handleEvent(Event) + */ + @Override + public void handleEvent(Event event) { + if (event.type == SWT.Selection) { + if (event.widget == removeButton) { + final DialogStatus confirmed = new DialogStatus(false); + Display.getDefault().syncExec(() -> { + boolean status = MessageDialog.openConfirm(getShell(), + Messages.HeaderPreferencePage_Confirm_Removal_Title, + Messages.HeaderPreferencePage_Confirm_Removal_Msg); + confirmed.setStatus(status); + }); + if (!confirmed.getStatus()) { + return; + } + int[] indicies = hdrTable.getSelectionIndices(); + for (int idx = indicies.length - 1; idx >= 0; idx--) { + IPath dirPath = directories.get(idx); + File f = dirPath.toFile(); + if (f.exists() && f.isDirectory()) { + recursiveDelete(f); + } + directories.remove(idx); + } + + hdrTableViewer.refresh(); + } + + // Update table buttons based on changes + if (hdrTable.getSelectionCount() > 0) { + removeButton.setEnabled(true); + } else { + removeButton.setEnabled(false); + } + } + } + + private void recursiveDelete(File dir) { + File[] contents = dir.listFiles(); + if (contents != null) { + for (File f : contents) { + recursiveDelete(f); + } + } + dir.delete(); + } +} + diff --git a/remote/org.eclipse.cdt.remote.core/src/org/eclipse/cdt/remote/core/RemoteCommandLauncher.java b/remote/org.eclipse.cdt.remote.core/src/org/eclipse/cdt/remote/core/RemoteCommandLauncher.java index 85546854402..da2ee6f03f7 100644 --- a/remote/org.eclipse.cdt.remote.core/src/org/eclipse/cdt/remote/core/RemoteCommandLauncher.java +++ b/remote/org.eclipse.cdt.remote.core/src/org/eclipse/cdt/remote/core/RemoteCommandLauncher.java @@ -17,7 +17,7 @@ import java.net.URI; import java.util.Map; import java.util.Properties; -import org.eclipse.cdt.core.CommandLauncher; +import org.eclipse.cdt.core.CommandLauncherManager; import org.eclipse.cdt.core.ICommandLauncher; import org.eclipse.cdt.remote.internal.core.Activator; import org.eclipse.cdt.remote.internal.core.messages.Messages; @@ -40,6 +40,8 @@ import org.eclipse.remote.core.RemoteProcessAdapter; public class RemoteCommandLauncher implements ICommandLauncher { private static final String CYGWIN_PREFIX = "cygdrive"; //$NON-NLS-1$ + + private boolean usingLocalLauncher = false; /** * Convert a local (workspace) path into the remote equivalent. If the local path is not @@ -102,7 +104,7 @@ public class RemoteCommandLauncher implements ICommandLauncher { return s; } - private final ICommandLauncher fLocalLauncher = new CommandLauncher(); + private ICommandLauncher fLocalLauncher = CommandLauncherManager.getInstance().getCommandLauncher(); private boolean fShowCommand; private String[] fCommandArgs; private IRemoteConnection fConnection; @@ -129,13 +131,18 @@ public class RemoteCommandLauncher implements ICommandLauncher { @Override public Process execute(IPath commandPath, String[] args, String[] env, IPath workingDirectory, IProgressMonitor monitor) throws CoreException { + ICommandLauncher localLauncher = CommandLauncherManager.getInstance().getCommandLauncher(getProject()); + localLauncher.setProject(getProject()); + localLauncher.setErrorMessage(getErrorMessage()); + usingLocalLauncher = false; + fLocalLauncher = localLauncher; if (getProject() != null) { IRemoteResource remRes = (IRemoteResource) getProject().getAdapter(IRemoteResource.class); if (remRes != null) { URI uri = remRes.getActiveLocationURI(); IRemoteServicesManager remoteServicesManager = Activator.getService(IRemoteServicesManager.class); IRemoteConnectionType connectionType = remoteServicesManager.getConnectionType(uri); - if (connectionType != null) { + if (connectionType != null && !connectionType.getScheme().equals("file")) { //$NON-NLS-1$ fConnection = connectionType.getConnection(uri); if (fConnection != null) { parseEnvironment(env); @@ -163,16 +170,23 @@ public class RemoteCommandLauncher implements ICommandLauncher { } } } + usingLocalLauncher = true; return fLocalLauncher.execute(commandPath, args, env, workingDirectory, monitor); } @Override public String[] getCommandArgs() { + if (usingLocalLauncher) { + return fLocalLauncher.getCommandArgs(); + } return fCommandArgs; } @Override public String getCommandLine() { + if (usingLocalLauncher) { + return fLocalLauncher.getCommandLine(); + } return getCommandLine(fCommandArgs); } @@ -202,6 +216,9 @@ public class RemoteCommandLauncher implements ICommandLauncher { @Override public Properties getEnvironment() { + if (usingLocalLauncher) { + return fLocalLauncher.getEnvironment(); + } return fEnvironment; } @@ -261,8 +278,15 @@ public class RemoteCommandLauncher implements ICommandLauncher { fShowCommand = show; } + @SuppressWarnings("deprecation") @Override public int waitAndRead(OutputStream out, OutputStream err) { + + if (usingLocalLauncher) { + return fLocalLauncher.waitAndRead(out, err); + } + + // otherwise remote process if (fShowCommand) { printCommandLine(out); } @@ -278,6 +302,11 @@ public class RemoteCommandLauncher implements ICommandLauncher { @Override public int waitAndRead(OutputStream out, OutputStream err, IProgressMonitor monitor) { + if (usingLocalLauncher) { + return fLocalLauncher.waitAndRead(out, err, monitor); + } + + // otherwise remote process if (fShowCommand) { printCommandLine(out); }