Skip to content

Commit aa08d13

Browse files
Move include detection from PdeProcessor/Sketch to the new HeuristicResolver class
This offers a more consistent interface to the library autodetection that can next be expanded to also detect inter-library dependencies. As a side effect, the resolver now looks through all sketch code, including any .c, .cpp and .h files. Previously, only the .ino and .pde files were inspected during preprocessing. This fixes arduino#636. This commit is based on code by Christian Maglie.
1 parent 5bbe358 commit aa08d13

File tree

6 files changed

+213
-74
lines changed

6 files changed

+213
-74
lines changed

Diff for: app/src/processing/app/Base.java

+9-18
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,11 @@
4444
import processing.app.helpers.FileUtils;
4545
import processing.app.helpers.PreferencesMap;
4646
import processing.app.helpers.filefilters.OnlyDirs;
47-
import processing.app.helpers.filefilters.OnlyFilesWithExtension;
4847
import processing.app.javax.swing.filechooser.FileNameExtensionFilter;
48+
import processing.app.packages.HeuristicResolver;
4949
import processing.app.packages.Library;
5050
import processing.app.packages.LibraryList;
51+
import processing.app.packages.LibraryResolver;
5152
import processing.app.tools.MenuScroller;
5253
import processing.app.tools.ZipDeflater;
5354
import processing.core.*;
@@ -110,7 +111,7 @@ public class Base {
110111
static private LibraryList libraries;
111112

112113
// maps #included files to their library folder
113-
static Map<String, Library> importToLibraryTable;
114+
static private LibraryResolver libraryResolver;
114115

115116
// classpath for all known libraries for p5
116117
// (both those in the p5/libs folder and those with lib subfolders
@@ -1346,22 +1347,8 @@ public void onBoardOrPortChange() {
13461347
showWarning(_("Error"), _("Error loading libraries"), e);
13471348
}
13481349

1349-
// Populate importToLibraryTable
1350-
importToLibraryTable = new HashMap<String, Library>();
1351-
for (Library lib : libraries) {
1352-
List<String> headers = lib.getPublicHeaders();
1353-
for (String header : headers) {
1354-
Library old = importToLibraryTable.get(header);
1355-
if (old != null) {
1356-
// If a library was already found with this header, keep
1357-
// it if the library's name matches the header name.
1358-
String name = header.substring(0, header.length() - 2);
1359-
if (old.getFolder().getPath().endsWith(name))
1360-
continue;
1361-
}
1362-
importToLibraryTable.put(header, lib);
1363-
}
1364-
}
1350+
// Create library resolver
1351+
libraryResolver = new HeuristicResolver(libraries);
13651352

13661353
// Update editors status bar
13671354
for (Editor editor : editors)
@@ -2996,4 +2983,8 @@ public void handleAddLibrary() {
29962983
public static DiscoveryManager getDiscoveryManager() {
29972984
return discoveryManager;
29982985
}
2986+
2987+
public static LibraryResolver getLibraryResolver() {
2988+
return libraryResolver;
2989+
}
29992990
}

Diff for: app/src/processing/app/Sketch.java

+3-24
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,13 @@
3434
import processing.app.helpers.FileUtils;
3535
import processing.app.packages.Library;
3636
import processing.app.packages.LibraryList;
37+
import processing.app.packages.LibraryResolver;
3738
import processing.app.preproc.*;
3839
import processing.core.*;
3940
import static processing.app.I18n._;
4041

4142
import java.io.*;
4243
import java.util.*;
43-
import java.util.List;
4444

4545
import javax.swing.*;
4646

@@ -91,11 +91,6 @@ public class Sketch {
9191
/** Class path determined during build. */
9292
private String classPath;
9393

94-
/**
95-
* List of library folders.
96-
*/
97-
private LibraryList importedLibraries;
98-
9994
/**
10095
* File inside the build directory that contains the build options
10196
* used for the last build.
@@ -1348,18 +1343,8 @@ public void preprocess(String buildPath, PdePreprocessor preprocessor) throws Ru
13481343
ex.printStackTrace();
13491344
throw new RunnerException(ex.toString());
13501345
}
1351-
1352-
// grab the imports from the code just preproc'd
1353-
1354-
importedLibraries = new LibraryList();
1355-
for (String item : preprocessor.getExtraImports()) {
1356-
Library lib = Base.importToLibraryTable.get(item);
1357-
if (lib != null && !importedLibraries.contains(lib)) {
1358-
importedLibraries.add(lib);
1359-
}
1360-
}
1361-
1362-
// 3. then loop over the code[] and save each .java file
1346+
1347+
// 2. then loop over the code[] and save each .java file
13631348

13641349
for (SketchCode sc : code) {
13651350
if (sc.isExtension(Base.SOURCE_EXTENSIONS) || sc.isExtension(Base.HEADER_EXTENSIONS)) {
@@ -1383,12 +1368,6 @@ public void preprocess(String buildPath, PdePreprocessor preprocessor) throws Ru
13831368
}
13841369
}
13851370

1386-
1387-
public LibraryList getImportedLibraries() {
1388-
return importedLibraries;
1389-
}
1390-
1391-
13921371
/**
13931372
* Map an error from a set of processed .java files back to its location
13941373
* in the actual sketch.

Diff for: app/src/processing/app/debug/Compiler.java

+6-9
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import processing.app.helpers.StringReplacer;
4646
import processing.app.helpers.filefilters.OnlyDirs;
4747
import processing.app.packages.Library;
48+
import processing.app.packages.LibraryList;
4849
import processing.core.PApplet;
4950

5051
public class Compiler implements MessageConsumer {
@@ -89,7 +90,8 @@ public boolean compile(boolean _verbose) throws RunnerException {
8990
includeFolders.add(prefs.getFile("build.core.path"));
9091
if (prefs.getFile("build.variant.path") != null)
9192
includeFolders.add(prefs.getFile("build.variant.path"));
92-
for (Library lib : sketch.getImportedLibraries()) {
93+
LibraryList libs = Base.getLibraryResolver().findDirectDependencies(sketch);
94+
for (Library lib : libs) {
9395
if (verbose)
9496
System.out.println(I18n
9597
.format(_("Using library {0} in folder: {1} {2}"), lib.getName(),
@@ -105,7 +107,7 @@ public boolean compile(boolean _verbose) throws RunnerException {
105107
String[] overrides = prefs.get("architecture.override_check").split(",");
106108
archs.addAll(Arrays.asList(overrides));
107109
}
108-
for (Library lib : sketch.getImportedLibraries()) {
110+
for (Library lib : libs) {
109111
if (!lib.supportsArchitecture(archs)) {
110112
System.err.println(I18n
111113
.format(_("WARNING: library {0} claims to run on {1} "
@@ -124,7 +126,8 @@ public boolean compile(boolean _verbose) throws RunnerException {
124126
// 2. compile the libraries, outputting .o files to: <buildPath>/<library>/
125127
// Doesn't really use configPreferences
126128
sketch.setCompilingProgress(40);
127-
compileLibraries(includeFolders);
129+
for (Library lib : libs)
130+
compileLibrary(lib, includeFolders);
128131

129132
// 3. compile the core, outputting .o files to <buildPath> and then
130133
// collecting them into the core.a library file.
@@ -613,12 +616,6 @@ void compileSketch(List<File> includeFolders) throws RunnerException {
613616

614617
// 2. compile the libraries, outputting .o files to:
615618
// <buildPath>/<library>/
616-
void compileLibraries(List<File> includeFolders) throws RunnerException {
617-
for (Library lib : sketch.getImportedLibraries()) {
618-
compileLibrary(lib, includeFolders);
619-
}
620-
}
621-
622619
private void compileLibrary(Library lib, List<File> includeFolders)
623620
throws RunnerException {
624621
File libFolder = lib.getSrcFolder();
+142
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/*
2+
* This file is part of Arduino.
3+
*
4+
* Arduino is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program; if not, write to the Free Software
16+
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17+
*
18+
* As a special exception, you may use this file as part of a free software
19+
* library without restriction. Specifically, if other files instantiate
20+
* templates or use macros or inline functions from this file, or you compile
21+
* this file and link it with other files to produce an executable, this
22+
* file does not by itself cause the resulting executable to be covered by
23+
* the GNU General Public License. This exception does not however
24+
* invalidate any other reasons why the executable file might be covered by
25+
* the GNU General Public License.
26+
*
27+
* Copyright 2013 Arduino LLC (http://www.arduino.cc/)
28+
*/
29+
30+
package processing.app.packages;
31+
32+
import java.io.File;
33+
import java.io.IOException;
34+
import java.util.ArrayList;
35+
import java.util.HashMap;
36+
import java.util.List;
37+
import java.util.LinkedList;
38+
import java.util.Map;
39+
40+
import processing.app.Sketch;
41+
import processing.app.SketchCode;
42+
import processing.app.helpers.FileUtils;
43+
import processing.app.helpers.filefilters.OnlyFilesWithExtension;
44+
import processing.app.preproc.PdePreprocessor;
45+
import processing.core.PApplet;
46+
47+
/**
48+
* This resolver uses an heuristic approach to resolve dependencies
49+
* without looking into metadata.
50+
*/
51+
public class HeuristicResolver implements LibraryResolver {
52+
53+
private LibraryList libraries;
54+
private Map<String, Library> importToLibrary;
55+
56+
public HeuristicResolver(LibraryList _libraries) {
57+
libraries = _libraries;
58+
importToLibrary = new HashMap<String, Library>();
59+
60+
// Populate importToLibrary table
61+
for (Library library : libraries) {
62+
for (String header : library.getPublicHeaders()) {
63+
Library old = importToLibrary.get(header);
64+
if (old != null) {
65+
// If a library was already found with this header, keep
66+
// it if the library's name matches the header name.
67+
String name = header.substring(0, header.length() - 2);
68+
if (old.getFolder().getPath().endsWith(name))
69+
continue;
70+
}
71+
importToLibrary.put(header, library);
72+
}
73+
}
74+
}
75+
76+
@Override
77+
public LibraryList findDirectDependencies(Sketch sketch) {
78+
SketchCode files[] = sketch.getCode();
79+
LibraryList result = new LibraryList();
80+
for (SketchCode code : files)
81+
result.addOrReplaceAll(findSourceDependencies(code.getFile(), null, null));
82+
return result;
83+
}
84+
85+
/**
86+
* Inspect headerFile and search for dependencies
87+
*
88+
* @param headerFile
89+
* @param excluded
90+
* @param library
91+
*/
92+
private LibraryList findSourceDependencies(File headerFile,
93+
List<String> excluded,
94+
Library library) {
95+
LibraryList res = new LibraryList();
96+
97+
98+
// Extract #includes from header file
99+
List<String> imports;
100+
String contents;
101+
try {
102+
contents = FileUtils.readFileToString(headerFile);
103+
} catch (IOException e) {
104+
e.printStackTrace();
105+
return res;
106+
}
107+
108+
String importRegexp = "^\\s*#include\\s*[<\"](\\S+)[\">]";
109+
String[][] pieces = PApplet.matchAll(contents, importRegexp);
110+
111+
// For every #include found...
112+
if (pieces != null) {
113+
for (int i = 0; i < pieces.length; i++) {
114+
String libImport = pieces[i][1];
115+
116+
// ...check if the include is not in the exclusion list
117+
if (excluded != null && excluded.contains(libImport))
118+
continue;
119+
120+
// ...check if there is a matching library
121+
Library depLib = importToLibrary.get(libImport);
122+
if (depLib == null || depLib == library)
123+
continue;
124+
125+
// ...that we didn't see before
126+
if (res.contains(depLib))
127+
continue;
128+
129+
// add the dependency
130+
res.add(depLib);
131+
}
132+
}
133+
134+
return res;
135+
}
136+
137+
@Override
138+
public Library importToLibrary(String h) {
139+
return importToLibrary.get(h);
140+
}
141+
142+
}

Diff for: app/src/processing/app/packages/LibraryResolver.java

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* This file is part of Arduino.
3+
*
4+
* Arduino is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program; if not, write to the Free Software
16+
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17+
*
18+
* As a special exception, you may use this file as part of a free software
19+
* library without restriction. Specifically, if other files instantiate
20+
* templates or use macros or inline functions from this file, or you compile
21+
* this file and link it with other files to produce an executable, this
22+
* file does not by itself cause the resulting executable to be covered by
23+
* the GNU General Public License. This exception does not however
24+
* invalidate any other reasons why the executable file might be covered by
25+
* the GNU General Public License.
26+
*
27+
* Copyright 2013 Arduino LLC (http://www.arduino.cc/)
28+
*/
29+
30+
package processing.app.packages;
31+
32+
import processing.app.Sketch;
33+
34+
public interface LibraryResolver {
35+
36+
/**
37+
* Resolve direct dependencies for a sketch
38+
*
39+
* @param sketch
40+
* @return A LibraryList containing the dependencies
41+
*/
42+
public abstract LibraryList findDirectDependencies(Sketch sketch);
43+
44+
/**
45+
* Returns the Library referenced by the include file name
46+
*
47+
* @param header
48+
* The include file name, for example "SPI.h".
49+
* @return The referenced library
50+
*/
51+
public abstract Library importToLibrary(String header);
52+
53+
}

0 commit comments

Comments
 (0)