Skip to content

Commit f76d1d5

Browse files
committed
Merge pull request #2850 from PaulStoffregen/duplicate-library-detect
Advise of duplicate libraries after compiling
2 parents 5dd4935 + b5367bc commit f76d1d5

File tree

4 files changed

+126
-28
lines changed

4 files changed

+126
-28
lines changed

arduino-core/src/cc/arduino/contributions/libraries/LibrariesIndexer.java

+25-4
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public class LibrariesIndexer {
5252

5353
private LibrariesIndex index;
5454
private final LibraryList installedLibraries = new LibraryList();
55+
private final LibraryList installedLibrariesWithDuplicates = new LibraryList();
5556
private List<File> librariesFolders;
5657
private final File indexFile;
5758
private final File stagingFolder;
@@ -92,15 +93,16 @@ public void setLibrariesFolders(List<File> _librariesFolders) {
9293
public void rescanLibraries() {
9394
// Clear all installed flags
9495
installedLibraries.clear();
96+
installedLibrariesWithDuplicates.clear();
9597
for (ContributedLibrary lib : index.getLibraries())
9698
lib.setInstalled(false);
9799

98100
// Rescan libraries
99101
for (File folder : librariesFolders)
100-
scanInstalledLibraries(folder);
102+
scanInstalledLibraries(folder, folder.equals(sketchbookLibrariesFolder));
101103
}
102104

103-
private void scanInstalledLibraries(File folder) {
105+
private void scanInstalledLibraries(File folder, boolean isSketchbook) {
104106
File list[] = folder.listFiles(OnlyDirs.ONLY_DIRS);
105107
// if a bad folder or something like that, this might come back null
106108
if (list == null)
@@ -117,14 +119,14 @@ private void scanInstalledLibraries(File folder) {
117119
}
118120

119121
try {
120-
scanLibrary(subfolder);
122+
scanLibrary(subfolder, isSketchbook);
121123
} catch (IOException e) {
122124
System.out.println(I18n.format(_("Invalid library found in {0}: {1}"), subfolder, e.getMessage()));
123125
}
124126
}
125127
}
126128

127-
private void scanLibrary(File folder) throws IOException {
129+
private void scanLibrary(File folder, boolean isSketchbook) throws IOException {
128130
boolean readOnly = !FileUtils.isSubDirectory(sketchbookLibrariesFolder, folder);
129131

130132
// A library is considered "legacy" if it doesn't contains
@@ -135,13 +137,23 @@ private void scanLibrary(File folder) throws IOException {
135137
LegacyUserLibrary lib = LegacyUserLibrary.create(folder);
136138
lib.setReadOnly(readOnly);
137139
installedLibraries.addOrReplace(lib);
140+
if (isSketchbook) {
141+
installedLibrariesWithDuplicates.add(lib);
142+
} else {
143+
installedLibrariesWithDuplicates.addOrReplace(lib);
144+
}
138145
return;
139146
}
140147

141148
// Create a regular library
142149
UserLibrary lib = UserLibrary.create(folder);
143150
lib.setReadOnly(readOnly);
144151
installedLibraries.addOrReplace(lib);
152+
if (isSketchbook) {
153+
installedLibrariesWithDuplicates.add(lib);
154+
} else {
155+
installedLibrariesWithDuplicates.addOrReplace(lib);
156+
}
145157

146158
// Check if we can find the same library in the index
147159
// and mark it as installed
@@ -170,6 +182,15 @@ public LibraryList getInstalledLibraries() {
170182
return installedLibraries;
171183
}
172184

185+
// Same as getInstalledLibraries(), but allow duplicates between
186+
// builtin+package libraries and sketchbook installed libraries.
187+
// However, do not report duplicates among builtin and packages, to
188+
// allow any package to override builtin libraries without being
189+
// reported as duplicates.
190+
public LibraryList getInstalledLibrariesWithDuplicates() {
191+
return installedLibrariesWithDuplicates;
192+
}
193+
173194
public File getStagingFolder() {
174195
return stagingFolder;
175196
}

arduino-core/src/processing/app/BaseNoGui.java

+51-18
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public class BaseNoGui {
4545
static private File toolsFolder;
4646

4747
// maps #included files to their library folder
48-
public static Map<String, UserLibrary> importToLibraryTable;
48+
public static Map<String, LibraryList> importToLibraryTable;
4949

5050
// maps library name to their library folder
5151
static private LibraryList libraries;
@@ -771,14 +771,24 @@ static private void createToolPreferences(ContributionsIndexer indexer) {
771771
}
772772

773773
static public void populateImportToLibraryTable() {
774-
// Populate importToLibraryTable
775-
importToLibraryTable = new HashMap<String, UserLibrary>();
774+
// Populate importToLibraryTable. Each header filename maps to
775+
// a list of libraries. Compiler.java will use only the first
776+
// library on each list. The others are used only to advise
777+
// user of ambiguously matched and duplicate libraries.
778+
importToLibraryTable = new HashMap<String, LibraryList>();
776779
for (UserLibrary lib : librariesIndexer.getInstalledLibraries()) {
777780
try {
778781
String headers[] = headerListFromIncludePath(lib.getSrcFolder());
779782
for (String header : headers) {
780-
UserLibrary old = importToLibraryTable.get(header);
781-
if (old != null) {
783+
LibraryList list = importToLibraryTable.get(header);
784+
if (list == null) {
785+
// This is the first library found with this header
786+
list = new LibraryList();
787+
list.addFirst(lib);
788+
importToLibraryTable.put(header, list);
789+
} else {
790+
UserLibrary old = list.peekFirst();
791+
boolean useThisLib = true;
782792
// This is the case where 2 libraries have a .h header
783793
// with the same name. We must decide which library to
784794
// use when a sketch has #include "name.h"
@@ -796,58 +806,81 @@ static public void populateImportToLibraryTable() {
796806
String oldName = old.getInstalledFolder().getName(); // just the library folder name
797807
String libName = lib.getInstalledFolder().getName(); // just the library folder name
798808
//System.out.println("name conflict: " + name);
799-
//System.out.println(" old = " + oldName + " -> " + old.getFolder().getPath());
800-
//System.out.println(" new = " + libName + " -> " + lib.getFolder().getPath());
809+
//System.out.println(" old = " + oldName + " -> " + old.getInstalledFolder().getPath());
810+
//System.out.println(" new = " + libName + " -> " + lib.getInstalledFolder().getPath());
801811
String name_lc = name.toLowerCase();
802812
String oldName_lc = oldName.toLowerCase();
803813
String libName_lc = libName.toLowerCase();
804814
// always favor a perfect name match
805815
if (libName.equals(name)) {
806816
} else if (oldName.equals(name)) {
807-
continue;
817+
useThisLib = false;
808818
// check for "-master" appended (zip file from github)
809819
} else if (libName.equals(name+"-master")) {
810820
} else if (oldName.equals(name+"-master")) {
811-
continue;
821+
useThisLib = false;
812822
// next, favor a match with other stuff appended
813823
} else if (libName.startsWith(name)) {
814824
} else if (oldName.startsWith(name)) {
815-
continue;
825+
useThisLib = false;
816826
// otherwise, favor a match with stuff prepended
817827
} else if (libName.endsWith(name)) {
818828
} else if (oldName.endsWith(name)) {
819-
continue;
829+
useThisLib = false;
820830
// as a last resort, match if stuff prepended and appended
821831
} else if (libName.contains(name)) {
822832
} else if (oldName.contains(name)) {
823-
continue;
833+
useThisLib = false;
824834
// repeat all the above tests, with case insensitive matching
825835
} else if (libName_lc.equals(name_lc)) {
826836
} else if (oldName_lc.equals(name_lc)) {
827-
continue;
837+
useThisLib = false;
828838
} else if (libName_lc.equals(name_lc+"-master")) {
829839
} else if (oldName_lc.equals(name_lc+"-master")) {
830-
continue;
840+
useThisLib = false;
831841
} else if (libName_lc.startsWith(name_lc)) {
832842
} else if (oldName_lc.startsWith(name_lc)) {
833-
continue;
843+
useThisLib = false;
834844
} else if (libName_lc.endsWith(name_lc)) {
835845
} else if (oldName_lc.endsWith(name_lc)) {
836-
continue;
846+
useThisLib = false;
837847
} else if (libName_lc.contains(name_lc)) {
838848
} else if (oldName_lc.contains(name_lc)) {
839-
continue;
849+
useThisLib = false;
840850
} else {
841851
// none of these tests matched, so just default to "libName".
842852
}
853+
if (useThisLib) {
854+
list.addFirst(lib);
855+
} else {
856+
list.addLast(lib);
857+
}
843858
}
844-
importToLibraryTable.put(header, lib);
845859
}
846860
} catch (IOException e) {
847861
showWarning(_("Error"), I18n
848862
.format("Unable to list header files in {0}", lib.getSrcFolder()), e);
849863
}
850864
}
865+
// repeat for ALL libraries, to pick up duplicates not visible normally.
866+
// any new libraries found here are NEVER used, but they are added to the
867+
// end of already-found headers, to allow Compiler to report them if
868+
// the sketch tries to use them.
869+
for (UserLibrary lib : librariesIndexer.getInstalledLibrariesWithDuplicates()) {
870+
try {
871+
String headers[] = headerListFromIncludePath(lib.getSrcFolder());
872+
for (String header : headers) {
873+
LibraryList list = importToLibraryTable.get(header);
874+
if (list != null) {
875+
if (!(list.hasLibrary(lib))) {
876+
list.addLast(lib);
877+
//System.out.println(" duplicate lib: " + lib.getInstalledFolder().getPath());
878+
}
879+
}
880+
}
881+
} catch (IOException e) {
882+
}
883+
}
851884
}
852885

853886
static public void initParameters(String args[]) {

arduino-core/src/processing/app/debug/Compiler.java

+43-6
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,16 @@ static public String build(SketchData data, String buildPath, File tempBuildFold
114114

115115
// compile the program. errors will happen as a RunnerException
116116
// that will bubble up to whomever called build().
117-
if (compiler.compile(verbose)) {
118-
compiler.size(compiler.getBuildPreferences());
119-
return primaryClassName;
117+
try {
118+
if (compiler.compile(verbose)) {
119+
compiler.size(compiler.getBuildPreferences());
120+
return primaryClassName;
121+
}
122+
} catch (RunnerException e) {
123+
// when the compile fails, take this opportunity to show
124+
// any helpful info possible before throwing the exception
125+
compiler.adviseDuplicateLibraries();
126+
throw e;
120127
}
121128
return null;
122129
}
@@ -428,10 +435,29 @@ public boolean compile(boolean _verbose) throws RunnerException, PreferencesMapE
428435

429436
// Hook runs at End of Compilation
430437
runActions("hooks.postbuild", prefs);
438+
adviseDuplicateLibraries();
431439

432440
return true;
433441
}
434442

443+
private void adviseDuplicateLibraries() {
444+
for (int i=0; i < importedDuplicateHeaders.size(); i++) {
445+
System.out.println(I18n.format(_("Multiple libraries were found for \"{0}\""),
446+
importedDuplicateHeaders.get(i)));
447+
boolean first = true;
448+
for (UserLibrary lib : importedDuplicateLibraries.get(i)) {
449+
if (first) {
450+
System.out.println(I18n.format(_(" Used: {0}"),
451+
lib.getInstalledFolder().getPath()));
452+
first = false;
453+
} else {
454+
System.out.println(I18n.format(_(" Not used: {0}"),
455+
lib.getInstalledFolder().getPath()));
456+
}
457+
}
458+
}
459+
}
460+
435461
private PreferencesMap createBuildPreferences(String _buildPath,
436462
String _primaryClassName)
437463
throws RunnerException {
@@ -1166,10 +1192,19 @@ public void preprocess(String buildPath, PdePreprocessor preprocessor) throws Ru
11661192
// grab the imports from the code just preproc'd
11671193

11681194
importedLibraries = new LibraryList();
1195+
importedDuplicateHeaders = new ArrayList<String>();
1196+
importedDuplicateLibraries = new ArrayList<LibraryList>();
11691197
for (String item : preprocessor.getExtraImports()) {
1170-
UserLibrary lib = BaseNoGui.importToLibraryTable.get(item);
1171-
if (lib != null && !importedLibraries.contains(lib)) {
1172-
importedLibraries.add(lib);
1198+
LibraryList list = BaseNoGui.importToLibraryTable.get(item);
1199+
if (list != null) {
1200+
UserLibrary lib = list.peekFirst();
1201+
if (lib != null && !importedLibraries.contains(lib)) {
1202+
importedLibraries.add(lib);
1203+
if (list.size() > 1) {
1204+
importedDuplicateHeaders.add(item);
1205+
importedDuplicateLibraries.add(list);
1206+
}
1207+
}
11731208
}
11741209
}
11751210

@@ -1201,6 +1236,8 @@ public void preprocess(String buildPath, PdePreprocessor preprocessor) throws Ru
12011236
* List of library folders.
12021237
*/
12031238
private LibraryList importedLibraries;
1239+
private List<String> importedDuplicateHeaders;
1240+
private List<LibraryList> importedDuplicateLibraries;
12041241

12051242
/**
12061243
* Map an error from a set of processed .java files back to its location

arduino-core/src/processing/app/packages/LibraryList.java

+7
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,11 @@ public LibraryList filterLibrariesInSubfolder(File subFolder) {
8181
}
8282
return res;
8383
}
84+
85+
public boolean hasLibrary(UserLibrary lib) {
86+
for (UserLibrary l : this)
87+
if (l == lib) return true;
88+
return false;
89+
}
8490
}
91+

0 commit comments

Comments
 (0)