From 0c577f6e7e0726cf375ec9f774fdafde1762c0bc Mon Sep 17 00:00:00 2001 From: Marc-Andre Laperle Date: Sat, 28 Sep 2019 00:38:26 -0400 Subject: [PATCH] Bug 548730 - Compilation database (CDB) language settings provider This language settings provider lets you specify the path to the compile_commands.json and reuses existing build output parsers to figure out all the language entries for each file to be used while indexing. With this, there is no need to do a full build and in fact no need to have a working build configured in CDT for indexing to work and be fully configured. This is especially useful for non-CMake build systems that have no existing integration in CDT but indexing would still works with little effort. The build output parser (GCC, MSVC, etc) is selectable as part of the configuration of the CDB provider. There is also an option to exclude any file that is not present in the CDB. This option is useful for large projects in order to speed up indexing but also to increase index accuracy (conflicting symbol names, etc). Change-Id: If21455ec529f9e162cdf3e5aff7a1bca83e362f6 Signed-off-by: Marc-Andre Laperle --- .../META-INF/MANIFEST.MF | 2 + .../AllLanguageSettingsProvidersMBSTests.java | 1 + .../tests/CompilationDatabaseParserTest.java | 924 ++++++++++++++++++ .../META-INF/MANIFEST.MF | 5 +- .../plugin.properties | 1 + .../plugin.xml | 6 + .../providers/CompilationDatabaseParser.java | 460 +++++++++ .../settings/providers/CompileCommand.java | 34 + .../language/settings/providers/Messages.java | 37 + .../settings/providers/messages.properties | 21 + .../META-INF/MANIFEST.MF | 2 +- .../plugin.xml | 7 + .../CompilationDatabaseParserOptionPage.java | 295 ++++++ .../language/settings/providers/Messages.java | 32 + .../settings/providers/messages.properties | 19 + releng/CDT.setup | 4 + releng/org.eclipse.cdt.target/cdt.target | 4 +- 17 files changed, 1850 insertions(+), 4 deletions(-) create mode 100644 build/org.eclipse.cdt.managedbuilder.core.tests/tests/org/eclipse/cdt/managedbuilder/language/settings/providers/tests/CompilationDatabaseParserTest.java create mode 100644 build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/language/settings/providers/CompilationDatabaseParser.java create mode 100644 build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/language/settings/providers/CompileCommand.java create mode 100644 build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/language/settings/providers/Messages.java create mode 100644 build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/language/settings/providers/messages.properties create mode 100644 build/org.eclipse.cdt.managedbuilder.ui/src/org/eclipse/cdt/managedbuilder/internal/ui/language/settings/providers/CompilationDatabaseParserOptionPage.java create mode 100644 build/org.eclipse.cdt.managedbuilder.ui/src/org/eclipse/cdt/managedbuilder/internal/ui/language/settings/providers/Messages.java create mode 100644 build/org.eclipse.cdt.managedbuilder.ui/src/org/eclipse/cdt/managedbuilder/internal/ui/language/settings/providers/messages.properties diff --git a/build/org.eclipse.cdt.managedbuilder.core.tests/META-INF/MANIFEST.MF b/build/org.eclipse.cdt.managedbuilder.core.tests/META-INF/MANIFEST.MF index af5e610101c..1ba05c98949 100644 --- a/build/org.eclipse.cdt.managedbuilder.core.tests/META-INF/MANIFEST.MF +++ b/build/org.eclipse.cdt.managedbuilder.core.tests/META-INF/MANIFEST.MF @@ -14,6 +14,7 @@ Export-Package: org.eclipse.cdt.managedbuilder.core.tests, org.eclipse.cdt.projectmodel.tests Require-Bundle: org.eclipse.core.runtime, org.junit, + org.junit.jupiter.api, org.eclipse.core.resources, org.eclipse.ui, org.eclipse.ui.ide, @@ -25,3 +26,4 @@ Require-Bundle: org.eclipse.core.runtime, Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Automatic-Module-Name: org.eclipse.cdt.managedbuilder.core.tests +Import-Package: com.google.gson diff --git a/build/org.eclipse.cdt.managedbuilder.core.tests/tests/org/eclipse/cdt/managedbuilder/language/settings/providers/tests/AllLanguageSettingsProvidersMBSTests.java b/build/org.eclipse.cdt.managedbuilder.core.tests/tests/org/eclipse/cdt/managedbuilder/language/settings/providers/tests/AllLanguageSettingsProvidersMBSTests.java index 2a13429717c..e638ee210a1 100644 --- a/build/org.eclipse.cdt.managedbuilder.core.tests/tests/org/eclipse/cdt/managedbuilder/language/settings/providers/tests/AllLanguageSettingsProvidersMBSTests.java +++ b/build/org.eclipse.cdt.managedbuilder.core.tests/tests/org/eclipse/cdt/managedbuilder/language/settings/providers/tests/AllLanguageSettingsProvidersMBSTests.java @@ -32,5 +32,6 @@ public class AllLanguageSettingsProvidersMBSTests extends TestSuite { addTestSuite(GCCBuildCommandParserTest.class); addTestSuite(BuiltinSpecsDetectorTest.class); addTestSuite(GCCBuiltinSpecsDetectorTest.class); + addTestSuite(CompilationDatabaseParserTest.class); } } diff --git a/build/org.eclipse.cdt.managedbuilder.core.tests/tests/org/eclipse/cdt/managedbuilder/language/settings/providers/tests/CompilationDatabaseParserTest.java b/build/org.eclipse.cdt.managedbuilder.core.tests/tests/org/eclipse/cdt/managedbuilder/language/settings/providers/tests/CompilationDatabaseParserTest.java new file mode 100644 index 00000000000..4a0490c26f9 --- /dev/null +++ b/build/org.eclipse.cdt.managedbuilder.core.tests/tests/org/eclipse/cdt/managedbuilder/language/settings/providers/tests/CompilationDatabaseParserTest.java @@ -0,0 +1,924 @@ +/******************************************************************************* + * Copyright (c) 2019 Marc-Andre Laperle. + * + * 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.managedbuilder.language.settings.providers.tests; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.io.ByteArrayInputStream; +import java.io.FileReader; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.dom.ast.gnu.cpp.GPPLanguage; +import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsProvider; +import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsProvidersKeeper; +import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsManager; +import org.eclipse.cdt.core.model.CoreModel; +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.core.settings.model.CIncludePathEntry; +import org.eclipse.cdt.core.settings.model.CMacroEntry; +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; +import org.eclipse.cdt.core.settings.model.ICProjectDescription; +import org.eclipse.cdt.core.settings.model.ICProjectDescriptionManager; +import org.eclipse.cdt.core.settings.model.util.CDataUtil; +import org.eclipse.cdt.core.testplugin.ResourceHelper; +import org.eclipse.cdt.core.testplugin.util.BaseTestCase; +import org.eclipse.cdt.internal.core.language.settings.providers.LanguageSettingsProvidersSerializer; +import org.eclipse.cdt.managedbuilder.internal.language.settings.providers.CompilationDatabaseParser; +import org.eclipse.cdt.managedbuilder.internal.language.settings.providers.CompileCommand; +import org.eclipse.cdt.managedbuilder.language.settings.providers.AbstractBuildCommandParser; +import org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuildCommandParser; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.jobs.Job; + +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; + +/** + * Test cases to test CompilationDatabaseParser (compile_commands.json). + */ +public class CompilationDatabaseParserTest extends BaseTestCase { + private static final String COMPILATION_DATABASE_PARSER_EXT = "org.eclipse.cdt.managedbuilder.core.CompilationDatabaseParser"; //$NON-NLS-1$ + private static final String GCC_BUILD_COMMAND_PARSER_EXT = "org.eclipse.cdt.managedbuilder.core.GCCBuildCommandParser"; //$NON-NLS-1$ + + private static final String ATTR_CDB_PATH = "cdb-path"; //$NON-NLS-1$ + private static final String ATTR_BUILD_PARSER_ID = "build-parser-id"; //$NON-NLS-1$ + private static final String ATTR_CDB_MODIFIED_TIME = "cdb-modified-time"; //$NON-NLS-1$ + private static final String ATTR_EXCLUDE_FILES = "exclude-files"; //$NON-NLS-1$ + + private IFile fCdbFile; + private IFile fSourceFile; + private IFile fSourceFile2; + private IFile fOutsideCdbSourceFile; + private IProject fProject; + private IFolder fFolder; + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + try { + joingLanguageSettingsJobs(); + } catch (Exception e) { + // ignore + } + super.tearDown(); + } + + private void createTestProject() throws Exception { + createTestProject(true, true, true, true, true); + } + + private void createTestProject(boolean useAbsoluteSourcePath, boolean haveCommandDir, boolean validCommandDir, + boolean haveCommandLine, boolean validCommandLine) throws Exception { + fProject = ResourceHelper.createCDTProjectWithConfig(getName()); + fFolder = ResourceHelper.createFolder(fProject, "folder"); + + IFile sourceFile = fFolder.getFile("test.cpp"); + if (sourceFile.exists()) { + sourceFile.delete(true, null); + } + + IFile sourceFile2 = fProject.getFile("test.cpp"); + if (sourceFile2.exists()) { + sourceFile2.delete(true, null); + } + + fSourceFile = ResourceHelper.createFile(sourceFile, "//comment"); + fSourceFile2 = ResourceHelper.createFile(sourceFile2, "//comment2"); + + IFile outsideSourceFile = fFolder.getFile("outside.cpp"); + if (outsideSourceFile.exists()) { + outsideSourceFile.delete(true, null); + } + fOutsideCdbSourceFile = ResourceHelper.createFile(outsideSourceFile, "//comment"); + + IFile file = fProject.getFile("compile_commands.json"); + if (file.exists()) { + file.delete(true, null); + } + + // Command for proj/folder/test.cpp + CompileCommand command = new CompileCommand(); + if (haveCommandDir) { + if (validCommandDir) + command.directory = fSourceFile.getParent().getLocation().toOSString(); + else + command.directory = "foo"; + } + String sourceFilePath = fSourceFile.getLocation().toOSString(); + if (!useAbsoluteSourcePath) { + sourceFilePath = fSourceFile.getLocation().makeRelativeTo(fSourceFile.getParent().getLocation()) + .toOSString(); + } + command.file = sourceFilePath; + if (haveCommandLine) { + if (validCommandLine) + command.command = "g++ -I" + fFolder.getLocation().toOSString() + " -DFOO=2 " + sourceFilePath; + else + command.command = "foo"; + } + + // Command for proj/test.cpp + CompileCommand command2 = new CompileCommand(); + if (haveCommandDir) { + if (validCommandDir) + command2.directory = fSourceFile2.getParent().getLocation().toOSString(); + else + command2.directory = "foo"; + } + String sourceFilePath2 = fSourceFile2.getLocation().toOSString(); + if (!useAbsoluteSourcePath) { + sourceFilePath2 = fSourceFile2.getLocation().makeRelativeTo(fSourceFile2.getParent().getLocation()) + .toOSString(); + } + command2.file = sourceFilePath2; + if (haveCommandLine) { + if (validCommandLine) + command2.command = "g++ -I" + fFolder.getLocation().toOSString() + " -DFOO=3 " + sourceFilePath2; + else + command2.command = "foo"; + } + + CompileCommand[] commands = new CompileCommand[2]; + commands[0] = command; + commands[1] = command2; + + String json = new Gson().toJson(commands); + fCdbFile = ResourceHelper.createFile(file, json); + + ICConfigurationDescription cfgDescription = getConfigurationDescription(fProject, true); + GCCBuildCommandParser buildCommandParser = (GCCBuildCommandParser) LanguageSettingsManager + .getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + assertTrue(cfgDescription instanceof ILanguageSettingsProvidersKeeper); + List providers = new ArrayList<>(); + providers.add(buildCommandParser); + ((ILanguageSettingsProvidersKeeper) cfgDescription).setLanguageSettingProviders(providers); + CoreModel.getDefault().setProjectDescription(cfgDescription.getProjectDescription().getProject(), + cfgDescription.getProjectDescription()); + } + + private void joingLanguageSettingsJobs() throws InterruptedException { + Job.getJobManager().join(CompilationDatabaseParser.JOB_FAMILY_COMPILATION_DATABASE_PARSER, null); + Job.getJobManager().join(AbstractBuildCommandParser.JOB_FAMILY_BUILD_COMMAND_PARSER, null); + Job.getJobManager().join(LanguageSettingsProvidersSerializer.JOB_FAMILY_SERIALIZE_LANGUAGE_SETTINGS_PROJECT, + null); + Job.getJobManager().join(LanguageSettingsProvidersSerializer.JOB_FAMILY_SERIALIZE_LANGUAGE_SETTINGS_WORKSPACE, + null); + } + + /** + * Helper method to fetch a configuration description. + */ + private ICConfigurationDescription getConfigurationDescription(IProject project, boolean writable) { + CoreModel coreModel = CoreModel.getDefault(); + ICProjectDescriptionManager mngr = coreModel.getProjectDescriptionManager(); + // project description + ICProjectDescription projectDescription = mngr.getProjectDescription(project, writable); + assertNotNull(projectDescription); + assertEquals(1, projectDescription.getConfigurations().length); + // configuration description + ICConfigurationDescription[] cfgDescriptions = projectDescription.getConfigurations(); + return cfgDescriptions[0]; + } + + private void addLanguageSettingsProvider(ILanguageSettingsProvider provider) throws CoreException { + ICConfigurationDescription cfgDescription = getConfigurationDescription(fProject, true); + List providers = new ArrayList<>( + ((ILanguageSettingsProvidersKeeper) cfgDescription).getLanguageSettingProviders()); + providers.add(provider); + ((ILanguageSettingsProvidersKeeper) cfgDescription).setLanguageSettingProviders(providers); + CoreModel.getDefault().setProjectDescription(cfgDescription.getProjectDescription().getProject(), + cfgDescription.getProjectDescription()); + } + + private CompilationDatabaseParser getCompilationDatabaseParser() throws CoreException { + ICConfigurationDescription cfgDescription = getConfigurationDescription(fProject, false); + List settingProviders = ((ILanguageSettingsProvidersKeeper) cfgDescription) + .getLanguageSettingProviders(); + for (ILanguageSettingsProvider languageSettingsProvider : settingProviders) { + if (languageSettingsProvider instanceof CompilationDatabaseParser) { + return (CompilationDatabaseParser) languageSettingsProvider; + } + } + + return null; + } + + private void assertExpectedEntries(CompilationDatabaseParser parser) { + assertFalse(parser.isEmpty()); + ICConfigurationDescription resCfgDescription = getConfigurationDescription(fProject, false); + List entries = parser.getSettingEntries(resCfgDescription, fSourceFile, GPPLanguage.ID); + + CIncludePathEntry expected = new CIncludePathEntry("/${ProjName}/folder", + CIncludePathEntry.VALUE_WORKSPACE_PATH); + CIncludePathEntry entry = (CIncludePathEntry) entries.get(0); + assertEquals(expected.getName(), entry.getName()); + assertEquals(expected.getValue(), entry.getValue()); + assertEquals(expected.getKind(), entry.getKind()); + assertEquals(expected.getFlags(), entry.getFlags()); + assertEquals(expected, entry); + + assertEquals(new CMacroEntry("FOO", "2", 0), entries.get(1)); + + entries = parser.getSettingEntries(resCfgDescription, fSourceFile2, GPPLanguage.ID); + + entry = (CIncludePathEntry) entries.get(0); + assertEquals(expected.getName(), entry.getName()); + assertEquals(expected.getValue(), entry.getValue()); + assertEquals(expected.getKind(), entry.getKind()); + assertEquals(expected.getFlags(), entry.getFlags()); + assertEquals(expected, entry); + + assertEquals(new CMacroEntry("FOO", "3", 0), entries.get(1)); + } + + public void testParseCDB_WritableConfigDesc() throws Exception { + createTestProject(); + + ICProject cProject = CCorePlugin.getDefault().getCoreModel().create(fProject); + ICElement ce = CCorePlugin.getDefault().getCoreModel().create(fOutsideCdbSourceFile.getFullPath()); + ITranslationUnit tu = (ITranslationUnit) ce; + assertFalse( + CDataUtil.isExcluded(tu.getPath(), getConfigurationDescription(fProject, false).getSourceEntries())); + + CompilationDatabaseParser parser = (CompilationDatabaseParser) LanguageSettingsManager + .getExtensionProviderCopy(COMPILATION_DATABASE_PARSER_EXT, true); + assertTrue(parser.isEmpty()); + parser.setBuildParserId(GCC_BUILD_COMMAND_PARSER_EXT); + parser.setCompilationDataBasePath(fCdbFile.getLocation()); + addLanguageSettingsProvider(parser); + + ICConfigurationDescription cfgDescription = getConfigurationDescription(fProject, true); + + parser.processCompileCommandsFile(null, cfgDescription); + assertFalse(parser.isEmpty()); + CoreModel.getDefault().setProjectDescription(cfgDescription.getProjectDescription().getProject(), + cfgDescription.getProjectDescription()); + joingLanguageSettingsJobs(); + + ICConfigurationDescription resCfgDescription = getConfigurationDescription(fProject, false); + assertExpectedEntries(parser); + + assertFalse(CDataUtil.isExcluded(tu.getPath(), resCfgDescription.getSourceEntries())); + } + + public void testParseCDB_ReadonlyConfigDesc() throws Exception { + createTestProject(); + + ICProject cProject = CCorePlugin.getDefault().getCoreModel().create(fProject); + ICElement ce = CCorePlugin.getDefault().getCoreModel().create(fOutsideCdbSourceFile.getFullPath()); + ITranslationUnit tu = (ITranslationUnit) ce; + assertFalse( + CDataUtil.isExcluded(tu.getPath(), getConfigurationDescription(fProject, false).getSourceEntries())); + + CompilationDatabaseParser parser = (CompilationDatabaseParser) LanguageSettingsManager + .getExtensionProviderCopy(COMPILATION_DATABASE_PARSER_EXT, true); + assertTrue(parser.isEmpty()); + parser.setBuildParserId(GCC_BUILD_COMMAND_PARSER_EXT); + parser.setCompilationDataBasePath(fCdbFile.getLocation()); + addLanguageSettingsProvider(parser); + + ICConfigurationDescription cfgDescription = getConfigurationDescription(fProject, false); + + parser.processCompileCommandsFile(null, cfgDescription); + // processCompileCommandsFile restarts itself in a WorkspaceJob with a writable config desc so we have to wait for the job. + joingLanguageSettingsJobs(); + + ICConfigurationDescription resCfgDescription = getConfigurationDescription(fProject, false); + assertExpectedEntries(getCompilationDatabaseParser()); + + assertFalse(CDataUtil.isExcluded(tu.getPath(), resCfgDescription.getSourceEntries())); + } + + public void testParseCDB_WithExclusions() throws Exception { + createTestProject(); + + ICProject cProject = CCorePlugin.getDefault().getCoreModel().create(fProject); + ICElement ce = CCorePlugin.getDefault().getCoreModel().create(fOutsideCdbSourceFile.getFullPath()); + ITranslationUnit tu = (ITranslationUnit) ce; + assertFalse( + CDataUtil.isExcluded(tu.getPath(), getConfigurationDescription(fProject, false).getSourceEntries())); + + CompilationDatabaseParser parser = (CompilationDatabaseParser) LanguageSettingsManager + .getExtensionProviderCopy(COMPILATION_DATABASE_PARSER_EXT, true); + assertTrue(parser.isEmpty()); + parser.setBuildParserId(GCC_BUILD_COMMAND_PARSER_EXT); + parser.setCompilationDataBasePath(fCdbFile.getLocation()); + parser.setExcludeFiles(true); + addLanguageSettingsProvider(parser); + + ICConfigurationDescription cfgDescription = getConfigurationDescription(fProject, true); + + parser.processCompileCommandsFile(null, cfgDescription); + CoreModel.getDefault().setProjectDescription(cfgDescription.getProjectDescription().getProject(), + cfgDescription.getProjectDescription()); + joingLanguageSettingsJobs(); + + assertExpectedEntries(parser); + + ICConfigurationDescription resCfgDescription = getConfigurationDescription(fProject, false); + assertTrue(CDataUtil.isExcluded(tu.getPath(), resCfgDescription.getSourceEntries())); + } + + public void testParseCDB_ReadonlyConfigDescWithExclusions() throws Exception { + createTestProject(); + + ICProject cProject = CCorePlugin.getDefault().getCoreModel().create(fProject); + ICElement ce = CCorePlugin.getDefault().getCoreModel().create(fOutsideCdbSourceFile.getFullPath()); + ITranslationUnit tu = (ITranslationUnit) ce; + assertFalse( + CDataUtil.isExcluded(tu.getPath(), getConfigurationDescription(fProject, false).getSourceEntries())); + + CompilationDatabaseParser parser = (CompilationDatabaseParser) LanguageSettingsManager + .getExtensionProviderCopy(COMPILATION_DATABASE_PARSER_EXT, true); + assertTrue(parser.isEmpty()); + parser.setBuildParserId(GCC_BUILD_COMMAND_PARSER_EXT); + parser.setCompilationDataBasePath(fCdbFile.getLocation()); + parser.setExcludeFiles(true); + addLanguageSettingsProvider(parser); + + ICConfigurationDescription cfgDescription = getConfigurationDescription(fProject, false); + + parser.processCompileCommandsFile(null, cfgDescription); + // processCompileCommandsFile restarts itself in a WorkspaceJob with a writable config desc so we have to wait for the job. + joingLanguageSettingsJobs(); + + assertExpectedEntries(getCompilationDatabaseParser()); + + ICConfigurationDescription resCfgDescription = getConfigurationDescription(fProject, false); + assertTrue(CDataUtil.isExcluded(tu.getPath(), resCfgDescription.getSourceEntries())); + } + + public void testParseCDB_NoBuildCommandParser() throws Exception { + createTestProject(); + + ICProject cProject = CCorePlugin.getDefault().getCoreModel().create(fProject); + ICElement ce = CCorePlugin.getDefault().getCoreModel().create(fOutsideCdbSourceFile.getFullPath()); + ITranslationUnit tu = (ITranslationUnit) ce; + assertFalse( + CDataUtil.isExcluded(tu.getPath(), getConfigurationDescription(fProject, false).getSourceEntries())); + + CompilationDatabaseParser parser = (CompilationDatabaseParser) LanguageSettingsManager + .getExtensionProviderCopy(COMPILATION_DATABASE_PARSER_EXT, true); + assertTrue(parser.isEmpty()); + parser.setCompilationDataBasePath(fCdbFile.getLocation()); + parser.setExcludeFiles(true); + addLanguageSettingsProvider(parser); + + ICConfigurationDescription cfgDescription = getConfigurationDescription(fProject, true); + + assertThrows(CoreException.class, () -> parser.processCompileCommandsFile(null, cfgDescription)); + CoreModel.getDefault().setProjectDescription(cfgDescription.getProjectDescription().getProject(), + cfgDescription.getProjectDescription()); + joingLanguageSettingsJobs(); + + ICConfigurationDescription resCfgDescription = getConfigurationDescription(fProject, false); + CompilationDatabaseParser resultParser = getCompilationDatabaseParser(); + assertTrue(resultParser.isEmpty()); + List entries = resultParser.getSettingEntries(resCfgDescription, fSourceFile, + GPPLanguage.ID); + assertTrue(entries == null); + assertFalse(CDataUtil.isExcluded(tu.getPath(), resCfgDescription.getSourceEntries())); + } + + public void testParseCDB_InvalidBuildCommandParser() throws Exception { + createTestProject(); + + ICProject cProject = CCorePlugin.getDefault().getCoreModel().create(fProject); + ICElement ce = CCorePlugin.getDefault().getCoreModel().create(fOutsideCdbSourceFile.getFullPath()); + ITranslationUnit tu = (ITranslationUnit) ce; + assertFalse( + CDataUtil.isExcluded(tu.getPath(), getConfigurationDescription(fProject, false).getSourceEntries())); + + CompilationDatabaseParser parser = (CompilationDatabaseParser) LanguageSettingsManager + .getExtensionProviderCopy(COMPILATION_DATABASE_PARSER_EXT, true); + assertTrue(parser.isEmpty()); + parser.setBuildParserId(GCC_BUILD_COMMAND_PARSER_EXT + "foo"); + parser.setCompilationDataBasePath(fCdbFile.getLocation()); + parser.setExcludeFiles(true); + addLanguageSettingsProvider(parser); + + ICConfigurationDescription cfgDescription = getConfigurationDescription(fProject, true); + + assertThrows(CoreException.class, () -> parser.processCompileCommandsFile(null, cfgDescription)); + CoreModel.getDefault().setProjectDescription(cfgDescription.getProjectDescription().getProject(), + cfgDescription.getProjectDescription()); + joingLanguageSettingsJobs(); + + ICConfigurationDescription resCfgDescription = getConfigurationDescription(fProject, false); + CompilationDatabaseParser resultParser = getCompilationDatabaseParser(); + assertTrue(resultParser.isEmpty()); + List entries = resultParser.getSettingEntries(resCfgDescription, fSourceFile, + GPPLanguage.ID); + assertTrue(entries == null); + assertFalse(CDataUtil.isExcluded(tu.getPath(), resCfgDescription.getSourceEntries())); + } + + public void testParseCDB_NonExistantCDB() throws Exception { + createTestProject(); + + ICProject cProject = CCorePlugin.getDefault().getCoreModel().create(fProject); + ICElement ce = CCorePlugin.getDefault().getCoreModel().create(fOutsideCdbSourceFile.getFullPath()); + ITranslationUnit tu = (ITranslationUnit) ce; + assertFalse( + CDataUtil.isExcluded(tu.getPath(), getConfigurationDescription(fProject, false).getSourceEntries())); + + CompilationDatabaseParser parser = (CompilationDatabaseParser) LanguageSettingsManager + .getExtensionProviderCopy(COMPILATION_DATABASE_PARSER_EXT, true); + assertTrue(parser.isEmpty()); + parser.setBuildParserId(GCC_BUILD_COMMAND_PARSER_EXT); + parser.setCompilationDataBasePath(new Path("/testParseCDB_NonExistantCDB")); + parser.setExcludeFiles(true); + addLanguageSettingsProvider(parser); + + ICConfigurationDescription cfgDescription = getConfigurationDescription(fProject, true); + + assertThrows(CoreException.class, () -> parser.processCompileCommandsFile(null, cfgDescription)); + CoreModel.getDefault().setProjectDescription(cfgDescription.getProjectDescription().getProject(), + cfgDescription.getProjectDescription()); + joingLanguageSettingsJobs(); + + ICConfigurationDescription resCfgDescription = getConfigurationDescription(fProject, false); + CompilationDatabaseParser resultParser = getCompilationDatabaseParser(); + assertTrue(resultParser.isEmpty()); + List entries = resultParser.getSettingEntries(resCfgDescription, fSourceFile, + GPPLanguage.ID); + assertTrue(entries == null); + assertFalse(CDataUtil.isExcluded(tu.getPath(), resCfgDescription.getSourceEntries())); + } + + public void testParseCDB_EmptyCDBPath() throws Exception { + createTestProject(); + + ICProject cProject = CCorePlugin.getDefault().getCoreModel().create(fProject); + ICElement ce = CCorePlugin.getDefault().getCoreModel().create(fOutsideCdbSourceFile.getFullPath()); + ITranslationUnit tu = (ITranslationUnit) ce; + assertFalse( + CDataUtil.isExcluded(tu.getPath(), getConfigurationDescription(fProject, false).getSourceEntries())); + + CompilationDatabaseParser parser = (CompilationDatabaseParser) LanguageSettingsManager + .getExtensionProviderCopy(COMPILATION_DATABASE_PARSER_EXT, true); + assertTrue(parser.isEmpty()); + parser.setBuildParserId(GCC_BUILD_COMMAND_PARSER_EXT); + parser.setCompilationDataBasePath(new Path("")); + parser.setExcludeFiles(true); + addLanguageSettingsProvider(parser); + + ICConfigurationDescription cfgDescription = getConfigurationDescription(fProject, true); + + assertThrows(CoreException.class, () -> parser.processCompileCommandsFile(null, cfgDescription)); + CoreModel.getDefault().setProjectDescription(cfgDescription.getProjectDescription().getProject(), + cfgDescription.getProjectDescription()); + joingLanguageSettingsJobs(); + + ICConfigurationDescription resCfgDescription = getConfigurationDescription(fProject, false); + CompilationDatabaseParser resultParser = getCompilationDatabaseParser(); + assertTrue(resultParser.isEmpty()); + List entries = resultParser.getSettingEntries(resCfgDescription, fSourceFile, + GPPLanguage.ID); + assertTrue(entries == null); + assertFalse(CDataUtil.isExcluded(tu.getPath(), resCfgDescription.getSourceEntries())); + } + + public void testParseCDB_DirectoryCDBPath() throws Exception { + createTestProject(); + + ICProject cProject = CCorePlugin.getDefault().getCoreModel().create(fProject); + ICElement ce = CCorePlugin.getDefault().getCoreModel().create(fOutsideCdbSourceFile.getFullPath()); + ITranslationUnit tu = (ITranslationUnit) ce; + assertFalse( + CDataUtil.isExcluded(tu.getPath(), getConfigurationDescription(fProject, false).getSourceEntries())); + + CompilationDatabaseParser parser = (CompilationDatabaseParser) LanguageSettingsManager + .getExtensionProviderCopy(COMPILATION_DATABASE_PARSER_EXT, true); + assertTrue(parser.isEmpty()); + parser.setBuildParserId(GCC_BUILD_COMMAND_PARSER_EXT); + parser.setCompilationDataBasePath(fCdbFile.getParent().getLocation()); + parser.setExcludeFiles(true); + addLanguageSettingsProvider(parser); + + ICConfigurationDescription cfgDescription = getConfigurationDescription(fProject, true); + + assertThrows(CoreException.class, () -> parser.processCompileCommandsFile(null, cfgDescription)); + CoreModel.getDefault().setProjectDescription(cfgDescription.getProjectDescription().getProject(), + cfgDescription.getProjectDescription()); + joingLanguageSettingsJobs(); + + ICConfigurationDescription resCfgDescription = getConfigurationDescription(fProject, false); + CompilationDatabaseParser resultParser = getCompilationDatabaseParser(); + assertTrue(resultParser.isEmpty()); + List entries = resultParser.getSettingEntries(resCfgDescription, fSourceFile, + GPPLanguage.ID); + assertTrue(entries == null); + assertFalse(CDataUtil.isExcluded(tu.getPath(), resCfgDescription.getSourceEntries())); + } + + public void testParseCDB_InvalidJson() throws Exception { + createTestProject(); + + ICProject cProject = CCorePlugin.getDefault().getCoreModel().create(fProject); + ICElement ce = CCorePlugin.getDefault().getCoreModel().create(fOutsideCdbSourceFile.getFullPath()); + ITranslationUnit tu = (ITranslationUnit) ce; + assertFalse( + CDataUtil.isExcluded(tu.getPath(), getConfigurationDescription(fProject, false).getSourceEntries())); + + //Make the Json invalid + String cdbOsString = fCdbFile.getLocation().toOSString(); + Files.write(Paths.get(cdbOsString), new byte[] { 'f', 'o', 'o' }); + try (FileReader reader = new FileReader(cdbOsString)) { + Gson gson = new Gson(); + CompileCommand[] compileCommands = gson.fromJson(reader, CompileCommand[].class); + assertTrue("Json should have been invalid and thrown an JsonSyntaxException", false); + } catch (JsonSyntaxException e) { + + } catch (Exception e) { + assertTrue("Json should have been invalid and thrown an JsonSyntaxException", false); + } + + CompilationDatabaseParser parser = (CompilationDatabaseParser) LanguageSettingsManager + .getExtensionProviderCopy(COMPILATION_DATABASE_PARSER_EXT, true); + assertTrue(parser.isEmpty()); + parser.setBuildParserId(GCC_BUILD_COMMAND_PARSER_EXT); + parser.setCompilationDataBasePath(fCdbFile.getLocation()); + parser.setExcludeFiles(true); + addLanguageSettingsProvider(parser); + + ICConfigurationDescription cfgDescription = getConfigurationDescription(fProject, true); + + assertThrows(CoreException.class, () -> parser.processCompileCommandsFile(null, cfgDescription)); + + CoreModel.getDefault().setProjectDescription(cfgDescription.getProjectDescription().getProject(), + cfgDescription.getProjectDescription()); + joingLanguageSettingsJobs(); + + ICConfigurationDescription resCfgDescription = getConfigurationDescription(fProject, false); + CompilationDatabaseParser resultParser = getCompilationDatabaseParser(); + assertTrue(resultParser.isEmpty()); + List entries = resultParser.getSettingEntries(resCfgDescription, fSourceFile, + GPPLanguage.ID); + assertTrue(entries == null); + assertFalse(CDataUtil.isExcluded(tu.getPath(), resCfgDescription.getSourceEntries())); + } + + public void testParseCDB_RelativePaths() throws Exception { + createTestProject(false, true, true, true, true); + + ICProject cProject = CCorePlugin.getDefault().getCoreModel().create(fProject); + ICElement ce = CCorePlugin.getDefault().getCoreModel().create(fOutsideCdbSourceFile.getFullPath()); + ITranslationUnit tu = (ITranslationUnit) ce; + assertFalse( + CDataUtil.isExcluded(tu.getPath(), getConfigurationDescription(fProject, false).getSourceEntries())); + + CompilationDatabaseParser parser = (CompilationDatabaseParser) LanguageSettingsManager + .getExtensionProviderCopy(COMPILATION_DATABASE_PARSER_EXT, true); + assertTrue(parser.isEmpty()); + parser.setBuildParserId(GCC_BUILD_COMMAND_PARSER_EXT); + parser.setCompilationDataBasePath(fCdbFile.getLocation()); + addLanguageSettingsProvider(parser); + + ICConfigurationDescription cfgDescription = getConfigurationDescription(fProject, true); + + parser.processCompileCommandsFile(null, cfgDescription); + assertFalse(parser.isEmpty()); + CoreModel.getDefault().setProjectDescription(cfgDescription.getProjectDescription().getProject(), + cfgDescription.getProjectDescription()); + joingLanguageSettingsJobs(); + + assertExpectedEntries(parser); + + ICConfigurationDescription resCfgDescription = getConfigurationDescription(fProject, false); + assertFalse(CDataUtil.isExcluded(tu.getPath(), resCfgDescription.getSourceEntries())); + } + + public void testParseCDB_InvalidCommandDir() throws Exception { + createTestProject(true, true, false, true, true); + + ICProject cProject = CCorePlugin.getDefault().getCoreModel().create(fProject); + ICElement ce = CCorePlugin.getDefault().getCoreModel().create(fOutsideCdbSourceFile.getFullPath()); + ITranslationUnit tu = (ITranslationUnit) ce; + assertFalse( + CDataUtil.isExcluded(tu.getPath(), getConfigurationDescription(fProject, false).getSourceEntries())); + + CompilationDatabaseParser parser = (CompilationDatabaseParser) LanguageSettingsManager + .getExtensionProviderCopy(COMPILATION_DATABASE_PARSER_EXT, true); + assertTrue(parser.isEmpty()); + parser.setBuildParserId(GCC_BUILD_COMMAND_PARSER_EXT); + parser.setCompilationDataBasePath(fCdbFile.getLocation()); + addLanguageSettingsProvider(parser); + + ICConfigurationDescription cfgDescription = getConfigurationDescription(fProject, true); + + parser.processCompileCommandsFile(null, cfgDescription); + assertFalse(parser.isEmpty()); + CoreModel.getDefault().setProjectDescription(cfgDescription.getProjectDescription().getProject(), + cfgDescription.getProjectDescription()); + joingLanguageSettingsJobs(); + + ICConfigurationDescription resCfgDescription = getConfigurationDescription(fProject, false); + List entries = parser.getSettingEntries(resCfgDescription, fSourceFile2, + GPPLanguage.ID); + + // Since the directory could not be used as working dir, both files CDB entries will match + // the same source file and only the last one will be recorder in language setting entries. + CIncludePathEntry expected = new CIncludePathEntry("/${ProjName}/folder", + CIncludePathEntry.VALUE_WORKSPACE_PATH); + CIncludePathEntry entry = (CIncludePathEntry) entries.get(0); + assertEquals(expected.getName(), entry.getName()); + assertEquals(expected.getValue(), entry.getValue()); + assertEquals(expected.getKind(), entry.getKind()); + assertEquals(expected.getFlags(), entry.getFlags()); + assertEquals(expected, entry); + + assertEquals(new CMacroEntry("FOO", "3", 0), entries.get(1)); + + assertFalse(CDataUtil.isExcluded(tu.getPath(), resCfgDescription.getSourceEntries())); + } + + public void testParseCDB_NoCommandDir() throws Exception { + createTestProject(true, false, true, true, true); + + ICProject cProject = CCorePlugin.getDefault().getCoreModel().create(fProject); + ICElement ce = CCorePlugin.getDefault().getCoreModel().create(fOutsideCdbSourceFile.getFullPath()); + ITranslationUnit tu = (ITranslationUnit) ce; + assertFalse( + CDataUtil.isExcluded(tu.getPath(), getConfigurationDescription(fProject, false).getSourceEntries())); + + CompilationDatabaseParser parser = (CompilationDatabaseParser) LanguageSettingsManager + .getExtensionProviderCopy(COMPILATION_DATABASE_PARSER_EXT, true); + assertTrue(parser.isEmpty()); + parser.setBuildParserId(GCC_BUILD_COMMAND_PARSER_EXT); + parser.setCompilationDataBasePath(fCdbFile.getLocation()); + addLanguageSettingsProvider(parser); + + ICConfigurationDescription cfgDescription = getConfigurationDescription(fProject, true); + + parser.processCompileCommandsFile(null, cfgDescription); + assertFalse(parser.isEmpty()); + CoreModel.getDefault().setProjectDescription(cfgDescription.getProjectDescription().getProject(), + cfgDescription.getProjectDescription()); + joingLanguageSettingsJobs(); + + ICConfigurationDescription resCfgDescription = getConfigurationDescription(fProject, false); + List entries = parser.getSettingEntries(resCfgDescription, fSourceFile2, + GPPLanguage.ID); + + // Since the directory could not be used as working dir, both files CDB entries will match + // the same source file and only the last one will be recorder in language setting entries. + CIncludePathEntry expected = new CIncludePathEntry("/${ProjName}/folder", + CIncludePathEntry.VALUE_WORKSPACE_PATH); + CIncludePathEntry entry = (CIncludePathEntry) entries.get(0); + assertEquals(expected.getName(), entry.getName()); + assertEquals(expected.getValue(), entry.getValue()); + assertEquals(expected.getKind(), entry.getKind()); + assertEquals(expected.getFlags(), entry.getFlags()); + assertEquals(expected, entry); + + assertEquals(new CMacroEntry("FOO", "3", 0), entries.get(1)); + + assertFalse(CDataUtil.isExcluded(tu.getPath(), resCfgDescription.getSourceEntries())); + } + + public void testParseCDB_InvalidCommandLine() throws Exception { + createTestProject(true, true, true, true, false); + + ICProject cProject = CCorePlugin.getDefault().getCoreModel().create(fProject); + ICElement ce = CCorePlugin.getDefault().getCoreModel().create(fOutsideCdbSourceFile.getFullPath()); + ITranslationUnit tu = (ITranslationUnit) ce; + assertFalse( + CDataUtil.isExcluded(tu.getPath(), getConfigurationDescription(fProject, false).getSourceEntries())); + + CompilationDatabaseParser parser = (CompilationDatabaseParser) LanguageSettingsManager + .getExtensionProviderCopy(COMPILATION_DATABASE_PARSER_EXT, true); + assertTrue(parser.isEmpty()); + parser.setBuildParserId(GCC_BUILD_COMMAND_PARSER_EXT); + parser.setCompilationDataBasePath(fCdbFile.getLocation()); + addLanguageSettingsProvider(parser); + + ICConfigurationDescription cfgDescription = getConfigurationDescription(fProject, true); + + parser.processCompileCommandsFile(null, cfgDescription); + assertFalse(parser.isEmpty()); + CoreModel.getDefault().setProjectDescription(cfgDescription.getProjectDescription().getProject(), + cfgDescription.getProjectDescription()); + joingLanguageSettingsJobs(); + + ICConfigurationDescription resCfgDescription = getConfigurationDescription(fProject, false); + assertNull(parser.getSettingEntries(resCfgDescription, fSourceFile, GPPLanguage.ID)); + assertNull(parser.getSettingEntries(resCfgDescription, fSourceFile2, GPPLanguage.ID)); + + assertFalse(CDataUtil.isExcluded(tu.getPath(), resCfgDescription.getSourceEntries())); + } + + public void testParseCDB_NoCommandLine() throws Exception { + createTestProject(true, true, true, false, true); + + ICProject cProject = CCorePlugin.getDefault().getCoreModel().create(fProject); + ICElement ce = CCorePlugin.getDefault().getCoreModel().create(fOutsideCdbSourceFile.getFullPath()); + ITranslationUnit tu = (ITranslationUnit) ce; + assertFalse( + CDataUtil.isExcluded(tu.getPath(), getConfigurationDescription(fProject, false).getSourceEntries())); + + CompilationDatabaseParser parser = (CompilationDatabaseParser) LanguageSettingsManager + .getExtensionProviderCopy(COMPILATION_DATABASE_PARSER_EXT, true); + assertTrue(parser.isEmpty()); + parser.setBuildParserId(GCC_BUILD_COMMAND_PARSER_EXT); + parser.setCompilationDataBasePath(fCdbFile.getLocation()); + addLanguageSettingsProvider(parser); + + ICConfigurationDescription cfgDescription = getConfigurationDescription(fProject, true); + + parser.processCompileCommandsFile(null, cfgDescription); + assertFalse(parser.isEmpty()); + CoreModel.getDefault().setProjectDescription(cfgDescription.getProjectDescription().getProject(), + cfgDescription.getProjectDescription()); + joingLanguageSettingsJobs(); + + ICConfigurationDescription resCfgDescription = getConfigurationDescription(fProject, false); + assertNull(parser.getSettingEntries(resCfgDescription, fSourceFile, GPPLanguage.ID)); + assertNull(parser.getSettingEntries(resCfgDescription, fSourceFile2, GPPLanguage.ID)); + + assertFalse(CDataUtil.isExcluded(tu.getPath(), resCfgDescription.getSourceEntries())); + } + + public void testClear() throws Exception { + createTestProject(); + + ICProject cProject = CCorePlugin.getDefault().getCoreModel().create(fProject); + ICElement ce = CCorePlugin.getDefault().getCoreModel().create(fOutsideCdbSourceFile.getFullPath()); + ITranslationUnit tu = (ITranslationUnit) ce; + assertFalse( + CDataUtil.isExcluded(tu.getPath(), getConfigurationDescription(fProject, false).getSourceEntries())); + + CompilationDatabaseParser parser = (CompilationDatabaseParser) LanguageSettingsManager + .getExtensionProviderCopy(COMPILATION_DATABASE_PARSER_EXT, true); + assertTrue(parser.isEmpty()); + parser.setBuildParserId(GCC_BUILD_COMMAND_PARSER_EXT); + parser.setCompilationDataBasePath(fCdbFile.getLocation()); + addLanguageSettingsProvider(parser); + + ICConfigurationDescription cfgDescription = getConfigurationDescription(fProject, true); + + parser.processCompileCommandsFile(null, cfgDescription); + assertFalse(parser.isEmpty()); + + parser.clear(); + assertTrue(parser.isEmpty()); + assertEquals(parser.getProperty(ATTR_CDB_MODIFIED_TIME), ""); + } + + public void testCloneShallow() throws Exception { + createTestProject(); + + ICProject cProject = CCorePlugin.getDefault().getCoreModel().create(fProject); + ICElement ce = CCorePlugin.getDefault().getCoreModel().create(fOutsideCdbSourceFile.getFullPath()); + ITranslationUnit tu = (ITranslationUnit) ce; + assertFalse( + CDataUtil.isExcluded(tu.getPath(), getConfigurationDescription(fProject, false).getSourceEntries())); + + CompilationDatabaseParser parser = (CompilationDatabaseParser) LanguageSettingsManager + .getExtensionProviderCopy(COMPILATION_DATABASE_PARSER_EXT, true); + assertTrue(parser.isEmpty()); + parser.setBuildParserId(GCC_BUILD_COMMAND_PARSER_EXT); + parser.setCompilationDataBasePath(fCdbFile.getLocation()); + addLanguageSettingsProvider(parser); + + ICConfigurationDescription cfgDescription = getConfigurationDescription(fProject, true); + + parser.processCompileCommandsFile(null, cfgDescription); + assertFalse(parser.isEmpty()); + + CompilationDatabaseParser clonedShallow = parser.cloneShallow(); + assertEquals(clonedShallow.getProperty(ATTR_CDB_PATH), parser.getProperty(ATTR_CDB_PATH)); + assertEquals(clonedShallow.getProperty(ATTR_BUILD_PARSER_ID), parser.getProperty(ATTR_BUILD_PARSER_ID)); + assertEquals(clonedShallow.getProperty(ATTR_EXCLUDE_FILES), parser.getProperty(ATTR_EXCLUDE_FILES)); + assertEquals(clonedShallow.getProperty(ATTR_CDB_MODIFIED_TIME), ""); + } + + public void testClone() throws Exception { + createTestProject(); + + ICProject cProject = CCorePlugin.getDefault().getCoreModel().create(fProject); + ICElement ce = CCorePlugin.getDefault().getCoreModel().create(fOutsideCdbSourceFile.getFullPath()); + ITranslationUnit tu = (ITranslationUnit) ce; + assertFalse( + CDataUtil.isExcluded(tu.getPath(), getConfigurationDescription(fProject, false).getSourceEntries())); + + CompilationDatabaseParser parser = (CompilationDatabaseParser) LanguageSettingsManager + .getExtensionProviderCopy(COMPILATION_DATABASE_PARSER_EXT, true); + assertTrue(parser.isEmpty()); + parser.setBuildParserId(GCC_BUILD_COMMAND_PARSER_EXT); + parser.setCompilationDataBasePath(fCdbFile.getLocation()); + addLanguageSettingsProvider(parser); + + ICConfigurationDescription cfgDescription = getConfigurationDescription(fProject, true); + + parser.processCompileCommandsFile(null, cfgDescription); + assertFalse(parser.isEmpty()); + + ICConfigurationDescription resCfgDescription = getConfigurationDescription(fProject, false); + List entries = parser.getSettingEntries(resCfgDescription, fSourceFile, GPPLanguage.ID); + assertExpectedEntries(parser); + + CompilationDatabaseParser cloned = parser.clone(); + entries = cloned.getSettingEntries(resCfgDescription, fSourceFile, GPPLanguage.ID); + assertExpectedEntries(cloned); + assertEquals(cloned.getProperty(ATTR_CDB_MODIFIED_TIME), parser.getProperty(ATTR_CDB_MODIFIED_TIME)); + } + + public void testParseCDB_testUpdateWithModifiedCDB() throws Exception { + createTestProject(); + + ICProject cProject = CCorePlugin.getDefault().getCoreModel().create(fProject); + ICElement ce = CCorePlugin.getDefault().getCoreModel().create(fOutsideCdbSourceFile.getFullPath()); + ITranslationUnit tu = (ITranslationUnit) ce; + assertFalse( + CDataUtil.isExcluded(tu.getPath(), getConfigurationDescription(fProject, false).getSourceEntries())); + + CompilationDatabaseParser parser = (CompilationDatabaseParser) LanguageSettingsManager + .getExtensionProviderCopy(COMPILATION_DATABASE_PARSER_EXT, true); + assertTrue(parser.isEmpty()); + parser.setBuildParserId(GCC_BUILD_COMMAND_PARSER_EXT); + parser.setCompilationDataBasePath(fCdbFile.getLocation()); + addLanguageSettingsProvider(parser); + + ICConfigurationDescription cfgDescription = getConfigurationDescription(fProject, true); + + parser.processCompileCommandsFile(null, cfgDescription); + assertFalse(parser.isEmpty()); + CoreModel.getDefault().setProjectDescription(cfgDescription.getProjectDescription().getProject(), + cfgDescription.getProjectDescription()); + joingLanguageSettingsJobs(); + + ICConfigurationDescription resCfgDescription = getConfigurationDescription(fProject, false); + assertExpectedEntries(parser); + + assertFalse(CDataUtil.isExcluded(tu.getPath(), resCfgDescription.getSourceEntries())); + + // Modify the CDB, only to contain one file with different macro definition. + String sourceFilePath = fSourceFile.getLocation().toOSString(); + CompileCommand command = new CompileCommand(); + command.directory = fSourceFile.getParent().getLocation().toOSString(); + command.file = sourceFilePath; + command.command = "g++ -I" + fFolder.getLocation().toOSString() + " -DFOO=200 " + sourceFilePath; + CompileCommand[] commands = new CompileCommand[1]; + commands[0] = command; + String json = new Gson().toJson(commands); + InputStream inputStream = new ByteArrayInputStream(json.getBytes()); + // Make sure the timestamp is different, in case the code runs fast and + // in case the system doesn't support milliseconds granularity. + while (fCdbFile.getLocalTimeStamp() / 1000 == System.currentTimeMillis() / 1000) { + Thread.sleep(5); + } + fCdbFile.setContents(inputStream, IFile.FORCE, null); + + parser.processCompileCommandsFile(null, cfgDescription); + assertFalse(parser.isEmpty()); + CoreModel.getDefault().setProjectDescription(cfgDescription.getProjectDescription().getProject(), + cfgDescription.getProjectDescription()); + joingLanguageSettingsJobs(); + + resCfgDescription = getConfigurationDescription(fProject, false); + + assertFalse(CDataUtil.isExcluded(tu.getPath(), resCfgDescription.getSourceEntries())); + + assertFalse(parser.isEmpty()); + List entries = parser.getSettingEntries(resCfgDescription, fSourceFile, GPPLanguage.ID); + + CIncludePathEntry expected = new CIncludePathEntry("/${ProjName}/folder", + CIncludePathEntry.VALUE_WORKSPACE_PATH); + CIncludePathEntry entry = (CIncludePathEntry) entries.get(0); + assertEquals(expected.getName(), entry.getName()); + assertEquals(expected.getValue(), entry.getValue()); + assertEquals(expected.getKind(), entry.getKind()); + assertEquals(expected.getFlags(), entry.getFlags()); + assertEquals(expected, entry); + + assertEquals(new CMacroEntry("FOO", "200", 0), entries.get(1)); + + entries = parser.getSettingEntries(resCfgDescription, fSourceFile2, GPPLanguage.ID); + assertNull(entries); + } +} diff --git a/build/org.eclipse.cdt.managedbuilder.core/META-INF/MANIFEST.MF b/build/org.eclipse.cdt.managedbuilder.core/META-INF/MANIFEST.MF index 12509538aad..7557234565c 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/META-INF/MANIFEST.MF +++ b/build/org.eclipse.cdt.managedbuilder.core/META-INF/MANIFEST.MF @@ -17,7 +17,7 @@ Export-Package: org.eclipse.cdt.build.core.scannerconfig, org.eclipse.cdt.managedbuilder.internal.core;x-friends:="org.eclipse.cdt.managedbuilder.ui", org.eclipse.cdt.managedbuilder.internal.dataprovider;x-internal:=true, org.eclipse.cdt.managedbuilder.internal.envvar;x-internal:=true, - org.eclipse.cdt.managedbuilder.internal.language.settings.providers;x-internal:=true, + org.eclipse.cdt.managedbuilder.internal.language.settings.providers;x-friends:="org.eclipse.cdt.managedbuilder.ui", org.eclipse.cdt.managedbuilder.internal.macros;x-friends:="org.eclipse.cdt.managedbuilder.ui", org.eclipse.cdt.managedbuilder.internal.scannerconfig;x-internal:=true, org.eclipse.cdt.managedbuilder.language.settings.providers, @@ -41,5 +41,6 @@ Require-Bundle: org.eclipse.cdt.core;bundle-version="[5.0.0,7.0.0)", org.eclipse.core.filesystem;bundle-version="1.2.0" Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Import-Package: com.ibm.icu.text +Import-Package: com.google.gson;version="2.8.0", + com.ibm.icu.text Automatic-Module-Name: org.eclipse.cdt.managedbuilder.core diff --git a/build/org.eclipse.cdt.managedbuilder.core/plugin.properties b/build/org.eclipse.cdt.managedbuilder.core/plugin.properties index 5112695a91c..4c0da312928 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/plugin.properties +++ b/build/org.eclipse.cdt.managedbuilder.core/plugin.properties @@ -84,6 +84,7 @@ GCCBuildOutputParser.name = CDT GCC Build Output Parser GCCBuiltinCompilerSettings.name = CDT GCC Built-in Compiler Settings GCCBuiltinCompilerSettingsMinGW.name = CDT GCC Built-in Compiler Settings MinGW GCCBuiltinCompilerSettingsCygwin.name = CDT GCC Built-in Compiler Settings Cygwin +CompilationDatabaseParser.name = Compilation Database Parser ManagedBuildSettingEntries.name = CDT Managed Build Setting Entries extension.name.8 = C/C++ Scanner Discovery Problem extension.name.9 = HeadlessBuilder Additional Settings \ No newline at end of file diff --git a/build/org.eclipse.cdt.managedbuilder.core/plugin.xml b/build/org.eclipse.cdt.managedbuilder.core/plugin.xml index 8a3c70c4966..a9ebd32a791 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/plugin.xml +++ b/build/org.eclipse.cdt.managedbuilder.core/plugin.xml @@ -645,6 +645,12 @@ parameter="(g?cc)|([gc]\+\+)|(clang)" prefer-non-shared="true"> + + list = getSettingEntries(cfgDescription, tu.getResource(), + tu.getLanguage().getId()); + if (list == null) { + if (entries == null) { + entries = cfgDescription.getSourceEntries(); + } + entries = CDataUtil.setExcluded(tu.getResource().getFullPath(), false, true, entries); + } + } + monitor.worked(1); + if (nbChecked % 100 == 0) { + monitor.subTask(String.format(Messages.CompilationDatabaseParser_ProgressExcludingFiles, nbChecked, + sourceFilesCount)); + } + nbChecked++; + } + } + + private static class CDBWorkingDirectoryTracker implements IWorkingDirectoryTracker { + URI currentDirectory = null; + + @Override + public URI getWorkingDirectoryURI() { + return currentDirectory; + } + + public void setCurrentDirectory(URI currentDirectory) { + this.currentDirectory = currentDirectory; + } + } + + @Override + public void registerListener(ICConfigurationDescription cfgDescription) { + unregisterListener(); + try { + processCompileCommandsFile(null, cfgDescription); + } catch (CoreException e) { + ManagedBuilderCorePlugin.log(e); + } + } + + @Override + public void unregisterListener() { + } + + /** + * Processes the compilation database based on the attributes previously set. + * Parses the commands and sets the language setting entries. If cfgDescription is a writable configuration, it is assumed that the caller will call + * CoreModel#setProjectDescription. Otherwise if cfgDescription is read-only, the method will restart itself with a writable configuration description and call CoreModel#setProjectDescription. + */ + public boolean processCompileCommandsFile(IProgressMonitor monitor, ICConfigurationDescription cfgDescription) + throws CoreException { + if (cfgDescription.isReadOnly()) { + scheduleOnWritableCfgDescription(cfgDescription); + return false; + } + + if (getCompilationDataBasePath().isEmpty()) { + throw new CoreException(new Status(Status.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, + Messages.CompilationDatabaseParser_CDBNotConfigured)); + } + + if (!Files.exists(Paths.get(getCompilationDataBasePath().toOSString()))) { + throw new CoreException(new Status(Status.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, MessageFormat.format( + Messages.CompilationDatabaseParser_CDBNotFound, getCompilationDataBasePath().toOSString()))); + } + + try { + if (!getProperty(ATTR_CDB_MODIFIED_TIME).isEmpty() && getProperty(ATTR_CDB_MODIFIED_TIME) + .equals(getCDBModifiedTime(getCompilationDataBasePath().toOSString()).toString())) { + return false; + } + } catch (IOException e) { + throw new CoreException(new Status(Status.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, + Messages.CompilationDatabaseParser_ErrorProcessingCompilationDatabase, e)); + } + + if (getBuildParserId().isEmpty()) { + throw new CoreException(new Status(Status.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, + MessageFormat.format(Messages.CompilationDatabaseParser_BuildCommandParserNotConfigured, + getCompilationDataBasePath().toOSString()))); + } + + if (!isEmpty()) { + clear(); + } + String cdbPath = getCompilationDataBasePath().toOSString(); + Long cdbModifiedTime; + try { + cdbModifiedTime = getCDBModifiedTime(cdbPath); + } catch (Exception e) { + //setProperty(ATTR_CDB_MODIFIED_TIME, Long.toString(0L)); + throw new CoreException(new Status(Status.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, + Messages.CompilationDatabaseParser_ErrorProcessingCompilationDatabase, e)); + } + + int totalTicks = getExcludeFiles() ? 100 : 60; + SubMonitor subMonitor = SubMonitor.convert(monitor, totalTicks); + subMonitor.subTask(Messages.CompilationDatabaseParser_ProgressParsingJSONFile); + subMonitor.split(5); + + CompileCommand[] compileCommands = null; + try (FileReader reader = new FileReader(cdbPath)) { + Gson gson = new Gson(); + compileCommands = gson.fromJson(reader, CompileCommand[].class); + } catch (Exception e) { + //setProperty(ATTR_CDB_MODIFIED_TIME, Long.toString(0L)); + throw new CoreException(new Status(Status.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, + Messages.CompilationDatabaseParser_ErrorProcessingCompilationDatabase, e)); + } + + AbstractBuildCommandParser outputParser; + try { + outputParser = getBuildCommandParser(cfgDescription, getBuildParserId()); + } catch (Exception e) { + //setProperty(ATTR_CDB_MODIFIED_TIME, Long.toString(0L)); + throw new CoreException(new Status(Status.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, + Messages.CompilationDatabaseParser_ErrorProcessingCompilationDatabase, e)); + } + + CDBWorkingDirectoryTracker workingDirectoryTracker = new CDBWorkingDirectoryTracker(); + + SubMonitor parseCmdsMonitor = SubMonitor.convert(subMonitor.split(50), compileCommands.length); + outputParser.startup(cfgDescription, workingDirectoryTracker); + for (int i = 0; i < compileCommands.length; i++) { + CompileCommand c = compileCommands[i]; + // Don't spam the progress view too much + if (i % 100 == 0) { + parseCmdsMonitor.subTask(String.format(Messages.CompilationDatabaseParser_ProgressParsingBuildCommands, + i, compileCommands.length)); + } + String dir = c.getDirectory(); + workingDirectoryTracker.setCurrentDirectory(null); + if (dir != null) { + File file = new File(dir); + if (file.exists()) { + workingDirectoryTracker.setCurrentDirectory(file.toURI()); + } + } + + outputParser.processLine(c.getCommand()); + parseCmdsMonitor.worked(1); + } + LanguageSettingsStorage storage = outputParser.copyStorage(); + SubMonitor entriesMonitor = SubMonitor.convert(subMonitor.split(5), storage.getLanguages().size()); + entriesMonitor.subTask(Messages.CompilationDatabaseParser_ProgressApplyingEntries); + for (String language : storage.getLanguages()) { + SubMonitor langMonitor = entriesMonitor.split(1); + Set resourcePaths = storage.getResourcePaths(language); + SubMonitor langEntriesMonitor = SubMonitor.convert(langMonitor, resourcePaths.size()); + for (String resourcePath : resourcePaths) { + IFile file = cfgDescription.getProjectDescription().getProject().getFile(new Path(resourcePath)); + if (file.exists()) { + List settingEntries = storage.getSettingEntries(resourcePath, language); + setSettingEntries(cfgDescription, file, language, settingEntries); + } + langEntriesMonitor.worked(1); + } + } + + if (getExcludeFiles()) { + excludeFiles(cfgDescription, subMonitor); + } + + setProperty(ATTR_CDB_MODIFIED_TIME, cdbModifiedTime.toString()); + touchProjectDes(cfgDescription.getProjectDescription()); + return true; + } + + private void scheduleOnWritableCfgDescription(ICConfigurationDescription cfgDescription) { + WorkspaceJob job = new WorkspaceJob(Messages.CompilationDatabaseParser_Job) { + @Override + public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException { + // If the config description we have been given is read-only, we need to get a writable one instead in order to be able to set source entries (exclusions). + // The tricky thing is that in that situation, the CompilationDatabaseParser instance (this) came from the read-only project description so anything that is + // saved that is not stored in the project description (i.e. calls to setProperties) will be saved to the wrong instance so when we call setProjectDescription, our changes will be ignored. + // So instead, restart the whole thing with the corresponding CompilationDatabaseParser instance in the writable config. + IProject project = cfgDescription.getProjectDescription().getProject(); + ICProjectDescription projectDescription = CCorePlugin.getDefault().getCoreModel() + .getProjectDescription(project.getProject(), true); + ICConfigurationDescription writableCfg = projectDescription + .getConfigurationById(cfgDescription.getId()); + if (!(writableCfg instanceof ILanguageSettingsProvidersKeeper)) { + return Status.CANCEL_STATUS; + } + + CompilationDatabaseParser parser = null; + List settingProviders = ((ILanguageSettingsProvidersKeeper) writableCfg) + .getLanguageSettingProviders(); + for (ILanguageSettingsProvider languageSettingsProvider : settingProviders) { + if (languageSettingsProvider.getId().equals(CompilationDatabaseParser.this.getId()) + && languageSettingsProvider instanceof CompilationDatabaseParser) { + parser = (CompilationDatabaseParser) languageSettingsProvider; + break; + } + } + + if (parser == null) { + // Seems very unlikely to get here. This should mean that the provider was disabled before the job ran. + return Status.CANCEL_STATUS; + } + + try { + if (parser.processCompileCommandsFile(monitor, writableCfg)) { + CoreModel.getDefault().setProjectDescription(project.getProject(), projectDescription); + } + } catch (CoreException e) { + // If we are running this in a WorkspaceJob it's because the CfgDescription was read-only so we are probably loading the project. + // We don't want to pop-up jarring error dialogs on start-up. Ideally, CDT would have problem markers for project setup issues. + ManagedBuilderCorePlugin.log(e); + } + + return Status.OK_STATUS; + } + + @Override + public boolean belongsTo(Object family) { + return family == JOB_FAMILY_COMPILATION_DATABASE_PARSER; + } + }; + + // Using root rule because of call to setProjectDescription above + job.setRule(ResourcesPlugin.getWorkspace().getRoot()); + job.schedule(); + } + + private void excludeFiles(ICConfigurationDescription cfgDescription, SubMonitor subMonitor) throws CoreException { + ICProject cProject = CCorePlugin.getDefault().getCoreModel() + .create(cfgDescription.getProjectDescription().getProject()); + // Getting a approximation of the number of source files we will have to visit based on file names. + // Much faster than going through the CElements. Then do the real work and report progress. + // It's possible that the approximation will be pretty wrong if there are a lot of already excluded files + // then we won't visit them in the ExcludeSourceFilesVisitor and the progress monitor won't be ticked for those. + int sourceFilesCount[] = new int[1]; + cProject.getProject().accept(new IResourceProxyVisitor() { + @Override + public boolean visit(IResourceProxy proxy) throws CoreException { + if (CoreModel.isValidSourceUnitName(cProject.getProject(), proxy.getName())) + sourceFilesCount[0]++; + return true; + } + }, IResource.DEPTH_INFINITE, IResource.NONE); + SubMonitor sourceMonitor = SubMonitor.convert(subMonitor.split(35), sourceFilesCount[0]); + + ExcludeSourceFilesVisitor sourceFileVisitor = new ExcludeSourceFilesVisitor(sourceMonitor, sourceFilesCount[0], + cfgDescription); + cProject.accept(sourceFileVisitor); + ICSourceEntry[] sourceEntries = sourceFileVisitor.getSourceEntries(); + + subMonitor.split(5); + if (sourceEntries != null) { + cfgDescription.setSourceEntries(sourceEntries); + } + } + + private void touchProjectDes(ICProjectDescription desc) { + // Make sure the project description is marked as modified so that language settings serialization kicks in. + // We need to let the setProjectDescription do the serialization because we cannot do it on a writable description + // and we need a writable description because we need to call setSourceEntries! + final QualifiedName TOUCH_PROPERTY = new QualifiedName(CCorePlugin.PLUGIN_ID, "touch-project"); //$NON-NLS-1$ + desc.setSessionProperty(TOUCH_PROPERTY, ""); //$NON-NLS-1$ + desc.setSessionProperty(TOUCH_PROPERTY, null); + } + + private AbstractBuildCommandParser getBuildCommandParser(ICConfigurationDescription cfgDesc, String id) + throws CloneNotSupportedException { + ICConfigurationDescription configurationDescription = cfgDesc; + if (configurationDescription instanceof ILanguageSettingsProvidersKeeper) { + List settingProviders = ((ILanguageSettingsProvidersKeeper) configurationDescription) + .getLanguageSettingProviders(); + for (ILanguageSettingsProvider languageSettingsProvider : settingProviders) { + if (languageSettingsProvider instanceof AbstractBuildCommandParser + && languageSettingsProvider instanceof ILanguageSettingsEditableProvider) { + AbstractBuildCommandParser buildParser = (AbstractBuildCommandParser) languageSettingsProvider; + if (buildParser.getId().equals(id)) + return (AbstractBuildCommandParser) ((ILanguageSettingsEditableProvider) buildParser).clone(); + } + } + } + + throw new IllegalArgumentException(MessageFormat + .format(Messages.CompilationDatabaseParser_BuildCommandParserNotFound, id, cfgDesc.getName())); + } + + @Override + public boolean isEmpty() { + // treat provider that has been executed as not empty + // to let "Clear" button to restart the provider + return getProperty(ATTR_CDB_MODIFIED_TIME).isEmpty() && super.isEmpty(); + } + + @Override + public void clear() { + super.clear(); + setProperty(ATTR_CDB_MODIFIED_TIME, null); + } + + @Override + public CompilationDatabaseParser cloneShallow() throws CloneNotSupportedException { + CompilationDatabaseParser clone = (CompilationDatabaseParser) super.cloneShallow(); + clone.setProperty(ATTR_CDB_MODIFIED_TIME, null); + return clone; + } + + @Override + public CompilationDatabaseParser clone() throws CloneNotSupportedException { + return (CompilationDatabaseParser) super.clone(); + } +} diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/language/settings/providers/CompileCommand.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/language/settings/providers/CompileCommand.java new file mode 100644 index 00000000000..3675d94dc07 --- /dev/null +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/language/settings/providers/CompileCommand.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2016, 2019 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 + * + * Contributors: + * Marc-André Laperle - Moved to managed builder + *******************************************************************************/ +package org.eclipse.cdt.managedbuilder.internal.language.settings.providers; + +public class CompileCommand { + + public String directory; + public String command; + public String file; + + public String getDirectory() { + return directory; + } + + public String getCommand() { + return command; + } + + public String getFile() { + return file; + } + +} diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/language/settings/providers/Messages.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/language/settings/providers/Messages.java new file mode 100644 index 00000000000..1a15eaff8f6 --- /dev/null +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/language/settings/providers/Messages.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2019 Marc-Andre Laperle. + * + * 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.managedbuilder.internal.language.settings.providers; + +import org.eclipse.osgi.util.NLS; + +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.cdt.managedbuilder.internal.language.settings.providers.messages"; //$NON-NLS-1$ + + public static String CompilationDatabaseParser_BuildCommandParserNotConfigured; + public static String CompilationDatabaseParser_BuildCommandParserNotFound; + public static String CompilationDatabaseParser_CDBNotConfigured; + public static String CompilationDatabaseParser_CDBNotFound; + public static String CompilationDatabaseParser_ErrorProcessingCompilationDatabase; + public static String CompilationDatabaseParser_Job; + public static String CompilationDatabaseParser_ProgressApplyingEntries; + public static String CompilationDatabaseParser_ProgressExcludingFiles; + public static String CompilationDatabaseParser_ProgressParsingBuildCommands; + public static String CompilationDatabaseParser_ProgressParsingJSONFile; + + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/language/settings/providers/messages.properties b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/language/settings/providers/messages.properties new file mode 100644 index 00000000000..f5b2a80d63b --- /dev/null +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/language/settings/providers/messages.properties @@ -0,0 +1,21 @@ +################################################################################ +# Copyright (c) 2019 Marc-Andre Laperle. +# +# 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 +################################################################################ + +CompilationDatabaseParser_BuildCommandParserNotConfigured=Compilation database parser does not have a build command parser configured. +CompilationDatabaseParser_BuildCommandParserNotFound=Could not find '{0}' language settings provider in configuration '{1}' +CompilationDatabaseParser_CDBNotConfigured=Compilation database (usually compile_commands.json) path not set. +CompilationDatabaseParser_CDBNotFound=Compilation database (usually compile_commands.json) not found at location {0}. +CompilationDatabaseParser_ErrorProcessingCompilationDatabase=Error processing compilation database. +CompilationDatabaseParser_Job=Discover Compilation Database language settings +CompilationDatabaseParser_ProgressApplyingEntries=Applying language setting entries +CompilationDatabaseParser_ProgressExcludingFiles=Excluding files not in compilation database. Checking %d/%d (Estimate) +CompilationDatabaseParser_ProgressParsingBuildCommands=Parsing build commands (%d/%d) +CompilationDatabaseParser_ProgressParsingJSONFile=Parsing JSON file \ No newline at end of file diff --git a/build/org.eclipse.cdt.managedbuilder.ui/META-INF/MANIFEST.MF b/build/org.eclipse.cdt.managedbuilder.ui/META-INF/MANIFEST.MF index ccd25f10192..f8f7c4b19b5 100644 --- a/build/org.eclipse.cdt.managedbuilder.ui/META-INF/MANIFEST.MF +++ b/build/org.eclipse.cdt.managedbuilder.ui/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.cdt.managedbuilder.ui; singleton:=true -Bundle-Version: 9.1.300.qualifier +Bundle-Version: 9.1.400.qualifier Bundle-Activator: org.eclipse.cdt.managedbuilder.ui.properties.ManagedBuilderUIPlugin Bundle-Vendor: %providerName Bundle-Localization: plugin diff --git a/build/org.eclipse.cdt.managedbuilder.ui/plugin.xml b/build/org.eclipse.cdt.managedbuilder.ui/plugin.xml index e9df66e8a4f..198bcea8041 100644 --- a/build/org.eclipse.cdt.managedbuilder.ui/plugin.xml +++ b/build/org.eclipse.cdt.managedbuilder.ui/plugin.xml @@ -884,6 +884,13 @@ ui-clear-entries="true" ui-edit-entries="false"> + + diff --git a/build/org.eclipse.cdt.managedbuilder.ui/src/org/eclipse/cdt/managedbuilder/internal/ui/language/settings/providers/CompilationDatabaseParserOptionPage.java b/build/org.eclipse.cdt.managedbuilder.ui/src/org/eclipse/cdt/managedbuilder/internal/ui/language/settings/providers/CompilationDatabaseParserOptionPage.java new file mode 100644 index 00000000000..391fe05fdc8 --- /dev/null +++ b/build/org.eclipse.cdt.managedbuilder.ui/src/org/eclipse/cdt/managedbuilder/internal/ui/language/settings/providers/CompilationDatabaseParserOptionPage.java @@ -0,0 +1,295 @@ +/******************************************************************************* + * Copyright (c) 2019 Marc-Andre Laperle. + * + * 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.managedbuilder.internal.ui.language.settings.providers; + +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsProvider; +import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsProvidersKeeper; +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.internal.ui.newui.StatusMessageLine; +import org.eclipse.cdt.managedbuilder.internal.language.settings.providers.CompilationDatabaseParser; +import org.eclipse.cdt.managedbuilder.language.settings.providers.AbstractBuildCommandParser; +import org.eclipse.cdt.managedbuilder.ui.properties.ManagedBuilderUIPlugin; +import org.eclipse.cdt.ui.language.settings.providers.AbstractLanguageSettingProviderOptionPage; +import org.eclipse.cdt.utils.ui.controls.ControlFactory; +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.Path; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +/** + * Options page for {@link CompilationDatabaseParser}. + * + * @noinstantiate This class is not intended to be instantiated by clients. + */ +public final class CompilationDatabaseParserOptionPage extends AbstractLanguageSettingProviderOptionPage { + private boolean fEditable; + private Text fCompileCommandsPath; + + @SuppressWarnings("restriction") + private StatusMessageLine fStatusLine; + private Combo fBuildOutputParserCombo; + + @Override + public void createControl(Composite parent) { + fEditable = parent.isEnabled(); + CompilationDatabaseParser provider = (CompilationDatabaseParser) getProvider(); + + Composite composite = createCompositeForPageArea(parent); + createCompileCommandsPathInputControl(composite, provider); + createBrowseButton(composite); + createOutputParserCombo(composite); + createExclusionOptions(composite); + createStatusLine(composite, provider); + + setControl(composite); + } + + private Composite createCompositeForPageArea(Composite parent) { + Composite composite = new Composite(parent, SWT.NONE); + GridLayout layout = new GridLayout(); + layout.numColumns = 2; + layout.marginWidth = 1; + layout.marginHeight = 1; + layout.marginRight = 1; + composite.setLayout(layout); + composite.setLayoutData(new GridData(GridData.FILL_BOTH)); + Dialog.applyDialogFont(composite); + + GridData gd = new GridData(GridData.FILL_HORIZONTAL); + gd.horizontalSpan = 2; + composite.setLayoutData(gd); + return composite; + } + + private void createCompileCommandsPathInputControl(Composite composite, CompilationDatabaseParser provider) { + Label label = ControlFactory.createLabel(composite, + Messages.CompilationDatabaseParserOptionPage_CompileCommandsPath); + GridData gd = new GridData(); + gd.horizontalSpan = 2; + label.setLayoutData(gd); + label.setEnabled(fEditable); + + fCompileCommandsPath = ControlFactory.createTextField(composite, SWT.SINGLE | SWT.BORDER); + String command = provider.getCompilationDataBasePath().toOSString(); + fCompileCommandsPath.setText(command != null ? command : ""); //$NON-NLS-1$ + fCompileCommandsPath.setEnabled(fEditable); + fCompileCommandsPath.addModifyListener(new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + String text = fCompileCommandsPath.getText(); + CompilationDatabaseParser provider = (CompilationDatabaseParser) getProvider(); + if (provider.getCompilationDataBasePath() == null + || !text.equals(provider.getCompilationDataBasePath().toOSString())) { + CompilationDatabaseParser selectedProvider = (CompilationDatabaseParser) getProviderWorkingCopy(); + selectedProvider.setCompilationDataBasePath(Path.fromOSString(text)); + refreshItem(selectedProvider); + validate(); + } + } + }); + } + + private void createBrowseButton(Composite composite) { + Button button = ControlFactory.createPushButton(composite, Messages.CompilationDatabaseParserOptionPage_Browse); + button.setEnabled(fEditable); + button.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent evt) { + FileDialog dialog = new FileDialog(getShell(), SWT.NONE); + dialog.setText(Messages.CompilationDatabaseParserOptionPage_ChooseFile); + String fileName = fCompileCommandsPath.getText(); + IPath folder = new Path(fileName).removeLastSegments(1); + dialog.setFilterPath(folder.toOSString()); + String chosenFile = dialog.open(); + if (chosenFile != null) { + fCompileCommandsPath.insert(chosenFile); + } + } + }); + } + + private void createOutputParserCombo(Composite composite) { + ICConfigurationDescription configurationDescription = getConfigurationDescription(); + List buildParsers = new ArrayList<>(); + if (configurationDescription instanceof ILanguageSettingsProvidersKeeper) { + List settingProviders = ((ILanguageSettingsProvidersKeeper) configurationDescription) + .getLanguageSettingProviders(); + for (ILanguageSettingsProvider languageSettingsProvider : settingProviders) { + if (languageSettingsProvider instanceof AbstractBuildCommandParser) { + AbstractBuildCommandParser buildParser = (AbstractBuildCommandParser) languageSettingsProvider; + buildParsers.add(buildParser); + } + } + } + + Label parserLabel = ControlFactory.createLabel(composite, + Messages.CompilationDatabaseParserOptionPage_BuildParser); + GridData gd = new GridData(SWT.BEGINNING); + gd.horizontalSpan = 2; + parserLabel.setLayoutData(gd); + + fBuildOutputParserCombo = new Combo(composite, SWT.READ_ONLY); + fBuildOutputParserCombo.setEnabled(fEditable); + gd = new GridData(GridData.FILL_HORIZONTAL); + gd.horizontalSpan = 2; + fBuildOutputParserCombo.setLayoutData(gd); + if (buildParsers.isEmpty()) { + fBuildOutputParserCombo.add(Messages.CompilationDatabaseParserOptionPage_NoBuildOutputParserError); + fBuildOutputParserCombo.select(0); + fBuildOutputParserCombo.setEnabled(false); + // Can't call getProviderWorkingCopy().setBuildParserId() while creating the page since + // it will try to replace the selected provider in the table which + // doesn't have a proper selection index until one of them is clicked. + // Use combo.setData to encode invalid/valid data then set it on the working copy on setVisible(true)/validate. + fBuildOutputParserCombo.setData(null); + return; + } + + for (int i = 0; i < buildParsers.size(); i++) { + AbstractBuildCommandParser buildParser = buildParsers.get(i); + fBuildOutputParserCombo.add(buildParser.getName()); + fBuildOutputParserCombo.setData(buildParser.getName(), buildParser); + if (buildParser.getId().equals(((CompilationDatabaseParser) getProvider()).getBuildParserId())) { + fBuildOutputParserCombo.select(i); + fBuildOutputParserCombo.setData(buildParser.getId()); + } + } + + fBuildOutputParserCombo.addModifyListener(new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + AbstractBuildCommandParser parser = (AbstractBuildCommandParser) fBuildOutputParserCombo + .getData(fBuildOutputParserCombo.getText()); + CompilationDatabaseParser selectedProvider = (CompilationDatabaseParser) getProviderWorkingCopy(); + String parserId = ""; //$NON-NLS-1$ + if (parser != null) { + parserId = parser.getId(); + } + selectedProvider.setBuildParserId(parserId); + fBuildOutputParserCombo.setData(parserId); + validate(); + } + }); + + } + + private void createExclusionOptions(Composite parent) { + Button keepExclusion = new Button(parent, SWT.CHECK); + keepExclusion.setText(Messages.CompilationDatabaseParserOptionPage_ExcludeFiles); + GridData gd = new GridData(SWT.BEGINNING); + gd.horizontalSpan = 2; + keepExclusion.setLayoutData(gd); + + keepExclusion.setSelection(((CompilationDatabaseParser) getProvider()).getExcludeFiles()); + keepExclusion.addSelectionListener(new SelectionListener() { + + @Override + public void widgetSelected(SelectionEvent e) { + CompilationDatabaseParser selectedProvider = (CompilationDatabaseParser) getProviderWorkingCopy(); + selectedProvider.setExcludeFiles(keepExclusion.getSelection()); + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + } + }); + } + + @SuppressWarnings("restriction") + private void createStatusLine(Composite composite, CompilationDatabaseParser provider) { + fStatusLine = new StatusMessageLine(composite, SWT.LEFT, 2); + } + + @SuppressWarnings("restriction") + @Override + public void performApply(IProgressMonitor monitor) throws CoreException { + ILanguageSettingsProvider provider = providerTab.getProvider(providerId); + if ((provider instanceof CompilationDatabaseParser)) { // basically check for working copy + CompilationDatabaseParser compilationDatabaseParser = (CompilationDatabaseParser) provider; + ILanguageSettingsProvider initialProvider = providerTab.getInitialProvider(providerId); + if (!(initialProvider instanceof CompilationDatabaseParser) + || !((CompilationDatabaseParser) initialProvider).getCompilationDataBasePath() + .equals(compilationDatabaseParser.getCompilationDataBasePath()) + || !((CompilationDatabaseParser) initialProvider).getBuildParserId() + .equals(compilationDatabaseParser.getBuildParserId()) + || ((CompilationDatabaseParser) initialProvider).getExcludeFiles() != compilationDatabaseParser + .getExcludeFiles()) { + compilationDatabaseParser.clear(); + } + if (compilationDatabaseParser.isEmpty()) { + compilationDatabaseParser.processCompileCommandsFile(monitor, getConfigurationDescription()); + } + } + + super.performApply(monitor); + } + + @SuppressWarnings("restriction") + private void validate() { + if (fBuildOutputParserCombo.getData() == null) { + ((CompilationDatabaseParser) getProviderWorkingCopy()).setBuildParserId(null); + } + + CompilationDatabaseParser provider = (CompilationDatabaseParser) getProvider(); + if (provider.getCompilationDataBasePath() == null || provider.getCompilationDataBasePath().isEmpty() + || !Files.exists(Paths.get(provider.getCompilationDataBasePath().toOSString()))) { + fStatusLine.setErrorStatus(new Status(IStatus.ERROR, ManagedBuilderUIPlugin.getUniqueIdentifier(), + Messages.CompilationDatabaseParserOptionPage_CompileCommandsPathError)); + return; + } + + if (provider.getBuildParserId() == null || provider.getBuildParserId().isEmpty()) { + fStatusLine.setErrorStatus(new Status(IStatus.ERROR, ManagedBuilderUIPlugin.getUniqueIdentifier(), + Messages.CompilationDatabaseParserOptionPage_BuildOutputParserError)); + return; + } + + fStatusLine.setErrorStatus(Status.OK_STATUS); + } + + private ICConfigurationDescription getConfigurationDescription() { + if (providerTab.page.isForPrefs()) { + return null; + } + + return providerTab.getResDesc().getConfiguration().getConfiguration(); + } + + @Override + public void setVisible(boolean visible) { + super.setVisible(visible); + if (visible) { + validate(); + } + } +} diff --git a/build/org.eclipse.cdt.managedbuilder.ui/src/org/eclipse/cdt/managedbuilder/internal/ui/language/settings/providers/Messages.java b/build/org.eclipse.cdt.managedbuilder.ui/src/org/eclipse/cdt/managedbuilder/internal/ui/language/settings/providers/Messages.java new file mode 100644 index 00000000000..1b0d061dc91 --- /dev/null +++ b/build/org.eclipse.cdt.managedbuilder.ui/src/org/eclipse/cdt/managedbuilder/internal/ui/language/settings/providers/Messages.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2019 Marc-Andre Laperle. + * + * 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.managedbuilder.internal.ui.language.settings.providers; + +import org.eclipse.osgi.util.NLS; + +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.cdt.managedbuilder.internal.ui.language.settings.providers.messages"; //$NON-NLS-1$ + public static String CompilationDatabaseParserOptionPage_Browse; + public static String CompilationDatabaseParserOptionPage_BuildOutputParserError; + public static String CompilationDatabaseParserOptionPage_BuildParser; + public static String CompilationDatabaseParserOptionPage_ChooseFile; + public static String CompilationDatabaseParserOptionPage_CompileCommandsPath; + public static String CompilationDatabaseParserOptionPage_CompileCommandsPathError; + public static String CompilationDatabaseParserOptionPage_ExcludeFiles; + public static String CompilationDatabaseParserOptionPage_NoBuildOutputParserError; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/build/org.eclipse.cdt.managedbuilder.ui/src/org/eclipse/cdt/managedbuilder/internal/ui/language/settings/providers/messages.properties b/build/org.eclipse.cdt.managedbuilder.ui/src/org/eclipse/cdt/managedbuilder/internal/ui/language/settings/providers/messages.properties new file mode 100644 index 00000000000..c9657a74420 --- /dev/null +++ b/build/org.eclipse.cdt.managedbuilder.ui/src/org/eclipse/cdt/managedbuilder/internal/ui/language/settings/providers/messages.properties @@ -0,0 +1,19 @@ +################################################################################ +# Copyright (c) 2019 Marc-Andre Laperle. +# +# 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 +################################################################################ + +CompilationDatabaseParserOptionPage_Browse=Browse... +CompilationDatabaseParserOptionPage_BuildOutputParserError=Invalid build parser +CompilationDatabaseParserOptionPage_BuildParser=Build parser: +CompilationDatabaseParserOptionPage_ChooseFile=Choose File +CompilationDatabaseParserOptionPage_CompileCommandsPath=Compilation Database path (compile_commands.json): +CompilationDatabaseParserOptionPage_CompileCommandsPathError=Compilation Database path does not exist +CompilationDatabaseParserOptionPage_ExcludeFiles=Exclude files not in the Compilation Database +CompilationDatabaseParserOptionPage_NoBuildOutputParserError=No build output parser enabled in "Providers" diff --git a/releng/CDT.setup b/releng/CDT.setup index 9df0b1396f6..9430d61d0b3 100644 --- a/releng/CDT.setup +++ b/releng/CDT.setup @@ -122,6 +122,10 @@ name="org.junit"/> + + + @@ -17,6 +17,8 @@ + +