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 @@ + +