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 00000000000..e6e47bbf8b5 Binary files /dev/null and b/doc/org.eclipse.cdt.doc.user/images/prop_container.png differ 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 00000000000..1dea453d4bd Binary files /dev/null and b/doc/org.eclipse.cdt.doc.user/images/prop_datavolumes.png differ 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 00000000000..0bc60689c6d Binary files /dev/null and b/launch/org.eclipse.cdt.docker.launcher/icons/error_obj.gif differ diff --git a/launch/org.eclipse.cdt.docker.launcher/icons/file_obj.gif b/launch/org.eclipse.cdt.docker.launcher/icons/file_obj.gif new file mode 100644 index 00000000000..efa7a380141 Binary files /dev/null and b/launch/org.eclipse.cdt.docker.launcher/icons/file_obj.gif differ 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 00000000000..42e027c9331 Binary files /dev/null and b/launch/org.eclipse.cdt.docker.launcher/icons/folder_closed.gif differ 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 00000000000..1e5f5eb367c Binary files /dev/null and b/launch/org.eclipse.cdt.docker.launcher/icons/warning_obj.gif differ 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); }