1
/*******************************************************************************
2
* Copyright (c) 2008, 2010 Nokia and others.
3
* All rights reserved. This program and the accompanying materials
4
* are made available under the terms of the Eclipse Public License v1.0
5
* which accompanies this distribution, and is available at
6
* http://www.eclipse.org/legal/epl-v10.html
9
* Nokia - Initial API and implementation
10
*******************************************************************************/
12
package org.eclipse.cdt.debug.core.executables;
15
import java.io.IOException;
17
import java.util.ArrayList;
18
import java.util.HashMap;
21
import org.eclipse.cdt.core.CCorePlugin;
22
import org.eclipse.cdt.core.model.CoreModel;
23
import org.eclipse.cdt.core.model.ICProject;
24
import org.eclipse.cdt.core.model.ITranslationUnit;
25
import org.eclipse.cdt.debug.internal.core.Trace;
26
import org.eclipse.cdt.internal.core.Util;
27
import org.eclipse.cdt.internal.core.model.CModelManager;
28
import org.eclipse.cdt.internal.core.model.ExternalTranslationUnit;
29
import org.eclipse.cdt.internal.core.model.TranslationUnit;
30
import org.eclipse.core.filesystem.URIUtil;
31
import org.eclipse.core.resources.IFile;
32
import org.eclipse.core.resources.IProject;
33
import org.eclipse.core.resources.IResource;
34
import org.eclipse.core.resources.ResourcesPlugin;
35
import org.eclipse.core.runtime.IPath;
36
import org.eclipse.core.runtime.IProgressMonitor;
37
import org.eclipse.core.runtime.Path;
38
import org.eclipse.core.runtime.Platform;
39
import org.eclipse.core.runtime.PlatformObject;
40
import org.eclipse.core.runtime.content.IContentType;
41
import org.eclipse.core.runtime.content.IContentTypeManager;
43
public class Executable extends PlatformObject {
46
* Poorly named. This does not determine if the the file is an executable
47
* but rather a binary. Use {@link #isBinaryFile(IPath)} instead.
49
* @deprecated use {@link #isBinaryFile(IPath)}
52
static public boolean isExecutableFile(IPath path) {
53
return isBinaryFile(path);
57
* Determines if the given file is a binary file. For our purposes, an
58
* "executable" is a runnable program (an .exe file on Windows, e.g.,) or a
59
* shared library. A binary can be an executable but it can also be an
60
* instruction-containing artifact of a build, which typically is linked to
61
* make an executable (.e.,g .o and .obj files)
67
static public boolean isBinaryFile(IPath path) {
69
if (path.toFile().isDirectory()) {
72
// Only if file has no extension, has an extension that is an integer
73
// or is a binary file content type
74
String ext = path.getFileExtension();
76
// shared libraries often have a version number
77
boolean isNumber = true;
78
for (int i = 0; i < ext.length(); ++i)
79
if (!Character.isDigit(ext.charAt(i))) {
84
boolean isBinary = false;
85
final IContentTypeManager ctm = Platform.getContentTypeManager();
86
final IContentType ctbin = ctm.getContentType(CCorePlugin.CONTENT_TYPE_BINARYFILE);
87
final IContentType[] cts = ctm.findContentTypesFor(path.toFile().getName());
88
for (int i = 0; !isBinary && i < cts.length; i++) {
89
isBinary = cts[i].isKindOf(ctbin);
100
public boolean equals(Object arg0) {
101
if (arg0 instanceof Executable)
103
Executable exe = (Executable)arg0;
104
return exe.getPath().equals(this.getPath());
106
return super.equals(arg0);
109
private final IPath executablePath;
110
private final IProject project;
111
private final String name;
112
private final IResource resource;
113
private final Map<ITranslationUnit, String> remappedPaths;
114
private final ArrayList<ITranslationUnit> sourceFiles;
115
/** see {@link #setRefreshSourceFiles(boolean)} */
116
private boolean refreshSourceFiles;
117
/** see {@link #setRemapSourceFiles(boolean) */
118
private boolean remapSourceFiles;
119
private ISourceFileRemapping[] remappers;
120
/** The (unmapped) file specifications found in the executable */
121
private String[] symReaderSources;
123
public IPath getPath() {
124
return executablePath;
127
public IProject getProject() {
134
public Executable(IPath path, IProject project, IResource resource, ISourceFileRemapping[] sourceFileRemappings) {
135
this.executablePath = path;
136
this.project = project;
137
this.name = new File(path.toOSString()).getName();
138
this.resource = resource;
139
this.remappers = sourceFileRemappings;
140
remappedPaths = new HashMap<ITranslationUnit, String>();
141
sourceFiles = new ArrayList<ITranslationUnit>();
142
refreshSourceFiles = true;
143
remapSourceFiles = true;
146
public IResource getResource() {
151
public String toString() {
152
return executablePath.toString();
155
public String getName() {
159
@SuppressWarnings("rawtypes")
161
public Object getAdapter(Class adapter) {
162
if (adapter.equals(IResource.class))
163
if (getResource() != null)
164
return getResource();
166
return this.getProject();
167
return super.getAdapter(adapter);
170
private String remapSourceFile(String filename) {
171
for (ISourceFileRemapping remapper : remappers) {
172
String remapped = remapper.remapSourceFile(this.getPath(), filename);
173
if (!remapped.equals(filename)) {
182
* @noreference This method is not intended to be referenced by clients.
185
public synchronized ITranslationUnit[] getSourceFiles(IProgressMonitor monitor) {
186
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().traceEntry(null);
188
if (!refreshSourceFiles && !remapSourceFiles) {
189
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().trace(null, "returning cached result"); //$NON-NLS-1$
190
return sourceFiles.toArray(new TranslationUnit[sourceFiles.size()]) ;
193
// Try to get the list of source files used to build the binary from the
194
// symbol information.
196
remappedPaths.clear();
200
CModelManager factory = CModelManager.getDefault();
202
ICProject cproject = factory.create(project);
204
if (refreshSourceFiles)
206
symReaderSources = ExecutablesManager.getExecutablesManager().getSourceFiles(this, monitor);
208
if (symReaderSources != null && symReaderSources.length > 0) {
209
for (String filename : symReaderSources) {
210
String orgPath = filename;
212
filename = remapSourceFile(filename);
214
// Sometimes the path in the symbolics will have a different
215
// case than the actual file system path. Even if the file
216
// system is not case sensitive this will confuse the Path
217
// class. So make sure the path is canonical, otherwise
218
// breakpoints won't be resolved, etc. Make sure to do this only
219
// for files that are specified as an absolute path at this
220
// point. Paths that are not absolute can't be trusted to
221
// java.io.File to canonicalize since that class will
222
// arbitrarily give the specification a local context, and we
223
// don't want that. A source file that continues to be
224
// non-absolute after having been run through source lookups
225
// (done in remapSourceFile() above) is effectively ambiguous
226
// and we should leave it that way. Users will need to configure
227
// a source lookup to give the file a local context if in fact
228
// the file is available on his machine.
229
boolean fileExists = false;
230
boolean isNativeAbsPath = Util.isNativeAbsolutePath(filename);
231
if (isNativeAbsPath) {
233
File file = new File(filename);
234
fileExists = file.exists();
236
filename = file.getCanonicalPath();
238
} catch (IOException e) { // Do nothing.
242
IFile wkspFile = null;
243
IFile sourceFile = getProject().getFile(filename);
244
IPath sourcePath = new Path(filename);
246
// See if this source file is already in the project.
247
// We check this to determine if we should create a
248
// TranslationUnit or ExternalTranslationUnit
250
if (sourceFile.exists())
251
wkspFile = sourceFile;
253
IFile[] filesInWP = ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(URIUtil.toURI(sourcePath));
254
for (IFile fileInWP : filesInWP) {
255
if (fileInWP.isAccessible()) {
263
// Create a translation unit for this file and add it as
264
// a child of the binary
265
String id = CoreModel.getRegistedContentTypeId(sourceFile.getProject(), sourceFile.getName());
267
if (id != null) { // Don't add files we can't get an
270
if (wkspFile != null)
271
tu = new TranslationUnit(cproject, wkspFile, id);
273
// Be careful not to convert a unix path like
274
// "/src/home" to "c:\source\home" on Windows. See
276
URI uri = (isNativeAbsPath && sourcePath.toFile().exists()) ? URIUtil.toURI(sourcePath) : URIUtil.toURI(filename, true);
277
tu = new ExternalTranslationUnit(cproject, uri, id);
280
if (!sourceFiles.contains(tu)) {
284
if (!orgPath.equals(filename)) {
285
remappedPaths.put(tu, orgPath);
291
refreshSourceFiles = false;
292
remapSourceFiles = false;
293
return sourceFiles.toArray(new TranslationUnit[sourceFiles.size()]) ;
297
* Call this to force a subsequent call to
298
* {@link #getSourceFiles(IProgressMonitor)} to re-fetch the list of
299
* source files referenced in the executable. Digging into the binary for
300
* that list can be expensive, so we cache the results. However, if the
301
* executable is rebuilt, the cache can no longer be trusted.
303
* Note that calling this also invalidates any mappings we have cached, so
304
* there is no need to call both this method and
305
* {@link #setRemapSourceFiles(boolean)}. That latter is automatic.
309
public void setRefreshSourceFiles(boolean refreshSourceFiles) {
310
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().traceEntry(null, refreshSourceFiles);
312
this.refreshSourceFiles = refreshSourceFiles;
315
public synchronized String getOriginalLocation(ITranslationUnit tu) {
316
String orgLocation = remappedPaths.get(tu);
317
if (orgLocation == null)
318
orgLocation = tu.getLocation().toOSString();
323
* Call this to force a subsequent call to
324
* {@link #getSourceFiles(IProgressMonitor)} to remap the source files
325
* referenced in the binary. Mapping a source file means running the file
326
* specification found in the executable through any applicable source
327
* locators. Source locators are used to convert a file specification found
328
* in an executable to a usable path on the local machine. E.g., a user
329
* debugging an executable built by someone else likely needs to configure
330
* source locator(s) to point Eclipse to his local copy of the sources.
333
* Remapping source paths is expensive, so we cache the results. However, if
334
* applicable source locators have been added, removed or changed, then the
335
* cache can no longer be trusted.
338
* Note that we separately cache the (unmapped) file specifications
339
* referenced in the executable, as that is also expensive to fetch. Calling
340
* this method does not invalidate that cache. However, that cache can be
341
* invalidated via {@link #setRefreshSourceFiles(boolean)}, which also ends
342
* up invalidating any mappings we have cached.
346
public void setRemapSourceFiles(boolean remapSourceFiles) {
347
if (Trace.DEBUG_EXECUTABLES) Trace.getTrace().traceEntry(null, remapSourceFiles);
348
this.remapSourceFiles = remapSourceFiles;