diff --git a/.gitattributes b/.gitattributes
index 329a3eecb3c..784d85d78db 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -17,6 +17,7 @@ CONTRIBUTING text
*.cc text
*.cpp text
*.h text
+*.in text
*.s text
*.S text
*.elf binary
diff --git a/build/org.eclipse.cdt.meson.core/META-INF/MANIFEST.MF b/build/org.eclipse.cdt.meson.core/META-INF/MANIFEST.MF
index e270f1be98d..dd52385cbff 100644
--- a/build/org.eclipse.cdt.meson.core/META-INF/MANIFEST.MF
+++ b/build/org.eclipse.cdt.meson.core/META-INF/MANIFEST.MF
@@ -2,13 +2,13 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name.0
Bundle-SymbolicName: org.eclipse.cdt.meson.core;singleton:=true
-Bundle-Version: 1.1.200.qualifier
+Bundle-Version: 1.1.300.qualifier
Bundle-Activator: org.eclipse.cdt.meson.core.Activator
Bundle-Vendor: %provider
Require-Bundle: org.eclipse.core.runtime,
org.eclipse.core.resources,
org.eclipse.cdt.core;bundle-version="6.4.0",
- org.eclipse.tools.templates.freemarker
+ org.eclipse.tools.templates.freemarker;bundle-version="1.2.200"
Bundle-RequiredExecutionEnvironment: JavaSE-11
Bundle-ActivationPolicy: lazy
Bundle-Localization: plugin
diff --git a/build/org.eclipse.cdt.meson.ui/META-INF/MANIFEST.MF b/build/org.eclipse.cdt.meson.ui/META-INF/MANIFEST.MF
index 430cae96a31..80ac92825d8 100644
--- a/build/org.eclipse.cdt.meson.ui/META-INF/MANIFEST.MF
+++ b/build/org.eclipse.cdt.meson.ui/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name.0
Bundle-SymbolicName: org.eclipse.cdt.meson.ui;singleton:=true
-Bundle-Version: 1.1.200.qualifier
+Bundle-Version: 1.1.300.qualifier
Bundle-Vendor: %vendorName
Bundle-RequiredExecutionEnvironment: JavaSE-11
Bundle-Activator: org.eclipse.cdt.meson.ui.Activator
@@ -11,10 +11,9 @@ Bundle-Localization: plugin
Require-Bundle: org.eclipse.core.runtime;bundle-version="3.13.0",
org.eclipse.ui;bundle-version="3.109.0",
org.eclipse.cdt.meson.core;bundle-version="1.0.0",
- org.eclipse.tools.templates.core;bundle-version="1.1.0",
- org.eclipse.tools.templates.ui;bundle-version="1.1.1",
+ org.eclipse.tools.templates.ui;bundle-version="1.3.0",
+ org.eclipse.tools.templates.freemarker;bundle-version="1.2.200",
org.eclipse.ui.ide;bundle-version="3.13.1",
- org.eclipse.tools.templates.freemarker;bundle-version="1.0.0",
org.eclipse.cdt.core;bundle-version="6.4.0",
org.eclipse.core.resources;bundle-version="3.12.0",
org.eclipse.debug.core;bundle-version="3.11.0",
diff --git a/build/org.eclipse.cdt.meson.ui/plugin.xml b/build/org.eclipse.cdt.meson.ui/plugin.xml
index a517f8ec1cf..9c788aaff3c 100644
--- a/build/org.eclipse.cdt.meson.ui/plugin.xml
+++ b/build/org.eclipse.cdt.meson.ui/plugin.xml
@@ -147,7 +147,12 @@
-
-
+
+
+
+
diff --git a/build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/MesonProjectConfigurator.java b/build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/MesonProjectConfigurator.java
new file mode 100644
index 00000000000..3c38675aa16
--- /dev/null
+++ b/build/org.eclipse.cdt.meson.ui/src/org/eclipse/cdt/internal/meson/ui/MesonProjectConfigurator.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2021 Mat Booth and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+package org.eclipse.cdt.internal.meson.ui;
+
+import java.util.List;
+
+import org.eclipse.cdt.meson.core.MesonProjectGenerator;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.tools.templates.core.IGenerator;
+import org.eclipse.tools.templates.ui.ProjectImportConfigurator;
+
+/**
+ * Smart-import strategy for importing pre-existing Meson projects.
+ */
+public class MesonProjectConfigurator extends ProjectImportConfigurator {
+
+ @Override
+ protected List getProjectFileNames() {
+ return List.of("meson.build"); //$NON-NLS-1$
+ }
+
+ @Override
+ protected IGenerator getGenerator(IProject project) {
+ // Don't pass any template to the generator, we are importing an existing project
+ MesonProjectGenerator generator = new MesonProjectGenerator(null);
+ generator.setProjectName(project.getName());
+ generator.setLocationURI(project.getLocationURI());
+ return generator;
+ }
+}
diff --git a/cmake/org.eclipse.cdt.cmake.core/META-INF/MANIFEST.MF b/cmake/org.eclipse.cdt.cmake.core/META-INF/MANIFEST.MF
index a39ba297e2b..b53aecc42ae 100644
--- a/cmake/org.eclipse.cdt.cmake.core/META-INF/MANIFEST.MF
+++ b/cmake/org.eclipse.cdt.cmake.core/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.cdt.cmake.core;singleton:=true
-Bundle-Version: 1.4.200.qualifier
+Bundle-Version: 1.4.300.qualifier
Bundle-Activator: org.eclipse.cdt.cmake.core.internal.Activator
Bundle-Vendor: %providerName
Require-Bundle: org.eclipse.core.runtime,
@@ -10,7 +10,7 @@ Require-Bundle: org.eclipse.core.runtime,
org.eclipse.debug.core;bundle-version="3.10.0",
org.eclipse.launchbar.core;bundle-version="2.0.0",
org.eclipse.cdt.core;bundle-version="5.12.0",
- org.eclipse.tools.templates.freemarker;bundle-version="1.0.0";visibility:=reexport,
+ org.eclipse.tools.templates.freemarker;bundle-version="1.2.200",
com.google.gson,
org.eclipse.cdt.jsoncdb.core,
org.yaml.snakeyaml;bundle-version="1.14.0"
diff --git a/cmake/org.eclipse.cdt.cmake.ui.tests/META-INF/MANIFEST.MF b/cmake/org.eclipse.cdt.cmake.ui.tests/META-INF/MANIFEST.MF
index c7e426c65d2..0e122f34b15 100644
--- a/cmake/org.eclipse.cdt.cmake.ui.tests/META-INF/MANIFEST.MF
+++ b/cmake/org.eclipse.cdt.cmake.ui.tests/META-INF/MANIFEST.MF
@@ -11,3 +11,5 @@ Require-Bundle: org.eclipse.swtbot.go;bundle-version="2.7.0",
org.eclipse.cdt.cmake.core;bundle-version="1.2.0"
Automatic-Module-Name: org.eclipse.cdt.cmake.ui.tests
Bundle-Localization: plugin
+Import-Package: org.junit.jupiter.api;version="5.8.1",
+ org.junit.jupiter.api.io;version="5.8.1"
diff --git a/cmake/org.eclipse.cdt.cmake.ui.tests/build.properties b/cmake/org.eclipse.cdt.cmake.ui.tests/build.properties
index 4d08392689a..132ade6351a 100644
--- a/cmake/org.eclipse.cdt.cmake.ui.tests/build.properties
+++ b/cmake/org.eclipse.cdt.cmake.ui.tests/build.properties
@@ -4,5 +4,7 @@ bin.includes = META-INF/,\
.,\
plugin.properties,\
about.html,\
- swtbot-test-plugin.properties
-src.includes = about.html
+ swtbot-test-plugin.properties,\
+ projects/
+src.includes = about.html,\
+ projects/
diff --git a/cmake/org.eclipse.cdt.cmake.ui.tests/pom.xml b/cmake/org.eclipse.cdt.cmake.ui.tests/pom.xml
index f2bec5f1152..3ff99b7ad34 100644
--- a/cmake/org.eclipse.cdt.cmake.ui.tests/pom.xml
+++ b/cmake/org.eclipse.cdt.cmake.ui.tests/pom.xml
@@ -30,7 +30,6 @@
org.eclipse.tycho
target-platform-configuration
- ${tycho-version}
@@ -50,4 +49,4 @@
-
\ No newline at end of file
+
diff --git a/cmake/org.eclipse.cdt.cmake.ui.tests/projects/NestedProject/App1/CMakeLists.txt b/cmake/org.eclipse.cdt.cmake.ui.tests/projects/NestedProject/App1/CMakeLists.txt
new file mode 100644
index 00000000000..9cdcf22c485
--- /dev/null
+++ b/cmake/org.eclipse.cdt.cmake.ui.tests/projects/NestedProject/App1/CMakeLists.txt
@@ -0,0 +1,15 @@
+cmake_minimum_required(VERSION 3.10)
+
+# Set the project name and version
+project(App1 VERSION 1.0)
+
+set(CMAKE_EXPORT_COMPILE_COMMANDS "true")
+
+# Configuration header
+configure_file(app1.h.in app1.h)
+
+# Add project executable
+add_executable(${PROJECT_NAME} app1.c)
+
+# Include the configuration header
+target_include_directories(${PROJECT_NAME} PUBLIC "${PROJECT_BINARY_DIR}")
diff --git a/cmake/org.eclipse.cdt.cmake.ui.tests/projects/NestedProject/App1/app1.c b/cmake/org.eclipse.cdt.cmake.ui.tests/projects/NestedProject/App1/app1.c
new file mode 100644
index 00000000000..3c835fdd007
--- /dev/null
+++ b/cmake/org.eclipse.cdt.cmake.ui.tests/projects/NestedProject/App1/app1.c
@@ -0,0 +1,8 @@
+#include
+#include "app1.h"
+
+int main() {
+ printf("Welcome to App1\n");
+ printf("v%d.%d\n", App1_VERSION_MAJOR, App1_VERSION_MINOR);
+ return 0;
+}
diff --git a/cmake/org.eclipse.cdt.cmake.ui.tests/projects/NestedProject/App1/app1.h.in b/cmake/org.eclipse.cdt.cmake.ui.tests/projects/NestedProject/App1/app1.h.in
new file mode 100644
index 00000000000..fd3a262863f
--- /dev/null
+++ b/cmake/org.eclipse.cdt.cmake.ui.tests/projects/NestedProject/App1/app1.h.in
@@ -0,0 +1,2 @@
+#define App1_VERSION_MAJOR @App1_VERSION_MAJOR@
+#define App1_VERSION_MINOR @App1_VERSION_MINOR@
diff --git a/cmake/org.eclipse.cdt.cmake.ui.tests/projects/NestedProject/App2/CMakeLists.txt b/cmake/org.eclipse.cdt.cmake.ui.tests/projects/NestedProject/App2/CMakeLists.txt
new file mode 100644
index 00000000000..09be2e33ff1
--- /dev/null
+++ b/cmake/org.eclipse.cdt.cmake.ui.tests/projects/NestedProject/App2/CMakeLists.txt
@@ -0,0 +1,15 @@
+cmake_minimum_required(VERSION 3.10)
+
+# Set the project name and version
+project(App2 VERSION 2.0)
+
+set(CMAKE_EXPORT_COMPILE_COMMANDS "true")
+
+# Configuration header
+configure_file(app2.h.in app2.h)
+
+# Add project executable
+add_executable(${PROJECT_NAME} app2.c)
+
+# Include the configuration header
+target_include_directories(${PROJECT_NAME} PUBLIC "${PROJECT_BINARY_DIR}")
diff --git a/cmake/org.eclipse.cdt.cmake.ui.tests/projects/NestedProject/App2/app2.c b/cmake/org.eclipse.cdt.cmake.ui.tests/projects/NestedProject/App2/app2.c
new file mode 100644
index 00000000000..7657f365c00
--- /dev/null
+++ b/cmake/org.eclipse.cdt.cmake.ui.tests/projects/NestedProject/App2/app2.c
@@ -0,0 +1,8 @@
+#include
+#include "app2.h"
+
+int main() {
+ printf("Welcome to App2\n");
+ printf("v%d.%d\n", App2_VERSION_MAJOR, App2_VERSION_MINOR);
+ return 0;
+}
diff --git a/cmake/org.eclipse.cdt.cmake.ui.tests/projects/NestedProject/App2/app2.h.in b/cmake/org.eclipse.cdt.cmake.ui.tests/projects/NestedProject/App2/app2.h.in
new file mode 100644
index 00000000000..48cbed16ac5
--- /dev/null
+++ b/cmake/org.eclipse.cdt.cmake.ui.tests/projects/NestedProject/App2/app2.h.in
@@ -0,0 +1,2 @@
+#define App2_VERSION_MAJOR @App2_VERSION_MAJOR@
+#define App2_VERSION_MINOR @App2_VERSION_MINOR@
diff --git a/cmake/org.eclipse.cdt.cmake.ui.tests/projects/NestedProject/CMakeLists.txt b/cmake/org.eclipse.cdt.cmake.ui.tests/projects/NestedProject/CMakeLists.txt
new file mode 100644
index 00000000000..2f985a02c20
--- /dev/null
+++ b/cmake/org.eclipse.cdt.cmake.ui.tests/projects/NestedProject/CMakeLists.txt
@@ -0,0 +1,18 @@
+cmake_minimum_required(VERSION 3.10)
+
+# Set the project name
+project(NestedProject)
+
+include(ExternalProject)
+ExternalProject_Add(
+ App1
+ SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/App1"
+ PREFIX App1
+ INSTALL_COMMAND ""
+)
+ExternalProject_Add(
+ App2
+ SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/App2"
+ PREFIX App2
+ INSTALL_COMMAND ""
+)
diff --git a/cmake/org.eclipse.cdt.cmake.ui.tests/projects/SimpleProject/CMakeLists.txt b/cmake/org.eclipse.cdt.cmake.ui.tests/projects/SimpleProject/CMakeLists.txt
new file mode 100644
index 00000000000..33079324d57
--- /dev/null
+++ b/cmake/org.eclipse.cdt.cmake.ui.tests/projects/SimpleProject/CMakeLists.txt
@@ -0,0 +1,15 @@
+cmake_minimum_required(VERSION 3.10)
+
+# Set the project name and version
+project(SimpleProject VERSION 1.0)
+
+set(CMAKE_EXPORT_COMPILE_COMMANDS "true")
+
+# Configuration header
+configure_file(simple.h.in simple.h)
+
+# Add project executable
+add_executable(${PROJECT_NAME} simple.c)
+
+# Include the configuration header
+target_include_directories(${PROJECT_NAME} PUBLIC "${PROJECT_BINARY_DIR}")
diff --git a/cmake/org.eclipse.cdt.cmake.ui.tests/projects/SimpleProject/simple.c b/cmake/org.eclipse.cdt.cmake.ui.tests/projects/SimpleProject/simple.c
new file mode 100644
index 00000000000..88de1dcd6f4
--- /dev/null
+++ b/cmake/org.eclipse.cdt.cmake.ui.tests/projects/SimpleProject/simple.c
@@ -0,0 +1,8 @@
+#include
+#include "simple.h"
+
+int main() {
+ printf("Hello, World!\n");
+ printf("v%d.%d\n", SimpleProject_VERSION_MAJOR, SimpleProject_VERSION_MINOR);
+ return 0;
+}
diff --git a/cmake/org.eclipse.cdt.cmake.ui.tests/projects/SimpleProject/simple.h.in b/cmake/org.eclipse.cdt.cmake.ui.tests/projects/SimpleProject/simple.h.in
new file mode 100644
index 00000000000..6b3ff6931d3
--- /dev/null
+++ b/cmake/org.eclipse.cdt.cmake.ui.tests/projects/SimpleProject/simple.h.in
@@ -0,0 +1,2 @@
+#define SimpleProject_VERSION_MAJOR @SimpleProject_VERSION_MAJOR@
+#define SimpleProject_VERSION_MINOR @SimpleProject_VERSION_MINOR@
diff --git a/cmake/org.eclipse.cdt.cmake.ui.tests/src/org/eclipse/cdt/cmake/ui/internal/tests/AutomatedIntegrationSuite.java b/cmake/org.eclipse.cdt.cmake.ui.tests/src/org/eclipse/cdt/cmake/ui/internal/tests/AutomatedIntegrationSuite.java
deleted file mode 100644
index 35460381c2f..00000000000
--- a/cmake/org.eclipse.cdt.cmake.ui.tests/src/org/eclipse/cdt/cmake/ui/internal/tests/AutomatedIntegrationSuite.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2017 QNX Software Systems and others.
- *
- * This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License 2.0
- * which accompanies this distribution, and is available at
- * https://www.eclipse.org/legal/epl-2.0/
- *
- * SPDX-License-Identifier: EPL-2.0
- *******************************************************************************/
-package org.eclipse.cdt.cmake.ui.internal.tests;
-
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
-
-@RunWith(Suite.class)
-@Suite.SuiteClasses({ NewCMakeProjectTest.class })
-public class AutomatedIntegrationSuite {
-
-}
diff --git a/cmake/org.eclipse.cdt.cmake.ui.tests/src/org/eclipse/cdt/cmake/ui/internal/tests/NewCMakeProjectTest.java b/cmake/org.eclipse.cdt.cmake.ui.tests/src/org/eclipse/cdt/cmake/ui/internal/tests/NewCMakeProjectTest.java
index 0f18d9b54ec..d53ceb8f112 100644
--- a/cmake/org.eclipse.cdt.cmake.ui.tests/src/org/eclipse/cdt/cmake/ui/internal/tests/NewCMakeProjectTest.java
+++ b/cmake/org.eclipse.cdt.cmake.ui.tests/src/org/eclipse/cdt/cmake/ui/internal/tests/NewCMakeProjectTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2017 QNX Software Systems and others.
+ * Copyright (c) 2017, 2021 QNX Software Systems and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -11,7 +11,16 @@
package org.eclipse.cdt.cmake.ui.internal.tests;
import static org.eclipse.swtbot.eclipse.finder.matchers.WidgetMatcherFactory.withPartName;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.io.IOException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.concurrent.TimeUnit;
import org.eclipse.cdt.cmake.core.CMakeNature;
import org.eclipse.cdt.core.CCorePlugin;
@@ -19,45 +28,82 @@ import org.eclipse.cdt.core.index.IIndexManager;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.ICoreRunnable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot;
import org.eclipse.swtbot.eclipse.finder.waits.Conditions;
+import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotPerspective;
import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotView;
import org.eclipse.swtbot.swt.finder.utils.SWTBotPreferences;
+import org.eclipse.swtbot.swt.finder.waits.DefaultCondition;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotShell;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotTable;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotTableItem;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
+import org.eclipse.swtbot.swt.finder.widgets.SWTBotTree;
+import org.eclipse.swtbot.swt.finder.widgets.SWTBotTreeItem;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
+import org.junit.jupiter.api.io.TempDir;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
-@SuppressWarnings("nls")
public class NewCMakeProjectTest {
private static SWTWorkbenchBot bot;
- @BeforeClass
+ @TempDir
+ public static Path TEMP_DIR;
+
+ @BeforeAll
public static void beforeClass() {
SWTBotPreferences.KEYBOARD_LAYOUT = "EN_US";
SWTBotPreferences.TIMEOUT = 10000;
bot = new SWTWorkbenchBot();
}
- @Before
+ @BeforeEach
public void before() {
bot.resetWorkbench();
for (SWTBotView view : bot.views(withPartName("Welcome"))) {
view.close();
}
+ SWTBotPerspective perspective = bot.perspectiveById("org.eclipse.cdt.ui.CPerspective");
+ perspective.activate();
+ bot.shell().activate();
}
- @Test(timeout = 60000)
- public void createCMakeProject() throws Exception {
- // open C++ perspective
- if (!"C/C++".equals(bot.activePerspective().getLabel())) {
- bot.perspectiveByLabel("C/C++").activate();
+ @AfterEach
+ public void after() {
+ // Delete created projects after we are done with them
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+ IProject[] projects = workspace.getRoot().getProjects();
+ ICoreRunnable runnable = new ICoreRunnable() {
+ @Override
+ public void run(IProgressMonitor monitor) throws CoreException {
+ for (IProject project : projects) {
+ project.delete(true, true, null);
+ }
+ }
+ };
+ try {
+ workspace.run(runnable, workspace.getRoot(), IWorkspace.AVOID_UPDATE, new NullProgressMonitor());
+ } catch (CoreException e) {
+ fail(e);
}
+ }
+
+ @Test
+ @Timeout(value = 2, unit = TimeUnit.MINUTES)
+ void createCMakeProject() throws Exception {
// Activate C/C++ wizard
bot.menu("File").menu("New").menu("C/C++ Project").click();
@@ -86,22 +132,113 @@ public class NewCMakeProjectTest {
bot.button("Finish").click();
bot.waitUntil(Conditions.shellCloses(newProjectShell));
+ verifyProjectInExplorer(projectName);
+ }
+
+ @Test
+ @Timeout(value = 2, unit = TimeUnit.MINUTES)
+ void importSimpleCMakeProject() throws Exception {
+ importCMakeProject("SimpleProject", "SimpleProject");
+ }
+
+ @Test
+ @Timeout(value = 2, unit = TimeUnit.MINUTES)
+ void importNestedCMakeProject() throws Exception {
+ importCMakeProject("NestedProject", "NestedProject", "NestedProject/App1", "NestedProject/App2");
+ }
+
+ private void importCMakeProject(String projectDir, String... expectedProjects) throws Exception {
+ // Copy test project out of the bundle to a temp location first
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ URL url = FileLocator
+ .toFileURL(FileLocator.find(bundle, new org.eclipse.core.runtime.Path("projects/" + projectDir), null));
+ copyDir(Paths.get(url.getPath()), TEMP_DIR.resolve(projectDir));
+
+ // Activate import wizard
+ bot.menu("File").menu("Import...").click();
+ bot.shell("Import").activate();
+
+ // Open the smart import wizard
+ SWTBotTree wizTree = bot.tree();
+ SWTBotTreeItem generalItem = wizTree.getTreeItem("General").expand();
+ generalItem.getNode("Projects from Folder or Archive").doubleClick();
+
+ // Select path that contains projects to import and check the project type detection works
+ bot.comboBox().setText(TEMP_DIR.resolve(projectDir).toString());
+ SWTBotTree projectProposalTree = bot.tree();
+ assertEquals(expectedProjects.length, projectProposalTree.getAllItems().length);
+ for (SWTBotTreeItem item : projectProposalTree.getAllItems()) {
+ assertEquals("CMake Project", item.cell(1), "Project type was not detected");
+ }
+
+ // Import and verify the project(s)
+ SWTBotShell wizShell = bot.activeShell();
+ bot.button("Finish").click();
+ bot.waitUntil(Conditions.shellCloses(wizShell));
+ for (String expectedProject : expectedProjects) {
+ verifyProjectInExplorer(expectedProject);
+ }
+ }
+
+ private void verifyProjectInExplorer(String projectName) throws Exception {
// Make sure it shows up in Project Explorer
SWTBotView explorer = bot.viewByPartName("Project Explorer");
explorer.show();
explorer.setFocus();
- bot.tree().getTreeItem(projectName);
- // Make sure the project indexer completes. At that point we're stable.
- IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
- ICProject cproject = CoreModel.getDefault().create(project);
- IIndexManager indexManager = CCorePlugin.getIndexManager();
- while (!indexManager.isProjectContentSynced(cproject)) {
- Thread.sleep(1000);
+ // If the project name is a path with >1 segment it was nested, so we need expand the
+ // parent project node to make the child project nodes visible
+ Path path = Paths.get(projectName);
+ SWTBotTreeItem projectNode;
+ if (path.getNameCount() > 1) {
+ SWTBotTreeItem node = explorer.bot().tree().expandNode(path.getName(0).toString());
+ projectNode = node.getNode(path.getFileName().toString());
+ } else {
+ projectNode = explorer.bot().tree().getTreeItem(path.getFileName().toString());
}
+ // Tests can be unstable if we are too quick, so make sure the project indexer completes
+ // and project natures have been assigned before continuing with post-creation verification.
+ IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectNode.getText());
+ bot.waitUntil(new DefaultCondition() {
+
+ @Override
+ public boolean test() throws Exception {
+ int natures = project.getDescription().getNatureIds().length;
+ ICProject cproject = CoreModel.getDefault().create(project);
+ IIndexManager indexManager = CCorePlugin.getIndexManager();
+ return natures > 0 && indexManager.isProjectContentSynced(cproject);
+ }
+
+ @Override
+ public String getFailureMessage() {
+ return "Indexer never finished or natures never assigned for project " + project.getName();
+ }
+ });
+
// Make sure it has the right nature
- assertTrue(project.hasNature(CMakeNature.ID));
+ assertTrue(project.hasNature(CMakeNature.ID), "Project does not have expected nature");
+
+ // Ensure CMakeLists exists
+ assertTrue(project.getFile("CMakeLists.txt").exists(), "Project does not have a build file");
}
+ /**
+ * Utility to perform a depth-first copy of a directory tree.
+ */
+ private static void copyDir(Path src, Path dest) throws IOException {
+ Files.walk(src).forEach(a -> {
+ if (!a.equals(src)) {
+ Path b = dest.resolve(a.subpath(src.getNameCount(), a.getNameCount()));
+ try {
+ if (!Files.isDirectory(a)) {
+ Files.createDirectories(b.getParent());
+ Files.copy(a, b);
+ }
+ } catch (IOException e) {
+ fail(e);
+ }
+ }
+ });
+ }
}
diff --git a/cmake/org.eclipse.cdt.cmake.ui/META-INF/MANIFEST.MF b/cmake/org.eclipse.cdt.cmake.ui/META-INF/MANIFEST.MF
index 2d4887a339d..ed14892d7ef 100644
--- a/cmake/org.eclipse.cdt.cmake.ui/META-INF/MANIFEST.MF
+++ b/cmake/org.eclipse.cdt.cmake.ui/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.cdt.cmake.ui;singleton:=true
-Bundle-Version: 1.3.200.qualifier
+Bundle-Version: 1.3.300.qualifier
Bundle-Activator: org.eclipse.cdt.cmake.ui.internal.Activator
Bundle-Vendor: %providerName
Require-Bundle: org.eclipse.core.runtime,
@@ -10,7 +10,8 @@ Require-Bundle: org.eclipse.core.runtime,
org.eclipse.ui,
org.eclipse.ui.ide,
org.eclipse.cdt.cmake.core,
- org.eclipse.tools.templates.ui;bundle-version="1.1.0",
+ org.eclipse.tools.templates.ui;bundle-version="1.3.0",
+ org.eclipse.tools.templates.freemarker;bundle-version="1.2.200",
org.eclipse.cdt.core;bundle-version="6.1.0",
org.eclipse.debug.ui;bundle-version="3.11.200",
org.eclipse.cdt.launch;bundle-version="9.1.0",
diff --git a/cmake/org.eclipse.cdt.cmake.ui/plugin.xml b/cmake/org.eclipse.cdt.cmake.ui/plugin.xml
index 7c8cd4b1e0b..24251a4843b 100644
--- a/cmake/org.eclipse.cdt.cmake.ui/plugin.xml
+++ b/cmake/org.eclipse.cdt.cmake.ui/plugin.xml
@@ -74,5 +74,12 @@
tabClass="org.eclipse.cdt.cmake.ui.internal.CMakeBuildTab">
+
+
+
+
diff --git a/cmake/org.eclipse.cdt.cmake.ui/src/org/eclipse/cdt/cmake/ui/internal/CMakeProjectConfigurator.java b/cmake/org.eclipse.cdt.cmake.ui/src/org/eclipse/cdt/cmake/ui/internal/CMakeProjectConfigurator.java
new file mode 100644
index 00000000000..ed704582c5b
--- /dev/null
+++ b/cmake/org.eclipse.cdt.cmake.ui/src/org/eclipse/cdt/cmake/ui/internal/CMakeProjectConfigurator.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2021 Mat Booth and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+package org.eclipse.cdt.cmake.ui.internal;
+
+import java.util.List;
+
+import org.eclipse.cdt.cmake.core.CMakeProjectGenerator;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.tools.templates.core.IGenerator;
+import org.eclipse.tools.templates.ui.ProjectImportConfigurator;
+
+/**
+ * Smart-import strategy for importing pre-existing CMake projects.
+ */
+public class CMakeProjectConfigurator extends ProjectImportConfigurator {
+
+ @Override
+ protected List getProjectFileNames() {
+ return List.of("CMakeLists.txt"); //$NON-NLS-1$
+ }
+
+ @Override
+ protected IGenerator getGenerator(IProject project) {
+ // Don't pass any template to the generator, we are importing an existing project
+ CMakeProjectGenerator generator = new CMakeProjectGenerator(null);
+ generator.setProjectName(project.getName());
+ generator.setLocationURI(project.getLocationURI());
+ return generator;
+ }
+}
diff --git a/cmake/pom.xml b/cmake/pom.xml
index 04bb3aeb26c..bee2c53cbe9 100644
--- a/cmake/pom.xml
+++ b/cmake/pom.xml
@@ -27,8 +27,9 @@
org.eclipse.cdt.cmake.core
- org.eclipse.cdt.cmake.core.tests
+ org.eclipse.cdt.cmake.core.tests
org.eclipse.cdt.cmake.ui
+ org.eclipse.cdt.cmake.ui.tests
org.eclipse.cdt.cmake-feature
diff --git a/tools.templates/org.eclipse.tools.templates.freemarker/META-INF/MANIFEST.MF b/tools.templates/org.eclipse.tools.templates.freemarker/META-INF/MANIFEST.MF
index bb2b7622203..34cbb9e9ed2 100644
--- a/tools.templates/org.eclipse.tools.templates.freemarker/META-INF/MANIFEST.MF
+++ b/tools.templates/org.eclipse.tools.templates.freemarker/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
Automatic-Module-Name: org.eclipse.tools.templates.freemarker
Bundle-ManifestVersion: 2
Bundle-SymbolicName: org.eclipse.tools.templates.freemarker
-Bundle-Version: 1.2.100.qualifier
+Bundle-Version: 1.2.200.qualifier
Bundle-Name: %pluginName
Bundle-Vendor: %providerName
Require-Bundle: org.eclipse.core.runtime,
diff --git a/tools.templates/org.eclipse.tools.templates.freemarker/src/org/eclipse/tools/templates/freemarker/FMProjectGenerator.java b/tools.templates/org.eclipse.tools.templates.freemarker/src/org/eclipse/tools/templates/freemarker/FMProjectGenerator.java
index 008e4821471..cc0de554c85 100644
--- a/tools.templates/org.eclipse.tools.templates.freemarker/src/org/eclipse/tools/templates/freemarker/FMProjectGenerator.java
+++ b/tools.templates/org.eclipse.tools.templates.freemarker/src/org/eclipse/tools/templates/freemarker/FMProjectGenerator.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2016 QNX Software Systems and others.
+ * Copyright (c) 2021 QNX Software Systems and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -74,6 +74,7 @@ public abstract class FMProjectGenerator extends FMGenerator {
project = workspace.getRoot().getProject(projectName);
if (!project.exists()) {
+ // Create project from scratch
IProjectDescription description = workspace.newProjectDescription(projectName);
description.setLocationURI(locationURI);
if (referencedProjects != null) {
@@ -83,8 +84,11 @@ public abstract class FMProjectGenerator extends FMGenerator {
project.create(description, sub);
project.open(sub);
} else {
- // TODO make sure it's got all our settings or is this an error
- // condition?
+ // Project is already created by smart import, so just configure the description
+ IProjectDescription description = project.getDescription();
+ initProjectDescription(description);
+ project.setDescription(description, sub);
+ project.open(sub);
}
sub.worked(1);
diff --git a/tools.templates/org.eclipse.tools.templates.ui/META-INF/MANIFEST.MF b/tools.templates/org.eclipse.tools.templates.ui/META-INF/MANIFEST.MF
index a0ab5a6c7fd..1334462acd9 100644
--- a/tools.templates/org.eclipse.tools.templates.ui/META-INF/MANIFEST.MF
+++ b/tools.templates/org.eclipse.tools.templates.ui/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@ Automatic-Module-Name: org.eclipse.tools.templates.ui
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.tools.templates.ui;singleton:=true
-Bundle-Version: 1.2.200.qualifier
+Bundle-Version: 1.3.0.qualifier
Bundle-Activator: org.eclipse.tools.templates.ui.internal.Activator
Require-Bundle: org.eclipse.ui,
org.eclipse.core.runtime,
diff --git a/tools.templates/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/Messages.java b/tools.templates/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/Messages.java
new file mode 100644
index 00000000000..52c52c9422e
--- /dev/null
+++ b/tools.templates/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/Messages.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2021 Mat Booth and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+package org.eclipse.tools.templates.ui;
+
+import org.eclipse.osgi.util.NLS;
+
+class Messages extends NLS {
+
+ public static String ProjectImportConfigurator_Checking;
+
+ public static String TemplateWizard_CannotBeCreated;
+ public static String TemplateWizard_ErrorCreating;
+ public static String TemplateWizard_FailedToOpen;
+ public static String TemplateWizard_Generating;
+ public static String TemplateWizard_InternalError;
+
+ static {
+ NLS.initializeMessages(Messages.class.getPackageName() + ".messages", Messages.class); //$NON-NLS-1$
+ }
+
+ private Messages() {
+ }
+}
diff --git a/tools.templates/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/ProjectImportConfigurator.java b/tools.templates/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/ProjectImportConfigurator.java
new file mode 100644
index 00000000000..1873d5cac1c
--- /dev/null
+++ b/tools.templates/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/ProjectImportConfigurator.java
@@ -0,0 +1,173 @@
+/*******************************************************************************
+ * Copyright (c) 2021 Mat Booth and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+package org.eclipse.tools.templates.ui;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.FileSystems;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.PathMatcher;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFolder;
+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.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.tools.templates.core.IGenerator;
+import org.eclipse.ui.statushandlers.StatusManager;
+import org.eclipse.ui.wizards.datatransfer.ProjectConfigurator;
+import org.osgi.framework.FrameworkUtil;
+
+/**
+ * Smart-import strategy for importing pre-existing projects that are not yet
+ * Eclipse projects.
+ *
+ * Project types that have a {@link IGenerator} may extend this to offer smart
+ * import functionality by registering their implementation using the
+ * org.eclipse.ui.ide.projectConfigurator
extension point.
+ *
+ * It is important that implementations of this class should be stateless. See
+ * {@link ProjectConfigurator} for more details.
+ *
+ * @since 1.3
+ */
+public abstract class ProjectImportConfigurator implements ProjectConfigurator {
+
+ private static class Collector extends SimpleFileVisitor {
+ private IProgressMonitor monitor;
+ private List matchers;
+
+ // LinkedHashSet preserves insertion order so it looks nice in the UI
+ private Set locations = new LinkedHashSet<>();
+
+ private Collector(List matchers, IProgressMonitor monitor) {
+ this.monitor = monitor;
+ this.matchers = matchers;
+ }
+
+ @Override
+ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
+ // Could be a long operation, so offer opportunity to cancel
+ if (monitor.isCanceled()) {
+ return FileVisitResult.TERMINATE;
+ }
+ monitor.subTask(MessageFormat.format(Messages.ProjectImportConfigurator_Checking, dir));
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ // If file matches any of our patterns, save the location
+ Path name = file.getFileName();
+ boolean match = matchers.stream().anyMatch(p -> p.matches(name));
+ if (match) {
+ locations.add(file.getParent().toFile());
+ }
+ return FileVisitResult.CONTINUE;
+ }
+
+ public Set getCollected() {
+ return locations;
+ }
+ }
+
+ /**
+ * The presence of any of these files indicates a directory contains a project
+ * of your type. Whole filenames or glob patterns are acceptable, e.g.
+ * ("build.foo", "foo.*")
+ *
+ * @return a list of filenames and/or glob patterns
+ */
+ protected abstract List getProjectFileNames();
+
+ /**
+ * Returns the project generator implementation to be used to configure your project
+ * type. The base Eclipse project will be created for you, the generator just needs to
+ * know how to configure it. The generator will be run during the smart import batch job.
+ *
+ * @param project the project to be configured
+ * @return a project generator
+ */
+ protected abstract IGenerator getGenerator(IProject project);
+
+ /**
+ * Utility to create path matchers from glob patterns.
+ */
+ private List createPathMatchers(List globs) {
+ final List matchers = new ArrayList<>();
+ for (String glob : globs) {
+ matchers.add(FileSystems.getDefault().getPathMatcher("glob:" + glob)); //$NON-NLS-1$
+ }
+ return matchers;
+ }
+
+ @Override
+ public Set findConfigurableLocations(File root, IProgressMonitor monitor) {
+ List matchers = createPathMatchers(getProjectFileNames());
+ Collector c = new Collector(matchers, monitor);
+ try {
+ Files.walkFileTree(root.toPath(), c);
+ } catch (IOException e) {
+ StatusManager.getManager().handle(new Status(IStatus.ERROR,
+ FrameworkUtil.getBundle(getClass()).getSymbolicName(), e.getMessage(), e));
+ }
+ return c.getCollected();
+ }
+
+ @Override
+ public boolean shouldBeAnEclipseProject(IContainer container, IProgressMonitor monitor) {
+ List matchers = createPathMatchers(getProjectFileNames());
+
+ // Only if the location contains a file that matches the one of the given patterns
+ File location = container.getLocation().toFile();
+ for (File f : location.listFiles()) {
+ if (f.isFile() && matchers.stream().anyMatch(p -> p.matches(f.toPath().getFileName()))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Set getFoldersToIgnore(IProject project, IProgressMonitor monitor) {
+ // Default to ignoring nothing
+ return Set.of();
+ }
+
+ @Override
+ public boolean canConfigure(IProject project, Set ignoredPaths, IProgressMonitor monitor) {
+ return shouldBeAnEclipseProject(project, monitor);
+ }
+
+ @Override
+ public void configure(IProject project, Set ignoredPaths, IProgressMonitor monitor) {
+ try {
+ IGenerator generator = getGenerator(project);
+ generator.generate(monitor);
+ } catch (CoreException e) {
+ Status status = new Status(e.getStatus().getSeverity(),
+ FrameworkUtil.getBundle(getClass()).getSymbolicName(), e.getLocalizedMessage(), e);
+ StatusManager.getManager().handle(status, StatusManager.SHOW | StatusManager.LOG);
+ }
+ }
+}
diff --git a/tools.templates/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/TemplateWizard.java b/tools.templates/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/TemplateWizard.java
index 2ff629f4e2a..b2ff1c8b5a5 100644
--- a/tools.templates/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/TemplateWizard.java
+++ b/tools.templates/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/TemplateWizard.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2016 QNX Software Systems and others.
+ * Copyright (c) 2016, 2021 QNX Software Systems and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -74,7 +74,7 @@ public abstract class TemplateWizard extends BasicNewResourceWizard {
IDE.openEditor(activePage, file);
}
} catch (PartInitException e) {
- log("Failed to open editor", e); //$NON-NLS-1$
+ log(Messages.TemplateWizard_FailedToOpen, e);
}
}
@@ -91,7 +91,7 @@ public abstract class TemplateWizard extends BasicNewResourceWizard {
@Override
protected void execute(IProgressMonitor monitor)
throws CoreException, InvocationTargetException, InterruptedException {
- SubMonitor sub = SubMonitor.convert(monitor, "Generating", 1);
+ SubMonitor sub = SubMonitor.convert(monitor, Messages.TemplateWizard_Generating, 1);
generator.generate(model, sub);
getWorkbench().getDisplay().asyncExec(new Runnable() {
@Override
@@ -116,16 +116,16 @@ public abstract class TemplateWizard extends BasicNewResourceWizard {
}
private void handle(Throwable target) {
- String message = "Project cannot be created";
+ String message = Messages.TemplateWizard_CannotBeCreated;
log(message, target);
IStatus status;
if (target instanceof CoreException) {
status = ((CoreException) target).getStatus();
} else {
status = new Status(IStatus.ERROR, FrameworkUtil.getBundle(getClass()).getSymbolicName(),
- "Internal Error: ", target);
+ Messages.TemplateWizard_InternalError, target);
}
- ErrorDialog.openError(getShell(), "Error Creating Project", message, status);
+ ErrorDialog.openError(getShell(), Messages.TemplateWizard_ErrorCreating, message, status);
}
private void log(String message, Throwable e) {
diff --git a/tools.templates/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/messages.properties b/tools.templates/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/messages.properties
new file mode 100644
index 00000000000..20316146107
--- /dev/null
+++ b/tools.templates/org.eclipse.tools.templates.ui/src/org/eclipse/tools/templates/ui/messages.properties
@@ -0,0 +1,6 @@
+ProjectImportConfigurator_Checking=Checking: {0}
+TemplateWizard_CannotBeCreated=Project cannot be created
+TemplateWizard_ErrorCreating=Error Creating Project
+TemplateWizard_FailedToOpen=Failed to open editor
+TemplateWizard_Generating=Generating
+TemplateWizard_InternalError=Internal Error: