/******************************************************************************* * Copyright (c) 2004, 2011 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 * Tianchao Li (tianchao.li@gmail.com) - arbitrary build directory (bug #136136) *******************************************************************************/ package org.eclipse.cdt.make.internal.core.scannerconfig.gnu; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.cdt.core.IMarkerGenerator; import org.eclipse.cdt.internal.core.resources.ResourceLookup; import org.eclipse.cdt.make.core.MakeCorePlugin; import org.eclipse.cdt.make.internal.core.MakeMessages; import org.eclipse.cdt.make.internal.core.scannerconfig.util.TraceUtil; import org.eclipse.cdt.utils.EFSExtensionManager; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; /** * Implements error reporting mechanism and file/path translation mechanism * Taken from ErrorParserManager and modified. * * @author vhirsl */ public class ScannerInfoConsoleParserUtility extends AbstractGCCBOPConsoleParserUtility { /* * For tracking the location of files being compiled */ private Map<String, IFile> fFilesInProject; private List<IResource> fCollectedFiles; private List<String> fNameConflicts; public ScannerInfoConsoleParserUtility(IProject project, IPath workingDirectory, IMarkerGenerator markerGenerator) { super(project, workingDirectory, markerGenerator); fFilesInProject = new HashMap<String, IFile>(); fCollectedFiles = new ArrayList<IResource>(); fNameConflicts = new ArrayList<String>(); collectFiles(getProject(), fCollectedFiles); for (int i = 0; i < fCollectedFiles.size(); i++) { IFile curr = (IFile) fCollectedFiles.get(i); Object existing = fFilesInProject.put(curr.getName(), curr); if (existing != null) { fNameConflicts.add(curr.getName()); } } } /** * Called by the console line parsers to find a file with a given name. * @return IFile or null */ public IFile findFile(String fileName) { IFile file = findFilePath(fileName); if (file == null) { // Try the project's map. file = findFileName(fileName); if (file != null) { // If there is a conflict then try all files in the project. if (isConflictingName(fileName)) { file = null; // Create a problem marker final String error = MakeMessages.getString("ConsoleParser.Ambiguous_Filepath_Error_Message"); //$NON-NLS-1$ TraceUtil.outputError(error, fileName); generateMarker(getProject(), -1, error+fileName, IMarkerGenerator.SEVERITY_WARNING, null); } } } if (file!=null) { IPath filePath = new Path(fileName); if(filePath.segment(0).compareTo("..") == 0) { //$NON-NLS-1$ filePath = filePath.removeFirstSegments(1); } String foundLocation = file.getLocationURI().toString(); if (!foundLocation.endsWith(filePath.toString())) { file = null; } } return file; } /** * @return file in workspace as {@link IFile} or {@code null} if not found */ protected IFile findFilePath(String filePath) { IPath path = null; IPath fp = new Path(filePath); if (fp.isAbsolute()) { if (getBaseDirectory().isPrefixOf(fp)) { int segments = getBaseDirectory().matchingFirstSegments(fp); path = fp.removeFirstSegments(segments); } else { path = fp; } } else { path = getWorkingDirectory().append(filePath); } IFile file = null; // The workspace may throw an IllegalArgumentException // Catch it and the parser should fallback to scan the entire project. try { file = findFileInWorkspace(path); } catch (Exception e) { } // We have to do another try, on Windows for cases like "TEST.C" vs "test.c" // We use the java.io.File canonical path. if (file == null || !file.exists()) { File f = path.toFile(); try { String canon = f.getCanonicalPath(); path = new Path(canon); file = findFileInWorkspace(path); } catch (IOException e1) { } } return (file != null && file.exists()) ? file : null; } /** * @return file in workspace as {@link IFile} or {@code null} */ protected IFile findFileName(String fileName) { IPath path = new Path(fileName); return fFilesInProject.get(path.lastSegment()); } protected IFile findFileInWorkspace(IPath path) { IFile file = null; if (path.isAbsolute()) { IWorkspaceRoot root = getProject().getWorkspace().getRoot(); file = root.getFileForLocation(path); // It may be a link resource so we must check it also. if (file == null) { file= ResourceLookup.selectFileForLocation(path, getProject()); } } else { file = getProject().getFile(path); } return file; } protected void collectFiles(IContainer parent, List<IResource> result) { try { IResource[] resources = parent.members(); for (int i = 0; i < resources.length; i++) { IResource resource = resources[i]; if (resource instanceof IFile) { result.add(resource); } else if (resource instanceof IContainer) { collectFiles((IContainer) resource, result); } } } catch (CoreException e) { MakeCorePlugin.log(e.getStatus()); } } protected boolean isConflictingName(String fileName) { IPath path = new Path(fileName); return fNameConflicts.contains(path.lastSegment()); } public List<String> translateRelativePaths(IFile file, String fileName, List<String> includes) { List<String> translatedIncludes = new ArrayList<String>(includes.size()); for (String include : includes) { IPath includePath = new Path(include); if (includePath.isUNC()) { // do not translate UNC paths } else if (includePath.isAbsolute()) { if (includePath.getDevice()==null) { String device = getWorkingDirectory().getDevice(); IPath candidatePath = includePath.setDevice(device); File dir = candidatePath.toFile(); if (dir.exists()) { include = candidatePath.toString(); } else { final String error = MakeMessages.getString("ConsoleParser.Nonexistent_Include_Path_Error_Message"); //$NON-NLS-1$ TraceUtil.outputError(error, include); // generateMarker(file, -1, error+include, IMarkerGenerator.SEVERITY_WARNING, fileName); } } } else { // First try the current working directory IPath cwd = getWorkingDirectory(); if (!cwd.isAbsolute()) { cwd = getBaseDirectory().append(cwd); } IPath filePath = new Path(fileName); if (filePath.isAbsolute()) { if (filePath.getDevice()==null) { String device = getWorkingDirectory().getDevice(); filePath = filePath.setDevice(device); } } else { // check if the cwd is the right one // appending fileName to cwd should yield file path filePath = cwd.append(fileName); } if (!filePath.toString().equalsIgnoreCase(EFSExtensionManager.getDefault().getPathFromURI(file.getLocationURI()))) { // must be the cwd is wrong // check if file name starts with ".." if (fileName.startsWith("..")) { //$NON-NLS-1$ // probably multiple choices for cwd, hopeless final String error = MakeMessages.getString("ConsoleParser.Working_Directory_Error_Message"); //$NON-NLS-1$ TraceUtil.outputError(error, fileName); generateMarker(file, -1, error, IMarkerGenerator.SEVERITY_WARNING, fileName); break; } else { // remove common segments at the end IPath tPath = new Path(fileName); if (fileName.startsWith(".")) { //$NON-NLS-1$ tPath = tPath.removeFirstSegments(1); } // get the file path from the file filePath = new Path(EFSExtensionManager.getDefault().getPathFromURI(file.getLocationURI())); IPath lastFileSegment = filePath.removeFirstSegments(filePath.segmentCount() - tPath.segmentCount()); if (lastFileSegment.matchingFirstSegments(tPath) == tPath.segmentCount()) { cwd = filePath.removeLastSegments(tPath.segmentCount()); } } } IPath candidatePath = cwd.append(includePath); File dir = candidatePath.toFile(); include = candidatePath.toString(); if (!dir.exists()) { final String error = MakeMessages.getString("ConsoleParser.Nonexistent_Include_Path_Error_Message"); //$NON-NLS-1$ TraceUtil.outputError(error, include); // generateMarker(file, -1, error+include, IMarkerGenerator.SEVERITY_WARNING, fileName); } } // TODO VMIR for now add unresolved paths as well translatedIncludes.add(include); } return translatedIncludes; } public String normalizePath(String path) { int column = path.indexOf(':'); if (column > 0) { char driveLetter = path.charAt(column - 1); if (Character.isLowerCase(driveLetter)) { StringBuffer sb = new StringBuffer(); if (column - 1 > 0) { sb.append(path.substring(0, column-1)); } sb.append(Character.toUpperCase(driveLetter)); sb.append(path.substring(column)); path = sb.toString(); } } if (path.indexOf('.') == -1 || path.equals(".")) { //$NON-NLS-1$ return (new Path(path)).toString(); // convert separators to '/' } // lose "./" segments since they confuse the Path normalization StringBuffer buf = new StringBuffer(path); int len = buf.length(); StringBuffer newBuf = new StringBuffer(buf.length()); int scp = 0; // starting copy point int ssp = 0; // starting search point int sdot; boolean validPrefix; while (ssp < len && (sdot = buf.indexOf(".", ssp)) != -1) { //$NON-NLS-1$ validPrefix = false; int ddot = buf.indexOf("..", ssp);//$NON-NLS-1$ if (sdot < ddot || ddot == -1) { newBuf.append(buf.substring(scp, sdot)); scp = sdot; ssp = sdot + 1; if (ssp < len) { if (sdot == 0 || buf.charAt(sdot - 1) == '/' || buf.charAt(sdot - 1) == '\\') { validPrefix = true; } char nextChar = buf.charAt(ssp); if (validPrefix && nextChar == '/') { ++ssp; scp = ssp; } else if (validPrefix && nextChar == '\\') { ++ssp; if (ssp < len - 1 && buf.charAt(ssp) == '\\') { ++ssp; } scp = ssp; } else { // no path delimiter, must be '.' inside the path scp = ssp - 1; } } } else if (sdot == ddot) { ssp = sdot + 2; } } newBuf.append(buf.substring(scp, len)); IPath orgPath = new Path(newBuf.toString()); return orgPath.toString(); } }