diff --git a/build/org.eclipse.cdt.make.core/src/org/eclipse/cdt/make/core/MakeBuilder.java b/build/org.eclipse.cdt.make.core/src/org/eclipse/cdt/make/core/MakeBuilder.java index 7275c8e80f8..9a6eb5609ca 100644 --- a/build/org.eclipse.cdt.make.core/src/org/eclipse/cdt/make/core/MakeBuilder.java +++ b/build/org.eclipse.cdt.make.core/src/org/eclipse/cdt/make/core/MakeBuilder.java @@ -26,8 +26,17 @@ import org.eclipse.cdt.core.CommandLauncher; import org.eclipse.cdt.core.ErrorParserManager; import org.eclipse.cdt.core.ICommandLauncher; import org.eclipse.cdt.core.IConsoleParser; +import org.eclipse.cdt.core.language.settings.providers.ICBuildOutputParser; +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.IWorkingDirectoryTracker; +import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsManager; +import org.eclipse.cdt.core.language.settings.providers.ScannerDiscoveryLegacySupport; +import org.eclipse.cdt.core.model.CoreModel; import org.eclipse.cdt.core.resources.ACBuilder; import org.eclipse.cdt.core.resources.IConsole; +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.core.settings.model.ICProjectDescription; import org.eclipse.cdt.internal.core.BuildRunnerHelper; import org.eclipse.cdt.make.core.scannerconfig.IScannerInfoConsoleParser; import org.eclipse.cdt.make.internal.core.MakeMessages; @@ -175,6 +184,7 @@ public class MakeBuilder extends ACBuilder { String[] targets = getTargets(kind, info); if (targets.length != 0 && targets[targets.length - 1].equals(info.getCleanBuildTarget())) isClean = true; + boolean isOnlyClean = isClean && (targets.length == 1); String[] args = getCommandArguments(info, targets); @@ -187,8 +197,17 @@ public class MakeBuilder extends ACBuilder { ErrorParserManager epm = new ErrorParserManager(getProject(), workingDirectoryURI, this, errorParsers); List parsers = new ArrayList(); - IScannerInfoConsoleParser parserSD = ScannerInfoConsoleParserFactory.getScannerInfoConsoleParser(project, workingDirectoryURI, this); - parsers.add(parserSD); + if (!isOnlyClean) { + ICProjectDescription prjDescription = CoreModel.getDefault().getProjectDescription(project); + if (prjDescription != null) { + ICConfigurationDescription cfgDescription = prjDescription.getActiveConfiguration(); + collectLanguageSettingsConsoleParsers(cfgDescription, epm, parsers); + } + if (ScannerDiscoveryLegacySupport.isLegacyScannerDiscoveryOn(project)) { + IScannerInfoConsoleParser parserSD = ScannerInfoConsoleParserFactory.getScannerInfoConsoleParser(project, workingDirectoryURI, this); + parsers.add(parserSD); + } + } buildRunnerHelper.setLaunchParameters(launcher, buildCommand, args, workingDirectoryURI, envp); buildRunnerHelper.prepareStreams(epm, parsers, console, new SubProgressMonitor(monitor, TICKS_STREAM_PROGRESS_MONITOR, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK)); @@ -307,4 +326,26 @@ public class MakeBuilder extends ACBuilder { private String[] makeArray(String string) { return CommandLineUtil.argumentsToArray(string); } + + private static void collectLanguageSettingsConsoleParsers(ICConfigurationDescription cfgDescription, IWorkingDirectoryTracker cwdTracker, List parsers) { + if (cfgDescription instanceof ILanguageSettingsProvidersKeeper) { + List lsProviders = ((ILanguageSettingsProvidersKeeper) cfgDescription).getLanguageSettingProviders(); + for (ILanguageSettingsProvider lsProvider : lsProviders) { + ILanguageSettingsProvider rawProvider = LanguageSettingsManager.getRawProvider(lsProvider); + if (rawProvider instanceof ICBuildOutputParser) { + ICBuildOutputParser consoleParser = (ICBuildOutputParser) rawProvider; + try { + consoleParser.startup(cfgDescription, cwdTracker); + parsers.add(consoleParser); + } catch (CoreException e) { + MakeCorePlugin.log(new Status(IStatus.ERROR, MakeCorePlugin.PLUGIN_ID, + "Language Settings Provider failed to start up", e)); //$NON-NLS-1$ + } + } + } + } + + } + + } diff --git a/build/org.eclipse.cdt.make.core/src/org/eclipse/cdt/make/core/scannerconfig/ScannerConfigBuilder.java b/build/org.eclipse.cdt.make.core/src/org/eclipse/cdt/make/core/scannerconfig/ScannerConfigBuilder.java index 304a6c78e10..9a256489cdd 100644 --- a/build/org.eclipse.cdt.make.core/src/org/eclipse/cdt/make/core/scannerconfig/ScannerConfigBuilder.java +++ b/build/org.eclipse.cdt.make.core/src/org/eclipse/cdt/make/core/scannerconfig/ScannerConfigBuilder.java @@ -13,6 +13,8 @@ package org.eclipse.cdt.make.core.scannerconfig; import java.util.Map; import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.language.settings.providers.ScannerDiscoveryLegacySupport; +import org.eclipse.cdt.core.model.CoreModel; import org.eclipse.cdt.core.resources.ACBuilder; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.core.settings.model.ICProjectDescription; @@ -30,12 +32,12 @@ import org.eclipse.core.runtime.SubProgressMonitor; /** * Runs after standard make builder. * Consolidates discovered scanner configuration and updates project's scanner configuration. - * + * * @deprecated as of CDT 4.0. Used by legacy CDT 3.X projects. * Replaced by ScannerConfigBuilder in org.eclipse.cdt.managedbuilder.core. - * + * * @see IncrementalProjectBuilder - * + * * @noextend This class is not intended to be subclassed by clients. * @noinstantiate This class is not intended to be instantiated by clients. */ @@ -60,34 +62,39 @@ public class ScannerConfigBuilder extends ACBuilder { // boolean autodiscoveryEnabled; if(buildNewStyle(getProject(), monitor)) return getProject().getReferencedProjects(); + + if (!ScannerDiscoveryLegacySupport.isLegacyScannerDiscoveryOn(getProject())) { + return getProject().getReferencedProjects(); + } + boolean autodiscoveryEnabled2; IScannerConfigBuilderInfo2 buildInfo2 = null; try { // IScannerConfigBuilderInfo buildInfo = MakeCorePlugin.createScannerConfigBuildInfo(getProject(), BUILDER_ID); // autodiscoveryEnabled = buildInfo.isAutoDiscoveryEnabled(); -// +// // if (autodiscoveryEnabled) { // monitor.beginTask("ScannerConfigBuilder.Invoking_Builder", 100); //$NON-NLS-1$ -// monitor.subTask(MakeMessages.getString("ScannerConfigBuilder.Invoking_Builder") + //$NON-NLS-1$ +// monitor.subTask(MakeMessages.getString("ScannerConfigBuilder.Invoking_Builder") + //$NON-NLS-1$ // getProject().getName()); // ScannerInfoCollector.getInstance().updateScannerConfiguration(getProject(), new SubProgressMonitor(monitor, 100)); // } - + buildInfo2 = ScannerConfigProfileManager.createScannerConfigBuildInfo2(getProject()); autodiscoveryEnabled2 = buildInfo2.isAutoDiscoveryEnabled(); if (autodiscoveryEnabled2) { monitor.beginTask(MakeMessages.getString("ScannerConfigBuilder.Invoking_Builder"), 100); //$NON-NLS-1$ - monitor.subTask(MakeMessages.getString("ScannerConfigBuilder.Invoking_Builder") + //$NON-NLS-1$ + monitor.subTask(MakeMessages.getString("ScannerConfigBuilder.Invoking_Builder") + //$NON-NLS-1$ getProject().getName()); - + // get scanner info from all external providers SCJobsUtil.getProviderScannerInfo(getProject(), buildInfo2, new SubProgressMonitor(monitor, 70)); // update and persist scanner configuration SCJobsUtil.updateScannerConfiguration(getProject(), buildInfo2, new SubProgressMonitor(monitor, 30)); } - } + } catch (CoreException e) { // builder not installed or disabled // autodiscoveryEnabled = false; @@ -96,12 +103,12 @@ public class ScannerConfigBuilder extends ACBuilder { } return getProject().getReferencedProjects(); } - + protected boolean buildNewStyle(IProject project, IProgressMonitor monitor) throws CoreException{ ICProjectDescription des = CCorePlugin.getDefault().getProjectDescription(project, false); if(!CCorePlugin.getDefault().isNewStyleProject(des)) return false; - + ICConfigurationDescription[] cfgs = des.getConfigurations(); IScannerConfigBuilderInfo2Set container = ScannerConfigProfileManager.createScannerConfigBuildInfo2Set(project); monitor.beginTask(MakeMessages.getString("ScannerConfigBuilder.0"), cfgs.length + 1); //$NON-NLS-1$ @@ -119,32 +126,33 @@ public class ScannerConfigBuilder extends ACBuilder { if(build(project, context, info, new SubProgressMonitor(monitor, 1))) wasbuilt = true; } - + if(wasbuilt) CCorePlugin.getDefault().updateProjectDescriptions(new IProject[]{project}, new SubProgressMonitor(monitor, 1)); - + monitor.done(); return true; } - + protected boolean build(IProject project, InfoContext context, IScannerConfigBuilderInfo2 buildInfo2, IProgressMonitor monitor){ - boolean autodiscoveryEnabled2 = buildInfo2.isAutoDiscoveryEnabled(); + if (ScannerDiscoveryLegacySupport.isLegacyScannerDiscoveryOn(getProject())) { + boolean autodiscoveryEnabled2 = buildInfo2.isAutoDiscoveryEnabled(); + if (autodiscoveryEnabled2) { + monitor.beginTask(MakeMessages.getString("ScannerConfigBuilder.Invoking_Builder"), 100); //$NON-NLS-1$ + monitor.subTask(MakeMessages.getString("ScannerConfigBuilder.Invoking_Builder") + //$NON-NLS-1$ + getProject().getName()); - if (autodiscoveryEnabled2) { - monitor.beginTask(MakeMessages.getString("ScannerConfigBuilder.Invoking_Builder"), 100); //$NON-NLS-1$ - monitor.subTask(MakeMessages.getString("ScannerConfigBuilder.Invoking_Builder") + //$NON-NLS-1$ - getProject().getName()); - - // get scanner info from all external providers - SCJobsUtil.getProviderScannerInfo(getProject(), context, buildInfo2, new SubProgressMonitor(monitor, 70)); + // get scanner info from all external providers + SCJobsUtil.getProviderScannerInfo(getProject(), context, buildInfo2, new SubProgressMonitor(monitor, 70)); - // update and persist scanner configuration - SCJobsUtil.updateScannerConfiguration(getProject(), context, buildInfo2, new SubProgressMonitor(monitor, 30)); - return true; - } - return false; + // update and persist scanner configuration + SCJobsUtil.updateScannerConfiguration(getProject(), context, buildInfo2, new SubProgressMonitor(monitor, 30)); + return true; + } + } + return false; } - - + + } 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 8c5591f6aa4..6c7f02b07f2 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 @@ -7,6 +7,7 @@ Bundle-Activator: org.eclipse.cdt.managedbuilder.testplugin.CTestPlugin Bundle-Vendor: Eclipse CDT Bundle-Localization: plugin Export-Package: org.eclipse.cdt.managedbuilder.core.tests, + org.eclipse.cdt.managedbuilder.language.settings.providers.tests, org.eclipse.cdt.managedbuilder.templateengine.tests, org.eclipse.cdt.managedbuilder.testplugin, org.eclipse.cdt.managedbuilder.tests.suite, diff --git a/build/org.eclipse.cdt.managedbuilder.core.tests/plugin.xml b/build/org.eclipse.cdt.managedbuilder.core.tests/plugin.xml index 9e0a06463ee..67494d80a53 100644 --- a/build/org.eclipse.cdt.managedbuilder.core.tests/plugin.xml +++ b/build/org.eclipse.cdt.managedbuilder.core.tests/plugin.xml @@ -9301,4 +9301,14 @@ + + + + diff --git a/build/org.eclipse.cdt.managedbuilder.core.tests/suite/org/eclipse/cdt/managedbuilder/tests/suite/AllManagedBuildTests.java b/build/org.eclipse.cdt.managedbuilder.core.tests/suite/org/eclipse/cdt/managedbuilder/tests/suite/AllManagedBuildTests.java index d90c1e4adde..0ae86b7db21 100644 --- a/build/org.eclipse.cdt.managedbuilder.core.tests/suite/org/eclipse/cdt/managedbuilder/tests/suite/AllManagedBuildTests.java +++ b/build/org.eclipse.cdt.managedbuilder.core.tests/suite/org/eclipse/cdt/managedbuilder/tests/suite/AllManagedBuildTests.java @@ -4,7 +4,7 @@ * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html - * + * * Contributors: * IBM - Initial API and implementation * Markus Schorn (Wind River Systems) @@ -39,6 +39,7 @@ import org.eclipse.cdt.managedbuilder.core.tests.OptionCategoryEnablementTests; import org.eclipse.cdt.managedbuilder.core.tests.OptionEnablementTests; import org.eclipse.cdt.managedbuilder.core.tests.PathConverterTest; import org.eclipse.cdt.managedbuilder.core.tests.ResourceBuildCoreTests; +import org.eclipse.cdt.managedbuilder.language.settings.providers.tests.AllLanguageSettingsProvidersMBSTests; import org.eclipse.cdt.managedbuilder.templateengine.tests.AllTemplateEngineTests; import org.eclipse.cdt.projectmodel.tests.BackwardCompatiblityTests; import org.eclipse.cdt.projectmodel.tests.CProjectDescriptionSerializationTests; @@ -61,6 +62,9 @@ public class AllManagedBuildTests { suite.addTest(CfgScannerConfigProfileManagerTests.suite()); suite.addTestSuite(GCCSpecsConsoleParserTest.class); + // language settings providers tests + suite.addTest(AllLanguageSettingsProvidersMBSTests.suite()); + // managedbuilder.core.tests suite.addTest(ManagedBuildDependencyLibsTests.suite()); suite.addTest(ManagedBuildCoreTests20.suite()); 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 new file mode 100644 index 00000000000..4ab865896ad --- /dev/null +++ b/build/org.eclipse.cdt.managedbuilder.core.tests/tests/org/eclipse/cdt/managedbuilder/language/settings/providers/tests/AllLanguageSettingsProvidersMBSTests.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.managedbuilder.language.settings.providers.tests; + +import junit.framework.TestSuite; + +/** + * Test suite to test language settings providers defined in cdt.managedbuilder.core. + */ +public class AllLanguageSettingsProvidersMBSTests extends TestSuite { + + public static TestSuite suite() { + return new AllLanguageSettingsProvidersMBSTests(); + } + + public AllLanguageSettingsProvidersMBSTests() { + super(AllLanguageSettingsProvidersMBSTests.class.getName()); + + addTestSuite(GCCBuildCommandParserTest.class); + addTestSuite(BuiltinSpecsDetectorTest.class); + addTestSuite(GCCBuiltinSpecsDetectorTest.class); + } +} diff --git a/build/org.eclipse.cdt.managedbuilder.core.tests/tests/org/eclipse/cdt/managedbuilder/language/settings/providers/tests/BuiltinSpecsDetectorTest.java b/build/org.eclipse.cdt.managedbuilder.core.tests/tests/org/eclipse/cdt/managedbuilder/language/settings/providers/tests/BuiltinSpecsDetectorTest.java new file mode 100644 index 00000000000..4460d40c9e6 --- /dev/null +++ b/build/org.eclipse.cdt.managedbuilder.core.tests/tests/org/eclipse/cdt/managedbuilder/language/settings/providers/tests/BuiltinSpecsDetectorTest.java @@ -0,0 +1,569 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - Initial API and implementation + *******************************************************************************/ + package org.eclipse.cdt.managedbuilder.language.settings.providers.tests; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.URI; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.cdt.core.ICommandLauncher; +import org.eclipse.cdt.core.model.CoreModel; +import org.eclipse.cdt.core.settings.model.CIncludeFileEntry; +import org.eclipse.cdt.core.settings.model.CIncludePathEntry; +import org.eclipse.cdt.core.settings.model.CLibraryFileEntry; +import org.eclipse.cdt.core.settings.model.CLibraryPathEntry; +import org.eclipse.cdt.core.settings.model.CMacroEntry; +import org.eclipse.cdt.core.settings.model.CMacroFileEntry; +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.ICSettingEntry; +import org.eclipse.cdt.core.testplugin.ResourceHelper; +import org.eclipse.cdt.core.testplugin.util.BaseTestCase; +import org.eclipse.cdt.internal.core.XmlUtil; +import org.eclipse.cdt.managedbuilder.language.settings.providers.AbstractBuiltinSpecsDetector; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.jobs.Job; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * Test cases to test built-in specs detectors. + */ +public class BuiltinSpecsDetectorTest extends BaseTestCase { + private static final String PROVIDER_ID = "provider.id"; + private static final String PROVIDER_NAME = "provider name"; + private static final String LANGUAGE_ID = "language.test.id"; + private static final String CUSTOM_PARAMETER = "customParameter"; + private static final String CUSTOM_PARAMETER_2 = "customParameter2"; + private static final String ELEM_TEST = "test"; + + // those attributes must match that in AbstractBuiltinSpecsDetector + private static final String ATTR_PARAMETER = "parameter"; //$NON-NLS-1$ + private static final String ATTR_CONSOLE = "console"; //$NON-NLS-1$ + + /** + * Mock built-in specs detector to test basic functionality of {@link AbstractBuiltinSpecsDetector}. + */ + private class MockBuiltinSpecsDetector extends AbstractBuiltinSpecsDetector { + @Override + protected List parseOptions(String line) { + return null; + } + @Override + protected AbstractOptionParser[] getOptionParsers() { + return null; + } + @Override + protected String getCompilerCommand(String languageId) { + return null; + } + + @Override + protected void startupForLanguage(String languageId) throws CoreException { + super.startupForLanguage(languageId); + } + @Override + protected void shutdownForLanguage() { + super.shutdownForLanguage(); + } + } + + /** + * Mock built-in specs detector to test execute() functionality. + */ + private class MockBuiltinSpecsDetectorExecutedFlag extends AbstractBuiltinSpecsDetector { + @Override + protected List parseOptions(String line) { + return null; + } + @Override + protected AbstractOptionParser[] getOptionParsers() { + return null; + } + @Override + protected String getCompilerCommand(String languageId) { + return null; + } + + @Override + protected void execute() { + super.execute(); + } + protected boolean isExecuted() { + return isExecuted; + } + } + + /** + * Mock built-in specs detector to test parsing functionality. + */ + private class MockConsoleBuiltinSpecsDetector extends AbstractBuiltinSpecsDetector { + @SuppressWarnings("nls") + private final AbstractOptionParser[] optionParsers = { + new MacroOptionParser("#define (\\S*) *(\\S*)", "$1", "$2", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), + }; + @Override + protected int runProgramForLanguage(String languageId, String command, String[] envp, URI workingDirectoryURI, OutputStream consoleOut, OutputStream consoleErr, IProgressMonitor monitor) throws CoreException, IOException { + String line = "#define MACRO VALUE"; + consoleOut.write((line + '\n').getBytes()); + consoleOut.flush(); + return ICommandLauncher.OK; + } + @Override + protected IStatus runForEachLanguage(IProgressMonitor monitor) { + return super.runForEachLanguage(monitor); + } + @Override + protected List parseOptions(final String line) { + return new ArrayList() {{ add(line); }}; + } + @Override + protected AbstractOptionParser[] getOptionParsers() { + return optionParsers; + } + @Override + protected String getCompilerCommand(String languageId) { + return null; + } + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + try { + Job.getJobManager().join(AbstractBuiltinSpecsDetector.JOB_FAMILY_BUILTIN_SPECS_DETECTOR, null); + } catch (Exception e) { + // ignore + } + super.tearDown(); + } + + /** + * Helper method to fetch configuration descriptions. + */ + private ICConfigurationDescription[] getConfigurationDescriptions(IProject project) { + CoreModel coreModel = CoreModel.getDefault(); + ICProjectDescriptionManager mngr = coreModel.getProjectDescriptionManager(); + // project description + ICProjectDescription projectDescription = mngr.getProjectDescription(project, false); + assertNotNull(projectDescription); + assertEquals(1, projectDescription.getConfigurations().length); + // configuration description + ICConfigurationDescription[] cfgDescriptions = projectDescription.getConfigurations(); + return cfgDescriptions; + } + + /** + * Test configure, getters and setters. + */ + public void testAbstractBuiltinSpecsDetector_GettersSetters() throws Exception { + { + // provider configured with null parameters + MockBuiltinSpecsDetectorExecutedFlag provider = new MockBuiltinSpecsDetectorExecutedFlag(); + provider.configureProvider(PROVIDER_ID, PROVIDER_NAME, null, null, null); + + assertEquals(PROVIDER_ID, provider.getId()); + assertEquals(PROVIDER_NAME, provider.getName()); + assertEquals(null, provider.getLanguageScope()); + assertEquals(null, provider.getSettingEntries(null, null, null)); + assertEquals("", provider.getCommand()); + assertEquals(false, provider.isExecuted()); + assertEquals(false, provider.isConsoleEnabled()); + } + + { + // provider configured with non-null parameters + MockBuiltinSpecsDetectorExecutedFlag provider = new MockBuiltinSpecsDetectorExecutedFlag(); + List languages = new ArrayList(); + languages.add(LANGUAGE_ID); + Map properties = new HashMap(); + properties.put(ATTR_PARAMETER, CUSTOM_PARAMETER); + List entries = new ArrayList(); + ICLanguageSettingEntry entry = new CMacroEntry("MACRO", "VALUE", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY); + entries.add(entry); + + provider.configureProvider(PROVIDER_ID, PROVIDER_NAME, languages, entries, properties); + assertEquals(PROVIDER_ID, provider.getId()); + assertEquals(PROVIDER_NAME, provider.getName()); + assertEquals(languages, provider.getLanguageScope()); + assertEquals(entries, provider.getSettingEntries(null, null, null)); + assertEquals(CUSTOM_PARAMETER, provider.getCommand()); + assertEquals(false, provider.isConsoleEnabled()); + assertEquals(false, provider.isExecuted()); + + // setters + provider.setCommand(CUSTOM_PARAMETER_2); + assertEquals(CUSTOM_PARAMETER_2, provider.getCommand()); + provider.setConsoleEnabled(true); + assertEquals(true, provider.isConsoleEnabled()); + + provider.execute(); + assertEquals(true, provider.isExecuted()); + } + } + + /** + * Test clone() and equals(). + */ + public void testAbstractBuiltinSpecsDetector_CloneAndEquals() throws Exception { + // define mock detector + class MockDetectorCloneable extends MockBuiltinSpecsDetectorExecutedFlag implements Cloneable { + @Override + public MockDetectorCloneable clone() throws CloneNotSupportedException { + return (MockDetectorCloneable) super.clone(); + } + @Override + public MockDetectorCloneable cloneShallow() throws CloneNotSupportedException { + return (MockDetectorCloneable) super.cloneShallow(); + } + } + + // create instance to compare to + MockDetectorCloneable provider = new MockDetectorCloneable(); + + List languages = new ArrayList(); + languages.add(LANGUAGE_ID); + List entries = new ArrayList(); + ICLanguageSettingEntry entry = new CMacroEntry("MACRO", "VALUE", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY); + entries.add(entry); + + // check clone after initialization + MockDetectorCloneable clone0 = provider.clone(); + assertTrue(provider.equals(clone0)); + + // configure provider + Map properties = new HashMap(); + properties.put(ATTR_PARAMETER, CUSTOM_PARAMETER); + provider.configureProvider(PROVIDER_ID, PROVIDER_NAME, languages, entries, properties); + assertEquals(false, provider.isConsoleEnabled()); + provider.setConsoleEnabled(true); + provider.execute(); + assertEquals(true, provider.isExecuted()); + assertFalse(provider.equals(clone0)); + + // check another clone after configuring + { + MockDetectorCloneable clone = provider.clone(); + assertTrue(provider.equals(clone)); + } + + // check custom parameter + { + MockDetectorCloneable clone = provider.clone(); + clone.setCommand("changed"); + assertFalse(provider.equals(clone)); + } + + // check language scope + { + MockDetectorCloneable clone = provider.clone(); + clone.setLanguageScope(null); + assertFalse(provider.equals(clone)); + } + + // check console flag + { + MockDetectorCloneable clone = provider.clone(); + boolean isConsoleEnabled = clone.isConsoleEnabled(); + clone.setConsoleEnabled( ! isConsoleEnabled ); + assertFalse(provider.equals(clone)); + } + + // check isExecuted flag + { + MockDetectorCloneable clone = provider.clone(); + assertEquals(true, clone.isExecuted()); + clone.clear(); + assertEquals(false, clone.isExecuted()); + assertFalse(provider.equals(clone)); + } + + // check entries + { + MockDetectorCloneable clone = provider.clone(); + clone.setSettingEntries(null, null, null, null); + assertFalse(provider.equals(clone)); + } + + // check cloneShallow() + { + MockDetectorCloneable provider2 = provider.clone(); + MockDetectorCloneable clone = provider2.cloneShallow(); + assertEquals(false, clone.isExecuted()); + assertFalse(provider2.equals(clone)); + + provider2.setSettingEntries(null, null, null, null); + assertFalse(provider2.equals(clone)); + + clone.execute(); + assertTrue(provider2.equals(clone)); + } + } + + /** + * Test basic serialization functionality. + */ + public void testAbstractBuiltinSpecsDetector_SerializeDOM() throws Exception { + { + // create empty XML + Document doc = XmlUtil.newDocument(); + Element rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + + // initialize provider + MockBuiltinSpecsDetectorExecutedFlag provider = new MockBuiltinSpecsDetectorExecutedFlag(); + assertEquals(false, provider.isExecuted()); + // load the XML to new provider + provider.load(rootElement); + assertEquals(false, provider.isConsoleEnabled()); + assertEquals(false, provider.isExecuted()); + } + + Element elementProvider; + { + // define mock detector + MockBuiltinSpecsDetectorExecutedFlag provider = new MockBuiltinSpecsDetectorExecutedFlag(); + assertEquals(false, provider.isConsoleEnabled()); + assertEquals(false, provider.isExecuted()); + + // redefine the settings + provider.setConsoleEnabled(true); + assertEquals(true, provider.isConsoleEnabled()); + + // serialize in XML + Document doc = XmlUtil.newDocument(); + Element rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + elementProvider = provider.serialize(rootElement); + String xmlString = XmlUtil.toString(doc); + + assertTrue(xmlString.contains(ATTR_CONSOLE)); + } + { + // create another instance of the provider + MockBuiltinSpecsDetectorExecutedFlag provider = new MockBuiltinSpecsDetectorExecutedFlag(); + assertEquals(false, provider.isConsoleEnabled()); + assertEquals(false, provider.isExecuted()); + + // load element + provider.load(elementProvider); + assertEquals(true, provider.isConsoleEnabled()); + assertEquals(false, provider.isExecuted()); + } + } + + /** + * Test serialization of entries and "isExecuted" flag handling. + */ + public void testAbstractBuiltinSpecsDetector_SerializeEntriesDOM() throws Exception { + Element rootElement; + { + // create provider + MockBuiltinSpecsDetectorExecutedFlag provider = new MockBuiltinSpecsDetectorExecutedFlag(); + List entries = new ArrayList(); + entries.add(new CIncludePathEntry("path0", 1)); + provider.setSettingEntries(null, null, null, entries); + // serialize entries + Document doc = XmlUtil.newDocument(); + rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + provider.serializeEntries(rootElement); + // check XML + String xmlString = XmlUtil.toString(doc); + assertTrue(xmlString.contains("path0")); + } + + { + // create new provider + MockBuiltinSpecsDetectorExecutedFlag provider = new MockBuiltinSpecsDetectorExecutedFlag(); + assertEquals(true, provider.isEmpty()); + assertEquals(false, provider.isExecuted()); + + // load the XML to the new provider + provider.load(rootElement); + List entries = provider.getSettingEntries(null, null, null); + assertNotNull(entries); + assertTrue(entries.size() > 0); + assertEquals(new CIncludePathEntry("path0", 1), entries.get(0)); + assertEquals(false, provider.isEmpty()); + assertEquals(true, provider.isExecuted()); + + // clear the new provider + provider.clear(); + assertEquals(true, provider.isEmpty()); + assertEquals(false, provider.isExecuted()); + } + + { + // create new provider + MockBuiltinSpecsDetectorExecutedFlag provider = new MockBuiltinSpecsDetectorExecutedFlag(); + assertEquals(true, provider.isEmpty()); + assertEquals(false, provider.isExecuted()); + + // execute provider + provider.execute(); + List entries = provider.getSettingEntries(null, null, null); + assertEquals(null, entries); + // executed provider should NOT appear as empty even with no entries set + assertEquals(false, provider.isEmpty()); + assertEquals(true, provider.isExecuted()); + } + } + + /** + * Smoke test exercising passing {@code null} to the functions. + */ + public void testAbstractBuiltinSpecsDetector_Nulls() throws Exception { + { + // test AbstractBuiltinSpecsDetector.processLine(...) flow + MockBuiltinSpecsDetector provider = new MockBuiltinSpecsDetector(); + provider.startup(null, null); + provider.startupForLanguage(null); + provider.processLine(null); + provider.shutdownForLanguage(); + provider.shutdown(); + } + { + // test AbstractBuiltinSpecsDetector.processLine(...) flow + MockConsoleBuiltinSpecsDetector provider = new MockConsoleBuiltinSpecsDetector(); + provider.startup(null, null); + provider.runForEachLanguage(null); + provider.shutdown(); + } + } + + /** + * Test basic parsing functionality. + */ + public void testAbstractBuiltinSpecsDetector_RunConfiguration() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + MockConsoleBuiltinSpecsDetector provider = new MockConsoleBuiltinSpecsDetector(); + provider.setLanguageScope(new ArrayList() {{add(LANGUAGE_ID);}}); + + // Run provider + provider.startup(cfgDescription, null); + provider.runForEachLanguage(null); + provider.shutdown(); + + assertFalse(provider.isEmpty()); + + List noentries = provider.getSettingEntries(null, null, null); + assertNull(noentries); + + // Check parsed entries + List entries = provider.getSettingEntries(cfgDescription, null, LANGUAGE_ID); + ICLanguageSettingEntry expected = new CMacroEntry("MACRO", "VALUE", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY); + assertEquals(expected, entries.get(0)); + } + + /** + * Smoke test running as global provider on workspace level. + */ + public void testAbstractBuiltinSpecsDetector_RunGlobal() throws Exception { + // Create provider + MockConsoleBuiltinSpecsDetector provider = new MockConsoleBuiltinSpecsDetector(); + provider.setLanguageScope(new ArrayList() {{add(LANGUAGE_ID);}}); + + // Run provider + provider.startup(null, null); + provider.runForEachLanguage(null); + provider.shutdown(); + + assertFalse(provider.isEmpty()); + + // Check parsed entries + List entries = provider.getSettingEntries(null, null, LANGUAGE_ID); + ICLanguageSettingEntry expected = new CMacroEntry("MACRO", "VALUE", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY); + assertEquals(expected, entries.get(0)); + } + + /** + * Check that entries get grouped by kinds by stock built-in specs detector. + */ + public void testAbstractBuiltinSpecsDetector_GroupSettings() throws Exception { + // define benchmarks + final CIncludePathEntry includePath_1 = new CIncludePathEntry("/include/path_1", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY); + final CIncludePathEntry includePath_2 = new CIncludePathEntry("/include/path_2", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY); + final CIncludeFileEntry includeFile_1 = new CIncludeFileEntry(new Path("/include.file1"), ICSettingEntry.BUILTIN | ICSettingEntry.READONLY); + final CIncludeFileEntry includeFile_2 = new CIncludeFileEntry(new Path("/include.file2"), ICSettingEntry.BUILTIN | ICSettingEntry.READONLY); + final CMacroEntry macro_1 = new CMacroEntry("MACRO_1", "", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY); + final CMacroEntry macro_2 = new CMacroEntry("MACRO_2", "", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY |ICSettingEntry.UNDEFINED); + final CMacroFileEntry macroFile_1 = new CMacroFileEntry(new Path("/macro.file1"), ICSettingEntry.BUILTIN | ICSettingEntry.READONLY); + final CMacroFileEntry macroFile_2 = new CMacroFileEntry(new Path("/macro.file2"), ICSettingEntry.BUILTIN | ICSettingEntry.READONLY); + final CLibraryPathEntry libraryPath_1 = new CLibraryPathEntry(new Path("/lib/path_1"), ICSettingEntry.BUILTIN | ICSettingEntry.READONLY); + final CLibraryPathEntry libraryPath_2 = new CLibraryPathEntry(new Path("/lib/path_2"), ICSettingEntry.BUILTIN | ICSettingEntry.READONLY); + final CLibraryFileEntry libraryFile_1 = new CLibraryFileEntry("lib_1.a", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY); + final CLibraryFileEntry libraryFile_2 = new CLibraryFileEntry("lib_2.a", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY); + + // Define mock detector adding unorganized entries + MockBuiltinSpecsDetector provider = new MockBuiltinSpecsDetector() { + @Override + public boolean processLine(String line) { + detectedSettingEntries.add(libraryFile_1); + detectedSettingEntries.add(libraryPath_1); + detectedSettingEntries.add(macroFile_1); + detectedSettingEntries.add(macro_1); + detectedSettingEntries.add(includeFile_1); + detectedSettingEntries.add(includePath_1); + + detectedSettingEntries.add(includePath_2); + detectedSettingEntries.add(includeFile_2); + detectedSettingEntries.add(macro_2); + detectedSettingEntries.add(macroFile_2); + detectedSettingEntries.add(libraryPath_2); + detectedSettingEntries.add(libraryFile_2); + return true; + } + }; + + // run specs detector + provider.startup(null, null); + provider.startupForLanguage(null); + provider.processLine(""); + provider.shutdownForLanguage(); + provider.shutdown(); + + // compare benchmarks, expected well-sorted + List entries = provider.getSettingEntries(null, null, null); + + int i = 0; + assertEquals(includePath_1, entries.get(i++)); + assertEquals(includePath_2, entries.get(i++)); + assertEquals(includeFile_1, entries.get(i++)); + assertEquals(includeFile_2, entries.get(i++)); + assertEquals(macro_1, entries.get(i++)); + assertEquals(macro_2, entries.get(i++)); + assertEquals(macroFile_1, entries.get(i++)); + assertEquals(macroFile_2, entries.get(i++)); + assertEquals(libraryPath_1, entries.get(i++)); + assertEquals(libraryPath_2, entries.get(i++)); + assertEquals(libraryFile_1, entries.get(i++)); + assertEquals(libraryFile_2, entries.get(i++)); + + assertEquals(12, entries.size()); + } +} diff --git a/build/org.eclipse.cdt.managedbuilder.core.tests/tests/org/eclipse/cdt/managedbuilder/language/settings/providers/tests/GCCBuildCommandParserTest.java b/build/org.eclipse.cdt.managedbuilder.core.tests/tests/org/eclipse/cdt/managedbuilder/language/settings/providers/tests/GCCBuildCommandParserTest.java new file mode 100644 index 00000000000..8f49f26aa28 --- /dev/null +++ b/build/org.eclipse.cdt.managedbuilder.core.tests/tests/org/eclipse/cdt/managedbuilder/language/settings/providers/tests/GCCBuildCommandParserTest.java @@ -0,0 +1,2208 @@ +/******************************************************************************* + * Copyright (c) 2009, 2012 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.managedbuilder.language.settings.providers.tests; + +import java.net.URI; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.cdt.core.ErrorParserManager; +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.LanguageSettingsManager; +import org.eclipse.cdt.core.model.CoreModel; +import org.eclipse.cdt.core.settings.model.CIncludeFileEntry; +import org.eclipse.cdt.core.settings.model.CIncludePathEntry; +import org.eclipse.cdt.core.settings.model.CLibraryFileEntry; +import org.eclipse.cdt.core.settings.model.CLibraryPathEntry; +import org.eclipse.cdt.core.settings.model.CMacroEntry; +import org.eclipse.cdt.core.settings.model.CMacroFileEntry; +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.core.settings.model.ICLanguageSetting; +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.ICSettingEntry; +import org.eclipse.cdt.core.testplugin.ResourceHelper; +import org.eclipse.cdt.core.testplugin.util.BaseTestCase; +import org.eclipse.cdt.internal.core.XmlUtil; +import org.eclipse.cdt.internal.core.settings.model.CProjectDescriptionManager; +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.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.content.IContentType; +import org.eclipse.core.runtime.content.IContentTypeManager; +import org.eclipse.core.runtime.content.IContentTypeSettings; +import org.eclipse.core.runtime.jobs.Job; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * Test cases to test build command parsers. + */ +public class GCCBuildCommandParserTest extends BaseTestCase { + // ID of the parser taken from the extension point + private static final String GCC_BUILD_COMMAND_PARSER_EXT = "org.eclipse.cdt.managedbuilder.core.GCCBuildCommandParser"; //$NON-NLS-1$ + + private static final String PROVIDER_ID = "provider.id"; + private static final String PROVIDER_NAME = "provider name"; + private static final String LANGUAGE_ID = "language.test.id"; + private static final String CUSTOM_PARAMETER = "customParameter"; + private static final String CUSTOM_PARAMETER_2 = "customParameter2"; + private static final String ELEM_TEST = "test"; + private static final String LANG_CPP = GPPLanguage.ID; + + // those attributes must match that in AbstractBuildCommandParser + private static final String ATTR_PARAMETER = "parameter"; //$NON-NLS-1$ + private static final String ATTR_KEEP_RELATIVE_PATHS = "keep-relative-paths"; //$NON-NLS-1$ + + /** + * Mock build command parser. + */ + private class MockBuildCommandParser extends AbstractBuildCommandParser implements Cloneable { + public MockBuildCommandParser() { + setId("GCCBuildCommandParserTest.MockBuildCommandParser"); + } + @Override + protected AbstractOptionParser[] getOptionParsers() { + return new AbstractOptionParser[] {}; + } + @Override + public MockBuildCommandParser cloneShallow() throws CloneNotSupportedException { + return (MockBuildCommandParser) super.cloneShallow(); + } + @Override + public MockBuildCommandParser clone() throws CloneNotSupportedException { + return (MockBuildCommandParser) super.clone(); + } + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + try { + Job.getJobManager().join(AbstractBuildCommandParser.JOB_FAMILY_BUILD_COMMAND_PARSER, null); + } catch (Exception e) { + // ignore + } + super.tearDown(); + } + + /** + * Helper method to fetch configuration descriptions. + */ + private ICConfigurationDescription[] getConfigurationDescriptions(IProject project) { + CoreModel coreModel = CoreModel.getDefault(); + ICProjectDescriptionManager mngr = coreModel.getProjectDescriptionManager(); + // project description + ICProjectDescription projectDescription = mngr.getProjectDescription(project, false); + assertNotNull(projectDescription); + assertEquals(1, projectDescription.getConfigurations().length); + // configuration description + ICConfigurationDescription[] cfgDescriptions = projectDescription.getConfigurations(); + return cfgDescriptions; + } + + /** + * Sets build working directory for DefaultSettingConfiguration being tested. + */ + private void setBuilderCWD(IProject project, IPath buildCWD) throws CoreException { + CProjectDescriptionManager manager = CProjectDescriptionManager.getInstance(); + { + ICProjectDescription prjDescription = manager.getProjectDescription(project, true); + assertNotNull(prjDescription); + ICConfigurationDescription cfgDescription = prjDescription.getDefaultSettingConfiguration(); + assertNotNull(cfgDescription); + + cfgDescription.getBuildSetting().setBuilderCWD(buildCWD); + manager.setProjectDescription(project, prjDescription); + // doublecheck builderCWD + IPath actualBuildCWD = cfgDescription.getBuildSetting().getBuilderCWD(); + assertEquals(buildCWD, actualBuildCWD); + } + { + // triplecheck builderCWD for different project/configuration descriptions + ICProjectDescription prjDescription = CProjectDescriptionManager.getInstance().getProjectDescription(project, false); + assertNotNull(prjDescription); + ICConfigurationDescription cfgDescription = prjDescription.getDefaultSettingConfiguration(); + assertNotNull(cfgDescription); + + } + } + + /** + * Helper method to set reference project. + */ + private void setReference(IProject project, final IProject projectReferenced) throws CoreException { + { + CoreModel coreModel = CoreModel.getDefault(); + ICProjectDescriptionManager mngr = coreModel.getProjectDescriptionManager(); + // project description + ICProjectDescription projectDescription = mngr.getProjectDescription(project); + assertNotNull(projectDescription); + assertEquals(1, projectDescription.getConfigurations().length); + // configuration description + ICConfigurationDescription[] cfgDescriptions = projectDescription.getConfigurations(); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + final ICConfigurationDescription cfgDescriptionReferenced = getConfigurationDescriptions(projectReferenced)[0]; + cfgDescription.setReferenceInfo(new HashMap() {{ put(projectReferenced.getName(), cfgDescriptionReferenced.getId()); }}); + coreModel.setProjectDescription(project, projectDescription); + } + + { + // doublecheck that it's set as expected + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + Map refs = cfgDescription.getReferenceInfo(); + assertEquals(1, refs.size()); + Set referencedProjectsNames = new LinkedHashSet(refs.keySet()); + assertEquals(projectReferenced.getName(), referencedProjectsNames.toArray()[0]); + } + + } + + + /** + * Test getters and setters. + */ + public void testAbstractBuildCommandParser_GettersSetters() throws Exception { + { + // provider configured with null parameters + MockBuildCommandParser provider = new MockBuildCommandParser(); + provider.configureProvider(PROVIDER_ID, PROVIDER_NAME, null, null, null); + + assertEquals(PROVIDER_ID, provider.getId()); + assertEquals(PROVIDER_NAME, provider.getName()); + assertEquals(null, provider.getLanguageScope()); + assertEquals(null, provider.getSettingEntries(null, null, null)); + assertEquals("", provider.getCompilerPattern()); + assertEquals(AbstractBuildCommandParser.ResourceScope.FILE, provider.getResourceScope()); + } + + { + // provider configured with non-null parameters + MockBuildCommandParser provider = new MockBuildCommandParser(); + List languages = new ArrayList(); + languages.add(LANGUAGE_ID); + Map properties = new HashMap(); + properties.put(ATTR_PARAMETER, CUSTOM_PARAMETER); + List entries = new ArrayList(); + ICLanguageSettingEntry entry = new CMacroEntry("MACRO", "VALUE", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY); + entries.add(entry); + + provider.configureProvider(PROVIDER_ID, PROVIDER_NAME, languages, entries, properties); + assertEquals(PROVIDER_ID, provider.getId()); + assertEquals(PROVIDER_NAME, provider.getName()); + assertEquals(languages, provider.getLanguageScope()); + assertEquals(entries, provider.getSettingEntries(null, null, null)); + assertEquals(CUSTOM_PARAMETER, provider.getCompilerPattern()); + + // setters + provider.setCompilerPattern(CUSTOM_PARAMETER_2); + assertEquals(CUSTOM_PARAMETER_2, provider.getCompilerPattern()); + provider.setResourceScope(AbstractBuildCommandParser.ResourceScope.PROJECT); + assertEquals(AbstractBuildCommandParser.ResourceScope.PROJECT, provider.getResourceScope()); + } + } + + /** + * Test clone() and equals(). + */ + public void testAbstractBuildCommandParser_CloneAndEquals() throws Exception { + // create instance to compare to + MockBuildCommandParser parser = new MockBuildCommandParser(); + assertEquals(true, parser.isResolvingPaths()); + + // check clone after initialization + MockBuildCommandParser clone0 = parser.clone(); + assertTrue(parser.equals(clone0)); + + // configure provider + parser.setResolvingPaths(false); + assertFalse(parser.equals(clone0)); + parser.setResourceScope(AbstractBuildCommandParser.ResourceScope.PROJECT); + assertEquals(AbstractBuildCommandParser.ResourceScope.PROJECT, parser.getResourceScope()); + parser.setResourceScope(AbstractBuildCommandParser.ResourceScope.FOLDER); + assertEquals(AbstractBuildCommandParser.ResourceScope.FOLDER, parser.getResourceScope()); + parser.setResourceScope(AbstractBuildCommandParser.ResourceScope.FILE); + assertEquals(AbstractBuildCommandParser.ResourceScope.FILE, parser.getResourceScope()); + + // check another clone after changing settings + { + parser.setResolvingPaths(false); + assertFalse(parser.equals(clone0)); + parser.setResourceScope(AbstractBuildCommandParser.ResourceScope.PROJECT); + assertEquals(AbstractBuildCommandParser.ResourceScope.PROJECT, parser.getResourceScope()); + MockBuildCommandParser clone = parser.clone(); + assertTrue(parser.equals(clone)); + assertEquals(parser.isResolvingPaths(), clone.isResolvingPaths()); + assertEquals(parser.getResourceScope(), clone.getResourceScope()); + } + + // check 'expand relative paths' flag + { + MockBuildCommandParser clone = parser.clone(); + boolean expandRelativePaths = clone.isResolvingPaths(); + clone.setResolvingPaths( ! expandRelativePaths ); + assertFalse(parser.equals(clone)); + } + + // check resource scope + { + parser.setResourceScope(AbstractBuildCommandParser.ResourceScope.PROJECT); + assertEquals(AbstractBuildCommandParser.ResourceScope.PROJECT, parser.getResourceScope()); + MockBuildCommandParser clone = parser.clone(); + assertEquals(AbstractBuildCommandParser.ResourceScope.PROJECT, clone.getResourceScope()); + clone.setResourceScope(AbstractBuildCommandParser.ResourceScope.FOLDER); + assertFalse(parser.equals(clone)); + } + + // check cloneShallow() + { + MockBuildCommandParser parser2 = parser.clone(); + MockBuildCommandParser clone = parser2.cloneShallow(); + assertTrue(parser2.equals(clone)); + } + + } + + /** + * Test basic serialization functionality. + */ + public void testAbstractBuildCommandParser_SerializeDOM() throws Exception { + { + // create empty XML + Document doc = XmlUtil.newDocument(); + Element rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + + // load it to new provider + MockBuildCommandParser parser = new MockBuildCommandParser(); + parser.load(rootElement); + assertEquals(true, parser.isResolvingPaths()); + } + + Element elementProvider; + { + // define mock parser + MockBuildCommandParser parser = new MockBuildCommandParser(); + assertEquals(true, parser.isResolvingPaths()); + + // redefine the settings + parser.setResolvingPaths(false); + assertEquals(false, parser.isResolvingPaths()); + + // serialize in XML + Document doc = XmlUtil.newDocument(); + Element rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + elementProvider = parser.serialize(rootElement); + String xmlString = XmlUtil.toString(doc); + + assertTrue(xmlString.contains(ATTR_KEEP_RELATIVE_PATHS)); + } + { + // create another instance of the provider + MockBuildCommandParser parser = new MockBuildCommandParser(); + assertEquals(true, parser.isResolvingPaths()); + + // load element + parser.load(elementProvider); + assertEquals(false, parser.isResolvingPaths()); + } + } + + /** + * Smoke test exercising passing {@code null} to the functions. + */ + public void testAbstractBuildCommandParser_Nulls() throws Exception { + MockBuildCommandParser parser = new MockBuildCommandParser(); + parser.startup(null, null); + parser.processLine(null); + parser.shutdown(); + + List entries = parser.getSettingEntries(null, null, null); + assertNull(entries); + } + + /** + * Test basic parsing functionality. + */ + public void testAbstractBuildCommandParser_Basic() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + final IFile file=ResourceHelper.createFile(project, "file.cpp"); + + // create test class + AbstractBuildCommandParser parser = new MockBuildCommandParser() { + @Override + public boolean processLine(String line) { + // pretending that we parsed the line + currentResource = file; + List entries = new ArrayList(); + ICLanguageSettingEntry entry = new CMacroEntry("MACRO", "VALUE", ICSettingEntry.BUILTIN); + entries.add(entry); + setSettingEntries(entries); + return true; + } + }; + // parse line + parser.startup(cfgDescription, null); + parser.processLine("gcc -DMACRO=VALUE file.cpp"); + parser.shutdown(); + + // sanity check that it does not return same values for all inputs + List noentries = parser.getSettingEntries(null, null, null); + assertNull(noentries); + + // check populated entries + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + CMacroEntry expected = new CMacroEntry("MACRO", "VALUE", ICSettingEntry.BUILTIN); + assertEquals(expected, entries.get(0)); + } + + /** + * Test parsing of one typical entry. + */ + public void testOneEntry() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + IFile file=ResourceHelper.createFile(project, "file.cpp"); + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + + // parse line + parser.startup(cfgDescription, null); + parser.processLine("gcc -I/path0 file.cpp"); + parser.shutdown(); + + // check populated entries + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + { + IPath path = new Path("/path0").setDevice(project.getLocation().getDevice()); + CIncludePathEntry expected = new CIncludePathEntry(path, 0); + 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); + } + } + + /** + * Test possible variations of compiler command. + */ + public void testGccFlavors() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + IFile file1=ResourceHelper.createFile(project, "file1.cpp"); + IFile file2=ResourceHelper.createFile(project, "file2.cpp"); + IFile file3=ResourceHelper.createFile(project, "file3.cpp"); + IFile file4=ResourceHelper.createFile(project, "file4.cpp"); + IFile file5=ResourceHelper.createFile(project, "file5.cpp"); + IFile file6=ResourceHelper.createFile(project, "file6.cpp"); + IFile file7=ResourceHelper.createFile(project, "file7.cpp"); + IFile file8=ResourceHelper.createFile(project, "file8.cpp"); + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file1.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + + // parse line + parser.startup(cfgDescription, null); + parser.processLine("gcc -I/path0 file1.cpp"); + parser.processLine("gcc-4.2 -I/path0 file2.cpp"); + parser.processLine("g++ -I/path0 file3.cpp"); + parser.processLine("c++ -I/path0 file4.cpp"); + parser.processLine("\"gcc\" -I/path0 file5.cpp"); + parser.processLine("/absolute/path/gcc -I/path0 file6.cpp"); + parser.processLine(" \"/absolute/path/gcc\" -I/path0 file7.cpp"); + parser.processLine("../relative/path/gcc -I/path0 file8.cpp"); + parser.shutdown(); + + // check populated entries + IPath path0 = new Path("/path0").setDevice(project.getLocation().getDevice()); + { + List entries = parser.getSettingEntries(cfgDescription, file1, languageId); + assertEquals(new CIncludePathEntry(path0, 0), entries.get(0)); + } + { + List entries = parser.getSettingEntries(cfgDescription, file2, languageId); + assertEquals(new CIncludePathEntry(path0, 0), entries.get(0)); + } + { + List entries = parser.getSettingEntries(cfgDescription, file3, languageId); + assertEquals(new CIncludePathEntry(path0, 0), entries.get(0)); + } + { + List entries = parser.getSettingEntries(cfgDescription, file4, languageId); + assertEquals(new CIncludePathEntry(path0, 0), entries.get(0)); + } + { + List entries = parser.getSettingEntries(cfgDescription, file5, languageId); + assertEquals(new CIncludePathEntry(path0, 0), entries.get(0)); + } + { + List entries = parser.getSettingEntries(cfgDescription, file6, languageId); + assertEquals(new CIncludePathEntry(path0, 0), entries.get(0)); + } + { + List entries = parser.getSettingEntries(cfgDescription, file7, languageId); + assertEquals(new CIncludePathEntry(path0, 0), entries.get(0)); + } + { + List entries = parser.getSettingEntries(cfgDescription, file8, languageId); + assertEquals(new CIncludePathEntry(path0, 0), entries.get(0)); + } + } + + /** + * Parse variations of -I options. + */ + public void testCIncludePathEntry() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + IFile file=ResourceHelper.createFile(project, "file.cpp"); + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + + // parse line + parser.startup(cfgDescription, null); + parser.processLine("gcc" + // regular + + " -I/path0 " + // space after -I + + " -I /path1 " + // unknown option, should be ignored + + " -? " + // double-quoted path with spaces + + " -I\"/path with spaces\"" + // single-quoted path with spaces + + " -I'/path with spaces2'" + // second single-quoted and space after -I + + " -I '/path with spaces3'" + + " file.cpp"); + parser.shutdown(); + + // check populated entries + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + { + IPath path = new Path("/path0").setDevice(project.getLocation().getDevice()); + CIncludePathEntry expected = new CIncludePathEntry(path, 0); + 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); + } + { + IPath path = new Path("/path1").setDevice(project.getLocation().getDevice()); + CIncludePathEntry expected = new CIncludePathEntry(path, 0); + CIncludePathEntry entry = (CIncludePathEntry)entries.get(1); + assertEquals(expected, entry); + } + { + IPath path = new Path("/path with spaces").setDevice(project.getLocation().getDevice()); + CIncludePathEntry expected = new CIncludePathEntry(path, 0); + CIncludePathEntry entry = (CIncludePathEntry)entries.get(2); + assertEquals(expected, entry); + } + { + IPath path = new Path("/path with spaces2").setDevice(project.getLocation().getDevice()); + CIncludePathEntry expected = new CIncludePathEntry(path, 0); + CIncludePathEntry entry = (CIncludePathEntry)entries.get(3); + assertEquals(expected, entry); + } + { + IPath path = new Path("/path with spaces3").setDevice(project.getLocation().getDevice()); + CIncludePathEntry expected = new CIncludePathEntry(path, 0); + CIncludePathEntry entry = (CIncludePathEntry)entries.get(4); + assertEquals(expected, entry); + } + } + + /** + * Parse variations of -D options. + */ + public void testCMacroEntry() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + IFile file=ResourceHelper.createFile(project, "file.cpp"); + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + + // parse line + parser.startup(cfgDescription, null); + parser.processLine("gcc " + + " -DMACRO0" + + " -DMACRO1=value" + + " -DMACRO2=\"value with spaces\"" + + " -DMACRO3='value with spaces'" + + " -DMACRO4='\"quoted value\"'" + + " -D'MACRO5=\"quoted value\"'" + + " -DMACRO6=\\\"escape-quoted value\\\"" + + " -DMACRO7=\"'single-quoted value'\"" + + " file.cpp"); + parser.shutdown(); + + // check populated entries + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + { + CMacroEntry expected = new CMacroEntry("MACRO0", "", 0); + CMacroEntry entry = (CMacroEntry)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); + } + { + CMacroEntry expected = new CMacroEntry("MACRO1", "value", 0); + CMacroEntry entry = (CMacroEntry)entries.get(1); + assertEquals(expected, entry); + } + { + CMacroEntry expected = new CMacroEntry("MACRO2", "value with spaces", 0); + CMacroEntry entry = (CMacroEntry)entries.get(2); + assertEquals(expected, entry); + } + { + CMacroEntry expected = new CMacroEntry("MACRO3", "value with spaces", 0); + CMacroEntry entry = (CMacroEntry)entries.get(3); + assertEquals(expected, entry); + } + { + CMacroEntry expected = new CMacroEntry("MACRO4", "\"quoted value\"", 0); + CMacroEntry entry = (CMacroEntry)entries.get(4); + assertEquals(expected, entry); + } + { + CMacroEntry expected = new CMacroEntry("MACRO5", "\"quoted value\"", 0); + CMacroEntry entry = (CMacroEntry)entries.get(5); + assertEquals(expected, entry); + } + { + CMacroEntry expected = new CMacroEntry("MACRO6", "\"escape-quoted value\"", 0); + CMacroEntry entry = (CMacroEntry)entries.get(6); + assertEquals(expected, entry); + } + { + CMacroEntry expected = new CMacroEntry("MACRO7", "'single-quoted value'", 0); + CMacroEntry entry = (CMacroEntry)entries.get(7); + assertEquals(expected, entry); + } + } + + /** + * Parse -U option. + */ + public void testCMacroEntry_undef() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + IFile file=ResourceHelper.createFile(project, "file.cpp"); + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + + // parse line + parser.startup(cfgDescription, null); + parser.processLine("gcc " + + " -UMACRO" + + " file.cpp"); + parser.shutdown(); + + // check populated entries + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + { + assertEquals(new CMacroEntry("MACRO", null, ICSettingEntry.UNDEFINED), entries.get(0)); + } + } + + /** + * Parse variations of -include options. + */ + public void testCIncludeFileEntry() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + IFile file=ResourceHelper.createFile(project, "file.cpp"); + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + + // parse line + parser.startup(cfgDescription, null); + parser.processLine("gcc " + + " -include /include.file1" + + " -include '/include.file with spaces'" + + " -include ../../include.file2" + + " -include include.file3" + + " -include ../../include-file-with-dashes" + + " file.cpp"); + parser.shutdown(); + + // check populated entries + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + { + IPath incFile = new Path("/include.file1").setDevice(project.getLocation().getDevice()); + CIncludeFileEntry expected = new CIncludeFileEntry(incFile, 0); + CIncludeFileEntry entry = (CIncludeFileEntry)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); + } + + { + IPath incFile = new Path("/include.file with spaces").setDevice(project.getLocation().getDevice()); + assertEquals(new CIncludeFileEntry(incFile, 0), entries.get(1)); + } + { + assertEquals(new CIncludeFileEntry(project.getLocation().removeLastSegments(2).append("include.file2"), 0), entries.get(2)); + assertEquals(new CIncludeFileEntry(project.getFullPath().append("include.file3"), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(3)); + assertEquals(new CIncludeFileEntry(project.getLocation().removeLastSegments(2).append("include-file-with-dashes"), 0), entries.get(4)); + } + } + + /** + * Parse variations of -macros options. + */ + public void testCMacroFileEntry() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + IFile file=ResourceHelper.createFile(project, "file.cpp"); + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + + // parse line + parser.startup(cfgDescription, null); + parser.processLine("gcc -macros macro.file file.cpp"); + parser.processLine("gcc " + + " -macros /macro.file" + + " -macros '/macro.file with spaces'" + + " file.cpp"); + parser.shutdown(); + + // check populated entries + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + { + IPath path = new Path("/macro.file").setDevice(project.getLocation().getDevice()); + CMacroFileEntry expected = new CMacroFileEntry(path, 0); + CMacroFileEntry entry = (CMacroFileEntry)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); + } + { + IPath path = new Path("/macro.file with spaces").setDevice(project.getLocation().getDevice()); + CMacroFileEntry expected = new CMacroFileEntry(path, 0); + CMacroFileEntry entry = (CMacroFileEntry)entries.get(1); + assertEquals(expected, entry); + } + } + + /** + * Parse variations of -L options. + */ + public void testCLibraryPathEntry() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + IFile file=ResourceHelper.createFile(project, "file.cpp"); + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + + // parse line + parser.startup(cfgDescription, null); + parser.processLine("gcc " + + " -L/path0" + + " -L'/path with spaces'" + + " file.cpp"); + parser.shutdown(); + + // check populated entries + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + { + IPath path = new Path("/path0").setDevice(project.getLocation().getDevice()); + CLibraryPathEntry expected = new CLibraryPathEntry(path, 0); + CLibraryPathEntry entry = (CLibraryPathEntry)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); + } + { + IPath path = new Path("/path with spaces").setDevice(project.getLocation().getDevice()); + CLibraryPathEntry expected = new CLibraryPathEntry(path, 0); + CLibraryPathEntry entry = (CLibraryPathEntry)entries.get(1); + assertEquals(expected, entry); + } + } + + /** + * Parse variations of -l options. + */ + public void testCLibraryFileEntry() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + IFile file=ResourceHelper.createFile(project, "file.cpp"); + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + + // parse line + parser.startup(cfgDescription, null); + parser.processLine("gcc -ldomain file.cpp"); + parser.shutdown(); + + // check populated entries + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + CLibraryFileEntry expected = new CLibraryFileEntry("libdomain.a", 0); + CLibraryFileEntry entry = (CLibraryFileEntry) 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); + } + + /** + * Parse mixed options in the same command. + */ + public void testMixedSettingEntries() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + IFile file=ResourceHelper.createFile(project, "file.cpp"); + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + + // parse line + parser.startup(cfgDescription, null); + parser.processLine("gcc" + + " -I/path0 " + + " -DMACRO1=value" + + " -v" + + " -ldomain" + + " -E" + + " -I /path1 " + + " -DMACRO2=\"value with spaces\"" + + " -I\"/path with spaces\"" + + " -o file.exe" + + " -L/usr/lib" + + " file.cpp" + + " -mtune=pentiumpro" + ); + parser.shutdown(); + + // check populated entries + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + String device = project.getLocation().getDevice(); +// + " -I/path0 " + assertEquals(new CIncludePathEntry(new Path("/path0").setDevice(device), 0), entries.get(0)); +// + " -I /path1 " + assertEquals(new CIncludePathEntry(new Path("/path1").setDevice(device), 0), entries.get(1)); +// + " -I\"/path with spaces\"" + assertEquals(new CIncludePathEntry(new Path("/path with spaces").setDevice(device), 0), entries.get(2)); +// + " -DMACRO1=value" + assertEquals(new CMacroEntry("MACRO1", "value", 0), entries.get(3)); +// + " -DMACRO2=\"value with spaces\"" + assertEquals(new CMacroEntry("MACRO2", "value with spaces", 0), entries.get(4)); +// + " -L/usr/lib" + assertEquals(new CLibraryPathEntry(new Path("/usr/lib").setDevice(device), 0), entries.get(5)); +// + " -ldomain" + assertEquals(new CLibraryFileEntry("libdomain.a", 0), entries.get(6)); + assertEquals(7, entries.size()); + } + + /** + * Parse command where resource is missing. + */ + public void testFileMissing() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + + // parse line + parser.startup(cfgDescription, null); + parser.processLine("gcc -I/path0 missing.cpp"); + parser.shutdown(); + + // check entries + assertTrue(parser.isEmpty()); + } + + /** + * Parsing of absolute path to the file being compiled. + */ + public void testFileAbsolutePath() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + IFile file=ResourceHelper.createFile(project, "file.cpp"); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + + // parse line + parser.startup(cfgDescription, null); + parser.processLine("gcc " + + "-I/path0 " + + "-I. " + + file.getLocation().toOSString()); + parser.shutdown(); + + // check entries + IPath path0 = new Path("/path0").setDevice(project.getLocation().getDevice()); + { + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + assertEquals(new CIncludePathEntry(path0, 0), entries.get(0)); + assertEquals(new CIncludePathEntry(project.getFullPath(), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(1)); + } + } + + /** + * Parsing of absolute path to the file being compiled where provider is global. + */ + public void testFileAbsolutePath_NoProject() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + IFile file=ResourceHelper.createFile(project, "file.cpp"); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + + // parse line + parser.startup(null, null); + parser.processLine("gcc " + + "-I/path0 " + + "-I. " + + file.getLocation().toOSString()); + parser.shutdown(); + + // check entries + IPath path0 = new Path("/path0").setDevice(project.getLocation().getDevice()); + { + List entries = parser.getSettingEntries(null, file, languageId); + assertEquals(new CIncludePathEntry(path0, 0), entries.get(0)); + assertEquals(new CIncludePathEntry(file.getParent().getFullPath(), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(1)); + } + } + + /** + * Parsing where the name of the file being compiled contains spaces. + */ + public void testFileWithSpaces() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + IFile file1=ResourceHelper.createFile(project, "file with spaces 1.cpp"); + IFile file2=ResourceHelper.createFile(project, "file with spaces 2.cpp"); + IFile file3=ResourceHelper.createFile(project, "file with spaces 3.cpp"); + IFile file4=ResourceHelper.createFile(project, "file with spaces 4.cpp"); + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file1.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + + // parse line + parser.startup(cfgDescription, null); + parser.processLine("gcc -I/path0 'file with spaces 1.cpp'"); + parser.processLine("gcc -I/path0 \"file with spaces 2.cpp\""); + parser.processLine("gcc -I/path0 'file with spaces 3.cpp'\n"); + parser.processLine("gcc -I/path0 'file with spaces 4.cpp'\r\n"); + parser.shutdown(); + + // check populated entries + IPath path0 = new Path("/path0").setDevice(project.getLocation().getDevice()); + { + // in single quotes + List entries = parser.getSettingEntries(cfgDescription, file1, languageId); + CIncludePathEntry expected = new CIncludePathEntry(path0, 0); + assertEquals(expected, entries.get(0)); + } + { + // in double quotes + List entries = parser.getSettingEntries(cfgDescription, file2, languageId); + CIncludePathEntry expected = new CIncludePathEntry(path0, 0); + assertEquals(expected, entries.get(0)); + } + { + // Unix EOL + List entries = parser.getSettingEntries(cfgDescription, file3, languageId); + CIncludePathEntry expected = new CIncludePathEntry(path0, 0); + assertEquals(expected, entries.get(0)); + } + { + // Windows EOL + List entries = parser.getSettingEntries(cfgDescription, file4, languageId); + CIncludePathEntry expected = new CIncludePathEntry(path0, 0); + assertEquals(expected, entries.get(0)); + } + } + + /** + * Resolve disagreement between working directory and path to the file being compiled. + */ + public void testFileIgnoreWrongBuildDir() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + IFolder folder1=ResourceHelper.createFolder(project, "Folder1"); + IFile file=ResourceHelper.createFile(project, "Folder1/Folder2/file.cpp"); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + ErrorParserManager epm = new ErrorParserManager(project, null); + // Shift build directory, that could happen if Make Target from folder1 was run + // Build directory points to /project/Folder1/ + IFolder buildDir = folder1; + epm.pushDirectoryURI(buildDir.getLocationURI()); + + // parse line + parser.startup(cfgDescription, epm); + parser.processLine("gcc " + + "-I/path0 " + + "-I. " + // This implies the build working directory is /project/ + + "Folder1/Folder2/file.cpp"); + parser.shutdown(); + + // check entries + IPath path0 = new Path("/path0").setDevice(project.getLocation().getDevice()); + { + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + assertEquals(new CIncludePathEntry(path0, 0), entries.get(0)); + // Information from build output should take precedence over build dir + assertEquals(new CIncludePathEntry(project.getFullPath(), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(1)); + } + } + + /** + * Test various ends of lines for the lines being parsed. + */ + public void testEndOfLine() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + IFile file0=ResourceHelper.createFile(project, "file0.cpp"); + IFile file1=ResourceHelper.createFile(project, "file1.cpp"); + IFile file2=ResourceHelper.createFile(project, "file2.cpp"); + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file0.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + + // parse line + parser.startup(cfgDescription, null); + parser.processLine("gcc -I/path0 file0.cpp"); + parser.processLine("gcc -I/path0 file1.cpp\n"); + parser.processLine("gcc -I/path0 file2.cpp\r\n"); + parser.shutdown(); + + // check populated entries + IPath path0 = new Path("/path0").setDevice(project.getLocation().getDevice()); + { + List entries = parser.getSettingEntries(cfgDescription, file0, languageId); + CIncludePathEntry expected = new CIncludePathEntry(path0, 0); + CIncludePathEntry entry = (CIncludePathEntry)entries.get(0); + assertEquals(expected, entry); + } + { + List entries = parser.getSettingEntries(cfgDescription, file1, languageId); + CIncludePathEntry expected = new CIncludePathEntry(path0, 0); + CIncludePathEntry entry = (CIncludePathEntry)entries.get(0); + assertEquals(expected, entry); + } + { + List entries = parser.getSettingEntries(cfgDescription, file2, languageId); + CIncludePathEntry expected = new CIncludePathEntry(path0, 0); + CIncludePathEntry entry = (CIncludePathEntry)entries.get(0); + assertEquals(expected, entry); + } + } + + /** + * Test parsing of paths located on a different drive on Windows. + */ + public void testPathEntry_DriveLetter() throws Exception { + // do not test on non-windows systems where drive letters are not supported + if (! Platform.getOS().equals(Platform.OS_WIN32)) + return; + + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + IFile file=ResourceHelper.createFile(project, "file.cpp"); + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + parser.setResolvingPaths(true); + + // parse line + parser.startup(cfgDescription, null); + parser.processLine("gcc " + + " -IX:\\path" + + " file.cpp"); + parser.shutdown(); + + // check populated entries + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + assertEquals(new CIncludePathEntry(new Path("X:\\path"), 0), entries.get(0)); + } + + /** + * Test various relative paths provided in options with resolving. + */ + public void testPathEntry_ExpandRelativePath() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + IFile file=ResourceHelper.createFile(project, "file.cpp"); + IFolder folder=ResourceHelper.createFolder(project, "Folder"); + IFolder folderComposite=ResourceHelper.createFolder(project, "Folder-Icomposite"); + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + parser.setResolvingPaths(true); + + // parse line + parser.startup(cfgDescription, null); + parser.processLine("gcc " + + " -I." + + " -I.." + + " -IFolder" + + " -IFolder-Icomposite" // to test case when "-I" is a part of folder name + + " file.cpp"); + parser.shutdown(); + + // check populated entries + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + { + // check that relative paths are relative to CWD which is the location of the project + assertEquals(new CIncludePathEntry(project.getFullPath(), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(0)); + assertEquals(new CIncludePathEntry(project.getLocation().removeLastSegments(1), 0), entries.get(1)); + assertEquals(new CIncludePathEntry(folder.getFullPath(), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(2)); + assertEquals(new CIncludePathEntry(folderComposite.getFullPath(), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(3)); + } + } + + /** + * Test various relative paths provided in options without resolving. + */ + public void testPathEntry_DoNotExpandRelativePath() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + IFile file=ResourceHelper.createFile(project, "file.cpp"); + @SuppressWarnings("unused") + IFolder folder=ResourceHelper.createFolder(project, "Folder"); + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser with expandRelativePaths=false + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + parser.setResolvingPaths(false); + + // parse line + parser.startup(cfgDescription, null); + parser.processLine("gcc " + + " -I." + + " -I.." + + " -IFolder" + + " file.cpp"); + parser.shutdown(); + + // check populated entries + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + { + assertEquals(new CIncludePathEntry(".", 0), entries.get(0)); + assertEquals(new CIncludePathEntry("..", 0), entries.get(1)); + assertEquals(new CIncludePathEntry("Folder", 0), entries.get(2)); + } + } + + /** + * Ensure that duplicate paths are ignored. + */ + public void testPathEntry_DuplicatePath() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + IFile file=ResourceHelper.createFile(project, "file.cpp"); + IFolder folder=ResourceHelper.createFolder(project, "Folder"); + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + parser.setResolvingPaths(true); + + // parse line + parser.startup(cfgDescription, null); + parser.processLine("gcc " + + " -IFolder" + + " -IFolder" + + " file.cpp"); + parser.shutdown(); + + // check populated entries + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + { + assertEquals(new CIncludePathEntry(folder.getFullPath(), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(0)); + assertEquals(1, entries.size()); + } + } + + /** + * Test that working directory supplied by ErrorParserManager is considered. + */ + public void testPathEntry_FollowCWD() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + IFolder buildDir=ResourceHelper.createFolder(project, "BuildDir"); + IFolder folder=ResourceHelper.createFolder(project, "BuildDir/Folder"); + IFile file=ResourceHelper.createFile(project, "BuildDir/file.cpp"); + @SuppressWarnings("unused") + IFile fakeFile=ResourceHelper.createFile(project, "file.cpp"); + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + ErrorParserManager epm = new ErrorParserManager(project, null); + // Set different working directory + epm.pushDirectoryURI(buildDir.getLocationURI()); + + // parse line + parser.startup(cfgDescription, epm); + parser.processLine("gcc " + + " -I." + + " -I.." + + " -I../../.." + + " -IFolder" + + " -IMissingFolder" + + " file.cpp"); + parser.shutdown(); + + // check populated entries + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + { + assertEquals(new CIncludePathEntry(buildDir.getFullPath(), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(0)); + assertEquals(new CIncludePathEntry(buildDir.getFullPath().removeLastSegments(1), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(1)); + assertEquals(new CIncludePathEntry(buildDir.getLocation().removeLastSegments(3), 0), entries.get(2)); + assertEquals(new CIncludePathEntry(folder.getFullPath(), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(3)); + assertEquals(new CIncludePathEntry(buildDir.getFullPath().append("MissingFolder"), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(4)); + } + } + + /** + * Determine working directory basing on file being compiled. + */ + public void testPathEntry_GuessCWD() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + IFolder folder=ResourceHelper.createFolder(project, "BuildDir/Folder"); + IFile file=ResourceHelper.createFile(project, "BuildDir/file.cpp"); + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + + ErrorParserManager epm = new ErrorParserManager(project, null); + + // parse line + parser.startup(cfgDescription, epm); + parser.processLine("gcc " + + " -IFolder" + + " file.cpp"); + parser.shutdown(); + + // check populated entries + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + assertEquals(new CIncludePathEntry(folder.getFullPath(), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(0)); + } + + /** + * Test case when build command indicates impossible working directory. + */ + public void testPathEntry_NonExistentCWD_Workspace() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + IFolder buildDir=project.getFolder("Missing/Folder"); + IFile file=ResourceHelper.createFile(project, "file.cpp"); + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + + ErrorParserManager epm = new ErrorParserManager(project, null); + // define working directory + epm.pushDirectoryURI(buildDir.getLocationURI()); + + // parse line + parser.startup(cfgDescription, epm); + parser.processLine("gcc " + + " -I." + + " -I.." + + " -IFolder" + // indicates non-existing working directory + + " ../file.cpp"); + parser.shutdown(); + + // check populated entries + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + { + assertEquals(new CIncludePathEntry(buildDir.getFullPath(), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(0)); + assertEquals(new CIncludePathEntry(buildDir.getFullPath().removeLastSegments(1), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(1)); + assertEquals(new CIncludePathEntry(buildDir.getFullPath().append("Folder"), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(2)); + } + } + + /** + * Test case when build command indicates impossible working directory and + * ErrorParserManager indicates non-existing working directory. + */ + public void testPathEntry_NonExistentCWD_Filesystem() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + IFile file=ResourceHelper.createFile(project, "file.cpp"); + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + + ErrorParserManager epm = new ErrorParserManager(project, null); + URI uriBuildDir = new URI("file:/non-existing/path"); + // define non-existing working directory + epm.pushDirectoryURI(uriBuildDir); + + // parse line + parser.startup(cfgDescription, epm); + parser.processLine("gcc " + + " -I." + + " -I.." + + " -IFolder" + // indicates non-existing working directory + + " ../file.cpp"); + parser.shutdown(); + + // check populated entries + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + { + IPath buildPath = new Path(uriBuildDir.getPath()).setDevice(project.getLocation().getDevice()); + assertEquals(new CIncludePathEntry(buildPath, 0), entries.get(0)); + assertEquals(new CIncludePathEntry(buildPath.removeLastSegments(1), 0), entries.get(1)); + assertEquals(new CIncludePathEntry(buildPath.append("Folder"), 0), entries.get(2)); + } + } + + /** + * Simulate mapping of a sub-folder in the project to remote URI. + */ + public void testPathEntry_MappedRemoteFolder() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + IFolder buildDir=ResourceHelper.createFolder(project, "Local/BuildDir"); + IFolder folder=ResourceHelper.createFolder(project, "Local/BuildDir/Folder"); + IFolder folder2=ResourceHelper.createFolder(project, "Local/BuildDir/Folder2"); + IFile file=ResourceHelper.createFile(project, "Local/BuildDir/file.cpp"); + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + + ErrorParserManager epm = new ErrorParserManager(project, null); + // define working directory as URI pointing outside workspace + URI uriBuildDir = new URI("file:/BuildDir"); + epm.pushDirectoryURI(uriBuildDir); + + // parse line + parser.startup(cfgDescription, epm); + parser.processLine("gcc " + + " -I." + + " -I/BuildDir/Folder" + + " -I../BuildDir/Folder2" + + " -I/BuildDir/MissingFolder" + + " -I../BuildDir/MissingFolder2" + + " /BuildDir/file.cpp"); + parser.shutdown(); + + // check populated entries + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + { + assertEquals(new CIncludePathEntry(buildDir.getFullPath(), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(0)); + assertEquals(new CIncludePathEntry(folder.getFullPath(), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(1)); + assertEquals(new CIncludePathEntry(folder2.getFullPath(), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(2)); + assertEquals(new CIncludePathEntry(buildDir.getFullPath().append("MissingFolder"), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(3)); + assertEquals(new CIncludePathEntry(buildDir.getFullPath().append("MissingFolder2"), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(4)); + } + } + + /** + * Test mapping folders heuristics - inside a project. + */ + public void testPathEntry_MappedFolderInProject() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + IFile file=ResourceHelper.createFile(project, "BuildDir/file.cpp"); + IFolder mappedFolder=ResourceHelper.createFolder(project, "Mapped/Folder"); + IFolder folder=ResourceHelper.createFolder(project, "Mapped/Folder/Subfolder"); + @SuppressWarnings("unused") + IFolder ambiguousFolder1=ResourceHelper.createFolder(project, "One/Ambiguous/Folder"); + @SuppressWarnings("unused") + IFolder ambiguousFolder2=ResourceHelper.createFolder(project, "Another/Ambiguous/Folder"); + + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + ErrorParserManager epm = new ErrorParserManager(project, null); + + // parse line + parser.startup(cfgDescription, epm); + parser.processLine("gcc " + + " -I/Folder/Subfolder" + + " -I/Mapped/Folder" + + " -I/Ambiguous/Folder" + + " -I/Missing/Folder" + + " file.cpp"); + parser.shutdown(); + + // check populated entries + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + { + assertEquals(new CIncludePathEntry(folder.getFullPath(), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(0)); + assertEquals(new CIncludePathEntry(mappedFolder.getFullPath(), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(1)); + } + { + IPath path = new Path("/Ambiguous/Folder").setDevice(file.getLocation().getDevice()); + assertEquals(new CIncludePathEntry(path, 0), entries.get(2)); + } + { + IPath path = new Path("/Missing/Folder").setDevice(file.getLocation().getDevice()); + assertEquals(new CIncludePathEntry(path, 0), entries.get(3)); + } + } + + /** + * Test mapping folders heuristics - mapping to another project. + */ + public void testPathEntry_MappedFolderInAnotherProject() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + // create files and folders + IFile file=ResourceHelper.createFile(project, "file.cpp"); + // another project + IProject anotherProject = ResourceHelper.createCDTProjectWithConfig(projectName+"-another"); + IFolder folder=ResourceHelper.createFolder(anotherProject, "Mapped/Folder/Subfolder"); + @SuppressWarnings("unused") + IFolder ambiguousFolder1=ResourceHelper.createFolder(anotherProject, "One/Ambiguous/Folder"); + @SuppressWarnings("unused") + IFolder ambiguousFolder2=ResourceHelper.createFolder(anotherProject, "Another/Ambiguous/Folder"); + + + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + ErrorParserManager epm = new ErrorParserManager(project, null); + + // parse line + parser.startup(cfgDescription, epm); + parser.processLine("gcc " + + " -I/Folder/Subfolder" + + " -I/Ambiguous/Folder" + + " -I/Missing/Folder" + + " file.cpp"); + parser.shutdown(); + + // check populated entries + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + { + assertEquals(new CIncludePathEntry(folder.getFullPath(), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(0)); + } + { + IPath path = new Path("/Ambiguous/Folder").setDevice(file.getLocation().getDevice()); + assertEquals(new CIncludePathEntry(path, 0), entries.get(1)); + } + { + IPath path = new Path("/Missing/Folder").setDevice(file.getLocation().getDevice()); + assertEquals(new CIncludePathEntry(path, 0), entries.get(2)); + } + } + + /** + * Test mapping folders heuristics - mapping to a referenced project. + */ + public void testPathEntry_MappedFolderInReferencedProject() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + + // create main project + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + IFile file=ResourceHelper.createFile(project, "file.cpp"); + + // create another project (non-referenced) + IProject anotherProject = ResourceHelper.createCDTProjectWithConfig(projectName+"-another"); + @SuppressWarnings("unused") + IFolder folderInAnotherProject=ResourceHelper.createFolder(anotherProject, "Mapped/Folder/Subfolder"); + + // create referenced project + IProject referencedProject = ResourceHelper.createCDTProjectWithConfig(projectName+"-referenced"); + IFolder folderInReferencedProject=ResourceHelper.createFolder(referencedProject, "Mapped/Folder/Subfolder"); + @SuppressWarnings("unused") + IFolder ambiguousFolder1=ResourceHelper.createFolder(referencedProject, "One/Ambiguous/Folder"); + @SuppressWarnings("unused") + IFolder ambiguousFolder2=ResourceHelper.createFolder(referencedProject, "Another/Ambiguous/Folder"); + + setReference(project, referencedProject); + + // get cfgDescription and language to work with + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + ErrorParserManager epm = new ErrorParserManager(project, null); + + // parse line + parser.startup(cfgDescription, epm); + parser.processLine("gcc " + + " -I/Folder/Subfolder" + + " -I/Ambiguous/Folder" + + " file.cpp"); + parser.shutdown(); + + // check populated entries + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + { + assertEquals(new CIncludePathEntry(folderInReferencedProject.getFullPath(), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(0)); + + IPath path = new Path("/Ambiguous/Folder").setDevice(file.getLocation().getDevice()); + assertEquals(new CIncludePathEntry(path, 0), entries.get(1)); + } + } + + /** + * Test ".." in symbolic links where the symbolic link is present as absolute path. + */ + public void testPathEntry_NavigateSymbolicLinkUpAbsolute() throws Exception { + // do not test on systems where symbolic links are not supported + if (!ResourceHelper.isSymbolicLinkSupported()) + return; + + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + String languageId = LANG_CPP; + IFile file=ResourceHelper.createFile(project, "file.cpp"); + + // create link on the filesystem + IPath dir1 = ResourceHelper.createTemporaryFolder(); + IPath dir2 = dir1.removeLastSegments(1); + IPath linkPath = dir1.append("linked"); + ResourceHelper.createSymbolicLink(linkPath, dir2); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + ErrorParserManager epm = new ErrorParserManager(project, null); + + // parse line + parser.startup(cfgDescription, epm); + // "../" should navigate along filesystem path, not along the link itself + parser.processLine("gcc -I"+linkPath.toString()+"/.."+" file.cpp"); + parser.shutdown(); + + // check populated entries + { + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + CIncludePathEntry expected = new CIncludePathEntry(dir2.removeLastSegments(1), 0); + assertEquals(expected, entries.get(0)); + } + } + + /** + * Test ".." in symbolic links where the symbolic link is present as relative path. + */ + public void testPathEntry_NavigateSymbolicLinkUpRelative() throws Exception { + // do not test on systems where symbolic links are not supported + if (!ResourceHelper.isSymbolicLinkSupported()) + return; + + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + String languageId = LANG_CPP; + IFile file=ResourceHelper.createFile(project, "file.cpp"); + + // create link + IFolder folder = ResourceHelper.createFolder(project, "folder"); + IFolder subfolder = ResourceHelper.createFolder(project, "folder/subfolder"); + IPath linkPath = project.getLocation().append("linked"); + ResourceHelper.createSymbolicLink(linkPath, subfolder.getLocation()); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + ErrorParserManager epm = new ErrorParserManager(project, null); + + // parse line + parser.startup(cfgDescription, epm); + // "../" should navigate along filesystem path, not along the link itself + parser.processLine("gcc -Ilinked/.."+" file.cpp"); + parser.shutdown(); + + // check populated entries + { + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + CIncludePathEntry expected = new CIncludePathEntry(folder.getFullPath(), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED); + assertEquals(expected, entries.get(0)); + } + } + + /** + * Determine working directory from configuration builder settings. + */ + public void testPathEntry_BuildDirDefinedByConfiguration_RelativePath() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + // Create resources trying to confuse the parser + @SuppressWarnings("unused") + IFile fileInProjectRoot=ResourceHelper.createFile(project, "file.cpp"); + @SuppressWarnings("unused") + IFolder includeDirInProjectRoot=ResourceHelper.createFolder(project, "include"); + // Create resources meant to be found + IFolder buildDir=ResourceHelper.createFolder(project, "BuildDir"); + IFile file=ResourceHelper.createFile(project, "BuildDir/file.cpp"); + IFolder includeDir=ResourceHelper.createFolder(project, "BuildDir/include"); + // Change build dir + setBuilderCWD(project, buildDir.getLocation()); + + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + + // parse line + parser.startup(cfgDescription, null); + parser.processLine("gcc " + + " -I." + + " -Iinclude" + + " file.cpp"); + parser.shutdown(); + + // check populated entries + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + { + assertEquals(new CIncludePathEntry(buildDir.getFullPath(), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(0)); + assertEquals(new CIncludePathEntry(includeDir.getFullPath(), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(1)); + } + } + + /** + * Test where working directory from command line disagrees with configuration builder settings. + */ + public void testPathEntry_BuildDirDefinedByConfiguration_AbsolutePath() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + // Create resources trying to confuse the parser + @SuppressWarnings("unused") + IFile fileInProjectRoot=ResourceHelper.createFile(project, "file.cpp"); + @SuppressWarnings("unused") + IFolder includeDirInProjectRoot=ResourceHelper.createFolder(project, "include"); + // Create resources meant to be found + IFolder buildDir=ResourceHelper.createFolder(project, "BuildDir"); + IFile file=ResourceHelper.createFile(project, "BuildDir/file.cpp"); + IFolder includeDir=ResourceHelper.createFolder(project, "BuildDir/include"); + // Change build dir + setBuilderCWD(project, buildDir.getLocation()); + + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + + // parse line + parser.startup(cfgDescription, null); + parser.processLine("gcc " + + " -I." + + " -Iinclude" + + " " + file.getLocation().toOSString() + ); + parser.shutdown(); + + // check populated entries + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + { + assertEquals(new CIncludePathEntry(buildDir.getFullPath(), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(0)); + assertEquals(new CIncludePathEntry(includeDir.getFullPath(), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(1)); + } + + } + + /** + * Smoke test when non-C files appear in output, should not choke. + */ + public void testContentType_None() throws Exception { + MockBuildCommandParser parser = new MockBuildCommandParser() { + @Override + protected String parseResourceName(String line) { + return "file.wrong-content-type"; + } + }; + parser.startup(null, null); + parser.processLine("gcc file.wrong-content-type"); + parser.shutdown(); + + List entries = parser.getSettingEntries(null, null, null); + assertNull(entries); + } + + /** + * Test that unsupported language is ignored. + */ + public void testContentType_Mismatch() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + ResourceHelper.createFile(project, "file.c"); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + // restrict the parser's language scope to C++ only + parser.setLanguageScope(new ArrayList() {{add(LANG_CPP);}}); + + // parse line + parser.startup(cfgDescription, null); + parser.processLine("gcc -I/path0 file.c"); + parser.shutdown(); + + assertTrue(parser.isEmpty()); + } + + /** + * Test custom file extensions defined in content type. + */ + public void testContentType_FileExtensions() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + String languageId = LANG_CPP; + // add custom extension to C++ content type + IContentTypeManager manager = Platform.getContentTypeManager(); + IContentType contentType = manager.findContentTypeFor("file.cpp"); + contentType.addFileSpec("x++", IContentTypeSettings.FILE_EXTENSION_SPEC); + + IFile file=ResourceHelper.createFile(project, "file.x++"); + IContentType contentTypeX = manager.findContentTypeFor("file.x++"); + assertEquals(contentType, contentTypeX); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + + // parse line + parser.startup(cfgDescription, null); + parser.processLine("gcc -I/path0 file.x++"); + parser.shutdown(); + + // check populated entries + IPath path0 = new Path("/path0").setDevice(project.getLocation().getDevice()); + { + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + CIncludePathEntry expected = new CIncludePathEntry(path0, 0); + assertEquals(expected, entries.get(0)); + } + + // cleanup + contentType.removeFileSpec("x++", IContentTypeSettings.FILE_EXTENSION_SPEC); + } + + /** + * Test filenames appearing in upper-case. + */ + public void testUpperCase() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + String languageId = LANG_CPP; + + IFile file=ResourceHelper.createFile(project, "file.cpp"); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + ErrorParserManager epm = new ErrorParserManager(project, null); + + // parse line + parser.startup(cfgDescription, epm); + parser.processLine("gcc -I/path0 FILE.CPP"); + parser.shutdown(); + + // check populated entries + IPath path0 = new Path("/path0").setDevice(project.getLocation().getDevice()); + { + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + CIncludePathEntry expected = new CIncludePathEntry(path0, 0); + assertEquals(expected, entries.get(0)); + } + } + + /** + * Test sample output of boost builder utility bjam. + */ + public void testBoostBjam() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + IFile file=ResourceHelper.createFile(project, "libs/python/src/numeric.cpp"); + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + + // parse line + parser.startup(cfgDescription, null); + // "g++" -ftemplate-depth-128 -O0 -fno-inline -Wall -g -mthreads -DBOOST_ALL_NO_LIB=1 -DBOOST_PYTHON_SOURCE -DBOOST_PYTHON_STATIC_LIB -I"." -I"/Python25/Include" -c -o "bin.v2/libs/python/build/gcc-mingw-3.4.5/debug/link-static/threading-multi/numeric.o" "libs/python/src/numeric.cpp" + parser.processLine(" \"g++\"" + + " -ftemplate-depth-128 -O0 -fno-inline -Wall -g -mthreads" + + " -DBOOST_ALL_NO_LIB=1" + + " -DBOOST_PYTHON_SOURCE" + + " -DBOOST_PYTHON_STATIC_LIB" + + " -I\".\"" + + " -I\"/Python1025/Include\"" + + " -c -o \"bin.v2/libs/python/build/gcc-mingw-3.4.5/debug/link-static/threading-multi/numeric.o\"" + + " libs/python/src/numeric.cpp"); + parser.shutdown(); + + // check populated entries + { + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + assertEquals(new CIncludePathEntry(project.getFullPath(), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(0)); + assertEquals(new CIncludePathEntry(new Path("/Python1025/Include").setDevice(project.getLocation().getDevice()), 0), entries.get(1)); + assertEquals(new CMacroEntry("BOOST_ALL_NO_LIB", "1", 0), entries.get(2)); + assertEquals(new CMacroEntry("BOOST_PYTHON_SOURCE", "", 0), entries.get(3)); + assertEquals(new CMacroEntry("BOOST_PYTHON_STATIC_LIB", "", 0), entries.get(4)); + assertEquals(5, entries.size()); + } + } + + /** + * Test resource file residing on EFS file-system. + */ + public void testPathEntry_Efs() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + // create folder structure + @SuppressWarnings("unused") + IFolder buildDir=ResourceHelper.createEfsFolder(project, "BuildDir", new URI("mem:/EfsProject/BuildDir")); + IFolder folder=ResourceHelper.createEfsFolder(project, "BuildDir/Folder", new URI("mem:/EfsProject/BuildDir/Folder")); + IFile file=ResourceHelper.createEfsFile(project, "BuildDir/file.cpp", new URI("mem:/EfsProject/BuildDir/file.cpp")); + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + ErrorParserManager epm = new ErrorParserManager(project, null); + + // parse line + parser.startup(cfgDescription, epm); + parser.processLine("gcc " + + " -IFolder" + + " -I/Absolute/Folder" + + " file.cpp"); + parser.shutdown(); + + // check populated entries + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + { + String device = project.getLocation().getDevice(); + assertEquals(new CIncludePathEntry(folder.getFullPath(), ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED), entries.get(0)); + assertEquals(new CIncludePathEntry(new Path("/Absolute/Folder").setDevice(device), 0), entries.get(1)); + } + } + + /** + * Test mapping entries to EFS. + */ + public void testPathEntry_EfsMappedFolder() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + // create folder structure + @SuppressWarnings("unused") + IFolder buildDir=ResourceHelper.createEfsFolder(project, "BuildDir", new URI("mem:/MappedEfsProject/BuildDir")); + @SuppressWarnings("unused") + IFolder folder=ResourceHelper.createEfsFolder(project, "BuildDir/Folder", new URI("mem:/MappedEfsProject/BuildDir/Folder")); + IFile file=ResourceHelper.createEfsFile(project, "BuildDir/file.cpp", new URI("mem:/MappedEfsProject/BuildDir/file.cpp")); + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + ErrorParserManager epm = new ErrorParserManager(project, null); + + // parse line + parser.startup(cfgDescription, epm); + parser.processLine("gcc " + + " -I/BeingMappedFrom/Folder" // mapped to local folder in EFSExtensionProvider extension point + + " file.cpp"); + parser.shutdown(); + + // check populated entries + List entries = parser.getSettingEntries(cfgDescription, file, languageId); + { + String device = project.getLocation().getDevice(); + assertEquals(new CIncludePathEntry(new Path("/LocallyMappedTo/Folder").setDevice(device), 0), entries.get(0)); + } + } + + /** + * Test assigning entries on file level. + */ + public void testEntriesFileLevel() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + IFolder folder=ResourceHelper.createFolder(project, "folder"); + IFile file=ResourceHelper.createFile(project, "folder/file.cpp"); + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + parser.setResourceScope(AbstractBuildCommandParser.ResourceScope.FILE); + + // parse line + parser.startup(cfgDescription, null); + parser.processLine("gcc -I/path0 folder/file.cpp"); + parser.shutdown(); + + // check populated entries + List expected = new ArrayList(); + expected.add(new CIncludePathEntry(new Path("/path0").setDevice(project.getLocation().getDevice()), 0)); + + List entriesFile = parser.getSettingEntries(cfgDescription, file, languageId); + assertEquals(expected, entriesFile); + List entriesFolder = parser.getSettingEntries(cfgDescription, folder, languageId); + assertEquals(null, entriesFolder); + List entriesProject = parser.getSettingEntries(cfgDescription, project, languageId); + assertEquals(null, entriesProject); + } + + /** + * Test assigning entries on folder level. + */ + public void testEntriesFolderLevel() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + IFolder folder=ResourceHelper.createFolder(project, "folder"); + IFile file=ResourceHelper.createFile(project, "folder/file.cpp"); + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + parser.setResourceScope(AbstractBuildCommandParser.ResourceScope.FOLDER); + + // parse line + parser.startup(cfgDescription, null); + parser.processLine("gcc -I/path0 folder/file.cpp"); + parser.shutdown(); + + // check populated entries + List expected = new ArrayList(); + expected.add(new CIncludePathEntry(new Path("/path0").setDevice(project.getLocation().getDevice()), 0)); + + List entriesFile = parser.getSettingEntries(cfgDescription, file, languageId); + assertEquals(null, entriesFile); + List entriesFolder = parser.getSettingEntries(cfgDescription, folder, languageId); + assertEquals(expected, entriesFolder); + List entriesProject = parser.getSettingEntries(cfgDescription, project, languageId); + assertEquals(null, entriesProject); + } + + /** + * Test assigning entries on project level. + */ + public void testEntriesProjectLevel() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + IFolder folder=ResourceHelper.createFolder(project, "folder"); + IFile file=ResourceHelper.createFile(project, "folder/file.cpp"); + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(GCC_BUILD_COMMAND_PARSER_EXT, true); + parser.setResourceScope(AbstractBuildCommandParser.ResourceScope.PROJECT); + + // parse line + parser.startup(cfgDescription, null); + parser.processLine("gcc -I/path0 folder/file.cpp"); + parser.shutdown(); + + // check populated entries + List expected = new ArrayList(); + expected.add(new CIncludePathEntry(new Path("/path0").setDevice(project.getLocation().getDevice()), 0)); + + List entriesFile = parser.getSettingEntries(cfgDescription, file, languageId); + assertEquals(null, entriesFile); + List entriesFolder = parser.getSettingEntries(cfgDescription, folder, languageId); + assertEquals(null, entriesFolder); + List entriesProject = parser.getSettingEntries(cfgDescription, project, languageId); + assertEquals(expected, entriesProject); + } + + /** + * Test assigning entries for global provider. + */ + public void testEntriesProjectLevelGlobalProvider() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + IFile file=ResourceHelper.createFile(project, "file.cpp"); + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(file.getProjectRelativePath(), true); + String languageId = ls.getLanguageId(); + + // create GCCBuildCommandParser + ILanguageSettingsProvider wspProvider = LanguageSettingsManager.getWorkspaceProvider(GCC_BUILD_COMMAND_PARSER_EXT); + GCCBuildCommandParser parser = (GCCBuildCommandParser) LanguageSettingsManager.getRawProvider(wspProvider); + parser.setResourceScope(AbstractBuildCommandParser.ResourceScope.PROJECT); + + // parse line + parser.startup(cfgDescription, null); + parser.processLine("gcc -I/path0 file.cpp"); + parser.shutdown(); + + // check populated entries + List expected = new ArrayList(); + expected.add(new CIncludePathEntry(new Path("/path0").setDevice(project.getLocation().getDevice()), 0)); + assertEquals(expected, parser.getSettingEntries(null, project, languageId)); + } + +} diff --git a/build/org.eclipse.cdt.managedbuilder.core.tests/tests/org/eclipse/cdt/managedbuilder/language/settings/providers/tests/GCCBuiltinSpecsDetectorTest.java b/build/org.eclipse.cdt.managedbuilder.core.tests/tests/org/eclipse/cdt/managedbuilder/language/settings/providers/tests/GCCBuiltinSpecsDetectorTest.java new file mode 100644 index 00000000000..b9dfedc77d3 --- /dev/null +++ b/build/org.eclipse.cdt.managedbuilder.core.tests/tests/org/eclipse/cdt/managedbuilder/language/settings/providers/tests/GCCBuiltinSpecsDetectorTest.java @@ -0,0 +1,366 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - Initial API and implementation + *******************************************************************************/ + package org.eclipse.cdt.managedbuilder.language.settings.providers.tests; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.cdt.core.dom.ast.gnu.c.GCCLanguage; +import org.eclipse.cdt.core.settings.model.CIncludePathEntry; +import org.eclipse.cdt.core.settings.model.CMacroEntry; +import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; +import org.eclipse.cdt.core.settings.model.ICSettingEntry; +import org.eclipse.cdt.core.testplugin.ResourceHelper; +import org.eclipse.cdt.core.testplugin.util.BaseTestCase; +import org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; + +/** + * Test cases to test GCC built-in specs detector. + */ +public class GCCBuiltinSpecsDetectorTest extends BaseTestCase { + private static final String LANGUAGE_ID_C = GCCLanguage.ID; + + /** + * Mock GCCBuiltinSpecsDetector to gain access to protected methods. + */ + class MockGCCBuiltinSpecsDetector extends GCCBuiltinSpecsDetector { + @Override + public void startupForLanguage(String languageId) throws CoreException { + super.startupForLanguage(languageId); + } + @Override + public void shutdownForLanguage() { + super.shutdownForLanguage(); + } + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + /** + * Test expansion of variables in build command. + */ + public void testGCCBuiltinSpecsDetector_ResolvedCommand() throws Exception { + class MockGCCBuiltinSpecsDetectorLocal extends GCCBuiltinSpecsDetector { + @Override + public String resolveCommand(String languageId) throws CoreException { + return super.resolveCommand(languageId); + } + } + { + MockGCCBuiltinSpecsDetectorLocal detector = new MockGCCBuiltinSpecsDetectorLocal(); + detector.setLanguageScope(new ArrayList() {{add(LANGUAGE_ID_C);}}); + detector.setCommand("${COMMAND} -E -P -v -dD ${INPUTS}"); + + String resolvedCommand = detector.resolveCommand(LANGUAGE_ID_C); + assertTrue(resolvedCommand.startsWith("gcc -E -P -v -dD ")); + assertTrue(resolvedCommand.endsWith("spec.c")); + } + { + MockGCCBuiltinSpecsDetectorLocal detector = new MockGCCBuiltinSpecsDetectorLocal(); + detector.setLanguageScope(new ArrayList() {{add(LANGUAGE_ID_C);}}); + detector.setCommand("${COMMAND} -E -P -v -dD file.${EXT}"); + + String resolvedCommand = detector.resolveCommand(LANGUAGE_ID_C); + assertTrue(resolvedCommand.startsWith("gcc -E -P -v -dD ")); + assertTrue(resolvedCommand.endsWith("file.c")); + } + } + + /** + * Test parsing of macro without value. + */ + public void testGCCBuiltinSpecsDetector_Macro_NoValue() throws Exception { + MockGCCBuiltinSpecsDetector detector = new MockGCCBuiltinSpecsDetector(); + + detector.startup(null, null); + detector.startupForLanguage(null); + detector.processLine("#define MACRO"); + detector.shutdownForLanguage(); + detector.shutdown(); + + List entries = detector.getSettingEntries(null, null, null); + assertEquals(new CMacroEntry("MACRO", null, ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), entries.get(0)); + assertEquals(1, entries.size()); + } + + /** + * Test parsing of macro with ordinary value. + */ + public void testGCCBuiltinSpecsDetector_Macro_Simple() throws Exception { + MockGCCBuiltinSpecsDetector detector = new MockGCCBuiltinSpecsDetector(); + + detector.startup(null, null); + detector.startupForLanguage(null); + detector.processLine("#define MACRO VALUE"); + detector.shutdownForLanguage(); + detector.shutdown(); + + List entries = detector.getSettingEntries(null, null, null); + assertEquals(new CMacroEntry("MACRO", "VALUE", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), entries.get(0)); + assertEquals(1, entries.size()); + } + + /** + * Test parsing of macro with value in round brackets. + */ + public void testGCCBuiltinSpecsDetector_Macro_Const() throws Exception { + MockGCCBuiltinSpecsDetector detector = new MockGCCBuiltinSpecsDetector(); + + detector.startup(null, null); + detector.startupForLanguage(null); + detector.processLine("#define MACRO (3)"); + detector.shutdownForLanguage(); + detector.shutdown(); + + List entries = detector.getSettingEntries(null, null, null); + assertEquals(new CMacroEntry("MACRO", "(3)", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), entries.get(0)); + assertEquals(1, entries.size()); + } + + /** + * Test parsing of macro definition with tabs. + */ + public void testGCCBuiltinSpecsDetector_Macro_WhiteSpaces() throws Exception { + MockGCCBuiltinSpecsDetector detector = new MockGCCBuiltinSpecsDetector(); + + detector.startup(null, null); + detector.startupForLanguage(null); + detector.processLine("#define \t MACRO_1 VALUE"); + detector.processLine("#define MACRO_2 \t VALUE"); + detector.processLine("#define MACRO_3 VALUE \t"); + detector.shutdownForLanguage(); + detector.shutdown(); + + List entries = detector.getSettingEntries(null, null, null); + int index = 0; + assertEquals(new CMacroEntry("MACRO_1", "VALUE", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), entries.get(index++)); + assertEquals(new CMacroEntry("MACRO_2", "VALUE", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), entries.get(index++)); + assertEquals(new CMacroEntry("MACRO_3", "VALUE", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), entries.get(index++)); + assertEquals(index, entries.size()); + } + + /** + * Test parsing of macro definition with empty argument list. + */ + public void testGCCBuiltinSpecsDetector_Macro_EmptyArgList() throws Exception { + MockGCCBuiltinSpecsDetector detector = new MockGCCBuiltinSpecsDetector(); + + detector.startup(null, null); + detector.startupForLanguage(null); + detector.processLine("#define MACRO() VALUE"); + detector.shutdownForLanguage(); + detector.shutdown(); + + List entries = detector.getSettingEntries(null, null, null); + assertEquals(new CMacroEntry("MACRO()", "VALUE", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), entries.get(0)); + assertEquals(1, entries.size()); + } + + /** + * Test parsing of macro definition with unused parameter. + */ + public void testGCCBuiltinSpecsDetector_Macro_ParamUnused() throws Exception { + MockGCCBuiltinSpecsDetector detector = new MockGCCBuiltinSpecsDetector(); + + detector.startup(null, null); + detector.startupForLanguage(null); + detector.processLine("#define MACRO(X) VALUE"); + detector.shutdownForLanguage(); + detector.shutdown(); + + List entries = detector.getSettingEntries(null, null, null); + assertEquals(new CMacroEntry("MACRO(X)", "VALUE", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), entries.get(0)); + assertEquals(1, entries.size()); + } + + /** + * Test parsing of macro definition with multiple parameters. + */ + public void testGCCBuiltinSpecsDetector_Macro_ParamSpace() throws Exception { + MockGCCBuiltinSpecsDetector detector = new MockGCCBuiltinSpecsDetector(); + + detector.startup(null, null); + detector.startupForLanguage(null); + detector.processLine("#define MACRO(P1, P2) VALUE(P1, P2)"); + detector.shutdownForLanguage(); + detector.shutdown(); + + List entries = detector.getSettingEntries(null, null, null); + assertEquals(new CMacroEntry("MACRO(P1, P2)", "VALUE(P1, P2)", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), entries.get(0)); + assertEquals(1, entries.size()); + } + + /** + * Test parsing of macro definition with multiple parameters and no value. + */ + public void testGCCBuiltinSpecsDetector_Macro_ArgsNoValue() throws Exception { + MockGCCBuiltinSpecsDetector detector = new MockGCCBuiltinSpecsDetector(); + + detector.startup(null, null); + detector.startupForLanguage(null); + detector.processLine("#define MACRO(P1, P2) "); + detector.shutdownForLanguage(); + detector.shutdown(); + + List entries = detector.getSettingEntries(null, null, null); + assertEquals(new CMacroEntry("MACRO(P1, P2)", null, ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), entries.get(0)); + assertEquals(1, entries.size()); + } + + /** + * Test parsing of macro definition having white spaces in various places. + */ + public void testGCCBuiltinSpecsDetector_Macro_Args_WhiteSpaces() throws Exception { + MockGCCBuiltinSpecsDetector detector = new MockGCCBuiltinSpecsDetector(); + + detector.startup(null, null); + detector.startupForLanguage(null); + detector.processLine("#define \t MACRO_1(P1, P2) VALUE(P1, P2)"); + detector.processLine("#define MACRO_2(P1, P2) \t VALUE(P1, P2)"); + detector.processLine("#define MACRO_3(P1, P2) VALUE(P1, P2) \t"); + detector.shutdownForLanguage(); + detector.shutdown(); + + List entries = detector.getSettingEntries(null, null, null); + int index = 0; + assertEquals(new CMacroEntry("MACRO_1(P1, P2)", "VALUE(P1, P2)", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), entries.get(index++)); + assertEquals(new CMacroEntry("MACRO_2(P1, P2)", "VALUE(P1, P2)", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), entries.get(index++)); + assertEquals(new CMacroEntry("MACRO_3(P1, P2)", "VALUE(P1, P2)", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), entries.get(index++)); + assertEquals(index, entries.size()); + } + + /** + * Test parsing of include directives. + */ + public void testGCCBuiltinSpecsDetector_Includes() throws Exception { + // Create model project and folders to test + String projectName = getName(); + IProject project = ResourceHelper.createCDTProject(projectName); + IPath tmpPath = ResourceHelper.createTemporaryFolder(); + ResourceHelper.createFolder(project, "/misplaced/include1"); + ResourceHelper.createFolder(project, "/local/include"); + ResourceHelper.createFolder(project, "/usr/include"); + ResourceHelper.createFolder(project, "/usr/include2"); + ResourceHelper.createFolder(project, "/misplaced/include2"); + ResourceHelper.createFolder(project, "/System/Library/Frameworks"); + ResourceHelper.createFolder(project, "/Library/Frameworks"); + ResourceHelper.createFolder(project, "/misplaced/include3"); + String loc = tmpPath.toString(); + + MockGCCBuiltinSpecsDetector detector = new MockGCCBuiltinSpecsDetector(); + detector.startup(null, null); + detector.startupForLanguage(null); + + detector.processLine(" "+loc+"/misplaced/include1"); + detector.processLine("#include \"...\" search starts here:"); + detector.processLine(" "+loc+"/local/include"); + detector.processLine("#include <...> search starts here:"); + detector.processLine(" "+loc+"/usr/include"); + detector.processLine(" "+loc+"/usr/include/../include2"); + detector.processLine(" "+loc+"/missing/folder"); + detector.processLine(" "+loc+"/Library/Frameworks (framework directory)"); + detector.processLine("End of search list."); + detector.processLine(" "+loc+"/misplaced/include2"); + detector.processLine("Framework search starts here:"); + detector.processLine(" "+loc+"/System/Library/Frameworks"); + detector.processLine("End of framework search list."); + detector.processLine(" "+loc+"/misplaced/include3"); + detector.shutdownForLanguage(); + detector.shutdown(); + + List entries = detector.getSettingEntries(null, null, null); + int index = 0; + assertEquals(new CIncludePathEntry(loc+"/local/include", ICSettingEntry.LOCAL | ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), entries.get(index++)); + assertEquals(new CIncludePathEntry(loc+"/usr/include", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), entries.get(index++)); + assertEquals(new CIncludePathEntry(loc+"/usr/include2", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), entries.get(index++)); + assertEquals(new CIncludePathEntry(loc+"/missing/folder", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), entries.get(index++)); + assertEquals(new CIncludePathEntry(loc+"/Library/Frameworks", ICSettingEntry.FRAMEWORKS_MAC | ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), entries.get(index++)); + assertEquals(new CIncludePathEntry(loc+"/System/Library/Frameworks", ICSettingEntry.FRAMEWORKS_MAC | ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), entries.get(index++)); + assertEquals(index, entries.size()); + } + + /** + * Test parsing of macro definition of include directives having white spaces. + */ + public void testGCCBuiltinSpecsDetector_Includes_WhiteSpaces() throws Exception { + String loc = ResourceHelper.createTemporaryFolder().toString(); + + MockGCCBuiltinSpecsDetector detector = new MockGCCBuiltinSpecsDetector(); + detector.startup(null, null); + detector.startupForLanguage(null); + + detector.processLine("#include \"...\" search starts here:"); + detector.processLine(" \t "+loc+"/local/include"); + detector.processLine("#include <...> search starts here:"); + detector.processLine(loc+"/usr/include"); + detector.processLine(" "+loc+"/Library/Frameworks \t (framework directory)"); + detector.processLine("End of search list."); + detector.processLine("Framework search starts here:"); + detector.processLine(" "+loc+"/System/Library/Frameworks \t "); + detector.processLine("End of framework search list."); + detector.shutdownForLanguage(); + detector.shutdown(); + + List entries = detector.getSettingEntries(null, null, null); + int index = 0; + assertEquals(new CIncludePathEntry(loc+"/local/include", ICSettingEntry.LOCAL | ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), entries.get(index++)); + assertEquals(new CIncludePathEntry(loc+"/usr/include", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), entries.get(index++)); + assertEquals(new CIncludePathEntry(loc+"/Library/Frameworks", ICSettingEntry.FRAMEWORKS_MAC | ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), entries.get(index++)); + assertEquals(new CIncludePathEntry(loc+"/System/Library/Frameworks", ICSettingEntry.FRAMEWORKS_MAC | ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), entries.get(index++)); + assertEquals(index, entries.size()); + } + + /** + * Test parsing of include directives incorporating symbolic links. + */ + public void testGCCBuiltinSpecsDetector_Includes_SymbolicLinkUp() throws Exception { + // do not test on systems where symbolic links are not supported + if (!ResourceHelper.isSymbolicLinkSupported()) + return; + + // Create model project and folders to test + String projectName = getName(); + @SuppressWarnings("unused") + IProject project = ResourceHelper.createCDTProject(projectName); + // create link on the filesystem + IPath dir1 = ResourceHelper.createTemporaryFolder(); + IPath dir2 = dir1.removeLastSegments(1); + IPath linkPath = dir1.append("linked"); + ResourceHelper.createSymbolicLink(linkPath, dir2); + + MockGCCBuiltinSpecsDetector detector = new MockGCCBuiltinSpecsDetector(); + + detector.startup(null, null); + detector.startupForLanguage(null); + detector.processLine("#include <...> search starts here:"); + detector.processLine(" "+linkPath.toString()+"/.."); + detector.processLine("End of search list."); + detector.shutdownForLanguage(); + detector.shutdown(); + + // check populated entries + List entries = detector.getSettingEntries(null, null, null); + assertEquals(new CIncludePathEntry(dir2.removeLastSegments(1), ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), entries.get(0)); + assertEquals(1, entries.size()); + } + +} 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 363db3a759e..d08600713b7 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/META-INF/MANIFEST.MF +++ b/build/org.eclipse.cdt.managedbuilder.core/META-INF/MANIFEST.MF @@ -15,9 +15,12 @@ Export-Package: org.eclipse.cdt.build.core.scannerconfig, org.eclipse.cdt.managedbuilder.envvar, org.eclipse.cdt.managedbuilder.internal.buildmodel;x-friends:="org.eclipse.cdt.managedbuilder.ui", 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.macros;x-friends:="org.eclipse.cdt.managedbuilder.ui", org.eclipse.cdt.managedbuilder.internal.scannerconfig;x-internal:=true, + org.eclipse.cdt.managedbuilder.language.settings.providers, org.eclipse.cdt.managedbuilder.macros, org.eclipse.cdt.managedbuilder.makegen, org.eclipse.cdt.managedbuilder.makegen.gnu, diff --git a/build/org.eclipse.cdt.managedbuilder.core/plugin.xml b/build/org.eclipse.cdt.managedbuilder.core/plugin.xml index a34109351c3..ae8c2baf886 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/plugin.xml +++ b/build/org.eclipse.cdt.managedbuilder.core/plugin.xml @@ -597,6 +597,40 @@ + + + + + + + + + + + + + + + + parsers = new ArrayList(); - collectScannerInfoConsoleParsers(project, configuration, workingDirectoryURI, markerGenerator, parsers); + if (!isOnlyClean) { + ICConfigurationDescription cfgDescription = ManagedBuildManager.getDescriptionForConfiguration(configuration); + ManagedBuildManager.collectLanguageSettingsConsoleParsers(cfgDescription, epm, parsers); + if (ScannerDiscoveryLegacySupport.isLegacyScannerDiscoveryOn(cfgDescription)) { + collectScannerInfoConsoleParsers(project, configuration, workingDirectoryURI, markerGenerator, parsers); + } + } buildRunnerHelper.setLaunchParameters(launcher, buildCommand, args, workingDirectoryURI, envp); buildRunnerHelper.prepareStreams(epm, parsers, console, new SubProgressMonitor(monitor, TICKS_STREAM_PROGRESS_MONITOR)); diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/InternalBuildRunner.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/InternalBuildRunner.java index 3d86bdda889..92e447a093c 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/InternalBuildRunner.java +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/InternalBuildRunner.java @@ -14,11 +14,15 @@ package org.eclipse.cdt.managedbuilder.core; import java.io.IOException; import java.io.OutputStream; import java.net.URI; +import java.util.ArrayList; +import java.util.List; import org.eclipse.cdt.core.ErrorParserManager; import org.eclipse.cdt.core.ICommandLauncher; +import org.eclipse.cdt.core.IConsoleParser; import org.eclipse.cdt.core.IMarkerGenerator; import org.eclipse.cdt.core.resources.IConsole; +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.internal.core.BuildRunnerHelper; import org.eclipse.cdt.managedbuilder.buildmodel.BuildDescriptionManager; import org.eclipse.cdt.managedbuilder.buildmodel.IBuildDescription; @@ -77,9 +81,10 @@ public class InternalBuildRunner extends AbstractBuildRunner { flags = BuildDescriptionManager.REBUILD | BuildDescriptionManager.REMOVED | BuildDescriptionManager.DEPS; // delta = getDelta(currentProject); // } - boolean buildIncrementaly = delta != null; + ICConfigurationDescription cfgDescription = ManagedBuildManager.getDescriptionForConfiguration(configuration); + // Prepare launch parameters for BuildRunnerHelper String cfgName = configuration.getName(); String toolchainName = configuration.getToolChain().getName(); @@ -90,7 +95,10 @@ public class InternalBuildRunner extends AbstractBuildRunner { String[] errorParsers = builder.getErrorParsers(); ErrorParserManager epm = new ErrorParserManager(project, workingDirectoryURI, markerGenerator, errorParsers); - buildRunnerHelper.prepareStreams(epm, null, console, new SubProgressMonitor(monitor, TICKS_STREAM_PROGRESS_MONITOR)); + List parsers = new ArrayList(); + ManagedBuildManager.collectLanguageSettingsConsoleParsers(cfgDescription, epm, parsers); + + buildRunnerHelper.prepareStreams(epm, parsers, console, new SubProgressMonitor(monitor, TICKS_STREAM_PROGRESS_MONITOR)); IBuildDescription des = BuildDescriptionManager.createBuildDescription(configuration, cBS, delta, flags); DescriptionBuilder dBuilder = null; diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/ManagedBuildManager.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/ManagedBuildManager.java index 7c3d89a5367..590ce245604 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/ManagedBuildManager.java +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/ManagedBuildManager.java @@ -49,6 +49,12 @@ import javax.xml.transform.stream.StreamResult; import org.eclipse.cdt.core.AbstractCExtension; import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.IConsoleParser; +import org.eclipse.cdt.core.language.settings.providers.ICBuildOutputParser; +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.IWorkingDirectoryTracker; +import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsManager; import org.eclipse.cdt.core.model.CoreModel; import org.eclipse.cdt.core.model.CoreModelUtil; import org.eclipse.cdt.core.parser.IScannerInfo; @@ -148,7 +154,6 @@ import org.w3c.dom.ProcessingInstruction; * @noinstantiate This class is not intended to be instantiated by clients. */ public class ManagedBuildManager extends AbstractCExtension { - // private static final QualifiedName buildInfoProperty = new QualifiedName(ManagedBuilderCorePlugin.PLUGIN_ID, "managedBuildInfo"); //$NON-NLS-1$ private static final String ROOT_NODE_NAME = "ManagedProjectBuildInfo"; //$NON-NLS-1$ public static final String SETTINGS_FILE_NAME = ".cdtbuild"; //$NON-NLS-1$ @@ -4721,4 +4726,24 @@ public class ManagedBuildManager extends AbstractCExtension { return true; // no target platform - nothing to check. } + /*package*/ static void collectLanguageSettingsConsoleParsers(ICConfigurationDescription cfgDescription, IWorkingDirectoryTracker cwdTracker, List parsers) { + if (cfgDescription instanceof ILanguageSettingsProvidersKeeper) { + List lsProviders = ((ILanguageSettingsProvidersKeeper) cfgDescription).getLanguageSettingProviders(); + for (ILanguageSettingsProvider lsProvider : lsProviders) { + ILanguageSettingsProvider rawProvider = LanguageSettingsManager.getRawProvider(lsProvider); + if (rawProvider instanceof ICBuildOutputParser) { + ICBuildOutputParser consoleParser = (ICBuildOutputParser) rawProvider; + try { + consoleParser.startup(cfgDescription, cwdTracker); + parsers.add(consoleParser); + } catch (CoreException e) { + ManagedBuilderCorePlugin.log(new Status(IStatus.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, + "Language Settings Provider failed to start up", e)); //$NON-NLS-1$ + } + } + } + } + + } + } diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/ManagedBuilderCorePlugin.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/ManagedBuilderCorePlugin.java index d8baa7c6af2..03b68fa445d 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/ManagedBuilderCorePlugin.java +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/core/ManagedBuilderCorePlugin.java @@ -36,7 +36,7 @@ import org.osgi.framework.BundleContext; /** * ManagedBuilderCorePlugin is the life-cycle owner of the managedbuilder core plug-in. - * + * * @noextend This class is not intended to be subclassed by clients. * @noinstantiate This class is not intended to be instantiated by clients. */ @@ -48,12 +48,12 @@ public class ManagedBuilderCorePlugin extends Plugin { // The attribute name for the makefile generator public static final String MAKEGEN_ID ="makefileGenerator"; //$NON-NLS-1$ public static final String COMMANDLINEGEN_ID = "commandlineGenerator"; //$NON-NLS-1$ - // The unique id for all managed make projects + // The unique id for all managed make projects public static final String MANAGED_MAKE_PROJECT_ID = ManagedBuilderCorePlugin.getUniqueIdentifier() + ".managedMake"; //$NON-NLS-1$ // NOTE: The code below is for tracking resource renaming and deleting. This is needed to keep // ResourceConfiguration elements up to date. It may also be needed by AdditionalInput // elements - + private static ResourceChangeHandler2 listener; // private DiscoveredPathManager fDiscoveryPathManager; @@ -72,7 +72,7 @@ public class ManagedBuilderCorePlugin extends Plugin { } return getDefault().getBundle().getSymbolicName(); } - + /** * @return the shared instance. */ @@ -88,23 +88,23 @@ public class ManagedBuilderCorePlugin extends Plugin { // Turn on logging for plugin when debugging super.start(context); configurePluginDebugOptions(); - - + + // NOTE: The code below is for tracking resource renaming and deleting. This is needed to keep // ResourceConfiguration elements up to date. It may also be needed by AdditionalInput // elements - + // IJobManager jobManager = Platform.getJobManager(); // IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); listener = new ResourceChangeHandler2(); ResourcesPlugin.getWorkspace().addResourceChangeListener( - listener, - IResourceChangeEvent.POST_CHANGE + listener, + IResourceChangeEvent.POST_CHANGE | IResourceChangeEvent.PRE_DELETE | IResourceChangeEvent.PRE_CLOSE /*| IResourceChangeEvent.POST_BUILD*/); - + BuildStateManager.getInstance().startup(); /* try { jobManager.beginRule(root, null); @@ -115,7 +115,7 @@ public class ManagedBuilderCorePlugin extends Plugin { //or core exception is thrown by the startResourceChangeHandling() //in any case, schedule a job with the root rule //that will perform the resource change handling initialization -*/ +*/ //The startResourceChangeHandling() might result in throwing an error //see bug# 132001 //Always schedule a job @@ -135,7 +135,7 @@ public class ManagedBuilderCorePlugin extends Plugin { // null); // } // }; -// +// // rcJob.setRule(root); // rcJob.setPriority(Job.INTERACTIVE); // rcJob.setSystem(true); @@ -146,7 +146,7 @@ public class ManagedBuilderCorePlugin extends Plugin { } */ } - + /* * This method adds a save participant and resource change listener * Throws CoreException if the methods fails to add a save participant. @@ -159,8 +159,8 @@ public class ManagedBuilderCorePlugin extends Plugin { // ResourcesPlugin.getWorkspace().addSaveParticipant(ManagedBuilderCorePlugin.this, listener); // // ResourcesPlugin.getWorkspace().addResourceChangeListener( -// listener, -// IResourceChangeEvent.POST_CHANGE +// listener, +// IResourceChangeEvent.POST_CHANGE // | IResourceChangeEvent.PRE_DELETE // | IResourceChangeEvent.PRE_CLOSE // /*| IResourceChangeEvent.POST_BUILD*/); @@ -184,7 +184,7 @@ public class ManagedBuilderCorePlugin extends Plugin { // } - + // NOTE: The code below is for tracking resource renaming and deleting. This is needed to keep // ResourceConfiguration elements up to date. It may also be needed by AdditionalInput // elements @@ -196,7 +196,7 @@ public class ManagedBuilderCorePlugin extends Plugin { listener = null; super.stop(context); } - + private static final String PATH_ENTRY = ManagedBuilderCorePlugin.getUniqueIdentifier() + "/debug/pathEntry"; //$NON-NLS-1$ private static final String PATH_ENTRY_INIT = ManagedBuilderCorePlugin.getUniqueIdentifier() + "/debug/pathEntryInit"; //$NON-NLS-1$ private static final String BUILDER = ManagedBuilderCorePlugin.getUniqueIdentifier() + "/debug/builder"; //$NON-NLS-1$ @@ -236,7 +236,7 @@ public class ManagedBuilderCorePlugin extends Plugin { } /** - * + * */ private void configurePluginDebugOptions() { if (isDebugging()) { @@ -258,23 +258,23 @@ public class ManagedBuilderCorePlugin extends Plugin { } } } - + public static IBuilder[] createBuilders(IProject project, Map args){ return BuilderFactory.createBuilders(project, args); } - + public static IBuilder createCustomBuilder(IConfiguration cfg, String builderId) throws CoreException{ return BuilderFactory.createCustomBuilder(cfg, builderId); } - + public static IBuilder createCustomBuilder(IConfiguration cfg, IBuilder base){ return BuilderFactory.createCustomBuilder(cfg, base); } - + public static IBuilder createBuilderForEclipseBuilder(IConfiguration cfg, String eclipseBuilderID) throws CoreException { return BuilderFactory.createBuilderForEclipseBuilder(cfg, eclipseBuilderID); } - + public boolean isOldStyleMakeProject(IProject project){ return ProjectConverter.isOldStyleMakeProject(project); } @@ -283,12 +283,4 @@ public class ManagedBuilderCorePlugin extends Plugin { ProjectConverter.convertOldStdMakeToNewStyle(project, monitor); } - -// public IDiscoveredPathManager getDiscoveryManager() { -// if ( fDiscoveryPathManager == null) { -// fDiscoveryPathManager = new DiscoveredPathManager(); -// fDiscoveryPathManager.startup(); -// } -// return fDiscoveryPathManager; -// } } diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/PluginResources.properties b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/PluginResources.properties index 83ee8b40fa1..20bc65cd8d1 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/PluginResources.properties +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/core/PluginResources.properties @@ -169,3 +169,14 @@ MultiResourceInfo.MultiResourceInfo.UnhandledIHoldsOptionsType=Unhandled parent ResourceChangeHandler2.0=project build settings update job ToolInfo.0=conversion failure ToolInfo.1=the tool is removed + +#Language settings providers messages +AbstractBuiltinSpecsDetector.AddScannerDiscoveryMarkers=Adding Scanner Discovery markers +AbstractBuiltinSpecsDetector.ClearingMarkers=Clearing markers for {0} +AbstractBuiltinSpecsDetector.DiscoverBuiltInSettingsJobName=Discover compiler built-in language settings +AbstractBuiltinSpecsDetector.RunningScannerDiscovery=Running scanner discovery: {0} +AbstractBuiltinSpecsDetector.ScannerDiscoveryMarkerLocationPreferences=Preferences, C++/Build/Settings/Discovery, [{0}] options +AbstractBuiltinSpecsDetector.ScannerDiscoveryMarkerLocationProperties=Project Properties, C++ Preprocessor Include.../Providers, [{0}] options +AbstractBuiltinSpecsDetector.ScannerDiscoveryTaskTitle=CDT Scanner Discovery +AbstractBuiltinSpecsDetector.SerializingResults=Serializing results + diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/language/settings/providers/MBSLanguageSettingsProvider.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/language/settings/providers/MBSLanguageSettingsProvider.java new file mode 100644 index 00000000000..1026f2e5f56 --- /dev/null +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/internal/language/settings/providers/MBSLanguageSettingsProvider.java @@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright (c) 2009, 2011 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.managedbuilder.internal.language.settings.providers; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.cdt.core.AbstractExecutableExtensionBase; +import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsBroadcastingProvider; +import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsStorage; +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.core.settings.model.ICFileDescription; +import org.eclipse.cdt.core.settings.model.ICFolderDescription; +import org.eclipse.cdt.core.settings.model.ICLanguageSetting; +import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; +import org.eclipse.cdt.core.settings.model.ICResourceDescription; +import org.eclipse.cdt.core.settings.model.ICSettingBase; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.IPath; + +/** + * Implementation of language settings provider for CDT Managed Build System. + */ +public class MBSLanguageSettingsProvider extends AbstractExecutableExtensionBase implements ILanguageSettingsBroadcastingProvider { + @Override + public List getSettingEntries(ICConfigurationDescription cfgDescription, IResource rc, String languageId) { + + IPath projectPath = rc.getProjectRelativePath(); + ICLanguageSetting[] languageSettings = null; + + if (rc instanceof IFile) { + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(projectPath, true); + if (ls != null) { + languageSettings = new ICLanguageSetting[] { ls }; + } else { + return getSettingEntries(cfgDescription, rc.getParent(), languageId); + } + } else { + ICResourceDescription rcDescription = cfgDescription.getResourceDescription(projectPath, false); + languageSettings = getLanguageSettings(rcDescription); + } + + List list = new ArrayList(); + + if (languageSettings != null) { + for (ICLanguageSetting langSetting : languageSettings) { + if (langSetting != null) { + String id = langSetting.getLanguageId(); + if (id != null && id.equals(languageId)) { + int kindsBits = langSetting.getSupportedEntryKinds(); + for (int kind=1; kind <= kindsBits; kind <<= 1) { + if ((kindsBits & kind) != 0) { + list.addAll(langSetting.getSettingEntriesList(kind)); + } + } + } + } + } + } + return LanguageSettingsStorage.getPooledList(list); + } + + /** + * Get language settings for resource description. + */ + private ICLanguageSetting[] getLanguageSettings(ICResourceDescription rcDescription) { + ICLanguageSetting[] array = null; + switch (rcDescription.getType()) { + case ICSettingBase.SETTING_PROJECT: + case ICSettingBase.SETTING_CONFIGURATION: + case ICSettingBase.SETTING_FOLDER: + ICFolderDescription foDes = (ICFolderDescription)rcDescription; + array = foDes.getLanguageSettings(); + break; + case ICSettingBase.SETTING_FILE: + ICFileDescription fiDes = (ICFileDescription)rcDescription; + ICLanguageSetting ls = fiDes.getLanguageSetting(); + if (ls != null) { + array = new ICLanguageSetting[] { ls }; + } + } + if (array == null) { + array = new ICLanguageSetting[0]; + } + return array; + } + + @Override + public LanguageSettingsStorage copyStorage() { + class PretendStorage extends LanguageSettingsStorage { + @Override + public boolean isEmpty() { + return false; + } + @Override + public LanguageSettingsStorage clone() throws CloneNotSupportedException { + return this; + } + @Override + public boolean equals(Object obj) { + // Note that this always triggers change event even if nothing changed in MBS + return false; + } + } + return new PretendStorage(); + } + +} diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/language/settings/providers/AbstractBuildCommandParser.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/language/settings/providers/AbstractBuildCommandParser.java new file mode 100644 index 00000000000..e927462383b --- /dev/null +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/language/settings/providers/AbstractBuildCommandParser.java @@ -0,0 +1,280 @@ +/******************************************************************************* + * Copyright (c) 2009, 2011 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.managedbuilder.language.settings.providers; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.cdt.core.IErrorParser2; +import org.eclipse.cdt.core.IMarkerGenerator; +import org.eclipse.cdt.core.errorparsers.RegexErrorParser; +import org.eclipse.cdt.core.errorparsers.RegexErrorPattern; +import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsProvider; +import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsManager; +import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; + +/** + * Abstract class for providers parsing compiler option from build command when present in build output. + * + * @since 8.1 + */ +public abstract class AbstractBuildCommandParser extends AbstractLanguageSettingsOutputScanner { + public static final Object JOB_FAMILY_BUILD_COMMAND_PARSER = "org.eclipse.cdt.managedbuilder.AbstractBuildCommandParser"; //$NON-NLS-1$ + + private static final String ATTR_PARAMETER = "parameter"; //$NON-NLS-1$ + private static final String ATTR_RESOURCE_SCOPE = "resource-scope"; //$NON-NLS-1$ + private static final String VALUE_FILE_SCOPE = "per-file"; //$NON-NLS-1$ + private static final String VALUE_FOLDER_SCOPE = "per-folder"; //$NON-NLS-1$ + private static final String VALUE_PROJECT_SCOPE = "per-project"; //$NON-NLS-1$ + + + private static final String LEADING_PATH_PATTERN = "\\S+[/\\\\]"; //$NON-NLS-1$ + private static final Pattern OPTIONS_PATTERN = Pattern.compile("-[^\\s\"']*(\\s*((\".*?\")|('.*?')|([^-\\s][^\\s]+)))?"); //$NON-NLS-1$ + private static final int OPTION_GROUP = 0; + + public enum ResourceScope { + FILE, + FOLDER, + PROJECT, + } + + /** + * Note: design patterns to keep file group the same and matching {@link #FILE_GROUP} + */ + @SuppressWarnings("nls") + private static final String[] COMPILER_COMMAND_PATTERN_TEMPLATES = { + "${COMPILER_PATTERN}.*\\s" + "()([^'\"\\s]*\\.${EXTENSIONS_PATTERN})(\\s.*)?[\r\n]*", // compiling unquoted file + "${COMPILER_PATTERN}.*\\s" + "(['\"])(.*\\.${EXTENSIONS_PATTERN})\\${COMPILER_GROUPS+1}(\\s.*)?[\r\n]*" // compiling quoted file + }; + private static final int FILE_GROUP = 2; + + // cached value from properties, do not need to use in equals() and hashCode() + private ResourceScope resourceScope = null; + + /** + * The compiler command pattern without specifying compiler options. + * The options are intended to be handled with option parsers, + * see {@link #getOptionParsers()}. + * This is regular expression pattern. + * + * @return the compiler command pattern. + */ + public String getCompilerPattern() { + return getProperty(ATTR_PARAMETER); + } + + /** + * Set compiler command pattern for the provider. See {@link #getCompilerPattern()}. + * @param commandPattern - value of the command pattern to set. + * This is regular expression pattern. + */ + public void setCompilerPattern(String commandPattern) { + setProperty(ATTR_PARAMETER, commandPattern); + } + + /** + * Sub-expression for compiler command pattern accounting for spaces, quotes etc. + */ + @SuppressWarnings("nls") + private String getCompilerPatternExtended() { + String compilerPattern = getCompilerPattern(); + return "\\s*\"?("+LEADING_PATH_PATTERN+")?(" + compilerPattern + ")\"?"; + } + + /** + * @return resource scope of the entries, i.e. level in resource hierarchy where language settings entries + * will be applied by the provider. Resource scope can be one of the following: + *
- {@code AbstractBuildCommandParser.ResourceScope.FILE} - apply entries to the file being parsed. + *
- {@code AbstractBuildCommandParser.ResourceScope.FOLDER} - apply entries to the enclosing folder. + *
- {@code AbstractBuildCommandParser.ResourceScope.PROJECT} - apply entries to the project level. + */ + public ResourceScope getResourceScope() { + if (resourceScope == null) { + String scopeStr = getProperty(ATTR_RESOURCE_SCOPE); + if (scopeStr.equals(VALUE_FILE_SCOPE)) { + resourceScope = ResourceScope.FILE; + } else if (scopeStr.equals(VALUE_FOLDER_SCOPE)) { + resourceScope = ResourceScope.FOLDER; + } else if (scopeStr.equals(VALUE_PROJECT_SCOPE)) { + resourceScope = ResourceScope.PROJECT; + } else { + resourceScope = ResourceScope.FILE; + } + } + return resourceScope; + } + + /** + * Set resource scope of the entries, i.e. level in resource hierarchy where language settings entries + * will be applied by the provider. + * + * @param rcScope - resource scope can be one of the following: + *
- {@code AbstractBuildCommandParser.ResourceScope.FILE} - apply entries to the file being parsed. + *
- {@code AbstractBuildCommandParser.ResourceScope.FOLDER} - apply entries to the enclosing folder. + *
- {@code AbstractBuildCommandParser.ResourceScope.PROJECT} - apply entries to the project level. + */ + public void setResourceScope(ResourceScope rcScope) { + resourceScope = rcScope; + switch (rcScope) { + case FILE: + setProperty(ATTR_RESOURCE_SCOPE, VALUE_FILE_SCOPE); + break; + case FOLDER: + setProperty(ATTR_RESOURCE_SCOPE, VALUE_FOLDER_SCOPE); + break; + case PROJECT: + setProperty(ATTR_RESOURCE_SCOPE, VALUE_PROJECT_SCOPE); + break; + default: + setProperty(ATTR_RESOURCE_SCOPE, VALUE_FILE_SCOPE); + break; + } + } + + @Override + protected void setSettingEntries(List entries) { + IResource rc = null; + switch (getResourceScope()) { + case FILE: + rc = currentResource; + break; + case FOLDER: + if (currentResource instanceof IFile) { + rc = currentResource.getParent(); + } + break; + case PROJECT: + if (currentResource != null) { + rc = currentResource.getProject(); + } + break; + default: + break; + + } + + setSettingEntries(currentCfgDescription, rc, currentLanguageId, entries); + } + + /** + * Adjust count for file group taking into consideration extra groups added by {@link #getCompilerPatternExtended()}. + */ + private int adjustFileGroup() { + return countGroups(getCompilerPatternExtended()) + FILE_GROUP; + } + + /** + * Make search pattern for compiler command based on template. + */ + private String makePattern(String template) { + @SuppressWarnings("nls") + String pattern = template + .replace("${COMPILER_PATTERN}", getCompilerPatternExtended()) + .replace("${EXTENSIONS_PATTERN}", getPatternFileExtensions()) + .replace("${COMPILER_GROUPS+1}", new Integer(countGroups(getCompilerPatternExtended()) + 1).toString()); + return pattern; + } + + @Override + protected String parseResourceName(String line) { + if (line == null) { + return null; + } + + for (String template : COMPILER_COMMAND_PATTERN_TEMPLATES) { + String pattern = makePattern(template); + Matcher fileMatcher = Pattern.compile(pattern).matcher(line); + if (fileMatcher.matches()) { + int fileGroup = adjustFileGroup(); + String sourceFileName = fileMatcher.group(fileGroup); + return sourceFileName; + } + } + return null; + } + + @Override + protected List parseOptions(String line) { + if (line == null || currentResource == null) { + return null; + } + + List options = new ArrayList(); + Matcher optionMatcher = OPTIONS_PATTERN.matcher(line); + while (optionMatcher.find()) { + String option = optionMatcher.group(OPTION_GROUP); + if (option!=null) { + options.add(option); + } + } + return options; + } + + private void serializeLanguageSettingsInBackground() { + ILanguageSettingsProvider wspProvider = LanguageSettingsManager.getWorkspaceProvider(getId()); + ILanguageSettingsProvider rawProvider = LanguageSettingsManager.getRawProvider(wspProvider); + if (rawProvider == this) { + // this is workspace provider + serializeLanguageSettingsInBackground(null); + } else { + serializeLanguageSettingsInBackground(currentCfgDescription); + } + } + + @Override + public void shutdown() { + serializeLanguageSettingsInBackground(); + super.shutdown(); + } + + /** + * Trivial Error Parser which allows highlighting of output lines matching the patterns + * of this parser. Intended for better troubleshooting experience. + * Implementers are supposed to add the error parser via extension point {@code org.eclipse.cdt.core.ErrorParser}. + */ + protected static abstract class AbstractBuildCommandPatternHighlighter extends RegexErrorParser implements IErrorParser2 { + /** + * Constructor. + * @param parserId - build command parser ID specified in the extension {@code org.eclipse.cdt.core.LanguageSettingsProvider}. + */ + public AbstractBuildCommandPatternHighlighter(String parserId) { + init(parserId); + } + + /** + * Initialize the error parser. + * @param parserId - language settings provider (the build command parser) ID. + */ + protected void init(String parserId) { + AbstractBuildCommandParser buildCommandParser = (AbstractBuildCommandParser) LanguageSettingsManager.getExtensionProviderCopy(parserId, false); + if (buildCommandParser != null) { + for (String template : COMPILER_COMMAND_PATTERN_TEMPLATES) { + String pattern = buildCommandParser.makePattern(template); + String fileExpr = "$"+buildCommandParser.adjustFileGroup(); //$NON-NLS-1$ + String descExpr = "$0"; //$NON-NLS-1$ + addPattern(new RegexErrorPattern(pattern, fileExpr, null, descExpr, null, IMarkerGenerator.SEVERITY_WARNING, true)); + } + } + } + + @Override + public int getProcessLineBehaviour() { + return KEEP_LONGLINES; + } + } + + +} diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/language/settings/providers/AbstractBuiltinSpecsDetector.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/language/settings/providers/AbstractBuiltinSpecsDetector.java new file mode 100644 index 00000000000..0ad1dd0ded6 --- /dev/null +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/language/settings/providers/AbstractBuiltinSpecsDetector.java @@ -0,0 +1,745 @@ +/******************************************************************************* + * Copyright (c) 2009, 2011 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.managedbuilder.language.settings.providers; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.URI; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.CommandLauncher; +import org.eclipse.cdt.core.ErrorParserManager; +import org.eclipse.cdt.core.ICommandLauncher; +import org.eclipse.cdt.core.IConsoleParser; +import org.eclipse.cdt.core.IMarkerGenerator; +import org.eclipse.cdt.core.ProblemMarkerInfo; +import org.eclipse.cdt.core.language.settings.providers.ICBuildOutputParser; +import org.eclipse.cdt.core.language.settings.providers.ICListenerAgent; +import org.eclipse.cdt.core.language.settings.providers.IWorkingDirectoryTracker; +import org.eclipse.cdt.core.model.ILanguage; +import org.eclipse.cdt.core.model.ILanguageDescriptor; +import org.eclipse.cdt.core.model.LanguageManager; +import org.eclipse.cdt.core.resources.IConsole; +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.internal.core.BuildRunnerHelper; +import org.eclipse.cdt.internal.core.XmlUtil; +import org.eclipse.cdt.managedbuilder.core.ManagedBuilderCorePlugin; +import org.eclipse.cdt.managedbuilder.internal.core.ManagedMakeMessages; +import org.eclipse.cdt.utils.CommandLineUtil; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +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.MultiStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubProgressMonitor; +import org.eclipse.core.runtime.content.IContentType; +import org.eclipse.core.runtime.jobs.Job; +import org.w3c.dom.Element; + +/** + * Abstract parser capable to execute compiler command printing built-in compiler + * specs and parse built-in language settings out of it. + * + * @since 8.1 + */ +public abstract class AbstractBuiltinSpecsDetector extends AbstractLanguageSettingsOutputScanner implements ICListenerAgent { + public static final String JOB_FAMILY_BUILTIN_SPECS_DETECTOR = "org.eclipse.cdt.managedbuilder.AbstractBuiltinSpecsDetector"; //$NON-NLS-1$ + + protected static final String COMPILER_MACRO = "${COMMAND}"; //$NON-NLS-1$ + protected static final String SPEC_FILE_MACRO = "${INPUTS}"; //$NON-NLS-1$ + protected static final String SPEC_EXT_MACRO = "${EXT}"; //$NON-NLS-1$ + protected static final String SPEC_FILE_BASE = "spec"; //$NON-NLS-1$ + + private static final String CDT_MANAGEDBUILDER_UI_PLUGIN_ID = "org.eclipse.cdt.managedbuilder.ui"; //$NON-NLS-1$ + private static final String SCANNER_DISCOVERY_CONSOLE = "org.eclipse.cdt.managedbuilder.ScannerDiscoveryConsole"; //$NON-NLS-1$ + private static final String SCANNER_DISCOVERY_GLOBAL_CONSOLE = "org.eclipse.cdt.managedbuilder.ScannerDiscoveryGlobalConsole"; //$NON-NLS-1$ + private static final String DEFAULT_CONSOLE_ICON = "icons/obj16/inspect_system.gif"; //$NON-NLS-1$ + private static final String GMAKE_ERROR_PARSER_ID = "org.eclipse.cdt.core.GmakeErrorParser"; //$NON-NLS-1$ + + private static final String ATTR_PARAMETER = "parameter"; //$NON-NLS-1$ + private static final String ATTR_CONSOLE = "console"; //$NON-NLS-1$ + + private static final int MONITOR_SCALE = 100; + private static final int TICKS_REMOVE_MARKERS = 1 * MONITOR_SCALE; + private static final int TICKS_RUN_FOR_ONE_LANGUAGE = 10 * MONITOR_SCALE; + private static final int TICKS_SERIALIZATION = 1 * MONITOR_SCALE; + private static final int TICKS_OUTPUT_PARSING = 1 * MONITOR_SCALE; + private static final int TICKS_EXECUTE_COMMAND = 1 * MONITOR_SCALE; + + protected URI mappedRootURI = null; + protected URI buildDirURI = null; + protected java.io.File specFile = null; + protected boolean preserveSpecFile = false; + protected List detectedSettingEntries = null; + protected int collected = 0; + protected boolean isExecuted = false; + + private BuildRunnerHelper buildRunnerHelper; + private SDMarkerGenerator markerGenerator = new SDMarkerGenerator(); + private boolean isConsoleEnabled = false; + private String currentCommandResolved = null; + + private class SDMarkerGenerator implements IMarkerGenerator { + // Reuse scanner discovery markers defined in org.eclipse.cdt.managedbuilder.core plugin.xml + protected static final String SCANNER_DISCOVERY_PROBLEM_MARKER = "org.eclipse.cdt.managedbuilder.core.scanner.discovery.problem"; //$NON-NLS-1$ + protected static final String ATTR_PROVIDER = "provider"; //$NON-NLS-1$ + + @Override + public void addMarker(IResource rc, int lineNumber, String errorDesc, int severity, String errorVar) { + ProblemMarkerInfo info = new ProblemMarkerInfo(rc, lineNumber, errorDesc, severity, errorVar); + addMarker(info); + } + + @Override + public void addMarker(final ProblemMarkerInfo problemMarkerInfo) { + final String providerName = getName(); + final String providerId = getId(); + // Add markers in a job to avoid deadlocks + Job markerJob = new Job(ManagedMakeMessages.getResourceString("AbstractBuiltinSpecsDetector.AddScannerDiscoveryMarkers")) { //$NON-NLS-1$ + @Override + protected IStatus run(IProgressMonitor monitor) { + // Avoid duplicates as different languages can generate identical errors + try { + IMarker[] markers = problemMarkerInfo.file.findMarkers(SDMarkerGenerator.SCANNER_DISCOVERY_PROBLEM_MARKER, false, IResource.DEPTH_ZERO); + for (IMarker marker : markers) { + int sev = ((Integer) marker.getAttribute(IMarker.SEVERITY)).intValue(); + if (sev == problemMarkerInfo.severity) { + String msg = (String) marker.getAttribute(IMarker.MESSAGE); + if (msg != null && msg.equals(problemMarkerInfo.description)) { + return Status.OK_STATUS; + } + } + } + } catch (CoreException e) { + return new Status(Status.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, "Error checking markers.", e); //$NON-NLS-1$ + } + + try { + IMarker marker = problemMarkerInfo.file.createMarker(SDMarkerGenerator.SCANNER_DISCOVERY_PROBLEM_MARKER); + marker.setAttribute(IMarker.MESSAGE, problemMarkerInfo.description); + marker.setAttribute(IMarker.SEVERITY, problemMarkerInfo.severity); + marker.setAttribute(SDMarkerGenerator.ATTR_PROVIDER, providerId); + + if (problemMarkerInfo.file instanceof IWorkspaceRoot) { + String msgPreferences = ManagedMakeMessages.getFormattedString("AbstractBuiltinSpecsDetector.ScannerDiscoveryMarkerLocationPreferences", providerName); //$NON-NLS-1$ + marker.setAttribute(IMarker.LOCATION, msgPreferences); + } else { + String msgProperties = ManagedMakeMessages.getFormattedString("AbstractBuiltinSpecsDetector.ScannerDiscoveryMarkerLocationProperties", providerName); //$NON-NLS-1$ + marker.setAttribute(IMarker.LOCATION, msgProperties); + } + } catch (CoreException e) { + return new Status(Status.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, "Error adding markers.", e); //$NON-NLS-1$ + } + + return Status.OK_STATUS; + } + }; + + markerJob.setRule(problemMarkerInfo.file); + markerJob.schedule(); + } + + /** + * Delete markers previously set by this provider for the resource. + * + * @param rc - resource to check markers. + */ + public void deleteMarkers(IResource rc) { + String providerId = getId(); + try { + IMarker[] markers = rc.findMarkers(SCANNER_DISCOVERY_PROBLEM_MARKER, false, IResource.DEPTH_ZERO); + for (IMarker marker : markers) { + if (providerId.equals(marker.getAttribute(ATTR_PROVIDER))) { + marker.delete(); + } + } + } catch (CoreException e) { + ManagedBuilderCorePlugin.log(new Status(Status.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, "Error deleting markers.", e)); //$NON-NLS-1$ + } + } + + } + + /** + * Internal ICConsoleParser to handle individual run for one language. + */ + private class ConsoleParserAdapter implements ICBuildOutputParser { + @Override + public void startup(ICConfigurationDescription cfgDescription, IWorkingDirectoryTracker cwdTracker) throws CoreException { + AbstractBuiltinSpecsDetector.this.cwdTracker = cwdTracker; + } + @Override + public boolean processLine(String line) { + return AbstractBuiltinSpecsDetector.this.processLine(line); + } + @Override + public void shutdown() { + AbstractBuiltinSpecsDetector.this.cwdTracker = null; + } + } + + /** + * Compiler command without arguments. This value is used to replace macro ${COMMAND}. + * In particular, this method is implemented in {@link ToolchainBuiltinSpecsDetector} + * which retrieves the command from tool-chain. + * + * @param languageId - language ID. + * @return compiler command without arguments, i.e. compiler program. + */ + protected abstract String getCompilerCommand(String languageId); + + /** + * The command to run. Some macros could be specified in there: + *
    + * ${COMMAND} - compiler command without arguments (compiler program). + * Normally would come from the tool-chain.
    + * ${INPUTS} - path to spec file which will be placed in workspace area.
    + * ${EXT} - file extension calculated from language ID. + *
+ * The parameter could be taken from the extension + * in {@code plugin.xml} or from property file. + * + * @return the command to run or empty string if command is not defined. + */ + public String getCommand() { + return getProperty(ATTR_PARAMETER); + } + + /** + * Set custom command for the provider. See {@link #getCommand()}. + * @param command - value of custom command to set. + */ + public void setCommand(String command) { + setProperty(ATTR_PARAMETER, command); + } + + /** + * @return {@code true} if console output is enabled for this provider, {@code false} otherwise. + */ + public boolean isConsoleEnabled() { + return isConsoleEnabled; + } + + /** + * Enable or disable console output for this provider. + * + * @param enable - {@code true} to enable console output or {@code false} to disable. + */ + public void setConsoleEnabled(boolean enable) { + isConsoleEnabled = enable; + } + + /** + * Expand macros specified in the compiler command. See {@link #getCommand()} for + * the recognized list of macros. + * + * @param languageId - language ID. + * @return - resolved command to run. + * @throws CoreException if something goes wrong. + */ + protected String resolveCommand(String languageId) throws CoreException { + String cmd = getCommand(); + if (cmd != null) { + if (cmd.contains(COMPILER_MACRO)) { + String compiler = getCompilerCommand(languageId); + if (compiler != null) + cmd = cmd.replace(COMPILER_MACRO, compiler); + } + if (cmd.contains(SPEC_FILE_MACRO)) { + String specFileName = getSpecFile(languageId); + if (specFileName != null) + cmd = cmd.replace(SPEC_FILE_MACRO, specFileName); + } + if (cmd.contains(SPEC_EXT_MACRO)) { + String specFileExt = getSpecFileExtension(languageId); + if (specFileExt != null) + cmd = cmd.replace(SPEC_EXT_MACRO, specFileExt); + } + } + return cmd; + } + + @Override + protected String parseResourceName(String line) { + // Normally built-in specs detectors are per-language and the result applies for the whole workspace. + // Returning null works workspace-wide here. + return null; + } + + @Override + protected String determineLanguage() { + // language id is supposed to be set by run(), just return it + return currentLanguageId; + } + + @Override + protected URI getMappedRootURI(IResource sourceFile, String parsedResourceName) { + // Do not calculate mappedRootURI for each line + if (mappedRootURI == null) { + mappedRootURI = super.getMappedRootURI(sourceFile, parsedResourceName); + } + return mappedRootURI; + } + + @Override + protected URI getBuildDirURI(URI mappedRootURI) { + // Do not calculate buildDirURI for each line + if (buildDirURI == null) { + buildDirURI = super.getBuildDirURI(mappedRootURI); + } + return buildDirURI; + } + + @Override + public void registerListener(ICConfigurationDescription cfgDescription) { + currentCfgDescription = cfgDescription; + execute(); + } + + @Override + public void unregisterListener() { + } + + @Override + public void startup(ICConfigurationDescription cfgDescription, IWorkingDirectoryTracker cwdTracker) throws CoreException { + super.startup(cfgDescription, cwdTracker); + + mappedRootURI = null; + buildDirURI = super.getBuildDirURI(mappedRootURI); + } + + @Override + public void shutdown() { + mappedRootURI = null; + buildDirURI = null; + + super.shutdown(); + } + + /** + * Execute provider's command which is expected to print built-in compiler options (specs) to build output. + * The parser will parse output and generate language settings for corresponding resources. + */ + protected void execute() { + if (isExecuted) { + return; + } + isExecuted = true; + + Job job = new Job(ManagedMakeMessages.getResourceString("AbstractBuiltinSpecsDetector.DiscoverBuiltInSettingsJobNam")) { //$NON-NLS-1$ + @Override + protected IStatus run(IProgressMonitor monitor) { + IStatus status; + try { + startup(currentCfgDescription, null); + status = runForEachLanguage(monitor); + } catch (CoreException e) { + ManagedBuilderCorePlugin.log(e); + status = new Status(IStatus.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, IStatus.ERROR, "Error running Builtin Specs Detector", e); //$NON-NLS-1$ + } finally { + shutdown(); + } + + return status; + } + @Override + public boolean belongsTo(Object family) { + return family == JOB_FAMILY_BUILTIN_SPECS_DETECTOR; + } + }; + + IProject project = null; + if (currentCfgDescription != null) { + ICProjectDescription prjDescription = currentCfgDescription.getProjectDescription(); + if (prjDescription != null) { + project = prjDescription.getProject(); + } + } + job.setRule(project); + job.schedule(); + } + + /** + * Run built-in specs command for each language. + * + * @param monitor - progress monitor in the initial state where {@link IProgressMonitor#beginTask(String, int)} + * has not been called yet. + * @return status of operation. + */ + protected IStatus runForEachLanguage(IProgressMonitor monitor) { + MultiStatus status = new MultiStatus(ManagedBuilderCorePlugin.PLUGIN_ID, IStatus.OK, "Problem running CDT Scanner Discovery provider " + getId(), null); //$NON-NLS-1$ + + if (monitor == null) { + monitor = new NullProgressMonitor(); + } + + try { + boolean isChanged = false; + + List languageIds = getLanguageScope(); + if (languageIds != null) { + monitor.beginTask(ManagedMakeMessages.getResourceString("AbstractBuiltinSpecsDetector.ScannerDiscoveryTaskTitle"), //$NON-NLS-1$ + TICKS_REMOVE_MARKERS + languageIds.size()*TICKS_RUN_FOR_ONE_LANGUAGE + TICKS_SERIALIZATION); + + IResource markersResource = currentProject != null ? currentProject : ResourcesPlugin.getWorkspace().getRoot(); + + monitor.subTask(ManagedMakeMessages.getFormattedString("AbstractBuiltinSpecsDetector.ClearingMarkers", markersResource.getFullPath().toString())); //$NON-NLS-1$ + markerGenerator.deleteMarkers(markersResource); + if (monitor.isCanceled()) + throw new OperationCanceledException(); + + monitor.worked(TICKS_REMOVE_MARKERS); + + for (String languageId : languageIds) { + List oldEntries = getSettingEntries(currentCfgDescription, null, languageId); + try { + startupForLanguage(languageId); + runForLanguage(new SubProgressMonitor(monitor, TICKS_RUN_FOR_ONE_LANGUAGE)); + } catch (Exception e) { + IStatus s = new Status(IStatus.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, IStatus.ERROR, "Error running Builtin Specs Detector", e); //$NON-NLS-1$ + ManagedBuilderCorePlugin.log(s); + status.merge(s); + } finally { + shutdownForLanguage(); + } + if (!isChanged) { + List newEntries = getSettingEntries(currentCfgDescription, null, languageId); + isChanged = newEntries != oldEntries; + } + + if (monitor.isCanceled()) + throw new OperationCanceledException(); + } + } + + monitor.subTask(ManagedMakeMessages.getResourceString("AbstractBuiltinSpecsDetector.SerializingResults")); //$NON-NLS-1$ + if (isChanged) { // avoids resource and settings change notifications + IStatus s = serializeLanguageSettings(currentCfgDescription); + status.merge(s); + } + monitor.worked(TICKS_SERIALIZATION); + + } catch (OperationCanceledException e) { + // user chose to cancel operation, do not threaten them with red error signs + } catch (Exception e) { + status.merge(new Status(IStatus.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, IStatus.ERROR, "Error running Builtin Specs Detector", e)); //$NON-NLS-1$ + ManagedBuilderCorePlugin.log(status); + } finally { + monitor.done(); + } + + return status; + } + + /** + * Initialize provider before running for a language. + * + * @param languageId - language ID. + * @throws CoreException if something goes wrong. + */ + protected void startupForLanguage(String languageId) throws CoreException { + currentLanguageId = languageId; + + specFile = null; // init specFile *before* calling resolveCommand(), can be changed in there + currentCommandResolved = resolveCommand(currentLanguageId); + + detectedSettingEntries = new ArrayList(); + collected = 0; + } + + /** + * Save collected entries and dispose temporary data used during run for the language. + */ + protected void shutdownForLanguage() { + if (detectedSettingEntries != null && detectedSettingEntries.size() > 0) { + collected = detectedSettingEntries.size(); + setSettingEntries(currentCfgDescription, currentResource, currentLanguageId, detectedSettingEntries); + } + detectedSettingEntries = null; + + currentCommandResolved = null; + if (specFile!=null && !preserveSpecFile) { + specFile.delete(); + specFile = null; + } + + currentLanguageId = null; + } + + /** + * Run built-in specs command for one language. + * + * @param monitor - progress monitor in the initial state where {@link IProgressMonitor#beginTask(String, int)} + * has not been called yet. + */ + private void runForLanguage(IProgressMonitor monitor) throws CoreException { + buildRunnerHelper = new BuildRunnerHelper(currentProject); + + if (monitor == null) { + monitor = new NullProgressMonitor(); + } + try { + monitor.beginTask(ManagedMakeMessages.getFormattedString("AbstractBuiltinSpecsDetector.RunningScannerDiscovery", getName()), //$NON-NLS-1$ + TICKS_EXECUTE_COMMAND + TICKS_OUTPUT_PARSING); + + IConsole console; + if (isConsoleEnabled) { + console = startProviderConsole(); + } else { + // that looks in extension points registry and won't find the id, this console is not shown + console = CCorePlugin.getDefault().getConsole(ManagedBuilderCorePlugin.PLUGIN_ID + ".console.hidden"); //$NON-NLS-1$ + } + console.start(currentProject); + + ICommandLauncher launcher = new CommandLauncher(); + launcher.setProject(currentProject); + + IPath program = new Path(""); //$NON-NLS-1$ + String[] args = new String[0]; + String[] cmdArray = CommandLineUtil.argumentsToArray(currentCommandResolved); + if (cmdArray != null && cmdArray.length > 0) { + program = new Path(cmdArray[0]); + if (cmdArray.length > 1) { + args = new String[cmdArray.length-1]; + System.arraycopy(cmdArray, 1, args, 0, args.length); + } + } + + String[] envp = BuildRunnerHelper.getEnvp(currentCfgDescription); + + // Using GMAKE_ERROR_PARSER_ID as it can handle generated error messages + ErrorParserManager epm = new ErrorParserManager(currentProject, buildDirURI, markerGenerator, new String[] {GMAKE_ERROR_PARSER_ID}); + ConsoleParserAdapter consoleParser = new ConsoleParserAdapter(); + consoleParser.startup(currentCfgDescription, epm); + List parsers = new ArrayList(); + parsers.add(consoleParser); + + buildRunnerHelper.setLaunchParameters(launcher, program, args, buildDirURI, envp); + buildRunnerHelper.prepareStreams(epm, parsers, console, new SubProgressMonitor(monitor, TICKS_OUTPUT_PARSING)); + + buildRunnerHelper.greeting(ManagedMakeMessages.getFormattedString("AbstractBuiltinSpecsDetector.RunningScannerDiscovery", getName())); //$NON-NLS-1$ + + OutputStream outStream = buildRunnerHelper.getOutputStream(); + OutputStream errStream = buildRunnerHelper.getErrorStream(); + runProgramForLanguage(currentLanguageId, currentCommandResolved, envp, buildDirURI, outStream, errStream, + new SubProgressMonitor(monitor, TICKS_EXECUTE_COMMAND, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK)); + + buildRunnerHelper.close(); + buildRunnerHelper.goodbye(); + + } catch (Exception e) { + ManagedBuilderCorePlugin.log(new CoreException(new Status(IStatus.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, "Error running Builtin Specs Detector" , e))); //$NON-NLS-1$ + } finally { + try { + buildRunnerHelper.close(); + } catch (IOException e) { + ManagedBuilderCorePlugin.log(e); + } + monitor.done(); + } + } + + protected int runProgramForLanguage(String languageId, String command, String[] envp, URI workingDirectoryURI, OutputStream consoleOut, OutputStream consoleErr, IProgressMonitor monitor) throws CoreException, IOException { + return buildRunnerHelper.build(monitor); + } + + @Override + protected void setSettingEntries(List entries) { + // Built-in specs detectors collect entries not per line but for the whole output + // so collect them to save later when output finishes + if (entries != null) { + detectedSettingEntries.addAll(entries); + } + } + + /** + * Create and start the provider console. + * @return CDT console. + */ + private IConsole startProviderConsole() { + IConsole console = null; + + if (isConsoleEnabled && currentLanguageId != null) { + String extConsoleId; + if (currentProject != null) { + extConsoleId = SCANNER_DISCOVERY_CONSOLE; + } else { + extConsoleId = SCANNER_DISCOVERY_GLOBAL_CONSOLE; + } + ILanguage ld = LanguageManager.getInstance().getLanguage(currentLanguageId); + if (ld != null) { + String consoleId = ManagedBuilderCorePlugin.PLUGIN_ID + '.' + getId() + '.' + currentLanguageId; + String consoleName = getName() + ", " + ld.getName(); //$NON-NLS-1$ + URL defaultIcon = Platform.getBundle(CDT_MANAGEDBUILDER_UI_PLUGIN_ID).getEntry(DEFAULT_CONSOLE_ICON); + if (defaultIcon == null) { + @SuppressWarnings("nls") + String msg = "Unable to find icon " + DEFAULT_CONSOLE_ICON + " in plugin " + CDT_MANAGEDBUILDER_UI_PLUGIN_ID; + ManagedBuilderCorePlugin.log(new Status(IStatus.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, msg)); + } + + console = CCorePlugin.getDefault().getConsole(extConsoleId, consoleId, consoleName, defaultIcon); + } + } + + if (console == null) { + // that looks in extension points registry and won't find the id, this console is not shown + console = CCorePlugin.getDefault().getConsole(ManagedBuilderCorePlugin.PLUGIN_ID + ".console.hidden"); //$NON-NLS-1$ + } + + return console; + } + + /** + * Get path to spec file which normally would be placed in workspace area. + * This value is used to replace macro ${INPUTS}. + * + * @param languageId - language ID. + * @return full path to the specs file. + */ + protected String getSpecFile(String languageId) { + String specExt = getSpecFileExtension(languageId); + String ext = ""; //$NON-NLS-1$ + if (specExt != null) { + ext = '.' + specExt; + } + + String specFileName = SPEC_FILE_BASE + ext; + IPath workingLocation = ManagedBuilderCorePlugin.getDefault().getStateLocation(); + IPath fileLocation = workingLocation.append(specFileName); + + specFile = new java.io.File(fileLocation.toOSString()); + // will preserve spec file if it was already there otherwise will delete upon finishing + preserveSpecFile = specFile.exists(); + if (!preserveSpecFile) { + try { + // In the typical case it is sufficient to have an empty file. + specFile.createNewFile(); + } catch (IOException e) { + ManagedBuilderCorePlugin.log(e); + } + } + + return fileLocation.toString(); + } + + /** + * Determine file extension by language id. This implementation retrieves first extension + * from the list as there could be multiple extensions associated with the given language. + * This value is used to replace macro ${EXT}. + * + * @param languageId - given language ID. + * @return file extension associated with the language or {@code null} if not found. + */ + protected String getSpecFileExtension(String languageId) { + String ext = null; + ILanguageDescriptor langDescriptor = LanguageManager.getInstance().getLanguageDescriptor(languageId); + if (langDescriptor != null) { + IContentType[] contentTypes = langDescriptor.getContentTypes(); + if (contentTypes != null && contentTypes.length > 0) { + String[] fileExtensions = contentTypes[0].getFileSpecs(IContentType.FILE_EXTENSION_SPEC); + if (fileExtensions != null && fileExtensions.length > 0) { + ext = fileExtensions[0]; + } + } + } + + if (ext == null) { + ManagedBuilderCorePlugin.log(new Status(IStatus.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, "Unable to find file extension for language " + languageId)); //$NON-NLS-1$ + } + return ext; + } + + @Override + public Element serializeAttributes(Element parentElement) { + Element elementProvider = super.serializeAttributes(parentElement); + elementProvider.setAttribute(ATTR_CONSOLE, Boolean.toString(isConsoleEnabled)); + return elementProvider; + } + + @Override + public void loadAttributes(Element providerNode) { + super.loadAttributes(providerNode); + + String consoleValue = XmlUtil.determineAttributeValue(providerNode, ATTR_CONSOLE); + if (consoleValue != null) { + isConsoleEnabled = Boolean.parseBoolean(consoleValue); + } + } + + @Override + public void loadEntries(Element providerNode) { + super.loadEntries(providerNode); + if (!isEmpty()) { + isExecuted = true; + } + } + + @Override + public boolean isEmpty() { + // treat provider that has been executed as not empty + // to let "Clear" button to restart the provider + return !isExecuted && super.isEmpty(); + } + + @Override + public void clear() { + super.clear(); + isExecuted = false; + } + + @Override + protected AbstractBuiltinSpecsDetector cloneShallow() throws CloneNotSupportedException { + AbstractBuiltinSpecsDetector clone = (AbstractBuiltinSpecsDetector) super.cloneShallow(); + clone.isExecuted = false; + return clone; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + (isConsoleEnabled ? 1231 : 1237); + result = prime * result + (isExecuted ? 1231 : 1237); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (!(obj instanceof AbstractBuiltinSpecsDetector)) + return false; + AbstractBuiltinSpecsDetector other = (AbstractBuiltinSpecsDetector) obj; + if (isConsoleEnabled != other.isConsoleEnabled) + return false; + if (isExecuted != other.isExecuted) + return false; + return true; + } +} diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/language/settings/providers/AbstractLanguageSettingsOutputScanner.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/language/settings/providers/AbstractLanguageSettingsOutputScanner.java new file mode 100644 index 00000000000..c3e480fdff3 --- /dev/null +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/language/settings/providers/AbstractLanguageSettingsOutputScanner.java @@ -0,0 +1,1073 @@ +/******************************************************************************* + * Copyright (c) 2009, 2012 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.managedbuilder.language.settings.providers; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.ErrorParserManager; +import org.eclipse.cdt.core.cdtvariables.CdtVariableException; +import org.eclipse.cdt.core.cdtvariables.ICdtVariableManager; +import org.eclipse.cdt.core.language.settings.providers.ICBuildOutputParser; +import org.eclipse.cdt.core.language.settings.providers.IWorkingDirectoryTracker; +import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsManager; +import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsSerializableProvider; +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; +import org.eclipse.cdt.core.settings.model.ICSettingEntry; +import org.eclipse.cdt.core.settings.model.util.CDataUtil; +import org.eclipse.cdt.internal.core.XmlUtil; +import org.eclipse.cdt.managedbuilder.core.ManagedBuilderCorePlugin; +import org.eclipse.cdt.utils.EFSExtensionManager; +import org.eclipse.core.filesystem.EFS; +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.content.IContentType; +import org.eclipse.core.runtime.content.IContentTypeManager; +import org.w3c.dom.Element; + +/** + * Abstract class for language settings providers capable to parse build output. + * + * @since 8.1 + */ +public abstract class AbstractLanguageSettingsOutputScanner extends LanguageSettingsSerializableProvider implements ICBuildOutputParser { + protected static final String ATTR_KEEP_RELATIVE_PATHS = "keep-relative-paths"; //$NON-NLS-1$ + + protected ICConfigurationDescription currentCfgDescription = null; + protected IWorkingDirectoryTracker cwdTracker = null; + protected IProject currentProject = null; + protected IResource currentResource = null; + protected String currentLanguageId = null; + + protected String parsedResourceName = null; + protected boolean isResolvingPaths = true; + + /** + * Abstract class defining common functionality for option parsers. + * The purpose of this parser is to parse a portion of string representing + * a single option and create a language settings entry out of it. + * + * See {@link GCCBuildCommandParser} for an example how to define the parsers. + */ + protected static abstract class AbstractOptionParser { + private final int kind; + private final String patternStr; + private final Pattern pattern; + private final String nameExpression; + private final String valueExpression; + private final int extraFlag; + + private String parsedName; + private String parsedValue; + + /** + * Constructor. + * + * @param kind - kind of language settings entries being parsed by the parser. + * @param pattern - regular expression pattern being parsed by the parser. + * @param nameExpression - capturing group expression defining name of an entry. + * @param valueExpression - capturing group expression defining value of an entry. + * @param extraFlag - extra-flag to add while creating language settings entry. + */ + public AbstractOptionParser(int kind, String pattern, String nameExpression, String valueExpression, int extraFlag) { + this.kind = kind; + this.patternStr = pattern; + this.nameExpression = nameExpression; + this.valueExpression = valueExpression; + this.extraFlag = extraFlag; + + this.pattern = Pattern.compile(pattern); + } + + /** + * Create language settings entry of appropriate kind and considering extra-flag passed in constructor. + * + * @param name - name of language settings entry. + * @param value - value of language settings entry. + * @param flag - flag to set. Note that the flag will be amended with the extra-flag defined in constructor. + * @return new language settings entry. + */ + public ICLanguageSettingEntry createEntry(String name, String value, int flag) { + return (ICLanguageSettingEntry) CDataUtil.createEntry(kind, name, value, null, flag | extraFlag); + } + + /** + * Check if the king of option parsed by parser is "file". + * + * @return {@code true} if the kind is file, {@code false} otherwise. + */ + public boolean isForFile() { + return kind == ICSettingEntry.INCLUDE_FILE || kind == ICSettingEntry.MACRO_FILE; + } + + /** + * Check if the king of option parsed by parser is "folder". + * + * @return {@code true} if the kind is folder, {@code false} otherwise. + */ + public boolean isForFolder() { + return kind == ICSettingEntry.INCLUDE_PATH || kind == ICSettingEntry.LIBRARY_PATH; + } + + /** + * Return value represented by the capturing group expression. + */ + private String parseStr(Matcher matcher, String str) { + if (str != null) + return matcher.replaceAll(str); + return null; + } + + /** + * Test for a match and parse a portion of input string representing a single option + * to retrieve name and value. + * + * @param optionString - an option to test and parse, possibly with an argument. + * @return {@code true} if the option is a match to parser's regular expression + * or {@code false} otherwise. + */ + public boolean parseOption(String optionString) { + // get rid of extra text at the end (for example file name could be confused for an argument) + @SuppressWarnings("nls") + String option = optionString.replaceFirst("(" + patternStr + ").*", "$1"); + + Matcher matcher = pattern.matcher(option); + boolean isMatch = matcher.matches(); + if (isMatch) { + parsedName = parseStr(matcher, nameExpression); + parsedValue = parseStr(matcher, valueExpression); + } + return isMatch; + } + } + + /** + * Implementation of {@link AbstractOptionParser} for include path options parsing. + */ + protected static class IncludePathOptionParser extends AbstractOptionParser { + public IncludePathOptionParser(String pattern, String nameExpression) { + super(ICLanguageSettingEntry.INCLUDE_PATH, pattern, nameExpression, nameExpression, 0); + } + public IncludePathOptionParser(String pattern, String nameExpression, int extraFlag) { + super(ICLanguageSettingEntry.INCLUDE_PATH, pattern, nameExpression, nameExpression, extraFlag); + } + } + + /** + * Implementation of {@link AbstractOptionParser} for include file options parsing. + */ + protected static class IncludeFileOptionParser extends AbstractOptionParser { + /** + * Constructor. + * @param pattern - regular expression pattern being parsed by the parser. + * @param nameExpression - capturing group expression defining name of an entry. + */ + public IncludeFileOptionParser(String pattern, String nameExpression) { + super(ICLanguageSettingEntry.INCLUDE_FILE, pattern, nameExpression, nameExpression, 0); + } + /** + * Constructor. + * @param pattern - regular expression pattern being parsed by the parser. + * @param nameExpression - capturing group expression defining name of an entry. + * @param extraFlag - extra-flag to add while creating language settings entry. + */ + public IncludeFileOptionParser(String pattern, String nameExpression, int extraFlag) { + super(ICLanguageSettingEntry.INCLUDE_FILE, pattern, nameExpression, nameExpression, extraFlag); + } + } + + /** + * Implementation of {@link AbstractOptionParser} for macro options parsing. + */ + protected static class MacroOptionParser extends AbstractOptionParser { + /** + * Constructor. + * @param pattern - regular expression pattern being parsed by the parser. + * @param nameExpression - capturing group expression defining name of an entry. + * @param valueExpression - capturing group expression defining value of an entry. + */ + public MacroOptionParser(String pattern, String nameExpression, String valueExpression) { + super(ICLanguageSettingEntry.MACRO, pattern, nameExpression, valueExpression, 0); + } + /** + * Constructor. + * @param pattern - regular expression pattern being parsed by the parser. + * @param nameExpression - capturing group expression defining name of an entry. + * @param valueExpression - capturing group expression defining value of an entry. + * @param extraFlag - extra-flag to add while creating language settings entry. + */ + public MacroOptionParser(String pattern, String nameExpression, String valueExpression, int extraFlag) { + super(ICLanguageSettingEntry.MACRO, pattern, nameExpression, valueExpression, extraFlag); + } + /** + * Constructor. + * @param pattern - regular expression pattern being parsed by the parser. + * @param nameExpression - capturing group expression defining name of an entry. + * @param extraFlag - extra-flag to add while creating language settings entry. + */ + public MacroOptionParser(String pattern, String nameExpression, int extraFlag) { + super(ICLanguageSettingEntry.MACRO, pattern, nameExpression, null, extraFlag); + } + } + + /** + * Implementation of {@link AbstractOptionParser} for macro file options parsing. + */ + protected static class MacroFileOptionParser extends AbstractOptionParser { + /** + * Constructor. + * @param pattern - regular expression pattern being parsed by the parser. + * @param nameExpression - capturing group expression defining name of an entry. + */ + public MacroFileOptionParser(String pattern, String nameExpression) { + super(ICLanguageSettingEntry.MACRO_FILE, pattern, nameExpression, nameExpression, 0); + } + /** + * Constructor. + * @param pattern - regular expression pattern being parsed by the parser. + * @param nameExpression - capturing group expression defining name of an entry. + * @param extraFlag - extra-flag to add while creating language settings entry. + */ + public MacroFileOptionParser(String pattern, String nameExpression, int extraFlag) { + super(ICLanguageSettingEntry.MACRO_FILE, pattern, nameExpression, nameExpression, extraFlag); + } + } + + /** + * Implementation of {@link AbstractOptionParser} for library path options parsing. + */ + protected static class LibraryPathOptionParser extends AbstractOptionParser { + /** + * Constructor. + * @param pattern - regular expression pattern being parsed by the parser. + * @param nameExpression - capturing group expression defining name of an entry. + */ + public LibraryPathOptionParser(String pattern, String nameExpression) { + super(ICLanguageSettingEntry.LIBRARY_PATH, pattern, nameExpression, nameExpression, 0); + } + /** + * Constructor. + * @param pattern - regular expression pattern being parsed by the parser. + * @param nameExpression - capturing group expression defining name of an entry. + * @param extraFlag - extra-flag to add while creating language settings entry. + */ + public LibraryPathOptionParser(String pattern, String nameExpression, int extraFlag) { + super(ICLanguageSettingEntry.LIBRARY_PATH, pattern, nameExpression, nameExpression, extraFlag); + } + } + + /** + * Implementation of {@link AbstractOptionParser} for library file options parsing. + */ + protected static class LibraryFileOptionParser extends AbstractOptionParser { + /** + * Constructor. + * @param pattern - regular expression pattern being parsed by the parser. + * @param nameExpression - capturing group expression defining name of an entry. + */ + public LibraryFileOptionParser(String pattern, String nameExpression) { + super(ICLanguageSettingEntry.LIBRARY_FILE, pattern, nameExpression, nameExpression, 0); + } + /** + * Constructor. + * @param pattern - regular expression pattern being parsed by the parser. + * @param nameExpression - capturing group expression defining name of an entry. + * @param extraFlag - extra-flag to add while creating language settings entry. + */ + public LibraryFileOptionParser(String pattern, String nameExpression, int extraFlag) { + super(ICLanguageSettingEntry.LIBRARY_FILE, pattern, nameExpression, nameExpression, extraFlag); + } + } + + /** + * Parse the line returning the resource name as appears in the output. + * This is the resource where {@link ICLanguageSettingEntry} list is being added. + * + * @param line - one input line from the output stripped from end of line characters. + * @return the resource name as appears in the output or {@code null}. + * Note that {@code null} can have different semantics and can mean "no resource found" + * or "applicable to any resource". By default "no resource found" is used in this + * abstract class but extenders can handle otherwise. + */ + protected abstract String parseResourceName(String line); + + /** + * Parse the line returning the list of substrings to be treated each as input to + * the option parsers. It is assumed that each substring presents one + * {@link ICLanguageSettingEntry} (for example compiler options {@code -I/path} or + * {@code -DMACRO=1}). + * + * @param line - one input line from the output stripped from end of line characters. + * @return list of substrings representing language settings entries. + */ + protected abstract List parseOptions(String line); + + /** + * @return array of option parsers defining how to parse a string to + * {@link ICLanguageSettingEntry}. + * See {@link AbstractOptionParser} and its specific extenders. + */ + protected abstract AbstractOptionParser[] getOptionParsers(); + + /** + * @return {@code true} when the provider tries to resolve relative or remote paths + * to the existing paths in the workspace or local file-system using certain heuristics. + */ + public boolean isResolvingPaths() { + return isResolvingPaths; + } + + /** + * Enable or disable resolving relative or remote paths to the existing paths + * in the workspace or local file-system. + * + * @param resolvePaths - set {@code true} to enable or {@code false} to disable + * resolving paths. When this parameter is set to {@code false} the paths will + * be kept as they appear in the build output. + */ + public void setResolvingPaths(boolean resolvePaths) { + this.isResolvingPaths = resolvePaths; + } + + + @Override + public void startup(ICConfigurationDescription cfgDescription, IWorkingDirectoryTracker cwdTracker) throws CoreException { + this.currentCfgDescription = cfgDescription; + this.currentProject = cfgDescription != null ? cfgDescription.getProjectDescription().getProject() : null; + this.cwdTracker = cwdTracker; + } + + @Override + public void shutdown() { + // release resources for garbage collector + // but keep currentCfgDescription for AbstractBuiltinSpecsDetector flow + parsedResourceName = null; + currentLanguageId = null; + currentResource = null; + cwdTracker = null; + } + + @Override + public boolean processLine(String line) { + parsedResourceName = parseResourceName(line); + currentResource = findResource(parsedResourceName); + + currentLanguageId = determineLanguage(); + if (!isLanguageInScope(currentLanguageId)) { + return false; + } + + /** + * URI of directory where the build is happening. This URI could point to a remote file-system + * for remote builds. Most often it is the same file-system as for currentResource but + * it can be different file-system (and different URI schema). + */ + URI buildDirURI = null; + + /** + * Where source tree starts if mapped. This kind of mapping is useful for example in cases when + * the absolute path to the source file on the remote system is simulated inside a project in the + * workspace. + * This URI is rooted on the same file-system where currentResource resides. In general this file-system + * (or even URI schema) does not have to match that of buildDirURI. + */ + URI mappedRootURI = null; + + if (isResolvingPaths) { + mappedRootURI = getMappedRootURI(currentResource, parsedResourceName); + buildDirURI = getBuildDirURI(mappedRootURI); + } + + List entries = new ArrayList(); + + List options = parseOptions(line); + if (options != null) { + AbstractOptionParser[] optionParsers = getOptionParsers(); + for (String option : options) { + for (AbstractOptionParser optionParser : optionParsers) { + try { + if (optionParser.parseOption(option)) { + ICLanguageSettingEntry entry = null; + if (isResolvingPaths && (optionParser.isForFile() || optionParser.isForFolder())) { + URI baseURI = mappedRootURI; + if (buildDirURI != null && !new Path(optionParser.parsedName).isAbsolute()) { + baseURI = EFSExtensionManager.getDefault().append(mappedRootURI, buildDirURI.getPath()); + } + entry = createResolvedPathEntry(optionParser, optionParser.parsedName, 0, baseURI); + } else { + entry = optionParser.createEntry(optionParser.parsedName, optionParser.parsedValue, 0); + } + + if (entry != null && !entries.contains(entry)) { + entries.add(entry); + break; + } + } + } catch (Throwable e) { + @SuppressWarnings("nls") + String msg = "Exception trying to parse option [" + option + "], class " + getClass().getSimpleName(); + ManagedBuilderCorePlugin.log(new Status(IStatus.ERROR, ManagedBuilderCorePlugin.PLUGIN_ID, msg, e)); + } + } + } + if (entries.size() > 0) { + setSettingEntries(entries); + } else { + setSettingEntries(null); + } + } + return false; + } + + /** + * In case when absolute path is mapped to the source tree in a project + * this function will try to figure mapping and return "mapped root", + * i.e URI where the root path would be mapped. The mapped root will be + * used to prepend to other "absolute" paths where appropriate. + * + * @param resource - a resource referred by parsed path + * @param parsedResourceName - path as appears in the output + * @return mapped path as URI + */ + protected URI getMappedRootURI(IResource resource, String parsedResourceName) { + if (resource == null) { + return null; + } + + URI resourceURI = resource.getLocationURI(); + String mappedRoot = "/"; //$NON-NLS-1$ + + if (parsedResourceName != null) { + IPath parsedSrcPath = new Path(parsedResourceName); + if (parsedSrcPath.isAbsolute()) { + IPath absResourcePath = resource.getLocation(); + int absSegmentsCount = absResourcePath.segmentCount(); + int relSegmentsCount = parsedSrcPath.segmentCount(); + if (absSegmentsCount >= relSegmentsCount) { + IPath ending = absResourcePath.removeFirstSegments(absSegmentsCount - relSegmentsCount); + ending = ending.setDevice(parsedSrcPath.getDevice()).makeAbsolute(); + if (ending.equals(parsedSrcPath.makeAbsolute())) { + // mappedRoot here is parsedSrcPath with removed parsedResourceName trailing segments, + // i.e. if absResourcePath="/path/workspace/project/file.c" and parsedResourceName="project/file.c" + // then mappedRoot="/path/workspace/" + mappedRoot = absResourcePath.removeLastSegments(relSegmentsCount).toString(); + } + } + } + } + // this creates URI with schema and other components from resourceURI but path as mappedRoot + URI uri = EFSExtensionManager.getDefault().createNewURIFromPath(resourceURI, mappedRoot); + return uri; + } + + /** + * Determine current build directory considering currentResource (resource being compiled), + * parsedResourceName and mappedRootURI. + * + * @param mappedRootURI - root of the source tree when mapped to remote file-system. + * @return {@link URI} of current build directory + */ + protected URI getBuildDirURI(URI mappedRootURI) { + URI buildDirURI = null; + + // try to deduce build directory from full path of currentResource and partial path of parsedResourceName + URI cwdURI = null; + if (currentResource != null && parsedResourceName != null && !new Path(parsedResourceName).isAbsolute()) { + cwdURI = findBaseLocationURI(currentResource.getLocationURI(), parsedResourceName); + } + String cwdPath = cwdURI != null ? EFSExtensionManager.getDefault().getPathFromURI(cwdURI) : null; + if (cwdPath != null && mappedRootURI != null) { + buildDirURI = EFSExtensionManager.getDefault().append(mappedRootURI, cwdPath); + } else { + buildDirURI = cwdURI; + } + + // try IWorkingDirectoryTracker + if (buildDirURI == null && cwdTracker != null) { + buildDirURI = cwdTracker.getWorkingDirectoryURI(); + } + + // try builder working directory + if (buildDirURI == null && currentCfgDescription != null) { + IPath pathBuilderCWD = currentCfgDescription.getBuildSetting().getBuilderCWD(); + if (pathBuilderCWD != null) { + String builderCWD = pathBuilderCWD.toString(); + try { + // here is a hack to overcome ${workspace_loc:/prj-name} returned by builder + // where "/" is treated as path separator by pathBuilderCWD + ICdtVariableManager vmanager = CCorePlugin.getDefault().getCdtVariableManager(); + builderCWD = vmanager.resolveValue(builderCWD, "", null, currentCfgDescription); //$NON-NLS-1$ + } catch (CdtVariableException e) { + ManagedBuilderCorePlugin.log(e); + } + if (builderCWD != null && !builderCWD.isEmpty()) { + buildDirURI = org.eclipse.core.filesystem.URIUtil.toURI(builderCWD); + } + } + } + + // try directory of the current project + if (buildDirURI == null && currentProject != null) { + buildDirURI = currentProject.getLocationURI(); + } + + // try parent folder of the resource + if (buildDirURI == null && currentResource != null) { + IContainer container; + if (currentResource instanceof IContainer) { + container = (IContainer) currentResource; + } else { + container = currentResource.getParent(); + } + buildDirURI = container.getLocationURI(); + } + return buildDirURI; + } + + /** + * Sets language settings entries for current configuration description, current resource + * and current language ID. + * + * @param entries - language settings entries to set. + */ + protected void setSettingEntries(List entries) { + setSettingEntries(currentCfgDescription, currentResource, currentLanguageId, entries); + } + + /** + * Determine a language associated with the resource. + * + * @return language ID for the resource. + */ + protected String determineLanguage() { + IResource rc = currentResource; + if (rc == null && currentProject != null && parsedResourceName != null) { + String fileName = new Path(parsedResourceName).lastSegment().toString(); + // use handle; resource does not need to exist + rc = currentProject.getFile("__" + fileName); //$NON-NLS-1$ + } + + if (rc == null) + return null; + + List languageIds = LanguageSettingsManager.getLanguages(rc, currentCfgDescription); + if (languageIds.isEmpty()) + return null; + + return languageIds.get(0); + } + + /** + * Determine if the language is in scope of the provider. + * + * @param languageId - language ID. + * @return {@code true} if the language is in scope, {@code false } otherwise. + */ + protected boolean isLanguageInScope(String languageId) { + List languageIds = getLanguageScope(); + return languageIds == null || languageIds.contains(languageId); + } + + /** + * Find resource in the workspace for a given URI with a preference for the resource + * to reside in the given project. + */ + private static IResource findFileForLocationURI(URI uri, IProject preferredProject) { + IResource sourceFile = null; + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + IResource[] resources = root.findFilesForLocationURI(uri); + if (resources.length > 0) { + sourceFile = resources[0]; + if (preferredProject != null) { + for (IResource rc : resources) { + if (rc.getProject().equals(preferredProject)) { + sourceFile = rc; + break; + } + } + } + } + return sourceFile; + } + + /** + * Return a resource in workspace corresponding the given folder {@link URI} preferable residing in + * the provided project. + */ + private static IResource findContainerForLocationURI(URI uri, IProject preferredProject) { + IResource resource = null; + IResource[] resources = ResourcesPlugin.getWorkspace().getRoot().findContainersForLocationURI(uri); + for (IResource rc : resources) { + if ((rc instanceof IProject || rc instanceof IFolder)) { // treat IWorkspaceRoot as non-workspace path + if (rc.getProject().equals(preferredProject)) { + resource = rc; + break; + } + if (resource == null) { + resource = rc; // to be deterministic the first qualified resource has preference + } + } + } + return resource; + } + + /** + * Determine resource in the workspace corresponding to the parsed resource name. + */ + private IResource findResource(String parsedResourceName) { + if (parsedResourceName == null || parsedResourceName.isEmpty()) { + return null; + } + + IResource sourceFile = null; + + // try ErrorParserManager + if (cwdTracker instanceof ErrorParserManager) { + sourceFile = ((ErrorParserManager) cwdTracker).findFileName(parsedResourceName); + } + + // try to find absolute path in the workspace + if (sourceFile == null && new Path(parsedResourceName).isAbsolute()) { + URI uri = org.eclipse.core.filesystem.URIUtil.toURI(parsedResourceName); + sourceFile = findFileForLocationURI(uri, currentProject); + } + + // try last known current working directory from build output + if (sourceFile == null && cwdTracker != null) { + URI cwdURI = cwdTracker.getWorkingDirectoryURI(); + if (cwdURI != null) { + URI uri = EFSExtensionManager.getDefault().append(cwdURI, parsedResourceName); + sourceFile = findFileForLocationURI(uri, currentProject); + } + } + + // try path relative to build dir from configuration + if (sourceFile == null && currentCfgDescription != null) { + IPath builderCWD = currentCfgDescription.getBuildSetting().getBuilderCWD(); + if (builderCWD!=null) { + IPath path = builderCWD.append(parsedResourceName); + URI uri = org.eclipse.core.filesystem.URIUtil.toURI(path); + sourceFile = findFileForLocationURI(uri, currentProject); + } + } + + // try path relative to the project + if (sourceFile == null && currentProject != null) { + sourceFile = currentProject.findMember(parsedResourceName); + } + + return sourceFile; + } + + /** + * Find base location of the file, i.e. location of the directory which + * results from removing trailing relativeFileName from fileURI or + * {@code null} if fileURI doesn't represent relativeFileName. + */ + private static URI findBaseLocationURI(URI fileURI, String relativeFileName) { + URI cwdURI = null; + String path = fileURI.getPath(); + + String[] segments = relativeFileName.split("[/\\\\]"); //$NON-NLS-1$ + + // start removing segments from the end of the path + for (int i = segments.length - 1; i >= 0; i--) { + String lastSegment = segments[i]; + if (lastSegment.length() > 0 && !lastSegment.equals(".")) { //$NON-NLS-1$ + if (lastSegment.equals("..")) { //$NON-NLS-1$ + // navigating ".." in the other direction is ambiguous, bailing out + return null; + } else { + if (path.endsWith("/" + lastSegment)) { //$NON-NLS-1$ + int pos = path.lastIndexOf("/" + lastSegment); //$NON-NLS-1$ + path = path.substring(0, pos); + continue; + } else { + // ouch, relativeFileName does not match fileURI, bailing out + return null; + } + } + } + } + + try { + cwdURI = new URI(fileURI.getScheme(), fileURI.getUserInfo(), fileURI.getHost(), + fileURI.getPort(), path + '/', fileURI.getQuery(), fileURI.getFragment()); + } catch (URISyntaxException e) { + // It should be valid URI here or something is really wrong + ManagedBuilderCorePlugin.log(e); + } + + return cwdURI; + } + + /** + * The manipulations here are done to resolve "../" navigation for symbolic links where "link/.." cannot + * be collapsed as it must follow the real file-system path. {@link java.io.File#getCanonicalPath()} deals + * with that correctly but {@link Path} or {@link URI} try to normalize the path which would be incorrect + * here. + */ + private static URI resolvePathFromBaseLocation(String name, IPath baseLocation) { + String pathName = name; + if (baseLocation != null && !baseLocation.isEmpty()) { + pathName = pathName.replace(File.separatorChar, '/'); + String device = new Path(pathName).getDevice(); + if (device==null || device.equals(baseLocation.getDevice())) { + if (device != null && device.length() > 0) { + pathName = pathName.substring(device.length()); + } + + baseLocation = baseLocation.addTrailingSeparator(); + if (pathName.startsWith("/")) { //$NON-NLS-1$ + pathName = pathName.substring(1); + } + pathName = baseLocation.toString() + pathName; + } + } + + try { + File file = new File(pathName); + file = file.getCanonicalFile(); + return file.toURI(); + } catch (IOException e) { + // if error just leave it as is + } + + return org.eclipse.core.filesystem.URIUtil.toURI(pathName); + } + + /** + * Determine URI on the local file-system considering possible mapping. + * + * @param pathStr - path to the resource, can be absolute or relative + * @param baseURI - base {@link URI} where path to the resource is rooted + * @return {@link URI} of the resource + */ + private static URI determineMappedURI(String pathStr, URI baseURI) { + URI uri = null; + + if (baseURI == null) { + if (new Path(pathStr).isAbsolute()) { + uri = resolvePathFromBaseLocation(pathStr, Path.ROOT); + } + } else if (baseURI.getScheme().equals(EFS.SCHEME_FILE)) { + // location on the local file-system + IPath baseLocation = org.eclipse.core.filesystem.URIUtil.toPath(baseURI); + // careful not to use Path here but 'pathStr' as String as we want to properly navigate symlinks + uri = resolvePathFromBaseLocation(pathStr, baseLocation); + } else { + // location on a remote file-system + IPath path = new Path(pathStr); // use canonicalized path here, in particular replace all '\' with '/' for Windows paths + URI remoteUri = EFSExtensionManager.getDefault().append(baseURI, path.toString()); + if (remoteUri != null) { + String localPath = EFSExtensionManager.getDefault().getMappedPath(remoteUri); + if (localPath != null) { + uri = org.eclipse.core.filesystem.URIUtil.toURI(localPath); + } + } + } + + if (uri == null) { + // if everything fails just wrap string to URI + uri = org.eclipse.core.filesystem.URIUtil.toURI(pathStr); + } + return uri; + } + + /** + * Find all resources in the folder which might be represented by relative path passed. + */ + private static List findPathInFolder(IPath path, IContainer folder) { + List paths = new ArrayList(); + IResource resource = folder.findMember(path); + if (resource != null) { + paths.add(resource); + } + + try { + for (IResource res : folder.members()) { + if (res instanceof IContainer) { + paths.addAll(findPathInFolder(path, (IContainer) res)); + } + } + } catch (CoreException e) { + // ignore + } + + return paths; + } + + /** + * Determine which resource in workspace is the best fit to parsedName passed. + */ + private static IResource resolveResourceInWorkspace(String parsedName, IProject preferredProject, Set referencedProjectsNames) { + IPath path = new Path(parsedName); + if (path.equals(new Path(".")) || path.equals(new Path(".."))) { //$NON-NLS-1$ //$NON-NLS-2$ + return null; + } + + // prefer current project + if (preferredProject != null) { + List result = findPathInFolder(path, preferredProject); + int size = result.size(); + if (size == 1) { // found the one + return result.get(0); + } else if (size > 1) { // ambiguous + return null; + } + } + + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + + // then prefer referenced projects + if (referencedProjectsNames.size() > 0) { + IResource rc = null; + for (String prjName : referencedProjectsNames) { + IProject prj = root.getProject(prjName); + if (prj.isOpen()) { + List result = findPathInFolder(path, prj); + int size = result.size(); + if (size == 1 && rc == null) { + rc = result.get(0); + } else if (size > 0) { + // ambiguous + rc = null; + break; + } + } + } + if (rc != null) { + return rc; + } + } + + // then check all other projects in workspace + IProject[] projects = root.getProjects(); + if (projects.length > 0) { + IResource rc = null; + for (IProject prj : projects) { + if (!prj.equals(preferredProject) && !referencedProjectsNames.contains(prj.getName()) && prj.isOpen()) { + List result = findPathInFolder(path, prj); + int size = result.size(); + if (size == 1 && rc == null) { + rc = result.get(0); + } else if (size > 0) { + // ambiguous + rc = null; + break; + } + } + } + if (rc != null) { + return rc; + } + } + + // not found or ambiguous + return null; + } + + /** + * Get location on the local file-system considering possible mapping by {@link EFSExtensionManager}. + */ + private static IPath getFilesystemLocation(URI uri) { + if (uri == null) + return null; + + String pathStr = EFSExtensionManager.getDefault().getMappedPath(uri); + uri = org.eclipse.core.filesystem.URIUtil.toURI(pathStr); + + try { + File file = new java.io.File(uri); + String canonicalPathStr = file.getCanonicalPath(); + return new Path(canonicalPathStr); + } catch (Exception e) { + ManagedBuilderCorePlugin.log(e); + } + return null; + } + + /** + * Resolve and create language settings path entry. + */ + private ICLanguageSettingEntry createResolvedPathEntry(AbstractOptionParser optionParser, + String parsedPath, int flag, URI baseURI) { + + String resolvedPath = null; + int resolvedFlag = 0; + + URI uri = determineMappedURI(parsedPath, baseURI); + IResource rc = null; + + // Try to resolve in the workspace + if (uri != null && uri.isAbsolute()) { + if (optionParser.isForFolder()) { + rc = findContainerForLocationURI(uri, currentProject); + } else if (optionParser.isForFile()) { + rc = findFileForLocationURI(uri, currentProject); + } + if (rc != null) { + resolvedPath = rc.getFullPath().toString(); + resolvedFlag = flag | ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED; + } + } + + // Try to resolve on the file-system + if (resolvedPath == null) { + IPath path = getFilesystemLocation(uri); + if (path != null && new File(path.toString()).exists()) { + resolvedPath = path.toString(); + resolvedFlag = flag; + } + if (resolvedPath == null) { + Set referencedProjectsNames = new LinkedHashSet(); + if (currentCfgDescription!=null) { + Map refs = currentCfgDescription.getReferenceInfo(); + referencedProjectsNames.addAll(refs.keySet()); + } + IResource resource = resolveResourceInWorkspace(parsedPath, currentProject, referencedProjectsNames); + if (resource != null) { + resolvedPath = resource.getFullPath().toString(); + resolvedFlag = flag | ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED; + } + } + if (resolvedPath == null && path != null) { + resolvedPath = path.toString(); + resolvedFlag = flag; + } + } + + // if cannot resolve keep parsed path + if (resolvedPath == null) { + resolvedPath = parsedPath; + resolvedFlag = flag; + } + + return optionParser.createEntry(resolvedPath, resolvedPath, resolvedFlag); + } + + /** + * Count how many groups are present in regular expression. + * The implementation is simplistic but should be sufficient for the cause. + * + * @param str - regular expression to count the groups. + * @return number of the groups (groups are enclosed in round brackets) present. + */ + protected static int countGroups(String str) { + @SuppressWarnings("nls") + int count = str.replaceAll("[^\\(]", "").length(); + return count; + } + + /** + * Helper method to construct logical "or" to be used inside regular expressions. + */ + @SuppressWarnings("nls") + private static String expressionLogicalOr(Set fileExts) { + String pattern = "("; + for (String ext : fileExts) { + if (pattern.length() != 1) + pattern += "|"; + pattern += "(" + Pattern.quote(ext) + ")"; + ext = ext.toUpperCase(); + if (!fileExts.contains(ext)) { + pattern += "|(" + Pattern.quote(ext) + ")"; + } + } + pattern += ")"; + return pattern; + } + + /** + * Construct regular expression to find any file extension for C or C++. + * Returns expression shaped in form of "((cpp)|(c++)|(c))". + * + * @return regular expression for searching C/C++ file extensions. + */ + protected String getPatternFileExtensions() { + IContentTypeManager manager = Platform.getContentTypeManager(); + + Set fileExts = new HashSet(); + + IContentType contentTypeCpp = manager.getContentType(CCorePlugin.CONTENT_TYPE_CXXSOURCE); + fileExts.addAll(Arrays.asList(contentTypeCpp.getFileSpecs(IContentType.FILE_EXTENSION_SPEC))); + + IContentType contentTypeC = manager.getContentType(CCorePlugin.CONTENT_TYPE_CSOURCE); + fileExts.addAll(Arrays.asList(contentTypeC.getFileSpecs(IContentType.FILE_EXTENSION_SPEC))); + + String pattern = expressionLogicalOr(fileExts); + + return pattern; + } + + @Override + public Element serializeAttributes(Element parentElement) { + Element elementProvider = super.serializeAttributes(parentElement); + elementProvider.setAttribute(ATTR_KEEP_RELATIVE_PATHS, Boolean.toString( ! isResolvingPaths )); + return elementProvider; + } + + @Override + public void loadAttributes(Element providerNode) { + super.loadAttributes(providerNode); + + String expandRelativePathsValue = XmlUtil.determineAttributeValue(providerNode, ATTR_KEEP_RELATIVE_PATHS); + if (expandRelativePathsValue!=null) + isResolvingPaths = ! Boolean.parseBoolean(expandRelativePathsValue); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + (isResolvingPaths ? 1231 : 1237); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + AbstractLanguageSettingsOutputScanner other = (AbstractLanguageSettingsOutputScanner) obj; + if (isResolvingPaths != other.isResolvingPaths) + return false; + return true; + } + +} diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/language/settings/providers/GCCBuildCommandParser.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/language/settings/providers/GCCBuildCommandParser.java new file mode 100644 index 00000000000..93d0e20062c --- /dev/null +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/language/settings/providers/GCCBuildCommandParser.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (c) 2009, 2012 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.managedbuilder.language.settings.providers; + + +import org.eclipse.cdt.core.errorparsers.RegexErrorPattern; +import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsEditableProvider; +import org.eclipse.cdt.core.settings.model.ICSettingEntry; + +/** + * Build command parser capable to parse gcc command in build output and generate + * language settings per file being compiled. + * + * @since 8.1 + */ +public class GCCBuildCommandParser extends AbstractBuildCommandParser implements ILanguageSettingsEditableProvider { + @SuppressWarnings("nls") + static final AbstractOptionParser[] optionParsers = { + new IncludePathOptionParser("-I\\s*([\"'])(.*)\\1", "$2"), + new IncludePathOptionParser("-I\\s*([^\\s\"']*)", "$1"), + new IncludeFileOptionParser("-include\\s*([\"'])(.*)\\1", "$2"), + new IncludeFileOptionParser("-include\\s*([^\\s\"']*)", "$1"), + new MacroOptionParser("-D\\s*([\"'])([^=]*)(=(.*))?\\1", "$2", "$4"), + new MacroOptionParser("-D\\s*([^\\s=\"']*)=(\\\\([\"']))(.*?)\\2", "$1", "$3$4$3"), + new MacroOptionParser("-D\\s*([^\\s=\"']*)=([\"'])(.*?)\\2", "$1", "$3"), + new MacroOptionParser("-D\\s*([^\\s=\"']*)(=([^\\s\"']*))?", "$1", "$3"), + new MacroOptionParser("-U\\s*([^\\s=\"']*)", "$1", ICSettingEntry.UNDEFINED), + new MacroFileOptionParser("-macros\\s*([\"'])(.*)\\1", "$2"), + new MacroFileOptionParser("-macros\\s*([^\\s\"']*)", "$1"), + new LibraryPathOptionParser("-L\\s*([\"'])(.*)\\1", "$2"), + new LibraryPathOptionParser("-L\\s*([^\\s\"']*)", "$1"), + new LibraryFileOptionParser("-l\\s*([^\\s\"']*)", "lib$1.a"), }; + + @Override + protected AbstractOptionParser[] getOptionParsers() { + return optionParsers; + } + + @Override + public GCCBuildCommandParser cloneShallow() throws CloneNotSupportedException { + return (GCCBuildCommandParser) super.cloneShallow(); + } + + @Override + public GCCBuildCommandParser clone() throws CloneNotSupportedException { + return (GCCBuildCommandParser) super.clone(); + } + + /** + * Error Parser which allows highlighting of output lines matching the patterns of this parser. + * Intended for better troubleshooting experience. + */ + public static class GCCBuildCommandPatternHighlighter extends AbstractBuildCommandParser.AbstractBuildCommandPatternHighlighter { + // ID of the parser taken from the existing extension point + private static final String GCC_BUILD_COMMAND_PARSER_EXT = "org.eclipse.cdt.managedbuilder.core.GCCBuildCommandParser"; //$NON-NLS-1$ + + /** + * Default constructor. + */ + public GCCBuildCommandPatternHighlighter() { + super(GCC_BUILD_COMMAND_PARSER_EXT); + } + + @Override + public Object clone() throws CloneNotSupportedException { + GCCBuildCommandPatternHighlighter that = new GCCBuildCommandPatternHighlighter(); + that.setId(getId()); + that.setName(getName()); + for (RegexErrorPattern pattern : getPatterns()) { + that.addPattern((RegexErrorPattern)pattern.clone()); + } + return that; + } + } + +} diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/language/settings/providers/GCCBuiltinSpecsDetector.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/language/settings/providers/GCCBuiltinSpecsDetector.java new file mode 100644 index 00000000000..865832c45ce --- /dev/null +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/language/settings/providers/GCCBuiltinSpecsDetector.java @@ -0,0 +1,133 @@ +/******************************************************************************* + * Copyright (c) 2009, 2012 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.managedbuilder.language.settings.providers; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsEditableProvider; +import org.eclipse.cdt.core.language.settings.providers.IWorkingDirectoryTracker; +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.core.settings.model.ICSettingEntry; +import org.eclipse.core.runtime.CoreException; + +/** + * Language settings provider to detect built-in compiler settings for GCC compiler. + * + * @since 8.1 + */ +public class GCCBuiltinSpecsDetector extends ToolchainBuiltinSpecsDetector implements ILanguageSettingsEditableProvider { + // ID must match the tool-chain definition in org.eclipse.cdt.managedbuilder.core.buildDefinitions extension point + private static final String GCC_TOOLCHAIN_ID = "cdt.managedbuild.toolchain.gnu.base"; //$NON-NLS-1$ + + private enum State {NONE, EXPECTING_LOCAL_INCLUDE, EXPECTING_SYSTEM_INCLUDE, EXPECTING_FRAMEWORKS} + private State state = State.NONE; + + @SuppressWarnings("nls") + private static final AbstractOptionParser[] optionParsers = { + new IncludePathOptionParser("#include \"(\\S.*)\"", "$1", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY | ICSettingEntry.LOCAL), + new IncludePathOptionParser("#include <(\\S.*)>", "$1", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), + new IncludePathOptionParser("#framework <(\\S.*)>", "$1", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY | ICSettingEntry.FRAMEWORKS_MAC), + new MacroOptionParser("#define\\s+(\\S*\\(.*?\\))\\s*(.*)", "$1", "$2", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), + new MacroOptionParser("#define\\s+(\\S*)\\s*(\\S*)", "$1", "$2", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), + }; + + @Override + protected String getToolchainId() { + return GCC_TOOLCHAIN_ID; + } + + @Override + protected AbstractOptionParser[] getOptionParsers() { + return optionParsers; + } + + /** + * Create a list from one item. + */ + private List makeList(String line) { + List list = new ArrayList(); + list.add(line); + return list; + } + + @SuppressWarnings("nls") + @Override + protected List parseOptions(String line) { + line = line.trim(); + + // contribution of -dD option + if (line.startsWith("#define")) { + return makeList(line); + } + + // contribution of includes + if (line.equals("#include \"...\" search starts here:")) { + state = State.EXPECTING_LOCAL_INCLUDE; + } else if (line.equals("#include <...> search starts here:")) { + state = State.EXPECTING_SYSTEM_INCLUDE; + } else if (line.startsWith("End of search list.")) { + state = State.NONE; + } else if (line.equals("Framework search starts here:")) { + state = State.EXPECTING_FRAMEWORKS; + } else if (line.startsWith("End of framework search list.")) { + state = State.NONE; + } else if (state==State.EXPECTING_LOCAL_INCLUDE) { + // making that up for the parser to figure out + line = "#include \""+line+"\""; + return makeList(line); + } else { + String frameworkIndicator = "(framework directory)"; + if (state==State.EXPECTING_SYSTEM_INCLUDE) { + // making that up for the parser to figure out + if (line.contains(frameworkIndicator)) { + line = "#framework <"+line.replace(frameworkIndicator, "").trim()+">"; + } else { + line = "#include <"+line+">"; + } + return makeList(line); + } else if (state==State.EXPECTING_FRAMEWORKS) { + // making that up for the parser to figure out + line = "#framework <"+line.replace(frameworkIndicator, "").trim()+">"; + return makeList(line); + } + } + + return null; + } + + @Override + public void startup(ICConfigurationDescription cfgDescription, IWorkingDirectoryTracker cwdTracker) throws CoreException { + super.startup(cfgDescription, cwdTracker); + + state = State.NONE; + } + + @Override + public void shutdown() { + state = State.NONE; + + super.shutdown(); + } + + @Override + public GCCBuiltinSpecsDetector cloneShallow() throws CloneNotSupportedException { + return (GCCBuiltinSpecsDetector) super.cloneShallow(); + } + + @Override + public GCCBuiltinSpecsDetector clone() throws CloneNotSupportedException { + return (GCCBuiltinSpecsDetector) super.clone(); + } + + +} diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/language/settings/providers/ToolchainBuiltinSpecsDetector.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/language/settings/providers/ToolchainBuiltinSpecsDetector.java new file mode 100644 index 00000000000..e919180d5d8 --- /dev/null +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/language/settings/providers/ToolchainBuiltinSpecsDetector.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2009, 2012 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.managedbuilder.language.settings.providers; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.cdt.managedbuilder.core.IInputType; +import org.eclipse.cdt.managedbuilder.core.ITool; +import org.eclipse.cdt.managedbuilder.core.IToolChain; +import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager; +import org.eclipse.cdt.managedbuilder.core.ManagedBuilderCorePlugin; + +/** + * Abstract parser capable to execute compiler command printing built-in compiler + * specs and parse built-in language settings out of it. The compiler to be used + * is taken from MBS tool-chain definition. + * + * @since 8.1 + */ +public abstract class ToolchainBuiltinSpecsDetector extends AbstractBuiltinSpecsDetector { + private Map toolMap = new HashMap(); + + /** + * Concrete compiler specs detectors need to supply tool-chain ID. + * + * Tool-chain id must be supplied for global providers where we don't + * have configuration description to figure that out programmatically. + */ + protected abstract String getToolchainId(); + + /** + * Finds a tool handling given language in the tool-chain of the provider. + * This returns the first tool found. + */ + private ITool getTool(String languageId) { + ITool langTool = toolMap.get(languageId); + if (langTool != null) { + return langTool; + } + + String toolchainId = getToolchainId(); + for (IToolChain toolchain = ManagedBuildManager.getExtensionToolChain(toolchainId);toolchain != null;toolchain = toolchain.getSuperClass()) { + ITool tool = getTool(languageId, toolchain); + if (tool != null) { + return tool; + } + } + ManagedBuilderCorePlugin.error("Unable to find tool in toolchain=" + toolchainId + " for language=" + languageId); //$NON-NLS-1$ //$NON-NLS-2$ + return null; + } + + /** + * Finds a tool handling given language in the tool-chain. + * This returns the first tool found. + */ + private ITool getTool(String languageId, IToolChain toolchain) { + ITool[] tools = toolchain.getTools(); + for (ITool tool : tools) { + IInputType[] inputTypes = tool.getInputTypes(); + for (IInputType inType : inputTypes) { + String lang = inType.getLanguageId(tool); + if (languageId.equals(lang)) { + toolMap.put(languageId, tool); + return tool; + } + } + } + return null; + } + + @Override + protected String getCompilerCommand(String languageId) { + ITool tool = getTool(languageId); + String compilerCommand = tool.getToolCommand(); + if (compilerCommand.isEmpty()) { + ManagedBuilderCorePlugin.error("Unable to find compiler command in toolchain=" + getToolchainId()); //$NON-NLS-1$ + } + return compilerCommand; + } + + @Override + protected String getSpecFileExtension(String languageId) { + String ext = null; + ITool tool = getTool(languageId); + String[] srcFileExtensions = tool.getAllInputExtensions(); + if (srcFileExtensions != null && srcFileExtensions.length > 0) { + ext = srcFileExtensions[0]; + } + if (ext == null || ext.isEmpty()) { + ManagedBuilderCorePlugin.error("Unable to find file extension for language " + languageId); //$NON-NLS-1$ + } + return ext; + } + +} diff --git a/core/org.eclipse.cdt.core.tests/misc/org/eclipse/cdt/core/internal/tests/filesystem/ram/MemoryEFSExtensionProvider.java b/core/org.eclipse.cdt.core.tests/misc/org/eclipse/cdt/core/internal/tests/filesystem/ram/MemoryEFSExtensionProvider.java new file mode 100644 index 00000000000..279f507aae8 --- /dev/null +++ b/core/org.eclipse.cdt.core.tests/misc/org/eclipse/cdt/core/internal/tests/filesystem/ram/MemoryEFSExtensionProvider.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2010, 2011 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.core.internal.tests.filesystem.ram; + +import java.net.URI; + +import org.eclipse.cdt.core.EFSExtensionProvider; + +/** + * Test stub to test EFSExtensionProvider mappings. + * + */ +public class MemoryEFSExtensionProvider extends EFSExtensionProvider { + + public String getMappedPath(URI locationURI) { + + String path = locationURI.getPath(); + if (path.contains("/BeingMappedFrom/Folder")) { + return path.replaceFirst("/BeingMappedFrom/Folder", "/LocallyMappedTo/Folder"); + } + + return super.getMappedPath(locationURI); + } + +} diff --git a/core/org.eclipse.cdt.core.tests/plugin.xml b/core/org.eclipse.cdt.core.tests/plugin.xml index 019bccf959b..1302c39e660 100644 --- a/core/org.eclipse.cdt.core.tests/plugin.xml +++ b/core/org.eclipse.cdt.core.tests/plugin.xml @@ -240,6 +240,13 @@
+ + + + lsProviders = ((ILanguageSettingsProvidersKeeper) cfgDescription).getLanguageSettingProviders(); + for (ILanguageSettingsProvider lsp : lsProviders) { + if (MBS_LANGUAGE_SETTINGS_PROVIDER_ID.equals(lsp.getId())) { + return true; + } + } + } + return false; + } + + /** + * @noreference This is internal helper method to support compatibility with previous versions + * which is not intended to be referenced by clients. + */ + public static boolean isLegacyScannerDiscoveryOn(ICConfigurationDescription cfgDescription) { + IProject project = null; + if (cfgDescription != null) { + ICProjectDescription prjDescription = cfgDescription.getProjectDescription(); + if (prjDescription != null) { + project = prjDescription.getProject(); + } + } + return isLanguageSettingsProvidersFunctionalityEnabled(project) || isMbsLanguageSettingsProviderOn(cfgDescription); + } + + /** + * @noreference This is internal helper method to support compatibility with previous versions + * which is not intended to be referenced by clients. + */ + public static boolean isLegacyScannerDiscoveryOn(IProject project) { + ICConfigurationDescription cfgDescription = null; + ICProjectDescription prjDescription = CoreModel.getDefault().getProjectDescription(project); + if (prjDescription != null) { + cfgDescription = prjDescription.getActiveConfiguration(); + } + return isLanguageSettingsProvidersFunctionalityEnabled(project) || isMbsLanguageSettingsProviderOn(cfgDescription); + } + /** * Return list containing MBS and User provider. Used to initialize for unaware tool-chains (backward compatibility). */ diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/settings/model/util/CDataUtil.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/settings/model/util/CDataUtil.java index ab217399b42..df806313cad 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/settings/model/util/CDataUtil.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/settings/model/util/CDataUtil.java @@ -334,10 +334,8 @@ public class CDataUtil { } /** - * Convenience method to create {@link ICLanguageSettingEntry} depending on kind. + * Convenience method to create {@link ICSettingEntry} depending on kind. * Note that this method keeps the entries in the pool to avoid proliferation of duplicates. - * - * Note that the method always returns {@link ICLanguageSettingEntry}. */ public static ICSettingEntry createEntry(int kind, String name, String value, IPath[] exclusionPatterns, int flags) { return createEntry(kind, name, value, exclusionPatterns, flags, null, null, null); diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsProvidersSerializer.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsProvidersSerializer.java index d1cf42c52cf..12b282eab0a 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsProvidersSerializer.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsProvidersSerializer.java @@ -36,6 +36,7 @@ import org.eclipse.cdt.core.settings.model.ICStorageElement; import org.eclipse.cdt.internal.core.XmlUtil; import org.eclipse.cdt.internal.core.settings.model.CConfigurationSpecSettings; import org.eclipse.cdt.internal.core.settings.model.IInternalCCfgInfo; +import org.eclipse.cdt.internal.core.settings.model.SettingsModelMessages; import org.eclipse.core.filesystem.URIUtil; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; @@ -47,13 +48,16 @@ import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.Assert; 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.ListenerList; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.ILock; +import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.InstanceScope; +import org.eclipse.osgi.util.NLS; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -70,11 +74,16 @@ public class LanguageSettingsProvidersSerializer { public static final String ELEM_PROVIDER = LanguageSettingsExtensionManager.ELEM_PROVIDER; public static final String ELEM_LANGUAGE_SCOPE = LanguageSettingsExtensionManager.ELEM_LANGUAGE_SCOPE; + private static final String JOB_FAMILY_SERIALIZE_LANGUAGE_SETTINGS_PROJECT = "CDT_JOB_FAMILY_SERIALIZE_LANGUAGE_SETTINGS_PROJECT"; //$NON-NLS-1$ + private static final String JOB_FAMILY_SERIALIZE_LANGUAGE_SETTINGS_WORKSPACE = "CDT_JOB_FAMILY_SERIALIZE_LANGUAGE_SETTINGS_WORKSPACE"; //$NON-NLS-1$ private static final String PREFERENCE_WORSPACE_PROVIDERS_SET = "language.settings.providers.workspace.prefs.toggle"; //$NON-NLS-1$ private static final String CPROJECT_STORAGE_MODULE = "org.eclipse.cdt.core.LanguageSettingsProviders"; //$NON-NLS-1$ private static final String STORAGE_WORKSPACE_LANGUAGE_SETTINGS = "language.settings.xml"; //$NON-NLS-1$ private static final String STORAGE_PROJECT_PATH = ".settings/language.settings.xml"; //$NON-NLS-1$ + private static final int PROGRESS_MONITOR_SCALE = 100; + private static final int TICKS_SERIALIZING = 1 * PROGRESS_MONITOR_SCALE; + private static final String ELEM_PLUGIN = "plugin"; //$NON-NLS-1$ private static final String ELEM_EXTENSION = "extension"; //$NON-NLS-1$ private static final String ATTR_EXTENSION_POINT = "point"; //$NON-NLS-1$ @@ -537,6 +546,46 @@ public class LanguageSettingsProvidersSerializer { } } + /** + * Save language settings providers of the workspace (global providers) to persistent storage + * in background. + */ + public static void serializeLanguageSettingsWorkspaceInBackground() { + Job[] jobs = Job.getJobManager().find(JOB_FAMILY_SERIALIZE_LANGUAGE_SETTINGS_WORKSPACE); + for (Job job : jobs) { + if (job.getState() == Job.WAITING) { + // do not schedule if there is serializing job in queue already + return; + } + } + + Job job = new Job(SettingsModelMessages.getString("LanguageSettingsProvidersSerializer.SerializeJobName")) { //$NON-NLS-1$ + @Override + protected IStatus run(IProgressMonitor monitor) { + try { + monitor.beginTask(SettingsModelMessages.getString("LanguageSettingsProvidersSerializer.SerializingForWorkspace"), //$NON-NLS-1$ + TICKS_SERIALIZING); + serializeLanguageSettingsWorkspace(); + monitor.worked(TICKS_SERIALIZING); + return Status.OK_STATUS; + } catch (Throwable e) { + String msg = "Internal error running job of serializing language settings in workspace"; //$NON-NLS-1$ + return new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, msg, e); + } finally { + monitor.done(); + } + } + @Override + public boolean belongsTo(Object family) { + return family == JOB_FAMILY_SERIALIZE_LANGUAGE_SETTINGS_WORKSPACE; + } + }; + + job.setRule(null); + job.schedule(); + } + + /** * Load language settings for workspace. */ @@ -811,6 +860,57 @@ public class LanguageSettingsProvidersSerializer { } } + /** + * Save language settings providers of a project to persistent storage in background. + * + * @param prjDescription - project description of the project. + */ + public static void serializeLanguageSettingsInBackground(final ICProjectDescription prjDescription) { + Job job = new Job(SettingsModelMessages.getString("LanguageSettingsProvidersSerializer.SerializeJobName")) { //$NON-NLS-1$ + @Override + protected IStatus run(IProgressMonitor monitor) { + try { + monitor.beginTask(NLS.bind(SettingsModelMessages.getString("LanguageSettingsProvidersSerializer.SerializingForProject"), //$NON-NLS-1$ + prjDescription.getName()), TICKS_SERIALIZING); + serializeLanguageSettings(prjDescription); + monitor.worked(TICKS_SERIALIZING); + return Status.OK_STATUS; + } catch (Throwable e) { + String msg = "Internal error running job of serializing language settings for project " + prjDescription.getName(); //$NON-NLS-1$ + return new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, msg, e); + } finally { + monitor.done(); + } + } + @Override + public boolean belongsTo(Object family) { + return family == JOB_FAMILY_SERIALIZE_LANGUAGE_SETTINGS_PROJECT; + } + }; + + ISchedulingRule rule = null; + IProject project = prjDescription.getProject(); + if (project != null) { + IFile fileStorePrj = getStoreInProjectArea(project); + IContainer folder = fileStorePrj.getParent(); + if (folder instanceof IFolder && !folder.exists()) { + try { + ((IFolder) folder).create(true, true, null); + } catch (CoreException e) { + CCorePlugin.log(e); + } + } + if (folder.isAccessible()) { + rule = fileStorePrj; + } + } + if (rule == null) { + rule = ResourcesPlugin.getWorkspace().getRoot(); + } + job.setRule(rule); + job.schedule(); + } + /** * Load language settings to the project description from XML. * diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/settings/model/SettingsModelMessages.properties b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/settings/model/SettingsModelMessages.properties index 38f30f17a08..0c15a9714c4 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/settings/model/SettingsModelMessages.properties +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/settings/model/SettingsModelMessages.properties @@ -51,6 +51,9 @@ ExtensionContainerFactory.4=invalid setting provider class specified ExtensionContainerFactory.5=provider element not specified LanguageSettingsBaseProvider.CanBeConfiguredOnlyOnce=LanguageSettingsBaseProvider can be configured only once LanguageSettingsScannerInfoProvider.UnableToDetermineLanguage=Error getting ScannerInfo: Unable to determine language for resource {0} +LanguageSettingsProvidersSerializer.SerializeJobName=Serialize CDT language settings entries +LanguageSettingsProvidersSerializer.SerializingForProject="Serializing language settings for project {0} +LanguageSettingsProvidersSerializer.SerializingForWorkspace="Serializing language settings for global providers SettingsContext.0=no project associated with the context SettingsContext.1=can not accept the not-context project description diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePlugin.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePlugin.java index d0f6842cb5f..2357f2e4473 100644 --- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePlugin.java +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePlugin.java @@ -531,8 +531,10 @@ public class CCorePlugin extends Plugin { * * * @return CDT console adapter. + * + * @since 5.4 */ - private IConsole getConsole(String extConsoleId, String contextId, String name, URL iconUrl) { + public IConsole getConsole(String extConsoleId, String contextId, String name, URL iconUrl) { try { IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(CCorePlugin.PLUGIN_ID, "CBuildConsole"); //$NON-NLS-1$ if (extensionPoint != null) { diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ErrorParserManager.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ErrorParserManager.java index 37777c8a269..ee224f5fa4c 100644 --- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ErrorParserManager.java +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ErrorParserManager.java @@ -24,6 +24,7 @@ import java.util.Map; import java.util.Vector; import org.eclipse.cdt.core.errorparsers.ErrorParserNamedWrapper; +import org.eclipse.cdt.core.language.settings.providers.IWorkingDirectoryTracker; import org.eclipse.cdt.core.resources.ACBuilder; import org.eclipse.cdt.internal.core.Cygwin; import org.eclipse.cdt.internal.core.IErrorMarkeredOutputStream; @@ -50,7 +51,7 @@ import org.osgi.service.prefs.BackingStoreException; * * @noextend This class is not intended to be subclassed by clients. */ -public class ErrorParserManager extends OutputStream implements IConsoleParser { +public class ErrorParserManager extends OutputStream implements IConsoleParser, IWorkingDirectoryTracker { /** * The list of error parsers stored in .project for 3.X projects * as key/value pair with key="org.eclipse.cdt.core.errorOutputParser" @@ -193,6 +194,7 @@ public class ErrorParserManager extends OutputStream implements IConsoleParser { * @return the current URI location where the build is being performed * @since 5.1 */ + @Override public URI getWorkingDirectoryURI() { if (!fDirectoryStack.isEmpty()) return fDirectoryStack.lastElement(); diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/IConsoleParser.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/IConsoleParser.java index 362bb11dee7..ca41651e021 100644 --- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/IConsoleParser.java +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/IConsoleParser.java @@ -1,10 +1,10 @@ /******************************************************************************* - * Copyright (c) 2004, 2009 IBM Corporation and others. + * Copyright (c) 2004, 2012 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html - * + * * Contributors: * IBM - Initial API and implementation *******************************************************************************/ @@ -12,21 +12,17 @@ package org.eclipse.cdt.core; /** * A basic interface for console parsers - * - * @author vhirsl - * @noextend This interface is not intended to be extended by clients. - * @noimplement This interface is not intended to be implemented by clients. */ public interface IConsoleParser { /** * Parse one line of output. - * + * * @param line * @return true if line was successfully processed; skip other console parsers

* false - try other console parsers */ public boolean processLine(String line); - + /** * Finalization of a console parser when the stream is closed. */ diff --git a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/internal/core/Cygwin.java b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/internal/core/Cygwin.java index 00c9abe7650..e0b770d87c9 100644 --- a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/internal/core/Cygwin.java +++ b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/internal/core/Cygwin.java @@ -31,13 +31,23 @@ public class Cygwin { * Check if cygwin path conversion utilities are available in the path. * * @param envPath - list of directories to search for cygwin utilities separated - * by path separator (format of environment variable $PATH). + * by path separator (format of environment variable $PATH) + * or {@code null} to use current $PATH. * @return {@code true} if cygwin is available, {@code false} otherwise. */ public static boolean isAvailable(String envPath) { return Platform.getOS().equals(Platform.OS_WIN32) && findCygpathLocation(envPath) != null; } + /** + * Check if cygwin path conversion utilities are available in $PATH. + * + * @return {@code true} if cygwin is available, {@code false} otherwise. + */ + public static boolean isAvailable() { + return Platform.getOS().equals(Platform.OS_WIN32) && findCygpathLocation(null) != null; + } + /** * Conversion from Cygwin path to Windows path. * diff --git a/xlc/org.eclipse.cdt.managedbuilder.xlc.core/plugin.properties b/xlc/org.eclipse.cdt.managedbuilder.xlc.core/plugin.properties index 9d000f26688..1f8857cdb4d 100644 --- a/xlc/org.eclipse.cdt.managedbuilder.xlc.core/plugin.properties +++ b/xlc/org.eclipse.cdt.managedbuilder.xlc.core/plugin.properties @@ -16,4 +16,7 @@ providerName=Eclipse CDT objectFileName=Object File profileName=XL C managed make per project scanner discovery profile -profileNameCPP=XL C++ managed make per project scanner discovery profile \ No newline at end of file +profileNameCPP=XL C++ managed make per project scanner discovery profile + +XlcBuiltinSpecsDetectorName=CDT XLC Builtin Compiler Settings +XlcBuildCommandParserName=CDT XLC Build Output Parser diff --git a/xlc/org.eclipse.cdt.managedbuilder.xlc.core/plugin.xml b/xlc/org.eclipse.cdt.managedbuilder.xlc.core/plugin.xml index 9e581224621..dafa1c27d85 100644 --- a/xlc/org.eclipse.cdt.managedbuilder.xlc.core/plugin.xml +++ b/xlc/org.eclipse.cdt.managedbuilder.xlc.core/plugin.xml @@ -74,6 +74,25 @@ class="org.eclipse.cdt.make.xlc.core.scannerconfig.XlCSpecsConsoleParser"> - + + + + + + + + + + diff --git a/xlc/org.eclipse.cdt.managedbuilder.xlc.core/src/org/eclipse/cdt/managedbuilder/xlc/core/XlcBuiltinSpecsDetector.java b/xlc/org.eclipse.cdt.managedbuilder.xlc.core/src/org/eclipse/cdt/managedbuilder/xlc/core/XlcBuiltinSpecsDetector.java new file mode 100644 index 00000000000..fe92bbb5839 --- /dev/null +++ b/xlc/org.eclipse.cdt.managedbuilder.xlc.core/src/org/eclipse/cdt/managedbuilder/xlc/core/XlcBuiltinSpecsDetector.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2009, 2011 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.managedbuilder.xlc.core; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsEditableProvider; +import org.eclipse.cdt.core.settings.model.ICSettingEntry; +import org.eclipse.cdt.managedbuilder.language.settings.providers.ToolchainBuiltinSpecsDetector; + +/** + * Language settings provider to detect built-in compiler settings for IBM XLC compiler. + */ +public class XlcBuiltinSpecsDetector extends ToolchainBuiltinSpecsDetector implements ILanguageSettingsEditableProvider { + // must match the toolchain definition in org.eclipse.cdt.managedbuilder.core.buildDefinitions extension point + private static final String XLC_TOOLCHAIN_ID = "cdt.managedbuild.toolchain.xlc.exe.debug"; //$NON-NLS-1$ + + private static final Pattern OPTIONS_PATTERN = Pattern.compile("-[^\\s\"']*(\\s*((\".*?\")|('.*?')|([^-\\s][^\\s]+)))?"); //$NON-NLS-1$ + private static final int OPTION_GROUP = 0; + + /* Sample output: + + > xlC -E -V -P -w ~/tmp/spec.C + export XL_CONFIG=/etc/vac.cfg:xlC + /usr/vac/exe/xlCcpp /home/me/tmp/spec.C - -qc++=/usr/vacpp/include -D_AIX -D_AIX32 -D_AIX41 -D_AIX43 -D_AIX50 -D_AIX51 -D_AIX52 -D_IBMR2 -D_POWER -E -P -w -qlanglvl=ansi -qansialias + rm /tmp/xlcW0lt4Jia + rm /tmp/xlcW1lt4Jib + rm /tmp/xlcW2lt4Jic + */ + @SuppressWarnings("nls") + private static final AbstractOptionParser[] optionParsers = { + new IncludePathOptionParser("-I\\s*([\"'])(.*)\\1", "$2", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY | ICSettingEntry.LOCAL), + new IncludePathOptionParser("-I\\s*([^\\s\"']*)", "$1", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), + new IncludePathOptionParser("-qc\\+\\+=\\s*([^\\s\"']*)", "$1", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), + new MacroOptionParser("-D\\s*([\"'])([^=]*)(=(.*))?\\1", "$2", "$4", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), + new MacroOptionParser("-D\\s*([^\\s=\"']*)=(\\\\([\"']))(.*?)\\2", "$1", "$3$4$3", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), + new MacroOptionParser("-D\\s*([^\\s=\"']*)=([\"'])(.*?)\\2", "$1", "$3", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), + new MacroOptionParser("-D\\s*([^\\s=\"']*)(=([^\\s\"']*))?", "$1", "$3", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), + }; + + @Override + protected String getToolchainId() { + return XLC_TOOLCHAIN_ID; + } + + @Override + protected AbstractOptionParser[] getOptionParsers() { + return optionParsers; + } + + @Override + protected List parseOptions(String line) { + List options = new ArrayList(); + Matcher optionMatcher = OPTIONS_PATTERN.matcher(line); + while (optionMatcher.find()) { + String option = optionMatcher.group(OPTION_GROUP); + if (option!=null) { + options.add(option); + } + } + return options; + } + + @Override + public XlcBuiltinSpecsDetector cloneShallow() throws CloneNotSupportedException { + return (XlcBuiltinSpecsDetector) super.cloneShallow(); + } + + @Override + public XlcBuiltinSpecsDetector clone() throws CloneNotSupportedException { + return (XlcBuiltinSpecsDetector) super.clone(); + } + +}