Merge pull request #2850 from PaulStoffregen/duplicate-library-detect

Advise of duplicate libraries after compiling
This commit is contained in:
Federico Fissore 2015-04-01 09:31:10 +02:00
commit f76d1d5678
4 changed files with 126 additions and 28 deletions

View File

@ -52,6 +52,7 @@ public class LibrariesIndexer {
private LibrariesIndex index; private LibrariesIndex index;
private final LibraryList installedLibraries = new LibraryList(); private final LibraryList installedLibraries = new LibraryList();
private final LibraryList installedLibrariesWithDuplicates = new LibraryList();
private List<File> librariesFolders; private List<File> librariesFolders;
private final File indexFile; private final File indexFile;
private final File stagingFolder; private final File stagingFolder;
@ -92,15 +93,16 @@ public class LibrariesIndexer {
public void rescanLibraries() { public void rescanLibraries() {
// Clear all installed flags // Clear all installed flags
installedLibraries.clear(); installedLibraries.clear();
installedLibrariesWithDuplicates.clear();
for (ContributedLibrary lib : index.getLibraries()) for (ContributedLibrary lib : index.getLibraries())
lib.setInstalled(false); lib.setInstalled(false);
// Rescan libraries // Rescan libraries
for (File folder : librariesFolders) for (File folder : librariesFolders)
scanInstalledLibraries(folder); scanInstalledLibraries(folder, folder.equals(sketchbookLibrariesFolder));
} }
private void scanInstalledLibraries(File folder) { private void scanInstalledLibraries(File folder, boolean isSketchbook) {
File list[] = folder.listFiles(OnlyDirs.ONLY_DIRS); File list[] = folder.listFiles(OnlyDirs.ONLY_DIRS);
// if a bad folder or something like that, this might come back null // if a bad folder or something like that, this might come back null
if (list == null) if (list == null)
@ -117,14 +119,14 @@ public class LibrariesIndexer {
} }
try { try {
scanLibrary(subfolder); scanLibrary(subfolder, isSketchbook);
} catch (IOException e) { } catch (IOException e) {
System.out.println(I18n.format(_("Invalid library found in {0}: {1}"), subfolder, e.getMessage())); System.out.println(I18n.format(_("Invalid library found in {0}: {1}"), subfolder, e.getMessage()));
} }
} }
} }
private void scanLibrary(File folder) throws IOException { private void scanLibrary(File folder, boolean isSketchbook) throws IOException {
boolean readOnly = !FileUtils.isSubDirectory(sketchbookLibrariesFolder, folder); boolean readOnly = !FileUtils.isSubDirectory(sketchbookLibrariesFolder, folder);
// A library is considered "legacy" if it doesn't contains // A library is considered "legacy" if it doesn't contains
@ -135,6 +137,11 @@ public class LibrariesIndexer {
LegacyUserLibrary lib = LegacyUserLibrary.create(folder); LegacyUserLibrary lib = LegacyUserLibrary.create(folder);
lib.setReadOnly(readOnly); lib.setReadOnly(readOnly);
installedLibraries.addOrReplace(lib); installedLibraries.addOrReplace(lib);
if (isSketchbook) {
installedLibrariesWithDuplicates.add(lib);
} else {
installedLibrariesWithDuplicates.addOrReplace(lib);
}
return; return;
} }
@ -142,6 +149,11 @@ public class LibrariesIndexer {
UserLibrary lib = UserLibrary.create(folder); UserLibrary lib = UserLibrary.create(folder);
lib.setReadOnly(readOnly); lib.setReadOnly(readOnly);
installedLibraries.addOrReplace(lib); installedLibraries.addOrReplace(lib);
if (isSketchbook) {
installedLibrariesWithDuplicates.add(lib);
} else {
installedLibrariesWithDuplicates.addOrReplace(lib);
}
// Check if we can find the same library in the index // Check if we can find the same library in the index
// and mark it as installed // and mark it as installed
@ -170,6 +182,15 @@ public class LibrariesIndexer {
return installedLibraries; return installedLibraries;
} }
// Same as getInstalledLibraries(), but allow duplicates between
// builtin+package libraries and sketchbook installed libraries.
// However, do not report duplicates among builtin and packages, to
// allow any package to override builtin libraries without being
// reported as duplicates.
public LibraryList getInstalledLibrariesWithDuplicates() {
return installedLibrariesWithDuplicates;
}
public File getStagingFolder() { public File getStagingFolder() {
return stagingFolder; return stagingFolder;
} }

View File

@ -45,7 +45,7 @@ public class BaseNoGui {
static private File toolsFolder; static private File toolsFolder;
// maps #included files to their library folder // maps #included files to their library folder
public static Map<String, UserLibrary> importToLibraryTable; public static Map<String, LibraryList> importToLibraryTable;
// maps library name to their library folder // maps library name to their library folder
static private LibraryList libraries; static private LibraryList libraries;
@ -771,14 +771,24 @@ public class BaseNoGui {
} }
static public void populateImportToLibraryTable() { static public void populateImportToLibraryTable() {
// Populate importToLibraryTable // Populate importToLibraryTable. Each header filename maps to
importToLibraryTable = new HashMap<String, UserLibrary>(); // a list of libraries. Compiler.java will use only the first
// library on each list. The others are used only to advise
// user of ambiguously matched and duplicate libraries.
importToLibraryTable = new HashMap<String, LibraryList>();
for (UserLibrary lib : librariesIndexer.getInstalledLibraries()) { for (UserLibrary lib : librariesIndexer.getInstalledLibraries()) {
try { try {
String headers[] = headerListFromIncludePath(lib.getSrcFolder()); String headers[] = headerListFromIncludePath(lib.getSrcFolder());
for (String header : headers) { for (String header : headers) {
UserLibrary old = importToLibraryTable.get(header); LibraryList list = importToLibraryTable.get(header);
if (old != null) { if (list == null) {
// This is the first library found with this header
list = new LibraryList();
list.addFirst(lib);
importToLibraryTable.put(header, list);
} else {
UserLibrary old = list.peekFirst();
boolean useThisLib = true;
// This is the case where 2 libraries have a .h header // This is the case where 2 libraries have a .h header
// with the same name. We must decide which library to // with the same name. We must decide which library to
// use when a sketch has #include "name.h" // use when a sketch has #include "name.h"
@ -796,58 +806,81 @@ public class BaseNoGui {
String oldName = old.getInstalledFolder().getName(); // just the library folder name String oldName = old.getInstalledFolder().getName(); // just the library folder name
String libName = lib.getInstalledFolder().getName(); // just the library folder name String libName = lib.getInstalledFolder().getName(); // just the library folder name
//System.out.println("name conflict: " + name); //System.out.println("name conflict: " + name);
//System.out.println(" old = " + oldName + " -> " + old.getFolder().getPath()); //System.out.println(" old = " + oldName + " -> " + old.getInstalledFolder().getPath());
//System.out.println(" new = " + libName + " -> " + lib.getFolder().getPath()); //System.out.println(" new = " + libName + " -> " + lib.getInstalledFolder().getPath());
String name_lc = name.toLowerCase(); String name_lc = name.toLowerCase();
String oldName_lc = oldName.toLowerCase(); String oldName_lc = oldName.toLowerCase();
String libName_lc = libName.toLowerCase(); String libName_lc = libName.toLowerCase();
// always favor a perfect name match // always favor a perfect name match
if (libName.equals(name)) { if (libName.equals(name)) {
} else if (oldName.equals(name)) { } else if (oldName.equals(name)) {
continue; useThisLib = false;
// check for "-master" appended (zip file from github) // check for "-master" appended (zip file from github)
} else if (libName.equals(name+"-master")) { } else if (libName.equals(name+"-master")) {
} else if (oldName.equals(name+"-master")) { } else if (oldName.equals(name+"-master")) {
continue; useThisLib = false;
// next, favor a match with other stuff appended // next, favor a match with other stuff appended
} else if (libName.startsWith(name)) { } else if (libName.startsWith(name)) {
} else if (oldName.startsWith(name)) { } else if (oldName.startsWith(name)) {
continue; useThisLib = false;
// otherwise, favor a match with stuff prepended // otherwise, favor a match with stuff prepended
} else if (libName.endsWith(name)) { } else if (libName.endsWith(name)) {
} else if (oldName.endsWith(name)) { } else if (oldName.endsWith(name)) {
continue; useThisLib = false;
// as a last resort, match if stuff prepended and appended // as a last resort, match if stuff prepended and appended
} else if (libName.contains(name)) { } else if (libName.contains(name)) {
} else if (oldName.contains(name)) { } else if (oldName.contains(name)) {
continue; useThisLib = false;
// repeat all the above tests, with case insensitive matching // repeat all the above tests, with case insensitive matching
} else if (libName_lc.equals(name_lc)) { } else if (libName_lc.equals(name_lc)) {
} else if (oldName_lc.equals(name_lc)) { } else if (oldName_lc.equals(name_lc)) {
continue; useThisLib = false;
} else if (libName_lc.equals(name_lc+"-master")) { } else if (libName_lc.equals(name_lc+"-master")) {
} else if (oldName_lc.equals(name_lc+"-master")) { } else if (oldName_lc.equals(name_lc+"-master")) {
continue; useThisLib = false;
} else if (libName_lc.startsWith(name_lc)) { } else if (libName_lc.startsWith(name_lc)) {
} else if (oldName_lc.startsWith(name_lc)) { } else if (oldName_lc.startsWith(name_lc)) {
continue; useThisLib = false;
} else if (libName_lc.endsWith(name_lc)) { } else if (libName_lc.endsWith(name_lc)) {
} else if (oldName_lc.endsWith(name_lc)) { } else if (oldName_lc.endsWith(name_lc)) {
continue; useThisLib = false;
} else if (libName_lc.contains(name_lc)) { } else if (libName_lc.contains(name_lc)) {
} else if (oldName_lc.contains(name_lc)) { } else if (oldName_lc.contains(name_lc)) {
continue; useThisLib = false;
} else { } else {
// none of these tests matched, so just default to "libName". // none of these tests matched, so just default to "libName".
} }
if (useThisLib) {
list.addFirst(lib);
} else {
list.addLast(lib);
}
} }
importToLibraryTable.put(header, lib);
} }
} catch (IOException e) { } catch (IOException e) {
showWarning(_("Error"), I18n showWarning(_("Error"), I18n
.format("Unable to list header files in {0}", lib.getSrcFolder()), e); .format("Unable to list header files in {0}", lib.getSrcFolder()), e);
} }
} }
// repeat for ALL libraries, to pick up duplicates not visible normally.
// any new libraries found here are NEVER used, but they are added to the
// end of already-found headers, to allow Compiler to report them if
// the sketch tries to use them.
for (UserLibrary lib : librariesIndexer.getInstalledLibrariesWithDuplicates()) {
try {
String headers[] = headerListFromIncludePath(lib.getSrcFolder());
for (String header : headers) {
LibraryList list = importToLibraryTable.get(header);
if (list != null) {
if (!(list.hasLibrary(lib))) {
list.addLast(lib);
//System.out.println(" duplicate lib: " + lib.getInstalledFolder().getPath());
}
}
}
} catch (IOException e) {
}
}
} }
static public void initParameters(String args[]) { static public void initParameters(String args[]) {

View File

@ -114,9 +114,16 @@ public class Compiler implements MessageConsumer {
// compile the program. errors will happen as a RunnerException // compile the program. errors will happen as a RunnerException
// that will bubble up to whomever called build(). // that will bubble up to whomever called build().
if (compiler.compile(verbose)) { try {
compiler.size(compiler.getBuildPreferences()); if (compiler.compile(verbose)) {
return primaryClassName; compiler.size(compiler.getBuildPreferences());
return primaryClassName;
}
} catch (RunnerException e) {
// when the compile fails, take this opportunity to show
// any helpful info possible before throwing the exception
compiler.adviseDuplicateLibraries();
throw e;
} }
return null; return null;
} }
@ -428,10 +435,29 @@ public class Compiler implements MessageConsumer {
// Hook runs at End of Compilation // Hook runs at End of Compilation
runActions("hooks.postbuild", prefs); runActions("hooks.postbuild", prefs);
adviseDuplicateLibraries();
return true; return true;
} }
private void adviseDuplicateLibraries() {
for (int i=0; i < importedDuplicateHeaders.size(); i++) {
System.out.println(I18n.format(_("Multiple libraries were found for \"{0}\""),
importedDuplicateHeaders.get(i)));
boolean first = true;
for (UserLibrary lib : importedDuplicateLibraries.get(i)) {
if (first) {
System.out.println(I18n.format(_(" Used: {0}"),
lib.getInstalledFolder().getPath()));
first = false;
} else {
System.out.println(I18n.format(_(" Not used: {0}"),
lib.getInstalledFolder().getPath()));
}
}
}
}
private PreferencesMap createBuildPreferences(String _buildPath, private PreferencesMap createBuildPreferences(String _buildPath,
String _primaryClassName) String _primaryClassName)
throws RunnerException { throws RunnerException {
@ -1166,10 +1192,19 @@ public class Compiler implements MessageConsumer {
// grab the imports from the code just preproc'd // grab the imports from the code just preproc'd
importedLibraries = new LibraryList(); importedLibraries = new LibraryList();
importedDuplicateHeaders = new ArrayList<String>();
importedDuplicateLibraries = new ArrayList<LibraryList>();
for (String item : preprocessor.getExtraImports()) { for (String item : preprocessor.getExtraImports()) {
UserLibrary lib = BaseNoGui.importToLibraryTable.get(item); LibraryList list = BaseNoGui.importToLibraryTable.get(item);
if (lib != null && !importedLibraries.contains(lib)) { if (list != null) {
importedLibraries.add(lib); UserLibrary lib = list.peekFirst();
if (lib != null && !importedLibraries.contains(lib)) {
importedLibraries.add(lib);
if (list.size() > 1) {
importedDuplicateHeaders.add(item);
importedDuplicateLibraries.add(list);
}
}
} }
} }
@ -1201,6 +1236,8 @@ public class Compiler implements MessageConsumer {
* List of library folders. * List of library folders.
*/ */
private LibraryList importedLibraries; private LibraryList importedLibraries;
private List<String> importedDuplicateHeaders;
private List<LibraryList> importedDuplicateLibraries;
/** /**
* Map an error from a set of processed .java files back to its location * Map an error from a set of processed .java files back to its location

View File

@ -81,4 +81,11 @@ public class LibraryList extends LinkedList<UserLibrary> {
} }
return res; return res;
} }
public boolean hasLibrary(UserLibrary lib) {
for (UserLibrary l : this)
if (l == lib) return true;
return false;
}
} }