diff --git a/build/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/CMakeBuildConfiguration.java b/build/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/CMakeBuildConfiguration.java index fab83820cc1..a3f1e77a0d5 100644 --- a/build/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/CMakeBuildConfiguration.java +++ b/build/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/CMakeBuildConfiguration.java @@ -26,6 +26,8 @@ import org.eclipse.cdt.core.ErrorParserManager; import org.eclipse.cdt.core.IConsoleParser; import org.eclipse.cdt.core.build.CBuildConfiguration; import org.eclipse.cdt.core.build.IToolChain; +import org.eclipse.cdt.core.envvar.EnvironmentVariable; +import org.eclipse.cdt.core.envvar.IEnvironmentVariable; import org.eclipse.cdt.core.model.ICModelMarker; import org.eclipse.cdt.core.resources.IConsole; import org.eclipse.cdt.utils.Platform; @@ -34,6 +36,7 @@ import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.jobs.Job; import org.osgi.service.prefs.BackingStoreException; import org.osgi.service.prefs.Preferences; @@ -43,6 +46,7 @@ public class CMakeBuildConfiguration extends CBuildConfiguration { public static final String CMAKE_GENERATOR = "cmake.generator"; //$NON-NLS-1$ public static final String CMAKE_ARGUMENTS = "cmake.arguments"; //$NON-NLS-1$ + public static final String CMAKE_ENV = "cmake.environment"; //$NON-NLS-1$ public static final String BUILD_COMMAND = "cmake.command.build"; //$NON-NLS-1$ public static final String CLEAN_COMMAND = "cmake.command.clean"; //$NON-NLS-1$ @@ -97,8 +101,9 @@ public class CMakeBuildConfiguration extends CBuildConfiguration { private boolean isLocal() throws CoreException { IToolChain toolchain = getToolChain(); - return Platform.getOS().equals(toolchain.getProperty(IToolChain.ATTR_OS)) - && Platform.getOSArch().equals(toolchain.getProperty(IToolChain.ATTR_ARCH)); + return (Platform.getOS().equals(toolchain.getProperty(IToolChain.ATTR_OS)) + || "linux-container".equals(toolchain.getProperty(IToolChain.ATTR_OS))) //$NON-NLS-1$ + && (Platform.getOSArch().equals(toolchain.getProperty(IToolChain.ATTR_ARCH))); } @Override @@ -142,16 +147,10 @@ public class CMakeBuildConfiguration extends CBuildConfiguration { } if (runCMake) { // $NON-NLS-1$ + List command = new ArrayList<>(); - // TODO location of CMake out of preferences if not found here - Path cmakePath = findCommand("cmake"); //$NON-NLS-1$ - if (cmakePath != null) { - command.add(cmakePath.toString()); - } else { - command.add("cmake"); //$NON-NLS-1$ - } - + command.add("cmake"); //$NON-NLS-1$ command.add("-G"); //$NON-NLS-1$ command.add(generator); @@ -164,8 +163,10 @@ public class CMakeBuildConfiguration extends CBuildConfiguration { case "debug": //$NON-NLS-1$ command.add("-DCMAKE_BUILD_TYPE=Debug"); //$NON-NLS-1$ break; + case "run": //$NON-NLS-1$ + command.add("-DCMAKE_BUILD_TYPE=Release"); //$NON-NLS-1$ + break; } - command.add("-DCMAKE_EXPORT_COMPILE_COMMANDS=ON"); //$NON-NLS-1$ String userArgs = getProperty(CMAKE_ARGUMENTS); @@ -175,31 +176,59 @@ public class CMakeBuildConfiguration extends CBuildConfiguration { command.add(new File(project.getLocationURI()).getAbsolutePath()); - ProcessBuilder processBuilder = new ProcessBuilder(command).directory(buildDir.toFile()); - setBuildEnvironment(processBuilder.environment()); - Process process = processBuilder.start(); outStream.write(String.join(" ", command) + '\n'); //$NON-NLS-1$ - watchProcess(process, console); + + Process p = startBuildProcess(command, new IEnvironmentVariable[0], console, monitor); + if (p == null) { + console.getErrorStream().write(String.format(Messages.CMakeBuildConfiguration_Failure, "")); //$NON-NLS-1$ + return null; + } + + watchProcess(p, console); } try (ErrorParserManager epm = new ErrorParserManager(project, getBuildDirectoryURI(), this, getToolChain().getErrorParserIds())) { epm.setOutputStream(console.getOutputStream()); + List command = new ArrayList<>(); + + String envStr = getProperty(CMAKE_ENV); + List envVars = new ArrayList<>(); + if (envStr != null) { + List envList = CMakeUtils.stripEnvVars(envStr); + for (String s : envList) { + int index = s.indexOf("="); //$NON-NLS-1$ + if (index == -1) { + envVars.add(new EnvironmentVariable(s)); + } else { + envVars.add(new EnvironmentVariable(s.substring(0, index), s.substring(index + 1))); + } + } + } + String buildCommand = getProperty(BUILD_COMMAND); - String[] command = buildCommand != null && !buildCommand.trim().isEmpty() ? buildCommand.split(" ") //$NON-NLS-1$ - : new String[] { "cmake", "--build", "." }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - - Path cmdPath = findCommand(command[0]); - if (cmdPath != null) { - command[0] = cmdPath.toString(); + if (buildCommand == null) { + command.add("cmake"); //$NON-NLS-1$ + command.add("--build"); //$NON-NLS-1$ + command.add("."); //$NON-NLS-1$ + if ("Ninja".equals(generator)) { + command.add("--"); //$NON-NLS-1$ + command.add("-v"); //$NON-NLS-1$ + } + } else { + command.addAll(Arrays.asList(buildCommand.split(" "))); //$NON-NLS-1$ } - ProcessBuilder processBuilder = new ProcessBuilder(command).directory(buildDir.toFile()); - setBuildEnvironment(processBuilder.environment()); - Process process = processBuilder.start(); outStream.write(String.join(" ", command) + '\n'); //$NON-NLS-1$ - watchProcess(process, new IConsoleParser[] { epm }); + + Process p = startBuildProcess(command, envVars.toArray(new IEnvironmentVariable[0]), console, monitor); + if (p == null) { + console.getErrorStream().write(String.format(Messages.CMakeBuildConfiguration_Failure, "")); //$NON-NLS-1$ + return null; + } + + watchProcess(p, new IConsoleParser[] { epm }); project.refreshLocal(IResource.DEPTH_INFINITE, monitor); @@ -233,26 +262,34 @@ public class CMakeBuildConfiguration extends CBuildConfiguration { return; } + List command = new ArrayList<>(); String cleanCommand = getProperty(CLEAN_COMMAND); if (cleanCommand == null) { if (generator == null || generator.equals("Ninja")) { //$NON-NLS-1$ - cleanCommand = "ninja clean"; //$NON-NLS-1$ + command.add("ninja"); //$NON-NLS-1$ + command.add("clean"); //$NON-NLS-1$ } else { - cleanCommand = "make clean"; //$NON-NLS-1$ + command.add("make"); //$NON-NLS-1$ + command.add("clean"); //$NON-NLS-1$ } - } - String[] command = cleanCommand.split(" "); //$NON-NLS-1$ + } else { + command.addAll(Arrays.asList(cleanCommand.split(" "))); //$NON-NLS-1$ + } + + IEnvironmentVariable[] env = new IEnvironmentVariable[0]; - Path cmdPath = findCommand(command[0]); - if (cmdPath != null) { - command[0] = cmdPath.toString(); - } - - ProcessBuilder processBuilder = new ProcessBuilder(command).directory(buildDir.toFile()); - Process process = processBuilder.start(); outStream.write(String.join(" ", command) + '\n'); //$NON-NLS-1$ - watchProcess(process, console); - + + Process p = startBuildProcess(command, env, console, monitor); + if (p == null) { + console.getErrorStream().write(String.format(Messages.CMakeBuildConfiguration_Failure, "")); //$NON-NLS-1$ + return; + } + + watchProcess(p, console); + + outStream.write(Messages.CMakeBuildConfiguration_BuildComplete); + project.refreshLocal(IResource.DEPTH_INFINITE, monitor); } catch (IOException e) { throw new CoreException(Activator.errorStatus(String.format(Messages.CMakeBuildConfiguration_Cleaning, project.getName()), e)); @@ -263,6 +300,7 @@ public class CMakeBuildConfiguration extends CBuildConfiguration { IProject project = getProject(); Path commandsFile = getBuildDirectory().resolve("compile_commands.json"); //$NON-NLS-1$ if (Files.exists(commandsFile)) { + List jobsList = new ArrayList<>(); monitor.setTaskName(Messages.CMakeBuildConfiguration_ProcCompJson); try (FileReader reader = new FileReader(commandsFile.toFile())) { Gson gson = new Gson(); @@ -272,7 +310,14 @@ public class CMakeBuildConfiguration extends CBuildConfiguration { dedupedCmds.put(command.getFile(), command); } for (CompileCommand command : dedupedCmds.values()) { - processLine(command.getCommand()); + processLine(command.getCommand(), jobsList); + } + for (Job j : jobsList) { + try { + j.join(); + } catch (InterruptedException e) { + // ignore + } } shutdown(); } catch (IOException e) { diff --git a/build/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/CMakeBuildConfigurationProvider.java b/build/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/CMakeBuildConfigurationProvider.java index b3e6b350156..e91fae99e85 100644 --- a/build/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/CMakeBuildConfigurationProvider.java +++ b/build/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/CMakeBuildConfigurationProvider.java @@ -108,13 +108,19 @@ public class CMakeBuildConfigurationProvider implements ICBuildConfigurationProv // create config StringBuilder configName = new StringBuilder("cmake."); //$NON-NLS-1$ configName.append(launchMode); - if (os != null) { + if ("linux-container".equals(os)) { //$NON-NLS-1$ + String osConfigName = toolChain.getProperty("linux-container-id"); //$NON-NLS-1$ configName.append('.'); - configName.append(os); - } - if (arch != null && !arch.isEmpty()) { - configName.append('.'); - configName.append(arch); + configName.append(osConfigName); + } else { + if (os != null) { + configName.append('.'); + configName.append(os); + } + if (arch != null && !arch.isEmpty()) { + configName.append('.'); + configName.append(arch); + } } String name = configName.toString(); int i = 0; diff --git a/build/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/CMakeUtils.java b/build/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/CMakeUtils.java new file mode 100644 index 00000000000..f264f69be49 --- /dev/null +++ b/build/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/CMakeUtils.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2018 Red Hat Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Red Hat Inc. - initial version + *******************************************************************************/ +package org.eclipse.cdt.cmake.core.internal; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class CMakeUtils { + + /** + * Parse a string containing environment variables into individual VAR=VALUE pairs. + * + * @param envString - String to parse + * @return List of var=value Strings + */ + public static List stripEnvVars(String envString) { + Pattern p1 = Pattern.compile("(\\w+[=]\\\".*?\\\").*"); //$NON-NLS-1$ + Pattern p2 = Pattern.compile("(\\w+[=]'.*?').*"); //$NON-NLS-1$ + Pattern p3 = Pattern.compile("(\\w+[=][^\\s]+).*"); //$NON-NLS-1$ + boolean finished = false; + List envVars = new ArrayList<>(); + while (!finished) { + Matcher m1 = p1.matcher(envString); + if (m1.matches()) { + envString = envString.replaceFirst("\\w+[=]\\\".*?\\\"","").trim(); //$NON-NLS-1$ //$NON-NLS-2$ + String s = m1.group(1).trim(); + envVars.add(s.replaceAll("\\\"", "")); //$NON-NLS-1$ //$NON-NLS-2$ + } else { + Matcher m2 = p2.matcher(envString); + if (m2.matches()) { + envString = envString.replaceFirst("\\w+[=]'.*?'", "").trim(); //$NON-NLS-1$ //$NON-NLS-2$ + String s = m2.group(1).trim(); + envVars.add(s.replaceAll("'", "")); //$NON-NLS-1$ //$NON-NLS-2$ + } else { + Matcher m3 = p3.matcher(envString); + if (m3.matches()) { + envString = envString.replaceFirst("\\w+[=][^\\s]+", "").trim(); //$NON-NLS-1$ //$NON-NLS-2$ + envVars.add(m3.group(1).trim()); + } else { + finished = true; + } + } + } + } + return envVars; + } + +} diff --git a/build/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/Messages.java b/build/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/Messages.java index e07d1e9ef65..58ec1b728b0 100644 --- a/build/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/Messages.java +++ b/build/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/Messages.java @@ -14,11 +14,13 @@ public class Messages extends NLS { public static String CMakeBuildConfiguration_Building; public static String CMakeBuildConfiguration_BuildingIn; public static String CMakeBuildConfiguration_BuildingComplete; + public static String CMakeBuildConfiguration_BuildComplete; public static String CMakeBuildConfiguration_Cleaning; public static String CMakeBuildConfiguration_NotFound; public static String CMakeBuildConfiguration_NoToolchainFile; public static String CMakeBuildConfiguration_ProcCompCmds; public static String CMakeBuildConfiguration_ProcCompJson; + public static String CMakeBuildConfiguration_Failure; static { // initialize resource bundle NLS.initializeMessages(BUNDLE_NAME, Messages.class); diff --git a/build/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/messages.properties b/build/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/messages.properties index 2dd536559d5..2fd81516d52 100644 --- a/build/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/messages.properties +++ b/build/org.eclipse.cdt.cmake.core/src/org/eclipse/cdt/cmake/core/internal/messages.properties @@ -8,8 +8,10 @@ CMakeBuildConfiguration_Building=Building %s CMakeBuildConfiguration_BuildingIn=Building in: %s\n CMakeBuildConfiguration_BuildingComplete=Build complete (%d errors, %d warnings): %s\n +CMakeBuildConfiguration_BuildComplete=Build complete\n CMakeBuildConfiguration_Cleaning=Cleaning %s CMakeBuildConfiguration_NotFound=CMakeFiles not found. Assuming clean. CMakeBuildConfiguration_NoToolchainFile=No CMake toolchain file found for this target. CMakeBuildConfiguration_ProcCompCmds=Processing compile commands %s CMakeBuildConfiguration_ProcCompJson=Processing compile_commands.json +CMakeBuildConfiguration_Failure=Failure running cmake: %s\n diff --git a/build/org.eclipse.cdt.cmake.core/templates/simple/main.cpp b/build/org.eclipse.cdt.cmake.core/templates/simple/main.cpp index c493b4a5566..f1d3a296cb5 100644 --- a/build/org.eclipse.cdt.cmake.core/templates/simple/main.cpp +++ b/build/org.eclipse.cdt.cmake.core/templates/simple/main.cpp @@ -1,4 +1,7 @@ +#include +using namespace std; int main(int argc, char **argv) { + cout << "Hello world"; return 0; } diff --git a/build/org.eclipse.cdt.cmake.ui/src/org/eclipse/cdt/cmake/ui/internal/CMakePropertyCombo.java b/build/org.eclipse.cdt.cmake.ui/src/org/eclipse/cdt/cmake/ui/internal/CMakePropertyCombo.java new file mode 100644 index 00000000000..590a8177870 --- /dev/null +++ b/build/org.eclipse.cdt.cmake.ui/src/org/eclipse/cdt/cmake/ui/internal/CMakePropertyCombo.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2018 Red Hat Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Red Hat Inc. - initial implementation + *******************************************************************************/ +package org.eclipse.cdt.cmake.ui.internal; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; + +public class CMakePropertyCombo implements ICMakePropertyPageControl { + + private String name; + private String initialValue; + private Combo combo; + + public CMakePropertyCombo(Composite composite, String name, String[] values, String initialValue, String tooltip) { + this.name = name; + this.initialValue = initialValue; + Label label = new Label(composite, SWT.NONE); + label.setText(name); + label.setLayoutData(new GridData()); + combo = new Combo(composite, SWT.BORDER | SWT.DROP_DOWN | SWT.READ_ONLY); + GridData data = new GridData(GridData.FILL_BOTH); + data.grabExcessHorizontalSpace = true; + combo.setLayoutData(data); + combo.setItems(values); + combo.setText(initialValue); + combo.setToolTipText(tooltip); + } + + + @Override + public String getFieldValue() { + return combo.getText(); + } + + @Override + public String getFieldName() { + return name; + } + + @Override + public boolean isValueChanged() { + return !combo.getText().equals(initialValue); + } + + @Override + public boolean isValid() { + return true; + } + + @Override + public String getErrorMessage() { + return null; + } + +} diff --git a/build/org.eclipse.cdt.cmake.ui/src/org/eclipse/cdt/cmake/ui/internal/CMakePropertyText.java b/build/org.eclipse.cdt.cmake.ui/src/org/eclipse/cdt/cmake/ui/internal/CMakePropertyText.java new file mode 100644 index 00000000000..1c62a059bd3 --- /dev/null +++ b/build/org.eclipse.cdt.cmake.ui/src/org/eclipse/cdt/cmake/ui/internal/CMakePropertyText.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2018 Red Hat Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Red Hat Inc. - initial implementation + *******************************************************************************/ +package org.eclipse.cdt.cmake.ui.internal; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +public class CMakePropertyText implements ICMakePropertyPageControl { + + private String name; + private String initialValue; + protected Text text; + + public CMakePropertyText(Composite composite, String name, String initialValue, String tooltip) { + this.name = name; + if (initialValue == null) { + initialValue = ""; //$NON-NLS-1$ + } + this.initialValue = initialValue; + Label label = new Label(composite, SWT.NONE); + label.setText(name); + label.setLayoutData(new GridData()); + text = new Text(composite, SWT.SINGLE | SWT.BORDER); + GridData data = new GridData(GridData.FILL_BOTH); + data.grabExcessHorizontalSpace = true; + text.setLayoutData(data); + text.setText(initialValue); + text.setToolTipText(tooltip); + } + + @Override + public String getFieldValue() { + return text.getText(); + } + + @Override + public String getFieldName() { + return name; + } + + @Override + public boolean isValueChanged() { + return !text.getText().equals(initialValue); + } + + @Override + public boolean isValid() { + return true; + } + + @Override + public String getErrorMessage() { + return null; + } +} diff --git a/build/org.eclipse.cdt.cmake.ui/src/org/eclipse/cdt/cmake/ui/internal/ICMakePropertyPageControl.java b/build/org.eclipse.cdt.cmake.ui/src/org/eclipse/cdt/cmake/ui/internal/ICMakePropertyPageControl.java new file mode 100644 index 00000000000..b1c1a076ab2 --- /dev/null +++ b/build/org.eclipse.cdt.cmake.ui/src/org/eclipse/cdt/cmake/ui/internal/ICMakePropertyPageControl.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2018 Red Hat Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Red Hat Inc. - initial implementation + *******************************************************************************/ +package org.eclipse.cdt.cmake.ui.internal; + +public interface ICMakePropertyPageControl { + + /** + * Get the value of the field + * @return field value + */ + String getFieldValue(); + + /** + * Get the name of the field to set for cmake command + * @return field name + */ + String getFieldName(); + + /** + * Has the initial value changed + * @return + */ + boolean isValueChanged(); + + /** + * Is this field valid? + * @return + */ + boolean isValid(); + + /** + * Get the command line parameter if already configured + * @return String containing command-line for configured build dir + */ + default String getConfiguredString() { + return "-D" + getFieldName() + "=" + getFieldValue(); //$NON-NLS-1$ //$NON-NLS-2$ + } + + /** + * Get any error message for the control + * @return error message + */ + String getErrorMessage(); + +} diff --git a/build/org.eclipse.cdt.cmake.ui/src/org/eclipse/cdt/cmake/ui/internal/Messages.java b/build/org.eclipse.cdt.cmake.ui/src/org/eclipse/cdt/cmake/ui/internal/Messages.java index 91e4a323295..f0606790b6f 100644 --- a/build/org.eclipse.cdt.cmake.ui/src/org/eclipse/cdt/cmake/ui/internal/Messages.java +++ b/build/org.eclipse.cdt.cmake.ui/src/org/eclipse/cdt/cmake/ui/internal/Messages.java @@ -30,6 +30,12 @@ public class Messages extends NLS { public static String CMakePreferencePage_Toolchain; public static String CMakePropertyPage_FailedToStartCMakeGui_Body; public static String CMakePropertyPage_FailedToStartCMakeGui_Title; + public static String CMakePropertyPage_FailedToGetOS_Body; + public static String CMakePropertyPage_FailedToGetOS_Title; + public static String CMakePropertyPage_FailedToGetCMakeConfiguration_Body; + public static String CMakePropertyPage_FailedToGetCMakeConfiguration_Title; + public static String CMakePropertyPage_FailedToConfigure; + public static String CMakePropertyPage_Terminated; public static String CMakePropertyPage_LaunchCMakeGui; public static String NewCMakeProjectWizard_Description; diff --git a/build/org.eclipse.cdt.cmake.ui/src/org/eclipse/cdt/cmake/ui/internal/messages.properties b/build/org.eclipse.cdt.cmake.ui/src/org/eclipse/cdt/cmake/ui/internal/messages.properties index 09d2055d8c4..3b556d1e5ea 100644 --- a/build/org.eclipse.cdt.cmake.ui/src/org/eclipse/cdt/cmake/ui/internal/messages.properties +++ b/build/org.eclipse.cdt.cmake.ui/src/org/eclipse/cdt/cmake/ui/internal/messages.properties @@ -17,6 +17,12 @@ CMakePreferencePage_Remove=Remove CMakePreferencePage_Toolchain=Toolchain CMakePropertyPage_FailedToStartCMakeGui_Body=Failed to run the CMake GUI: CMakePropertyPage_FailedToStartCMakeGui_Title=Failed to run CMake GUI +CMakePropertyPage_FailedToGetOS_Body=Failed to get target OS for CMake project: +CMakePropertyPage_FailedToGetOS_Title=Failed to get target OS +CMakePropertyPage_FailedToFetchCMakeConfiguration_Body=Failed to fetch CMake configuration values +CMakePropertyPage_FailedToFetchCMakeConfiguration_Title=Failed to fetch CMake configuration +CMakePropertyPage_FailedToConfigure=Failed to reconfigure CMake +CMakePropertyPage_Terminated=Command terminated with rc={0}\n CMakePropertyPage_LaunchCMakeGui=Launch CMake GUI... NewCMakeProjectWizard_Description=Specify properties of new CMake project. NewCMakeProjectWizard_PageTitle=New CMake Project diff --git a/build/org.eclipse.cdt.cmake.ui/src/org/eclipse/cdt/cmake/ui/properties/CMakePropertyPage.java b/build/org.eclipse.cdt.cmake.ui/src/org/eclipse/cdt/cmake/ui/properties/CMakePropertyPage.java index 7699a3c82d4..0a8b174de7e 100644 --- a/build/org.eclipse.cdt.cmake.ui/src/org/eclipse/cdt/cmake/ui/properties/CMakePropertyPage.java +++ b/build/org.eclipse.cdt.cmake.ui/src/org/eclipse/cdt/cmake/ui/properties/CMakePropertyPage.java @@ -10,12 +10,37 @@ *******************************************************************************/ package org.eclipse.cdt.cmake.ui.properties; +import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.eclipse.cdt.cmake.ui.internal.Activator; +import org.eclipse.cdt.cmake.ui.internal.CMakePropertyCombo; +import org.eclipse.cdt.cmake.ui.internal.CMakePropertyText; +import org.eclipse.cdt.cmake.ui.internal.ICMakePropertyPageControl; import org.eclipse.cdt.cmake.ui.internal.Messages; +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.CommandLauncherManager; +import org.eclipse.cdt.core.ICommandLauncher; +import org.eclipse.cdt.core.build.CBuildConfiguration; +import org.eclipse.cdt.core.build.ICBuildCommandLauncher; +import org.eclipse.cdt.core.build.ICBuildConfiguration; +import org.eclipse.cdt.core.build.IToolChain; +import org.eclipse.cdt.core.resources.IConsole; +import org.eclipse.core.resources.IBuildConfiguration; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Path; import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; @@ -24,6 +49,7 @@ 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.ui.dialogs.PropertyPage; /** @@ -35,32 +61,238 @@ import org.eclipse.ui.dialogs.PropertyPage; * PATH. */ public class CMakePropertyPage extends PropertyPage { + + private List componentList = new ArrayList<>(); @Override protected Control createContents(Composite parent) { Composite composite = new Composite(parent, SWT.NONE); composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); composite.setLayout(new GridLayout()); - - Button b = new Button(composite, SWT.NONE); - b.setText(Messages.CMakePropertyPage_LaunchCMakeGui); - b.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - IProject project = (IProject) getElement(); + + boolean isContainerBuild = false; + ICBuildConfiguration cconfig = null; + IProject project = (IProject) getElement(); + try { + IBuildConfiguration config = project.getActiveBuildConfig(); + cconfig = config.getAdapter(ICBuildConfiguration.class); + IToolChain toolChain = cconfig.getToolChain(); + String os = toolChain.getProperty(IToolChain.ATTR_OS); + isContainerBuild = os.equals("linux-container"); //$NON-NLS-1$ + } catch (CoreException e2) { + MessageDialog.openError(parent.getShell(), Messages.CMakePropertyPage_FailedToGetOS_Title, + Messages.CMakePropertyPage_FailedToGetOS_Body + e2.getMessage()); + } + + if (isContainerBuild) { + try { + ICommandLauncher launcher = CommandLauncherManager.getInstance().getCommandLauncher(project.getActiveBuildConfig().getAdapter(ICBuildConfiguration.class)); + launcher.setProject(project); + if (launcher instanceof ICBuildCommandLauncher) { + ((ICBuildCommandLauncher)launcher).setBuildConfiguration(cconfig); + } + IPath buildPath = project.getLocation().append("build").append(((CBuildConfiguration)cconfig).getName()); + Process p = launcher.execute(new Path("/bin/sh"), new String[] { "-c", "cmake -LAH ."}, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + new String[0], buildPath, new NullProgressMonitor()); + if (p != null) { + ByteArrayOutputStream stdout = new ByteArrayOutputStream(); + ByteArrayOutputStream stderr = new ByteArrayOutputStream(); + int rc = -1; try { - String configName = project.getActiveBuildConfig().getName(); - String sourceDir = project.getLocation().toOSString(); - String buildDir = project.getLocation().append("build").append(configName).toOSString(); //$NON-NLS-1$ - - Runtime.getRuntime().exec(new String[] { "cmake-gui", "-H" + sourceDir, "-B" + buildDir }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - } catch (CoreException | IOException e1) { - MessageDialog.openError(parent.getShell(), Messages.CMakePropertyPage_FailedToStartCMakeGui_Title, - Messages.CMakePropertyPage_FailedToStartCMakeGui_Body + e1.getMessage()); + if (launcher.waitAndRead(stdout, stderr, new NullProgressMonitor()) == ICommandLauncher.OK) { + p.waitFor(); + } + rc = p.exitValue(); + } catch (InterruptedException e) { + // ignore for now + } + if (rc == 0) { + componentList = parseConfigureOutput(stdout, composite); } } - }); + } catch (CoreException e) { + MessageDialog.openError(parent.getShell(), Messages.CMakePropertyPage_FailedToGetCMakeConfiguration_Title, + Messages.CMakePropertyPage_FailedToGetCMakeConfiguration_Body + e.getMessage()); + } + + + } else { + + Button b = new Button(composite, SWT.NONE); + b.setText(Messages.CMakePropertyPage_LaunchCMakeGui); + b.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + IProject project = (IProject) getElement(); + try { + String configName = project.getActiveBuildConfig().getName(); + String sourceDir = project.getLocation().toOSString(); + String buildDir = project.getLocation().append("build").append(configName).toOSString(); //$NON-NLS-1$ + + Runtime.getRuntime().exec(new String[] { "cmake-gui", "-H" + sourceDir, "-B" + buildDir }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } catch (CoreException | IOException e1) { + MessageDialog.openError(parent.getShell(), Messages.CMakePropertyPage_FailedToStartCMakeGui_Title, + Messages.CMakePropertyPage_FailedToStartCMakeGui_Body + e1.getMessage()); + } + } + }); + } return composite; } + + @Override + public boolean performOk() { + List args = new ArrayList<>(); + args.add("cmake"); //$NON-NLS-1$ + args.add("-LAH"); //$NON-NLS-1$ + for (ICMakePropertyPageControl control : componentList) { + if (control.isValueChanged()) { + args.add(control.getConfiguredString()); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + if (args.size() == 2) { + return true; + } + try { + IProject project = (IProject) getElement(); + ICBuildConfiguration buildConfig = project.getActiveBuildConfig().getAdapter(ICBuildConfiguration.class); + String configName = ((CBuildConfiguration)buildConfig).getName(); + IPath buildDir = project.getLocation().append("build").append(configName); //$NON-NLS-1$ + ICommandLauncher launcher = CommandLauncherManager.getInstance().getCommandLauncher(project.getActiveBuildConfig().getAdapter(ICBuildConfiguration.class)); + launcher.setProject(project); + if (launcher instanceof ICBuildCommandLauncher) { + ((ICBuildCommandLauncher)launcher).setBuildConfiguration(buildConfig); + } + StringBuilder b = new StringBuilder(); + for (String arg : args) { + b.append(arg); + b.append(" "); //$NON-NLS-1$ + } + b.append("."); + Process p = launcher.execute(new Path("/bin/sh"), new String[] { "-c", b.toString() }, new String[0], buildDir, new NullProgressMonitor()); //$NON-NLS-1$ //$NON-NLS-2$ + int rc = -1; + IConsole console = CCorePlugin.getDefault().getConsole(); + console.start(project); + try (OutputStream stdout = console.getOutputStream()) { + OutputStream stderr = stdout; + StringBuilder buf = new StringBuilder(); + for (String arg : args) { + buf.append(arg); + buf.append(" "); //$NON-NLS-1$ + } + buf.append(System.lineSeparator()); + stdout.write(buf.toString().getBytes()); + stdout.flush(); + try { + + if (launcher.waitAndRead(stdout, stderr, new NullProgressMonitor()) == ICommandLauncher.OK) { + p.waitFor(); + } + rc = p.exitValue(); + stdout.write(NLS.bind(Messages.CMakePropertyPage_Terminated, rc).getBytes()); + stdout.flush(); + if (rc != 0) { + Display.getDefault().syncExec(() -> { + MessageDialog.openError(getShell(), null, Messages.CMakePropertyPage_FailedToConfigure); + }); + } + } catch (InterruptedException e) { + // ignore for now + } + } catch (IOException e2) { + Activator.log(e2); + return false; + } + } catch (CoreException e3) { + // TODO Auto-generated catch block + Activator.log(e3); + return false; + } + return true; + } + + + public enum ParseState { + INIT, SEENCOMMENT + }; + + /** + * Parse output of cmake -LAH call to determine options to show to user + * @param stdout - ByteArrayOutputStream containing output of command + * @param composite - Composite to add Controls to + * @return - list of Controls + */ + List parseConfigureOutput(ByteArrayOutputStream stdout, Composite composite) { + List controls = new ArrayList<>(); + + try { + ParseState state = ParseState.INIT; + + String output = stdout.toString(StandardCharsets.UTF_8.name()); + String[] lines = output.split("\\r?\\n"); //$NON-NLS-1$ + Pattern commentPattern = Pattern.compile("//(.*)"); //$NON-NLS-1$ + Pattern argPattern = Pattern.compile("(\\w+):([a-zA-Z]+)=(.*)"); //$NON-NLS-1$ + Pattern optionPattern = Pattern.compile(".*?options are:((\\s+\\w+(\\(.*\\))?)+).*"); //$NON-NLS-1$ + + String lastComment = ""; //$NON-NLS-1$ + for (String line : lines) { + line = line.trim(); + switch (state) { + case INIT: + Matcher commentMatcher = commentPattern.matcher(line); + if (commentMatcher.matches()) { + state = ParseState.SEENCOMMENT; + + lastComment = commentMatcher.group(1); + } + break; + case SEENCOMMENT: + Matcher argMatcher = argPattern.matcher(line); + if (argMatcher.matches()) { + String name = argMatcher.group(1); + String type = argMatcher.group(2); + String initialValue = argMatcher.group(3); + Matcher optionMatcher = optionPattern.matcher(lastComment); + if (optionMatcher.matches()) { + String optionString = optionMatcher.group(1).trim(); + String[] options = optionString.split("\\s+"); //$NON-NLS-1$ + for (int i = 0; i < options.length; ++i) { + options[i] = options[i].replaceAll("\\(.*?\\)", "").trim(); //$NON-NLS-1$ + } + ICMakePropertyPageControl control = new CMakePropertyCombo(composite, name, options, initialValue, lastComment); + controls.add(control); + } else { + if ("BOOL".equals(type)) { + if ("ON".equals(initialValue) || ("OFF".equals(initialValue))) { + ICMakePropertyPageControl control = new CMakePropertyCombo(composite, name, new String[] {"ON","OFF"}, //$NON-NLS-1$ //$NON-NLS-2$ + initialValue, lastComment); + controls.add(control); + } else if ("YES".equals(initialValue) || "NO".equals(initialValue)) { + ICMakePropertyPageControl control = new CMakePropertyCombo(composite, name, new String[] {"YES","NO"}, //$NON-NLS-1$ //$NON-NLS-2$ + initialValue, lastComment); + controls.add(control); + } else { + ICMakePropertyPageControl control = new CMakePropertyCombo(composite, name, new String[] {"TRUE", "FALSE"}, //$NON-NLS-1$ //$NON-NLS-2$ + "TRUE".equals(initialValue) ? "TRUE" : "FALSE", lastComment); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + controls.add(control); + } + } else { + ICMakePropertyPageControl control = new CMakePropertyText(composite, name, initialValue, lastComment); + controls.add(control); + } + } + } + state = ParseState.INIT; + break; + } + } + + } catch (UnsupportedEncodingException e) { + return controls; + } + + return controls; + } + }