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 index 182ecfbd036..fa106fef2a6 100644 --- 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 @@ -1130,6 +1130,51 @@ public class GCCBuildCommandParserTest extends BaseTestCase { } } + /** + * Test use of a line-continuation character ('\') in build output. + */ + public void testLineContinuation() 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 file0.cpp -I/path0 \\"); + parser.processLine(" -I/path1"); + parser.processLine("gcc file1.cpp -I/path0 \\\\"); // not a continuation, '\' is escaped! + parser.processLine(" -I/path1"); + parser.processLine("gcc file2.cpp -I/path0 \\"); // continuation at end of last line + parser.shutdown(); + + // check populated entries + { + List entries = parser.getSettingEntries(cfgDescription, file0, languageId); + assertEquals(new CIncludePathEntry("/path0", 0), entries.get(0)); + assertEquals(new CIncludePathEntry("/path1", 0), entries.get(1)); + } + { + List entries = parser.getSettingEntries(cfgDescription, file1, languageId); + assertEquals(new CIncludePathEntry("/path0", 0), entries.get(0)); + assertEquals(1, entries.size()); + } + { + List entries = parser.getSettingEntries(cfgDescription, file2, languageId); + assertEquals(new CIncludePathEntry("/path0", 0), entries.get(0)); + } + } + /** * Test parsing of paths located on a different drive on Windows. */ 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 index 758081cd98d..f1b6af9112f 100644 --- 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 @@ -70,6 +70,9 @@ public abstract class AbstractBuildCommandParser extends AbstractLanguageSetting // cached value from properties, do not need to use in equals() and hashCode() private ResourceScope resourceScope = null; + + // Used to handle line continuations in the build output. + private String partialLine; /** * The compiler command pattern without specifying compiler options. @@ -241,9 +244,50 @@ public abstract class AbstractBuildCommandParser extends AbstractLanguageSetting @Override public void shutdown() { + // If there's an unprocessed partial line (because the last line of the build output ended + // in a line-continuation character), process it. + if (partialLine != null) { + processLine(partialLine); + partialLine = null; + } + serializeLanguageSettingsInBackground(); super.shutdown(); } + + @Override + public boolean processLine(String line) { + line = handleLineContinuation(line); + return super.processLine(line); + } + + /** + * Handle line continuations ('\' at the end of a line, indicating that the next line is a + * continuation of this one). + */ + private String handleLineContinuation(String line) { + if (line == null) + return null; + + // If the character preceding the '\' is also '\', it's not a line continuation - + // the first '\' escapes the second. + if (line.length() > 0 && line.charAt(line.length() - 1) == '\\' && + (line.length() == 1 || line.charAt(line.length() - 2) != '\\')) { + // Line ends in line continuation - save it for later. + String fragment = line.substring(0, line.length() - 1); + if (partialLine == null) { + partialLine = fragment; + } else { + partialLine += fragment; + } + return null; // line will not be processed now + } else if (partialLine != null) { + // Line doesn't end in continuation but previous lines did - use their contents. + line = partialLine + line; + partialLine = null; + } + return line; + } /** * Trivial Error Parser which allows highlighting of output lines matching the patterns