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: