diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/scanner/InclusionTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/scanner/InclusionTests.java index 003dee25a2c..e86e40256dc 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/scanner/InclusionTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/scanner/InclusionTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2004, 2008 IBM Corporation and others. + * Copyright (c) 2004, 2009 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 @@ -49,6 +49,7 @@ public class InclusionTests extends PreprocessorTestsBase { super(name); } + @Override protected void tearDown() throws Exception { if (fProject != null) { CProjectHelper.delete(fProject); @@ -77,6 +78,36 @@ public class InclusionTests extends PreprocessorTestsBase { return folder; } + // #include "one.h" + // #include "f1/two.h" + // #include "f1/f2/three.h" + public void testIncludeVariables() throws Exception { + String content= getAboveComment(); + + IFolder f0 = importFolder(".framework"); + importFolder("f1.framework"); + importFolder("f1"); + importFolder("f1/f2.framework"); + importFolder("f3"); + IFile base = importFile("base.cpp", content); + + importFile(".framework/one.h", "1"); + importFile("f1.framework/two.h", "2"); + importFile("f1/f2.framework/three.h", "3"); + + String[] path = { + f0.getLocation().removeLastSegments(1) + "/__framework__.framework/__filename__" + }; + IScannerInfo scannerInfo = new ExtendedScannerInfo(Collections.EMPTY_MAP, path, new String[]{}, null); + CodeReader reader= new CodeReader(base.getLocation().toString()); + initializeScanner(reader, ParserLanguage.C, ParserMode.COMPLETE_PARSE, scannerInfo); + + // first file is not picked up (no framework) + validateInteger("2"); + // third file is not picked up (framework must be a single folder) + validateEOF(); + } + public void testIncludeNext() throws Exception { String baseFile = "int zero; \n#include \"foo.h\""; //$NON-NLS-1$ String i1Next = "int one; \n#include_next "; //$NON-NLS-1$ diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/scanner/PreprocessorTestsBase.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/scanner/PreprocessorTestsBase.java index 455ec810a0b..44b6e85a248 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/scanner/PreprocessorTestsBase.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/scanner/PreprocessorTestsBase.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2008 Wind River Systems, Inc. and others. + * Copyright (c) 2007, 2009 Wind River Systems, Inc. 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 @@ -88,9 +88,17 @@ public abstract class PreprocessorTestsBase extends BaseTestCase { } protected void initializeScanner() throws Exception { + initializeScanner(getAboveComment()); + } + + protected StringBuffer[] getTestContent(int sections) throws IOException { StringBuffer[] input= TestSourceReader.getContentsForTest( - CTestPlugin.getDefault().getBundle(), "parser", getClass(), getName(), 1); - initializeScanner(input[0].toString()); + CTestPlugin.getDefault().getBundle(), "parser", getClass(), getName(), sections); + return input; + } + + protected String getAboveComment() throws IOException { + return getTestContent(1)[0].toString(); } protected void fullyTokenize() throws Exception { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/IScannerInfo.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/IScannerInfo.java index 52cb31bdb8a..e26adbfffc1 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/IScannerInfo.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/IScannerInfo.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2002, 2008 IBM Corporation and others. + * Copyright (c) 2002, 2009 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 @@ -30,6 +30,15 @@ public interface IScannerInfo { /** * Returns an array of paths that are searched when processing an include directive. * see {@link IExtendedScannerInfo#getLocalIncludePath()} + * + * In order to handle framework includes used on Apple Computers you can make use of + * the two variables: '__framework__' and '__filename__'. + *
E.g.: /System/Library/Frameworks/__framework__.framework/Headers/__filename__, + * /System/Library/Frameworks/__framework__.framework/PrivateHeaders/__filename__ + * would handle the framework search for '/System/Library/Frameworks' + * + * The variables are handled only, if a search path element makes use of both of the variables. + * Such a search path element is not used for directives that are not of the form 'folder/name'. */ public String[] getIncludePaths(); } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java index 4ec774f92bb..f21fa15c5a1 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java @@ -74,7 +74,6 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { private static final char[] EMPTY_CHAR_ARRAY = new char[0]; private static final char[] ONE = "1".toCharArray(); //$NON-NLS-1$ - private static final String EMPTY_STRING = ""; //$NON-NLS-1$ // standard built-ins @@ -96,15 +95,15 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { private static final Token END_OF_INPUT = new Token(IToken.tEND_OF_INPUT, null, 0, 0); private interface IIncludeFileTester { - T checkFile(String path, String fileName, boolean isHeuristicMatch); + T checkFile(String path, boolean isHeuristicMatch, IncludeSearchPathElement onPath); } - final private IIncludeFileTester createCodeReaderTester= new IIncludeFileTester() { - public IncludeFileContent checkFile(String path, String fileName, boolean isHeuristicMatch) { - final String finalPath = ScannerUtility.createReconciledPath(path, fileName); - final IncludeFileContent fc= fCodeReaderFactory.getContentForInclusion(finalPath); + final private IIncludeFileTester createCodeReaderTester= new IIncludeFileTester() { + public IncludeFileContent checkFile(String path, boolean isHeuristicMatch, IncludeSearchPathElement onPath) { + final IncludeFileContent fc= fCodeReaderFactory.getContentForInclusion(path); if (fc != null) { fc.setFoundByHeuristics(isHeuristicMatch); + fc.setFoundOnPath(onPath); } return fc; } @@ -112,12 +111,11 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { private static class IncludeResolution {String fLocation; boolean fHeuristic;} final private IIncludeFileTester createPathTester= new IIncludeFileTester() { - public IncludeResolution checkFile(String path, String fileName, boolean isHeuristicMatch) { - String finalPath= ScannerUtility.createReconciledPath(path, fileName); - if (fCodeReaderFactory.getInclusionExists(finalPath)) { + public IncludeResolution checkFile(String path, boolean isHeuristicMatch, IncludeSearchPathElement onPath) { + if (fCodeReaderFactory.getInclusionExists(path)) { IncludeResolution res= new IncludeResolution(); res.fHeuristic= isHeuristicMatch; - res.fLocation= finalPath; + res.fLocation= path; return res; } return null; @@ -168,8 +166,7 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { final private char[] fAdditionalNumericLiteralSuffixes; final private CharArrayIntMap fKeywords; final private CharArrayIntMap fPPKeywords; - final private String[] fIncludePaths; - final private String[] fQuoteIncludePaths; + private IncludeSearchPathElement[] fIncludeSearchPath; private String[][] fPreIncludedFiles= null; private int fContentAssistLimit= -1; @@ -206,9 +203,7 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { fKeywords= new CharArrayIntMap(40, -1); fPPKeywords= new CharArrayIntMap(40, -1); configureKeywords(language, configuration); - - fIncludePaths= info.getIncludePaths(); - fQuoteIncludePaths= getQuoteIncludePath(info); + configureIncludeSearchPath(info); fExpressionEvaluator= new ExpressionEvaluator(); fMacroDefinitionParser= new MacroDefinitionParser(); @@ -318,18 +313,25 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { return array == null ? EMPTY_CHAR_ARRAY : array; } - private String[] getQuoteIncludePath(IScannerInfo info) { + private void configureIncludeSearchPath(IScannerInfo info) { + String[] searchPath= info.getIncludePaths(); + int idx= 0; if (info instanceof IExtendedScannerInfo) { final IExtendedScannerInfo einfo= (IExtendedScannerInfo) info; - final String[] qip= einfo.getLocalIncludePath(); - if (qip != null && qip.length > 0) { - final String[] result= new String[qip.length + fIncludePaths.length]; - System.arraycopy(qip, 0, result, 0, qip.length); - System.arraycopy(fIncludePaths, 0, result, qip.length, fIncludePaths.length); - return result; - } + final String[] quoteIncludeSearchPath= einfo.getLocalIncludePath(); + if (quoteIncludeSearchPath != null && quoteIncludeSearchPath.length > 0) { + fIncludeSearchPath= new IncludeSearchPathElement[quoteIncludeSearchPath.length + searchPath.length]; + for (String qip : quoteIncludeSearchPath) { + fIncludeSearchPath[idx++]= new IncludeSearchPathElement(qip, true); + } + } } - return info.getIncludePaths(); + if (fIncludeSearchPath == null) { + fIncludeSearchPath= new IncludeSearchPathElement[searchPath.length]; + } + for (String path : searchPath) { + fIncludeSearchPath[idx++]= new IncludeSearchPathElement(path, false); + } } private void setupMacroDictionary(IScannerExtensionConfiguration config, IScannerInfo info, ParserLanguage lang) { @@ -872,20 +874,20 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { } } - private T findInclusion(final String filename, final boolean quoteInclude, + private T findInclusion(final String includeDirective, final boolean quoteInclude, final boolean includeNext, final String currentFile, final IIncludeFileTester tester) { T reader = null; // filename is an absolute path or it is a Linux absolute path on a windows machine - if (new File(filename).isAbsolute() || filename.startsWith("/")) { //$NON-NLS-1$ - return tester.checkFile(EMPTY_STRING, filename, false); + if (new File(includeDirective).isAbsolute() || includeDirective.startsWith("/")) { //$NON-NLS-1$ + return tester.checkFile(includeDirective, false, null); } if (currentFile != null && quoteInclude && !includeNext) { // Check to see if we find a match in the current directory final File currentDir= new File(currentFile).getParentFile(); if (currentDir != null) { - String absolutePath = currentDir.getAbsolutePath(); - reader = tester.checkFile(absolutePath, filename, false); + final String fileLocation = ScannerUtility.createReconciledPath(currentDir.getAbsolutePath(), includeDirective); + reader = tester.checkFile(fileLocation, false, null); if (reader != null) { return reader; } @@ -894,43 +896,31 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { // if we're not include_next, then we are looking for the first occurrence of // the file, otherwise, we ignore all the paths before the current directory - final String[] isp= quoteInclude ? fQuoteIncludePaths : fIncludePaths; - if (isp != null) { - int i=0; - if (includeNext && currentFile != null) { - final File currentDir= new File(currentFile).getParentFile(); - if (currentDir != null) { - i= findIncludePos(isp, currentDir) + 1; + IncludeSearchPathElement searchAfter= includeNext ? fCurrentContext.getFoundOnPath() : null; + for (IncludeSearchPathElement path : fIncludeSearchPath) { + if (searchAfter != null) { + if (searchAfter.equals(path)) { + searchAfter= null; } - } - for (; i < isp.length; ++i) { - reader= tester.checkFile(isp[i], filename, false); - if (reader != null) { - return reader; + } else if (quoteInclude || !path.isForQuoteIncludesOnly()) { + String fileLocation = path.getLocation(includeDirective); + if (fileLocation != null) { + reader= tester.checkFile(fileLocation, false, path); + if (reader != null) { + return reader; + } } } } if (fIncludeFileResolutionHeuristics != null) { - String location= fIncludeFileResolutionHeuristics.findInclusion(filename, currentFile); + String location= fIncludeFileResolutionHeuristics.findInclusion(includeDirective, currentFile); if (location != null) { - return tester.checkFile(null, location, true); + return tester.checkFile(location, true, null); } } return null; } - private int findIncludePos(String[] paths, File currentDirectory) { - for (; currentDirectory != null; currentDirectory = currentDirectory.getParentFile()) { - for (int i = 0; i < paths.length; ++i) { - File pathDir = new File(paths[i]); - if (currentDirectory.equals(pathDir)) - return i; - } - } - - return -1; - } - @Override public String toString() { StringBuffer buffer = new StringBuffer("Scanner @ file:"); //$NON-NLS-1$ @@ -1191,6 +1181,7 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { fAllIncludedFiles.add(path); ILocationCtx ctx= fLocationMap.pushInclusion(poundOffset, nameOffsets[0], nameOffsets[1], condEndOffset, reader.buffer, path, headerName, userInclude, isHeuristic, fi.isSource()); ScannerContext fctx= new ScannerContext(ctx, fCurrentContext, new Lexer(reader.buffer, fLexOptions, this, this)); + fctx.setFoundOnPath(fi.getFoundOnPath()); fCurrentContext= fctx; } break; diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/IncludeFileContent.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/IncludeFileContent.java index c32d7b1aaec..d238cc06eae 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/IncludeFileContent.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/IncludeFileContent.java @@ -46,6 +46,7 @@ public class IncludeFileContent { private boolean fHeuristic; private boolean fIsSource= false; private List fFiles; + private IncludeSearchPathElement fFoundOnPath; /** * For skipping include files. @@ -165,4 +166,12 @@ public class IncludeFileContent { public void setIsSource(boolean isSource) { fIsSource= isSource; } + + public IncludeSearchPathElement getFoundOnPath() { + return fFoundOnPath; + } + + public void setFoundOnPath(IncludeSearchPathElement isp) { + fFoundOnPath= isp; + } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/IncludeSearchPathElement.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/IncludeSearchPathElement.java new file mode 100644 index 00000000000..cec6e849536 --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/IncludeSearchPathElement.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2009 Wind River Systems, Inc. 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: + * Markus Schorn - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.core.parser.scanner; + +import java.io.File; + +/** + * Represents an entry of the include search path + */ +final class IncludeSearchPathElement { + private static final boolean NON_SLASH_SEPARATOR = File.separatorChar != '/'; + public static final String FRAMEWORK_VAR = "__framework__"; //$NON-NLS-1$ + public static final String FILE_VAR = "__filename__"; //$NON-NLS-1$ + + + private final String fPath; + private final boolean fForQuoteIncludesOnly; + private final boolean fIsFrameworkDirectory; + + IncludeSearchPathElement(String path, boolean forQuoteIncludesOnly) { + fPath= path; + fForQuoteIncludesOnly= forQuoteIncludesOnly; + + if (path.indexOf('_') != -1 && path.indexOf(FRAMEWORK_VAR) != -1 && path.indexOf(FILE_VAR) != -1) { + fIsFrameworkDirectory= true; + } else { + fIsFrameworkDirectory= false; + } + } + + public boolean isForQuoteIncludesOnly() { + return fForQuoteIncludesOnly; + } + + public String getLocation(String includeDirective) { + if (fIsFrameworkDirectory) { + int lastSep = lastSeparator(includeDirective); + if (lastSep < 0) { + return null; + } + String framework = includeDirective.substring(0, lastSep); + if (lastSeparator(framework) != -1 || framework.length() == 0) { + return null; + } + + String file= includeDirective.substring(lastSep+1); + if (file.length() == 0) + return null; + + StringBuilder buf= new StringBuilder(fPath); + replaceAll(buf, FRAMEWORK_VAR, framework); + replaceAll(buf, FILE_VAR, file); + return ScannerUtility.reconcilePath(buf.toString()); + } + return ScannerUtility.createReconciledPath(fPath, includeDirective); + } + + private int lastSeparator(String path) { + int lastSep= path.lastIndexOf('/'); + if (NON_SLASH_SEPARATOR) { + lastSep= Math.max(lastSep, path.lastIndexOf(File.separatorChar)); + } + return lastSep; + } + + private void replaceAll(StringBuilder buf, String find, final String replace) { + for (int idx= buf.indexOf(find); idx > 0; idx= buf.indexOf(find, idx)) { + buf.replace(idx, idx+find.length(), replace); + idx+= replace.length(); + } + } +} \ No newline at end of file diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/ScannerContext.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/ScannerContext.java index 6555cb3b6e4..9afec3dbe12 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/ScannerContext.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/ScannerContext.java @@ -44,6 +44,7 @@ final class ScannerContext { private Token fTokens; private ArrayList fConditionals= null; private CodeState fCurrentState= CodeState.eActive; + private IncludeSearchPathElement fFoundOnPath; /** * @param ctx @@ -56,9 +57,7 @@ final class ScannerContext { } public ScannerContext(ILocationCtx ctx, ScannerContext parent, TokenList tokens) { - fLocationCtx= ctx; - fParent= parent; - fLexer= null; + this (ctx, parent, (Lexer) null); fTokens= tokens.first(); fInactiveState= CodeState.eSkipInactive; // no branches in result of macro expansion } @@ -270,4 +269,18 @@ final class ScannerContext { return 0; return fConditionals.size(); } + + /** + * Returns the element of the include search path that was used to find this context, or null if not applicable. + */ + public IncludeSearchPathElement getFoundOnPath() { + return fFoundOnPath; + } + + /** + * Returns the element of the include search path that was used to find this context, or null if not applicable. + */ + public void setFoundOnPath(IncludeSearchPathElement foundOnPath) { + fFoundOnPath= foundOnPath; + } }