From 7382c0d74fbb5004eecdf54575f63e9ee2cb3a0e Mon Sep 17 00:00:00 2001 From: Martin Sidaway Date: Tue, 23 Jun 2015 18:57:23 +0100 Subject: [PATCH 01/15] Work out canonical names for libraries, and put them in special #include comments. --- app/src/processing/app/Sketch.java | 10 +++- .../libraries/ContributedLibrary.java | 8 ++- .../processing/app/packages/UserLibrary.java | 60 +++++++++++++++++++ 3 files changed, 72 insertions(+), 6 deletions(-) diff --git a/app/src/processing/app/Sketch.java b/app/src/processing/app/Sketch.java index 2bdcfedda31..0f6a5e702f4 100644 --- a/app/src/processing/app/Sketch.java +++ b/app/src/processing/app/Sketch.java @@ -931,14 +931,14 @@ public boolean addFile(File sourceFile) { public void importLibrary(UserLibrary lib) throws IOException { - importLibrary(lib.getSrcFolder()); + importLibrary(lib.getSrcFolder(), lib.getDepSpec()); } /** * Add import statements to the current tab for all of packages inside * the specified jar file. */ - private void importLibrary(File jarPath) throws IOException { + private void importLibrary(File jarPath, String depSpec) throws IOException { // make sure the user didn't hide the sketch folder ensureExistence(); @@ -960,7 +960,11 @@ private void importLibrary(File jarPath) throws IOException { for (String aList : list) { buffer.append("#include <"); buffer.append(aList); - buffer.append(">\n"); + buffer.append(">"); + if (depSpec != null) { + buffer.append(" //!Lib '" + depSpec + "'"); + } + buffer.append("\n"); } buffer.append('\n'); buffer.append(editor.getText()); diff --git a/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java b/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java index af42a493b89..66164830ae7 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java @@ -39,6 +39,8 @@ public abstract class ContributedLibrary extends DownloadableContribution { + public abstract String getGlobalName(); + public abstract String getName(); public abstract String getMaintainer(); @@ -142,10 +144,10 @@ public boolean equals(Object obj) { boolean versionEquals = thisVersion == otherVersion || (thisVersion != null && otherVersion != null && thisVersion.equals(otherVersion)); - String thisName = getName(); - String otherName = ((ContributedLibrary) obj).getName(); + String thisName = getGlobalName(); + String otherName = ((ContributedLibrary) obj).getGlobalName(); - boolean nameEquals = thisName == null || otherName == null || thisName.equals(otherName); + boolean nameEquals = thisName != null && otherName != null && thisName.equals(otherName); return versionEquals && nameEquals; } diff --git a/arduino-core/src/processing/app/packages/UserLibrary.java b/arduino-core/src/processing/app/packages/UserLibrary.java index ef24022abd9..f60db141a21 100644 --- a/arduino-core/src/processing/app/packages/UserLibrary.java +++ b/arduino-core/src/processing/app/packages/UserLibrary.java @@ -37,11 +37,13 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.LinkedList; import java.util.List; public class UserLibrary extends ContributedLibrary { + private String globalName; private String name; private String version; private String author; @@ -151,6 +153,12 @@ public static UserLibrary create(File libFolder) throws IOException { } UserLibrary res = new UserLibrary(); + + String globalName = properties.get("global_name"); + if (globalName != null) { + globalName = globalName.trim(); + } + res.setInstalledFolder(libFolder); res.setInstalled(true); res.name = properties.get("name").trim(); @@ -165,9 +173,60 @@ public static UserLibrary create(File libFolder) throws IOException { res.architectures = archs; res.layout = layout; res.declaredTypes = typesList; + res.setGlobalName(globalName); return res; } + /** + * Call this after setting website, author, and name. + */ + public void setGlobalName(String gn) { + globalName = gn; + boolean invalid = false; + if (globalName == null) { + invalid = true; + String rest = website.replaceFirst(".*://", "").replaceFirst("\\.[^/.]*$", ""); + List parts = Arrays.asList(rest.split("/")); + List hostParts = Arrays.asList(parts.get(0).split("\\.")); + Collections.reverse(hostParts); + globalName = String.join(".", hostParts); + if (globalName.endsWith(".www")) { + // Remove www component + globalName = globalName.substring(0, globalName.length() - 4); + } + if (parts.size() > 1) { + // Path parts + globalName += "." + String.join(".", parts.subList(1, parts.size())); + } + if (globalName.startsWith("com.github.")) { + // Better to use the user-only namespace. + globalName = "io.github." + globalName.substring(11); + } + } + if (globalName.equals("")) { + invalid = true; + // Fallback. Note: the global name is used to test for equality, + // so it must have a value, and for the sake of legacy libraries, it + // should include the name more or less as-is. + globalName = author.replace('/', '_') + "/" + name; + } + if (invalid) { + System.err.println("WARNING: global_name not set in library " + name + ". Guessing '" + globalName + "'.\nPlease set this to a suitable Java-style package name, e.g. io.github.myaccount.myproject\nThe name should be set manually to avoid confusion when forking."); + } + } + + @Override + public String getGlobalName() { + return globalName; + } + + public String getDepSpec() { + if (globalName != null && version != null) { + return globalName + ":" + version; + } + return null; + } + @Override public String getName() { return name; @@ -289,6 +348,7 @@ public boolean useRecursion() { @Override public String toString() { String res = "Library: " + name + "\n"; + res += " (global_name=" + globalName + ")\n"; res += " (version=" + version + ")\n"; res += " (author=" + author + ")\n"; res += " (maintainer=" + maintainer + ")\n"; From c3c57478b5fc3f429f958202de700f54ff1c46cc Mon Sep 17 00:00:00 2001 From: Martin Sidaway Date: Wed, 24 Jun 2015 20:07:40 +0100 Subject: [PATCH 02/15] Show library information when hover over #include statement. --- .../processing/app/syntax/SketchTextArea.java | 24 +++++++++++++++++++ .../src/processing/app/BaseNoGui.java | 17 +++++++++++++ .../app/preproc/PdePreprocessor.java | 9 ++----- 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/app/src/processing/app/syntax/SketchTextArea.java b/app/src/processing/app/syntax/SketchTextArea.java index fbd9e0c7c16..5b67561184a 100644 --- a/app/src/processing/app/syntax/SketchTextArea.java +++ b/app/src/processing/app/syntax/SketchTextArea.java @@ -39,6 +39,7 @@ import org.fife.ui.rtextarea.RTextAreaUI; import org.fife.ui.rtextarea.RUndoManager; import processing.app.*; +import processing.app.packages.UserLibrary; import javax.swing.*; import javax.swing.event.EventListenerList; @@ -396,6 +397,7 @@ public void mouseMoved(MouseEvent e) { t = new TokenImpl(t); } Cursor c2; + String tipText = null; if (t != null && t.isHyperlink()) { if (hoveredOverLinkOffset == -1 || hoveredOverLinkOffset != t.getOffset()) { @@ -432,12 +434,34 @@ public void mouseMoved(MouseEvent e) { c2 = Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR); hoveredOverLinkOffset = -1; // linkGeneratorResult = null; + + int pos = viewToModel(e.getPoint()); + if (pos > -1) { + int lineNo = -1; + try { + lineNo = getLineOfOffset(pos); + } catch (BadLocationException ex) {} + String line = getTextLine(lineNo); + if (line != null) { + UserLibrary lib = BaseNoGui.firstLibraryByCode(line); + if (lib != null) { + String ver = lib.getVersion(); + if (ver != null) { + ver = " " + ver; + } else { + ver = ""; + } + tipText = lib.getName() + ver + ": " + lib.getSrcFolder().toString(); + } + } + } } if (getCursor() != c2) { setCursor(c2); // TODO: Repaint just the affected line(s). repaint(); // Link either left or went into. } + setToolTipText(tipText); } private void stopScanningForLinks() { diff --git a/arduino-core/src/processing/app/BaseNoGui.java b/arduino-core/src/processing/app/BaseNoGui.java index 8aeffcc157b..19dbfc30f32 100644 --- a/arduino-core/src/processing/app/BaseNoGui.java +++ b/arduino-core/src/processing/app/BaseNoGui.java @@ -19,6 +19,7 @@ import processing.app.legacy.PApplet; import processing.app.packages.LibraryList; import processing.app.packages.UserLibrary; +import processing.app.preproc.PdePreprocessor; import java.io.*; import java.net.URISyntaxException; @@ -962,6 +963,22 @@ static public void populateImportToLibraryTable() { } } + static public UserLibrary firstLibraryByImport(String importName) { + LibraryList list = importToLibraryTable.get(importName); + if (list == null || list.isEmpty()) { + return null; + } + return list.peekFirst(); + } + + static public UserLibrary firstLibraryByCode(String code) { + List incs = PdePreprocessor.findIncludes(code); + if (incs.isEmpty()) { + return null; + } + return firstLibraryByImport(incs.get(0)); + } + static public void initParameters(String args[]) throws IOException { String preferencesFile = null; diff --git a/arduino-core/src/processing/app/preproc/PdePreprocessor.java b/arduino-core/src/processing/app/preproc/PdePreprocessor.java index dc30922a92e..e8d97962d67 100644 --- a/arduino-core/src/processing/app/preproc/PdePreprocessor.java +++ b/arduino-core/src/processing/app/preproc/PdePreprocessor.java @@ -97,13 +97,8 @@ public int writePrefix(String program) } //String importRegexp = "(?:^|\\s|;)(import\\s+)(\\S+)(\\s*;)"; - programImports = new ArrayList(); - String[][] pieces = PApplet.matchAll(program, IMPORT_REGEX); - - if (pieces != null) - for (int i = 0; i < pieces.length; i++) - programImports.add(pieces[i][1]); // the package name + programImports = findIncludes(program); codeFolderImports = new ArrayList(); // if (codeFolderPackages != null) { @@ -127,7 +122,7 @@ public static List findIncludes(String code){ String[][] pieces = PApplet.matchAll(code, IMPORT_REGEX); - ArrayList programImports = new ArrayList(); + ArrayList programImports = new ArrayList<>(); if (pieces != null) for (int i = 0; i < pieces.length; i++) From d395a6b269397aaa16f85b6d919bca962b81e322 Mon Sep 17 00:00:00 2001 From: Martin Sidaway Date: Wed, 24 Jun 2015 21:54:10 +0100 Subject: [PATCH 03/15] Get hierarchical deps, and show in tooltips. Also, ensure that legacy libraries have a global name. --- .../processing/app/syntax/SketchTextArea.java | 38 +++++++++++++----- .../libraries/ContributedLibrary.java | 4 ++ .../src/processing/app/BaseNoGui.java | 16 ++++++++ .../src/processing/app/debug/Compiler.java | 31 ++++++++++++++ .../app/packages/LegacyUserLibrary.java | 1 + .../processing/app/packages/UserLibrary.java | 40 ++++++++++++++++--- 6 files changed, 115 insertions(+), 15 deletions(-) diff --git a/app/src/processing/app/syntax/SketchTextArea.java b/app/src/processing/app/syntax/SketchTextArea.java index 5b67561184a..7d820222887 100644 --- a/app/src/processing/app/syntax/SketchTextArea.java +++ b/app/src/processing/app/syntax/SketchTextArea.java @@ -40,6 +40,7 @@ import org.fife.ui.rtextarea.RUndoManager; import processing.app.*; import processing.app.packages.UserLibrary; +import cc.arduino.contributions.libraries.ContributedLibrary; import javax.swing.*; import javax.swing.event.EventListenerList; @@ -443,16 +444,7 @@ public void mouseMoved(MouseEvent e) { } catch (BadLocationException ex) {} String line = getTextLine(lineNo); if (line != null) { - UserLibrary lib = BaseNoGui.firstLibraryByCode(line); - if (lib != null) { - String ver = lib.getVersion(); - if (ver != null) { - ver = " " + ver; - } else { - ver = ""; - } - tipText = lib.getName() + ver + ": " + lib.getSrcFolder().toString(); - } + tipText = getImportInfo(line); } } } @@ -464,6 +456,32 @@ public void mouseMoved(MouseEvent e) { setToolTipText(tipText); } + private String getImportInfo(String line) { + UserLibrary lib = BaseNoGui.firstLibraryByCode(line); + String info = null; + if (lib != null) { + info = getLibDescription(lib); + for (ContributedLibrary recLib : lib.getRequiredLibsRec()) { + info += "\n* " + getLibDescription(recLib); + } + } + return info; + } + + private String getLibDescription(ContributedLibrary lib) { + String ver = lib.getVersion(); + if (ver != null) { + ver = " " + ver; + } else { + ver = ""; + } + String folder = ""; + if (lib instanceof UserLibrary) { + folder = ": " + ((UserLibrary) lib).getSrcFolder().toString(); + } + return lib.getName() + " (" + lib.getGlobalName() + ")" + ver + folder; + } + private void stopScanningForLinks() { if (isScanningForLinks) { Cursor c = getCursor(); diff --git a/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java b/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java index 66164830ae7..6ca88686850 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java @@ -65,6 +65,10 @@ public abstract class ContributedLibrary extends DownloadableContribution { public abstract List getRequires(); + public abstract List getRequiredLibs(); + + public abstract List getRequiredLibsRec(); + public static final Comparator CASE_INSENSITIVE_ORDER = (o1, o2) -> o1.getName().compareToIgnoreCase(o2.getName()); /** diff --git a/arduino-core/src/processing/app/BaseNoGui.java b/arduino-core/src/processing/app/BaseNoGui.java index 19dbfc30f32..dac3bde3461 100644 --- a/arduino-core/src/processing/app/BaseNoGui.java +++ b/arduino-core/src/processing/app/BaseNoGui.java @@ -979,6 +979,22 @@ static public UserLibrary firstLibraryByCode(String code) { return firstLibraryByImport(incs.get(0)); } + static public List librariesByCode(String code) throws IOException { + List libs = new ArrayList<>(); + List incs = PdePreprocessor.findIncludes(code); + for (String inc : incs) { + UserLibrary lib = firstLibraryByImport(inc); + if (lib != null) { + libs.add(lib); + } + } + return libs; + } + + static public List librariesByCode(File file) throws IOException { + return librariesByCode(FileUtils.readFileToString(file)); + } + static public void initParameters(String args[]) throws IOException { String preferencesFile = null; diff --git a/arduino-core/src/processing/app/debug/Compiler.java b/arduino-core/src/processing/app/debug/Compiler.java index 5f1bf9ad567..f628226ff2b 100644 --- a/arduino-core/src/processing/app/debug/Compiler.java +++ b/arduino-core/src/processing/app/debug/Compiler.java @@ -32,6 +32,7 @@ import cc.arduino.packages.BoardPort; import cc.arduino.packages.Uploader; import cc.arduino.packages.UploaderFactory; +import cc.arduino.contributions.libraries.ContributedLibrary; import cc.arduino.packages.uploaders.MergeSketchWithBooloader; import org.apache.commons.compress.utils.IOUtils; @@ -608,6 +609,36 @@ private PreferencesMap createBuildPreferences(String _buildPath, return p; } + static public List findAllSources(File sourcePath, boolean recurse) { + List allSources = new ArrayList<>(); + allSources.addAll(findFilesInFolder(sourcePath, "S", recurse)); + allSources.addAll(findFilesInFolder(sourcePath, "c", recurse)); + allSources.addAll(findFilesInFolder(sourcePath, "cpp", recurse)); + allSources.addAll(findFilesInFolder(sourcePath, "h", recurse)); + allSources.addAll(findFilesInFolder(sourcePath, "hh", recurse)); + allSources.addAll(findFilesInFolder(sourcePath, "hpp", recurse)); + return allSources; + } + + static public List findRequiredLibs(File sourcePath, boolean recurse) { + List files = findAllSources(sourcePath, recurse); + List result = new ArrayList<>(); + for (File file : files) { + List libs = null; + try { + libs = BaseNoGui.librariesByCode(file); + } catch (IOException e) { + continue; + } + for (ContributedLibrary lib : libs) { + if (!result.contains(lib)) { + result.add(lib); + } + } + } + return result; + } + private List compileFiles(File outputPath, File sourcePath, boolean recurse, List includeFolders) throws RunnerException, PreferencesMapException { diff --git a/arduino-core/src/processing/app/packages/LegacyUserLibrary.java b/arduino-core/src/processing/app/packages/LegacyUserLibrary.java index 56a86ccb9c7..e5abc53776c 100644 --- a/arduino-core/src/processing/app/packages/LegacyUserLibrary.java +++ b/arduino-core/src/processing/app/packages/LegacyUserLibrary.java @@ -43,6 +43,7 @@ public static LegacyUserLibrary create(File libFolder) { res.setInstalled(true); res.layout = LibraryLayout.FLAT; res.name = libFolder.getName(); + res.globalName = res.name; res.setTypes(Arrays.asList("Contributed")); res.setCategory("Uncategorized"); return res; diff --git a/arduino-core/src/processing/app/packages/UserLibrary.java b/arduino-core/src/processing/app/packages/UserLibrary.java index f60db141a21..f3588075ad6 100644 --- a/arduino-core/src/processing/app/packages/UserLibrary.java +++ b/arduino-core/src/processing/app/packages/UserLibrary.java @@ -32,6 +32,7 @@ import cc.arduino.contributions.libraries.ContributedLibraryReference; import processing.app.helpers.FileUtils; import processing.app.helpers.PreferencesMap; +import processing.app.debug.Compiler; import java.io.File; import java.io.IOException; @@ -43,7 +44,7 @@ public class UserLibrary extends ContributedLibrary { - private String globalName; + protected String globalName; private String name; private String version; private String author; @@ -183,7 +184,7 @@ public static UserLibrary create(File libFolder) throws IOException { public void setGlobalName(String gn) { globalName = gn; boolean invalid = false; - if (globalName == null) { + if (globalName == null || globalName.equals("")) { invalid = true; String rest = website.replaceFirst(".*://", "").replaceFirst("\\.[^/.]*$", ""); List parts = Arrays.asList(rest.split("/")); @@ -203,11 +204,10 @@ public void setGlobalName(String gn) { globalName = "io.github." + globalName.substring(11); } } - if (globalName.equals("")) { + if (globalName == null || globalName.equals("")) { invalid = true; // Fallback. Note: the global name is used to test for equality, - // so it must have a value, and for the sake of legacy libraries, it - // should include the name more or less as-is. + // so it must have a value. globalName = author.replace('/', '_') + "/" + name; } if (invalid) { @@ -320,6 +320,36 @@ public List getRequires() { return null; } + private List requiredLibs = null; + private List requiredLibsRec = null; + + @Override + public List getRequiredLibs() { + if (requiredLibs == null) { + requiredLibs = Compiler.findRequiredLibs(getSrcFolder(), useRecursion()); + requiredLibs.remove(this); + } + return requiredLibs; + } + + @Override + public List getRequiredLibsRec() { + if (requiredLibsRec == null) { + requiredLibsRec = new ArrayList<>(); + for (ContributedLibrary lib : getRequiredLibs()) { + if (!requiredLibsRec.contains(lib) && lib != this) { + requiredLibsRec.add(lib); + for (ContributedLibrary libRec : lib.getRequiredLibsRec()) { + if (!requiredLibsRec.contains(libRec) && libRec != this) { + requiredLibsRec.add(libRec); + } + } + } + } + } + return requiredLibsRec; + } + public List getDeclaredTypes() { return declaredTypes; } From d8758c0b4a9aa399643a55c00053a94246554b10 Mon Sep 17 00:00:00 2001 From: Martin Sidaway Date: Thu, 25 Jun 2015 10:37:08 +0100 Subject: [PATCH 04/15] If version is null, omit from dep spec rather than returning null. --- .../src/processing/app/packages/UserLibrary.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/arduino-core/src/processing/app/packages/UserLibrary.java b/arduino-core/src/processing/app/packages/UserLibrary.java index f3588075ad6..d142bd58f00 100644 --- a/arduino-core/src/processing/app/packages/UserLibrary.java +++ b/arduino-core/src/processing/app/packages/UserLibrary.java @@ -221,10 +221,14 @@ public String getGlobalName() { } public String getDepSpec() { - if (globalName != null && version != null) { - return globalName + ":" + version; + String depSpec = null; + if (globalName != null) { + depSpec = globalName; + if (version != null) { + depSpec += ":" + version; + } } - return null; + return depSpec; } @Override From 968803ff007c7426d540b00ec90c4b898ab458f2 Mon Sep 17 00:00:00 2001 From: Martin Sidaway Date: Thu, 25 Jun 2015 11:43:53 +0100 Subject: [PATCH 05/15] Update cached dependency information when libraries change their dependencies. --- .../processing/app/packages/UserLibrary.java | 41 ++++++++++++++++++- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/arduino-core/src/processing/app/packages/UserLibrary.java b/arduino-core/src/processing/app/packages/UserLibrary.java index d142bd58f00..4f945c55f9c 100644 --- a/arduino-core/src/processing/app/packages/UserLibrary.java +++ b/arduino-core/src/processing/app/packages/UserLibrary.java @@ -41,6 +41,8 @@ import java.util.Collections; import java.util.LinkedList; import java.util.List; +import java.util.TreeMap; +import java.util.Map; public class UserLibrary extends ContributedLibrary { @@ -324,12 +326,47 @@ public List getRequires() { return null; } + private Map lastUpdateTimes = new TreeMap(); + private List requiredLibs = null; private List requiredLibsRec = null; + private boolean changedSinceLastUpdate(int idx) { + List files = Compiler.findAllSources(getSrcFolder(), useRecursion()); + // Important: must update timestamps from ALL files before returning. + boolean changed = false; + for (File file : files) { + String fname = file.toString(); + long modTime = file.lastModified(); + if (!lastUpdateTimes.containsKey(fname)) { + long[] times = new long[2]; + times[idx] = modTime; + lastUpdateTimes.put(fname, times); + changed = true; + } else if (lastUpdateTimes.get(fname)[idx] < modTime) { + lastUpdateTimes.get(fname)[idx] = modTime; + changed = true; + } + } + return changed; + } + + private boolean changedSinceLastUpdateRec(int idx) { + if (changedSinceLastUpdate(idx)) { + return true; + } + for (ContributedLibrary lib : getRequiredLibs()) { + if (lib instanceof UserLibrary && + ((UserLibrary) lib).changedSinceLastUpdateRec(idx)) { + return true; + } + } + return false; + } + @Override public List getRequiredLibs() { - if (requiredLibs == null) { + if (requiredLibs == null || changedSinceLastUpdate(0)) { requiredLibs = Compiler.findRequiredLibs(getSrcFolder(), useRecursion()); requiredLibs.remove(this); } @@ -338,7 +375,7 @@ public List getRequiredLibs() { @Override public List getRequiredLibsRec() { - if (requiredLibsRec == null) { + if (requiredLibsRec == null || changedSinceLastUpdateRec(1)) { requiredLibsRec = new ArrayList<>(); for (ContributedLibrary lib : getRequiredLibs()) { if (!requiredLibsRec.contains(lib) && lib != this) { From 1ff501fc26b576111f475d4a16e4205ba7765fd2 Mon Sep 17 00:00:00 2001 From: Martin Sidaway Date: Thu, 25 Jun 2015 13:17:26 +0100 Subject: [PATCH 06/15] Prevent infinite recursion caused by dependency loops. --- .../libraries/ContributedLibrary.java | 5 ++++ .../processing/app/packages/UserLibrary.java | 29 +++++++++++++++---- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java b/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java index 6ca88686850..ea2968e0a1c 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java @@ -34,6 +34,7 @@ import java.util.Comparator; import java.util.List; +import java.util.SortedSet; import static processing.app.I18n._; @@ -69,6 +70,10 @@ public abstract class ContributedLibrary extends DownloadableContribution { public abstract List getRequiredLibsRec(); + public abstract List getRequiredLibsRec(SortedSet visited); + + public abstract boolean changedSinceLastUpdateRec(int idx, SortedSet visited); + public static final Comparator CASE_INSENSITIVE_ORDER = (o1, o2) -> o1.getName().compareToIgnoreCase(o2.getName()); /** diff --git a/arduino-core/src/processing/app/packages/UserLibrary.java b/arduino-core/src/processing/app/packages/UserLibrary.java index 4f945c55f9c..3db05bd6c5b 100644 --- a/arduino-core/src/processing/app/packages/UserLibrary.java +++ b/arduino-core/src/processing/app/packages/UserLibrary.java @@ -43,6 +43,8 @@ import java.util.List; import java.util.TreeMap; import java.util.Map; +import java.util.TreeSet; +import java.util.SortedSet; public class UserLibrary extends ContributedLibrary { @@ -351,13 +353,19 @@ private boolean changedSinceLastUpdate(int idx) { return changed; } - private boolean changedSinceLastUpdateRec(int idx) { + @Override + public boolean changedSinceLastUpdateRec(int idx, SortedSet visited) { + // Prevent infinite recursion. + if (visited.contains(getDepSpec())) { + return false; + } + visited.add(getDepSpec()); + if (changedSinceLastUpdate(idx)) { return true; } for (ContributedLibrary lib : getRequiredLibs()) { - if (lib instanceof UserLibrary && - ((UserLibrary) lib).changedSinceLastUpdateRec(idx)) { + if (lib.changedSinceLastUpdateRec(idx, visited)) { return true; } } @@ -375,12 +383,23 @@ public List getRequiredLibs() { @Override public List getRequiredLibsRec() { - if (requiredLibsRec == null || changedSinceLastUpdateRec(1)) { + return getRequiredLibsRec(new TreeSet<>()); + } + + @Override + public List getRequiredLibsRec(SortedSet visited) { + // Prevent infinite recursion. + if (visited.contains(getDepSpec())) { + return new ArrayList<>(); + } + visited.add(getDepSpec()); + + if (requiredLibsRec == null || changedSinceLastUpdateRec(1, new TreeSet<>(visited))) { requiredLibsRec = new ArrayList<>(); for (ContributedLibrary lib : getRequiredLibs()) { if (!requiredLibsRec.contains(lib) && lib != this) { requiredLibsRec.add(lib); - for (ContributedLibrary libRec : lib.getRequiredLibsRec()) { + for (ContributedLibrary libRec : lib.getRequiredLibsRec(visited)) { if (!requiredLibsRec.contains(libRec) && libRec != this) { requiredLibsRec.add(libRec); } From 6e883ab301ec07f26e64268330303794915a6506 Mon Sep 17 00:00:00 2001 From: Martin Sidaway Date: Thu, 25 Jun 2015 13:40:19 +0100 Subject: [PATCH 07/15] Use UserLibrary instead of ContributedLibrary. --- .../processing/app/syntax/SketchTextArea.java | 7 +++---- .../libraries/ContributedLibrary.java | 8 -------- .../src/processing/app/debug/Compiler.java | 7 +++---- .../processing/app/packages/UserLibrary.java | 20 ++++++++----------- 4 files changed, 14 insertions(+), 28 deletions(-) diff --git a/app/src/processing/app/syntax/SketchTextArea.java b/app/src/processing/app/syntax/SketchTextArea.java index 7d820222887..9a32223be94 100644 --- a/app/src/processing/app/syntax/SketchTextArea.java +++ b/app/src/processing/app/syntax/SketchTextArea.java @@ -40,7 +40,6 @@ import org.fife.ui.rtextarea.RUndoManager; import processing.app.*; import processing.app.packages.UserLibrary; -import cc.arduino.contributions.libraries.ContributedLibrary; import javax.swing.*; import javax.swing.event.EventListenerList; @@ -461,14 +460,14 @@ private String getImportInfo(String line) { String info = null; if (lib != null) { info = getLibDescription(lib); - for (ContributedLibrary recLib : lib.getRequiredLibsRec()) { + for (UserLibrary recLib : lib.getRequiredLibsRec()) { info += "\n* " + getLibDescription(recLib); } } return info; } - private String getLibDescription(ContributedLibrary lib) { + private String getLibDescription(UserLibrary lib) { String ver = lib.getVersion(); if (ver != null) { ver = " " + ver; @@ -477,7 +476,7 @@ private String getLibDescription(ContributedLibrary lib) { } String folder = ""; if (lib instanceof UserLibrary) { - folder = ": " + ((UserLibrary) lib).getSrcFolder().toString(); + folder = ": " + lib.getSrcFolder().toString(); } return lib.getName() + " (" + lib.getGlobalName() + ")" + ver + folder; } diff --git a/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java b/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java index ea2968e0a1c..444a114d1f1 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/ContributedLibrary.java @@ -66,14 +66,6 @@ public abstract class ContributedLibrary extends DownloadableContribution { public abstract List getRequires(); - public abstract List getRequiredLibs(); - - public abstract List getRequiredLibsRec(); - - public abstract List getRequiredLibsRec(SortedSet visited); - - public abstract boolean changedSinceLastUpdateRec(int idx, SortedSet visited); - public static final Comparator CASE_INSENSITIVE_ORDER = (o1, o2) -> o1.getName().compareToIgnoreCase(o2.getName()); /** diff --git a/arduino-core/src/processing/app/debug/Compiler.java b/arduino-core/src/processing/app/debug/Compiler.java index f628226ff2b..aa01bb28ff4 100644 --- a/arduino-core/src/processing/app/debug/Compiler.java +++ b/arduino-core/src/processing/app/debug/Compiler.java @@ -32,7 +32,6 @@ import cc.arduino.packages.BoardPort; import cc.arduino.packages.Uploader; import cc.arduino.packages.UploaderFactory; -import cc.arduino.contributions.libraries.ContributedLibrary; import cc.arduino.packages.uploaders.MergeSketchWithBooloader; import org.apache.commons.compress.utils.IOUtils; @@ -620,9 +619,9 @@ static public List findAllSources(File sourcePath, boolean recurse) { return allSources; } - static public List findRequiredLibs(File sourcePath, boolean recurse) { + static public List findRequiredLibs(File sourcePath, boolean recurse) { List files = findAllSources(sourcePath, recurse); - List result = new ArrayList<>(); + List result = new ArrayList<>(); for (File file : files) { List libs = null; try { @@ -630,7 +629,7 @@ static public List findRequiredLibs(File sourcePath, boolean } catch (IOException e) { continue; } - for (ContributedLibrary lib : libs) { + for (UserLibrary lib : libs) { if (!result.contains(lib)) { result.add(lib); } diff --git a/arduino-core/src/processing/app/packages/UserLibrary.java b/arduino-core/src/processing/app/packages/UserLibrary.java index 3db05bd6c5b..03a9b1fdd4c 100644 --- a/arduino-core/src/processing/app/packages/UserLibrary.java +++ b/arduino-core/src/processing/app/packages/UserLibrary.java @@ -330,8 +330,8 @@ public List getRequires() { private Map lastUpdateTimes = new TreeMap(); - private List requiredLibs = null; - private List requiredLibsRec = null; + private List requiredLibs = null; + private List requiredLibsRec = null; private boolean changedSinceLastUpdate(int idx) { List files = Compiler.findAllSources(getSrcFolder(), useRecursion()); @@ -353,7 +353,6 @@ private boolean changedSinceLastUpdate(int idx) { return changed; } - @Override public boolean changedSinceLastUpdateRec(int idx, SortedSet visited) { // Prevent infinite recursion. if (visited.contains(getDepSpec())) { @@ -364,7 +363,7 @@ public boolean changedSinceLastUpdateRec(int idx, SortedSet visited) { if (changedSinceLastUpdate(idx)) { return true; } - for (ContributedLibrary lib : getRequiredLibs()) { + for (UserLibrary lib : getRequiredLibs()) { if (lib.changedSinceLastUpdateRec(idx, visited)) { return true; } @@ -372,8 +371,7 @@ public boolean changedSinceLastUpdateRec(int idx, SortedSet visited) { return false; } - @Override - public List getRequiredLibs() { + public List getRequiredLibs() { if (requiredLibs == null || changedSinceLastUpdate(0)) { requiredLibs = Compiler.findRequiredLibs(getSrcFolder(), useRecursion()); requiredLibs.remove(this); @@ -381,13 +379,11 @@ public List getRequiredLibs() { return requiredLibs; } - @Override - public List getRequiredLibsRec() { + public List getRequiredLibsRec() { return getRequiredLibsRec(new TreeSet<>()); } - @Override - public List getRequiredLibsRec(SortedSet visited) { + public List getRequiredLibsRec(SortedSet visited) { // Prevent infinite recursion. if (visited.contains(getDepSpec())) { return new ArrayList<>(); @@ -396,10 +392,10 @@ public List getRequiredLibsRec(SortedSet visited) { if (requiredLibsRec == null || changedSinceLastUpdateRec(1, new TreeSet<>(visited))) { requiredLibsRec = new ArrayList<>(); - for (ContributedLibrary lib : getRequiredLibs()) { + for (UserLibrary lib : getRequiredLibs()) { if (!requiredLibsRec.contains(lib) && lib != this) { requiredLibsRec.add(lib); - for (ContributedLibrary libRec : lib.getRequiredLibsRec(visited)) { + for (UserLibrary libRec : lib.getRequiredLibsRec(visited)) { if (!requiredLibsRec.contains(libRec) && libRec != this) { requiredLibsRec.add(libRec); } From 7f43ca6f1d5cb294fd82ebc3d907536abb5767d4 Mon Sep 17 00:00:00 2001 From: Martin Sidaway Date: Thu, 25 Jun 2015 14:32:30 +0100 Subject: [PATCH 08/15] Change method names. --- app/src/processing/app/syntax/SketchTextArea.java | 2 +- arduino-core/src/processing/app/BaseNoGui.java | 14 +++++++------- .../src/processing/app/debug/Compiler.java | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/src/processing/app/syntax/SketchTextArea.java b/app/src/processing/app/syntax/SketchTextArea.java index 9a32223be94..a7de1f5b395 100644 --- a/app/src/processing/app/syntax/SketchTextArea.java +++ b/app/src/processing/app/syntax/SketchTextArea.java @@ -456,7 +456,7 @@ public void mouseMoved(MouseEvent e) { } private String getImportInfo(String line) { - UserLibrary lib = BaseNoGui.firstLibraryByCode(line); + UserLibrary lib = BaseNoGui.findFirstLibraryByCode(line); String info = null; if (lib != null) { info = getLibDescription(lib); diff --git a/arduino-core/src/processing/app/BaseNoGui.java b/arduino-core/src/processing/app/BaseNoGui.java index dac3bde3461..b000bc5e376 100644 --- a/arduino-core/src/processing/app/BaseNoGui.java +++ b/arduino-core/src/processing/app/BaseNoGui.java @@ -963,7 +963,7 @@ static public void populateImportToLibraryTable() { } } - static public UserLibrary firstLibraryByImport(String importName) { + static public UserLibrary findFirstLibraryByImport(String importName) { LibraryList list = importToLibraryTable.get(importName); if (list == null || list.isEmpty()) { return null; @@ -971,19 +971,19 @@ static public UserLibrary firstLibraryByImport(String importName) { return list.peekFirst(); } - static public UserLibrary firstLibraryByCode(String code) { + static public UserLibrary findFirstLibraryByCode(String code) { List incs = PdePreprocessor.findIncludes(code); if (incs.isEmpty()) { return null; } - return firstLibraryByImport(incs.get(0)); + return findFirstLibraryByImport(incs.get(0)); } - static public List librariesByCode(String code) throws IOException { + static public List findLibrariesByCode(String code) throws IOException { List libs = new ArrayList<>(); List incs = PdePreprocessor.findIncludes(code); for (String inc : incs) { - UserLibrary lib = firstLibraryByImport(inc); + UserLibrary lib = findFirstLibraryByImport(inc); if (lib != null) { libs.add(lib); } @@ -991,8 +991,8 @@ static public List librariesByCode(String code) throws IOException return libs; } - static public List librariesByCode(File file) throws IOException { - return librariesByCode(FileUtils.readFileToString(file)); + static public List findLibrariesByCode(File file) throws IOException { + return findLibrariesByCode(FileUtils.readFileToString(file)); } static public void initParameters(String args[]) throws IOException { diff --git a/arduino-core/src/processing/app/debug/Compiler.java b/arduino-core/src/processing/app/debug/Compiler.java index aa01bb28ff4..55a1530b90a 100644 --- a/arduino-core/src/processing/app/debug/Compiler.java +++ b/arduino-core/src/processing/app/debug/Compiler.java @@ -625,7 +625,7 @@ static public List findRequiredLibs(File sourcePath, boolean recurs for (File file : files) { List libs = null; try { - libs = BaseNoGui.librariesByCode(file); + libs = BaseNoGui.findLibrariesByCode(file); } catch (IOException e) { continue; } From cedea25aaf7751a04d47c369e654d13797ad27d0 Mon Sep 17 00:00:00 2001 From: Martin Sidaway Date: Thu, 25 Jun 2015 14:54:22 +0100 Subject: [PATCH 09/15] Add recursive dependencies to a build. --- arduino-core/src/processing/app/debug/Compiler.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arduino-core/src/processing/app/debug/Compiler.java b/arduino-core/src/processing/app/debug/Compiler.java index 55a1530b90a..aae82169c91 100644 --- a/arduino-core/src/processing/app/debug/Compiler.java +++ b/arduino-core/src/processing/app/debug/Compiler.java @@ -1395,6 +1395,12 @@ public void preprocess(String buildPath, PdePreprocessor preprocessor) throws Ru importedDuplicateHeaders.add(item); importedDuplicateLibraries.add(list); } + // Add recursive dependencies + for (UserLibrary libRec : lib.getRequiredLibsRec()) { + if (!importedLibraries.contains(libRec)) { + importedLibraries.add(libRec); + } + } } } } From 7fce54d51e6e01d87f415d9b41525155d2f97d28 Mon Sep 17 00:00:00 2001 From: Martin Sidaway Date: Thu, 25 Jun 2015 22:14:34 +0100 Subject: [PATCH 10/15] Use dependency spec to find correct library. --- .../processing/app/syntax/SketchTextArea.java | 10 +-- .../src/processing/app/BaseNoGui.java | 42 ++++++----- .../src/processing/app/debug/Compiler.java | 62 +++++++++------- .../app/packages/LibrarySelection.java | 26 +++++++ .../processing/app/packages/UserLibrary.java | 70 +++++++++++++++---- .../app/preproc/PdePreprocessor.java | 23 +++--- 6 files changed, 163 insertions(+), 70 deletions(-) create mode 100644 arduino-core/src/processing/app/packages/LibrarySelection.java diff --git a/app/src/processing/app/syntax/SketchTextArea.java b/app/src/processing/app/syntax/SketchTextArea.java index a7de1f5b395..b6558795aeb 100644 --- a/app/src/processing/app/syntax/SketchTextArea.java +++ b/app/src/processing/app/syntax/SketchTextArea.java @@ -40,6 +40,7 @@ import org.fife.ui.rtextarea.RUndoManager; import processing.app.*; import processing.app.packages.UserLibrary; +import processing.app.packages.LibrarySelection; import javax.swing.*; import javax.swing.event.EventListenerList; @@ -456,12 +457,13 @@ public void mouseMoved(MouseEvent e) { } private String getImportInfo(String line) { - UserLibrary lib = BaseNoGui.findFirstLibraryByCode(line); + LibrarySelection libSel = BaseNoGui.findLibraryByCode(line); String info = null; - if (lib != null) { + if (libSel != null) { + UserLibrary lib = libSel.get(); info = getLibDescription(lib); - for (UserLibrary recLib : lib.getRequiredLibsRec()) { - info += "\n* " + getLibDescription(recLib); + for (LibrarySelection recLibSel : lib.getRequiredLibsRec()) { + info += "\n* " + getLibDescription(recLibSel.get()); } } return info; diff --git a/arduino-core/src/processing/app/BaseNoGui.java b/arduino-core/src/processing/app/BaseNoGui.java index b000bc5e376..8169a10c6ea 100644 --- a/arduino-core/src/processing/app/BaseNoGui.java +++ b/arduino-core/src/processing/app/BaseNoGui.java @@ -18,6 +18,7 @@ import processing.app.helpers.filefilters.OnlyFilesWithExtension; import processing.app.legacy.PApplet; import processing.app.packages.LibraryList; +import processing.app.packages.LibrarySelection; import processing.app.packages.UserLibrary; import processing.app.preproc.PdePreprocessor; @@ -851,10 +852,10 @@ static private void createToolPreferences(ContributionsIndexer indexer) { } static public void populateImportToLibraryTable() { - // Populate importToLibraryTable. Each header filename maps to - // 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. + // Populate importToLibraryTable. Each header filename maps to a list of + // libraries. Compiler.java will use the dependency specs in the source + // files to decide which library to use from the list. The list is also + // used to advise user of ambiguously matched and duplicate libraries. importToLibraryTable = new HashMap(); for (UserLibrary lib : librariesIndexer.getInstalledLibraries()) { try { @@ -963,27 +964,36 @@ static public void populateImportToLibraryTable() { } } - static public UserLibrary findFirstLibraryByImport(String importName) { - LibraryList list = importToLibraryTable.get(importName); + static public LibrarySelection findLibraryByImport(String[] importSpec) { + LibraryList list = importToLibraryTable.get(importSpec[0]); if (list == null || list.isEmpty()) { return null; } - return list.peekFirst(); + int index = 0; + if (importSpec[1] != null) { + for (int i = 0; i < list.size(); i++) { + if (list.get(i).matchesDepSpec(importSpec[1])) { + index = i; + } + } + } + return new LibrarySelection(list, index); } - static public UserLibrary findFirstLibraryByCode(String code) { - List incs = PdePreprocessor.findIncludes(code); + static public LibrarySelection findLibraryByCode(String code) { + List incs = PdePreprocessor.findIncludes(code); if (incs.isEmpty()) { return null; } - return findFirstLibraryByImport(incs.get(0)); + return findLibraryByImport(incs.get(0)); } - static public List findLibrariesByCode(String code) throws IOException { - List libs = new ArrayList<>(); - List incs = PdePreprocessor.findIncludes(code); - for (String inc : incs) { - UserLibrary lib = findFirstLibraryByImport(inc); + static public List findLibrariesByCode(String code) + throws IOException { + List libs = new ArrayList<>(); + List incs = PdePreprocessor.findIncludes(code); + for (String[] inc : incs) { + LibrarySelection lib = findLibraryByImport(inc); if (lib != null) { libs.add(lib); } @@ -991,7 +1001,7 @@ static public List findLibrariesByCode(String code) throws IOExcept return libs; } - static public List findLibrariesByCode(File file) throws IOException { + static public List findLibrariesByCode(File file) throws IOException { return findLibrariesByCode(FileUtils.readFileToString(file)); } diff --git a/arduino-core/src/processing/app/debug/Compiler.java b/arduino-core/src/processing/app/debug/Compiler.java index aae82169c91..932762a5ced 100644 --- a/arduino-core/src/processing/app/debug/Compiler.java +++ b/arduino-core/src/processing/app/debug/Compiler.java @@ -48,6 +48,7 @@ import processing.app.legacy.PApplet; import processing.app.packages.LegacyUserLibrary; import processing.app.packages.UserLibrary; +import processing.app.packages.LibrarySelection; import processing.app.tools.DoubleQuotedArgumentsOnWindowsCommandLine; public class Compiler implements MessageConsumer { @@ -489,12 +490,15 @@ private void adviseDuplicateLibraries() { 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 { + LibrarySelection libSel = importedDuplicateLibraries.get(i); + + System.out.println(I18n.format(_(" Used: {0}"), + libSel.get().getInstalledFolder().getPath())); + first = false; + + for (int j = 0; j < libSel.getList().size(); j++) { + if (j != libSel.getIndex()) { + UserLibrary lib = libSel.getList().get(j); System.out.println(I18n.format(_(" Not used: {0}"), lib.getInstalledFolder().getPath())); } @@ -619,19 +623,19 @@ static public List findAllSources(File sourcePath, boolean recurse) { return allSources; } - static public List findRequiredLibs(File sourcePath, boolean recurse) { + static public List findRequiredLibs(File sourcePath, boolean recurse) { List files = findAllSources(sourcePath, recurse); - List result = new ArrayList<>(); + List result = new ArrayList<>(); for (File file : files) { - List libs = null; + List libSels = null; try { - libs = BaseNoGui.findLibrariesByCode(file); + libSels = BaseNoGui.findLibrariesByCode(file); } catch (IOException e) { continue; } - for (UserLibrary lib : libs) { - if (!result.contains(lib)) { - result.add(lib); + for (LibrarySelection libSel : libSels) { + if (!result.contains(libSel)) { + result.add(libSel); } } } @@ -1383,22 +1387,26 @@ public void preprocess(String buildPath, PdePreprocessor preprocessor) throws Ru // grab the imports from the code just preproc'd importedLibraries = new LibraryList(); - importedDuplicateHeaders = new ArrayList(); - importedDuplicateLibraries = new ArrayList(); - for (String item : preprocessor.getExtraImports()) { - LibraryList list = BaseNoGui.importToLibraryTable.get(item); - if (list != null) { - UserLibrary lib = list.peekFirst(); - if (lib != null && !importedLibraries.contains(lib)) { + importedDuplicateHeaders = new ArrayList<>(); + importedDuplicateLibraries = new ArrayList<>(); + for (String[] item : preprocessor.getExtraImports()) { + LibrarySelection libSel = BaseNoGui.findLibraryByImport(item); + if (libSel != null) { + UserLibrary lib = libSel.get(); + if (!importedLibraries.contains(lib)) { importedLibraries.add(lib); - if (list.size() > 1) { - importedDuplicateHeaders.add(item); - importedDuplicateLibraries.add(list); + if (libSel.getList().size() > 1) { + importedDuplicateHeaders.add(item[0]); + importedDuplicateLibraries.add(libSel); } // Add recursive dependencies - for (UserLibrary libRec : lib.getRequiredLibsRec()) { - if (!importedLibraries.contains(libRec)) { - importedLibraries.add(libRec); + for (LibrarySelection libSelRec : lib.getRequiredLibsRec()) { + if (!importedLibraries.contains(libSelRec.get())) { + importedLibraries.add(libSelRec.get()); + if (libSelRec.getList().size() > 1) { + importedDuplicateHeaders.add("UNKNOWN"); // FIXME + importedDuplicateLibraries.add(libSelRec); + } } } } @@ -1434,7 +1442,7 @@ public void preprocess(String buildPath, PdePreprocessor preprocessor) throws Ru */ private LibraryList importedLibraries; private List importedDuplicateHeaders; - private List importedDuplicateLibraries; + private List importedDuplicateLibraries; /** * Map an error from a set of processed .java files back to its location diff --git a/arduino-core/src/processing/app/packages/LibrarySelection.java b/arduino-core/src/processing/app/packages/LibrarySelection.java new file mode 100644 index 00000000000..417024ef10b --- /dev/null +++ b/arduino-core/src/processing/app/packages/LibrarySelection.java @@ -0,0 +1,26 @@ +package processing.app.packages; + +public class LibrarySelection { + private LibraryList list; + private int index; + public LibrarySelection(LibraryList list, int index) { + this.list = list; + this.index = index; + } + public UserLibrary get() { + return list.get(index); + } + public LibraryList getList() { + return list; + } + public int getIndex() { + return index; + } + public boolean equals(Object otherObj) { + if (!(otherObj instanceof LibrarySelection)) { + return false; + } + LibrarySelection other = (LibrarySelection) otherObj; + return this.get().equals(other.get()); + } +} diff --git a/arduino-core/src/processing/app/packages/UserLibrary.java b/arduino-core/src/processing/app/packages/UserLibrary.java index 03a9b1fdd4c..e15ce109ca9 100644 --- a/arduino-core/src/processing/app/packages/UserLibrary.java +++ b/arduino-core/src/processing/app/packages/UserLibrary.java @@ -30,6 +30,7 @@ import cc.arduino.contributions.libraries.ContributedLibrary; import cc.arduino.contributions.libraries.ContributedLibraryReference; +import processing.app.packages.LibrarySelection; import processing.app.helpers.FileUtils; import processing.app.helpers.PreferencesMap; import processing.app.debug.Compiler; @@ -235,6 +236,49 @@ public String getDepSpec() { return depSpec; } + public boolean matchesDepSpec(String spec) { + String[] parts = spec.split(":", 2); + if (!getGlobalName().equals(parts[0])) { + // Global name does not match - reject. + return false; + } + if (parts.length == 1) { + // No version spec - accept any version. + return true; + } + if (getVersion() == null) { + // Unlabeled version - fails to match any version spec. + return false; + } + String[] vers = parts[1].split("-"); + if (vers.length == 2) { + return versionsOrdered(vers[0], getVersion()) && + versionsOrdered(getVersion(), vers[1]); + } else if (parts[1].endsWith("+")) { + return versionsOrdered(parts[1].substring(0, parts[1].length() - 1), getVersion()); + } else if (parts[1].endsWith("*")) { + return getVersion().startsWith(parts[1].substring(0, parts[1].length() - 1)); + } else { + return parts[1].equals(getVersion()); + } + } + + private boolean versionsOrdered(String ver1, String ver2) { + String[] c1 = ver1.split("\\."); + String[] c2 = ver2.split("\\."); + int i; + for (i = 0; i < c1.length && i < c2.length; i++) { + int n1 = Integer.parseInt(c1[i]); + int n2 = Integer.parseInt(c2[i]); + if (n1 > n2) { + return false; + } else if (n1 < n2) { + return true; + } + } + return c1.length <= c2.length; + } + @Override public String getName() { return name; @@ -330,8 +374,8 @@ public List getRequires() { private Map lastUpdateTimes = new TreeMap(); - private List requiredLibs = null; - private List requiredLibsRec = null; + private List requiredLibs = null; + private List requiredLibsRec = null; private boolean changedSinceLastUpdate(int idx) { List files = Compiler.findAllSources(getSrcFolder(), useRecursion()); @@ -363,15 +407,15 @@ public boolean changedSinceLastUpdateRec(int idx, SortedSet visited) { if (changedSinceLastUpdate(idx)) { return true; } - for (UserLibrary lib : getRequiredLibs()) { - if (lib.changedSinceLastUpdateRec(idx, visited)) { + for (LibrarySelection libSel : getRequiredLibs()) { + if (libSel.get().changedSinceLastUpdateRec(idx, visited)) { return true; } } return false; } - public List getRequiredLibs() { + public List getRequiredLibs() { if (requiredLibs == null || changedSinceLastUpdate(0)) { requiredLibs = Compiler.findRequiredLibs(getSrcFolder(), useRecursion()); requiredLibs.remove(this); @@ -379,11 +423,11 @@ public List getRequiredLibs() { return requiredLibs; } - public List getRequiredLibsRec() { + public List getRequiredLibsRec() { return getRequiredLibsRec(new TreeSet<>()); } - public List getRequiredLibsRec(SortedSet visited) { + public List getRequiredLibsRec(SortedSet visited) { // Prevent infinite recursion. if (visited.contains(getDepSpec())) { return new ArrayList<>(); @@ -392,12 +436,12 @@ public List getRequiredLibsRec(SortedSet visited) { if (requiredLibsRec == null || changedSinceLastUpdateRec(1, new TreeSet<>(visited))) { requiredLibsRec = new ArrayList<>(); - for (UserLibrary lib : getRequiredLibs()) { - if (!requiredLibsRec.contains(lib) && lib != this) { - requiredLibsRec.add(lib); - for (UserLibrary libRec : lib.getRequiredLibsRec(visited)) { - if (!requiredLibsRec.contains(libRec) && libRec != this) { - requiredLibsRec.add(libRec); + for (LibrarySelection libSel : getRequiredLibs()) { + if (!requiredLibsRec.contains(libSel) && libSel.get() != this) { + requiredLibsRec.add(libSel); + for (LibrarySelection libSelRec : libSel.get().getRequiredLibsRec(visited)) { + if (!requiredLibsRec.contains(libSelRec) && libSelRec.get() != this) { + requiredLibsRec.add(libSelRec); } } } diff --git a/arduino-core/src/processing/app/preproc/PdePreprocessor.java b/arduino-core/src/processing/app/preproc/PdePreprocessor.java index e8d97962d67..0a8b56034d7 100644 --- a/arduino-core/src/processing/app/preproc/PdePreprocessor.java +++ b/arduino-core/src/processing/app/preproc/PdePreprocessor.java @@ -43,7 +43,7 @@ Processing version Copyright (c) 2004-05 Ben Fry and Casey Reas */ public class PdePreprocessor { - private static final String IMPORT_REGEX = "^\\s*#include\\s*[<\"](\\S+)[\">]"; + private static final String IMPORT_REGEX = "^\\s*#include\\s*[<\"](\\S+)[\">](?:[ \\t]*//!Lib[ \\t]+[\"']([^\"']*)[\"'])?"; // stores number of built user-defined function prototypes public int prototypeCount = 0; @@ -58,7 +58,7 @@ public class PdePreprocessor { // these ones have the .* at the end, since a class name might be at the end // instead of .* which would make trouble other classes using this can lop // off the . and anything after it to produce a package name consistently. - List programImports; + List programImports; // imports just from the code folder, treated differently // than the others, since the imports are auto-generated. @@ -85,6 +85,12 @@ public int writePrefix(String program) // http://dev.processing.org/bugs/show_bug.cgi?id=5 program += "\n"; + //String importRegexp = "(?:^|\\s|;)(import\\s+)(\\S+)(\\s*;)"; + + // This must come before scrubbing the comments, so that we get the + // dependency specs (//!Lib comments) + programImports = findIncludes(program); + // if the program ends with an unterminated multi-line comment, // an OutOfMemoryError or NullPointerException will happen. // again, not gonna bother tracking this down, but here's a hack. @@ -96,10 +102,6 @@ public int writePrefix(String program) program = substituteUnicode(program); } - //String importRegexp = "(?:^|\\s|;)(import\\s+)(\\S+)(\\s*;)"; - - programImports = findIncludes(program); - codeFolderImports = new ArrayList(); // if (codeFolderPackages != null) { // for (String item : codeFolderPackages) { @@ -118,15 +120,16 @@ public int writePrefix(String program) return headerCount + prototypeCount; } - public static List findIncludes(String code){ + public static List findIncludes(String code){ String[][] pieces = PApplet.matchAll(code, IMPORT_REGEX); - ArrayList programImports = new ArrayList<>(); + ArrayList programImports = new ArrayList<>(); if (pieces != null) for (int i = 0; i < pieces.length; i++) - programImports.add(pieces[i][1]); // the package name + programImports.add(new String[] { pieces[i][1], pieces[i][2] }); + // the include file name, and the dependency spec (if any) return programImports; } @@ -208,7 +211,7 @@ protected void writeProgram(PrintStream out, String program, List protot protected void writeFooter(PrintStream out) throws java.lang.Exception {} - public List getExtraImports() { + public List getExtraImports() { return programImports; } From 99236c2ae65927f01019f55db8898c872339bae1 Mon Sep 17 00:00:00 2001 From: Martin Sidaway Date: Thu, 25 Jun 2015 22:29:36 +0100 Subject: [PATCH 11/15] Use | in fallback global name and "" for dep spec. --- app/src/processing/app/Sketch.java | 2 +- arduino-core/src/processing/app/packages/UserLibrary.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/processing/app/Sketch.java b/app/src/processing/app/Sketch.java index 0f6a5e702f4..2825de43c60 100644 --- a/app/src/processing/app/Sketch.java +++ b/app/src/processing/app/Sketch.java @@ -962,7 +962,7 @@ private void importLibrary(File jarPath, String depSpec) throws IOException { buffer.append(aList); buffer.append(">"); if (depSpec != null) { - buffer.append(" //!Lib '" + depSpec + "'"); + buffer.append(" //!Lib \"" + depSpec + "\""); } buffer.append("\n"); } diff --git a/arduino-core/src/processing/app/packages/UserLibrary.java b/arduino-core/src/processing/app/packages/UserLibrary.java index e15ce109ca9..54a4f257757 100644 --- a/arduino-core/src/processing/app/packages/UserLibrary.java +++ b/arduino-core/src/processing/app/packages/UserLibrary.java @@ -213,7 +213,7 @@ public void setGlobalName(String gn) { invalid = true; // Fallback. Note: the global name is used to test for equality, // so it must have a value. - globalName = author.replace('/', '_') + "/" + name; + globalName = author.replace('|', '_') + "|" + name; } if (invalid) { System.err.println("WARNING: global_name not set in library " + name + ". Guessing '" + globalName + "'.\nPlease set this to a suitable Java-style package name, e.g. io.github.myaccount.myproject\nThe name should be set manually to avoid confusion when forking."); From 15e106a2746c38624cb03f76336d4ffd4a3a4336 Mon Sep 17 00:00:00 2001 From: Martin Sidaway Date: Thu, 25 Jun 2015 22:52:38 +0100 Subject: [PATCH 12/15] Only use global name for matching, and warn about versions. Version warnings currently only implemented in the case of a direct import. --- .../processing/app/syntax/SketchTextArea.java | 21 ++++++++++++++----- .../processing/app/packages/UserLibrary.java | 9 ++++---- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/app/src/processing/app/syntax/SketchTextArea.java b/app/src/processing/app/syntax/SketchTextArea.java index b6558795aeb..d1e023203dc 100644 --- a/app/src/processing/app/syntax/SketchTextArea.java +++ b/app/src/processing/app/syntax/SketchTextArea.java @@ -41,6 +41,7 @@ import processing.app.*; import processing.app.packages.UserLibrary; import processing.app.packages.LibrarySelection; +import processing.app.preproc.PdePreprocessor; import javax.swing.*; import javax.swing.event.EventListenerList; @@ -457,19 +458,24 @@ public void mouseMoved(MouseEvent e) { } private String getImportInfo(String line) { - LibrarySelection libSel = BaseNoGui.findLibraryByCode(line); + java.util.List incs = PdePreprocessor.findIncludes(line); + LibrarySelection libSel = null; + if (!incs.isEmpty()) { + libSel = BaseNoGui.findLibraryByImport(incs.get(0)); + } String info = null; if (libSel != null) { UserLibrary lib = libSel.get(); - info = getLibDescription(lib); + info = getLibDescription(lib, incs.get(0)); for (LibrarySelection recLibSel : lib.getRequiredLibsRec()) { - info += "\n* " + getLibDescription(recLibSel.get()); + // TODO: also check import spec of recursive deps + info += "\n* " + getLibDescription(recLibSel.get(), null); } } return info; } - private String getLibDescription(UserLibrary lib) { + private String getLibDescription(UserLibrary lib, String[] importSpec) { String ver = lib.getVersion(); if (ver != null) { ver = " " + ver; @@ -480,7 +486,12 @@ private String getLibDescription(UserLibrary lib) { if (lib instanceof UserLibrary) { folder = ": " + lib.getSrcFolder().toString(); } - return lib.getName() + " (" + lib.getGlobalName() + ")" + ver + folder; + String desc = lib.getName() + " (" + lib.getGlobalName() + ")" + ver + folder; + if (importSpec != null && importSpec[1] != null && + !lib.matchesDepSpecVersion(importSpec[1])) { + desc += "\nWARNING: "+lib.getGlobalName()+" is an incorrect version."; + } + return desc; } private void stopScanningForLinks() { diff --git a/arduino-core/src/processing/app/packages/UserLibrary.java b/arduino-core/src/processing/app/packages/UserLibrary.java index 54a4f257757..d8ef4d53137 100644 --- a/arduino-core/src/processing/app/packages/UserLibrary.java +++ b/arduino-core/src/processing/app/packages/UserLibrary.java @@ -238,10 +238,11 @@ public String getDepSpec() { public boolean matchesDepSpec(String spec) { String[] parts = spec.split(":", 2); - if (!getGlobalName().equals(parts[0])) { - // Global name does not match - reject. - return false; - } + return getGlobalName().equals(parts[0]); + } + + public boolean matchesDepSpecVersion(String spec) { + String[] parts = spec.split(":", 2); if (parts.length == 1) { // No version spec - accept any version. return true; From 8fa7bbd7dd2085d24ff4fec9ce65217631557b1d Mon Sep 17 00:00:00 2001 From: Martin Sidaway Date: Fri, 26 Jun 2015 11:19:55 +0100 Subject: [PATCH 13/15] Prefer already-directly-imported (or current) library, if no descriptor The tooltips will sometimes get this wrong, unfortunately, as they do not have enough context. --- .../processing/app/syntax/SketchTextArea.java | 3 +- .../src/processing/app/BaseNoGui.java | 30 ++++++++++++++----- .../src/processing/app/debug/Compiler.java | 7 +++-- .../processing/app/packages/UserLibrary.java | 8 ++++- 4 files changed, 35 insertions(+), 13 deletions(-) diff --git a/app/src/processing/app/syntax/SketchTextArea.java b/app/src/processing/app/syntax/SketchTextArea.java index d1e023203dc..a8b83feb88b 100644 --- a/app/src/processing/app/syntax/SketchTextArea.java +++ b/app/src/processing/app/syntax/SketchTextArea.java @@ -461,7 +461,8 @@ private String getImportInfo(String line) { java.util.List incs = PdePreprocessor.findIncludes(line); LibrarySelection libSel = null; if (!incs.isEmpty()) { - libSel = BaseNoGui.findLibraryByImport(incs.get(0)); + // Note: does not have a valid preferSet, so answer may be inaccurate + libSel = BaseNoGui.findLibraryByImport(incs.get(0), new HashSet()); } String info = null; if (libSel != null) { diff --git a/arduino-core/src/processing/app/BaseNoGui.java b/arduino-core/src/processing/app/BaseNoGui.java index 8169a10c6ea..76b6b15cf4c 100644 --- a/arduino-core/src/processing/app/BaseNoGui.java +++ b/arduino-core/src/processing/app/BaseNoGui.java @@ -964,20 +964,31 @@ static public void populateImportToLibraryTable() { } } - static public LibrarySelection findLibraryByImport(String[] importSpec) { + static public LibrarySelection findLibraryByImport(String[] importSpec, + Set preferSet) { LibraryList list = importToLibraryTable.get(importSpec[0]); if (list == null || list.isEmpty()) { return null; } int index = 0; - if (importSpec[1] != null) { + if (importSpec[1] != null && !importSpec[1].isEmpty()) { for (int i = 0; i < list.size(); i++) { if (list.get(i).matchesDepSpec(importSpec[1])) { index = i; } } + } else if (preferSet != null) { + // No dep spec - prefer library already imported in this context + for (int i = 0; i < list.size(); i++) { + if (preferSet.contains(list.get(i))) { + index = i; + } + } } - return new LibrarySelection(list, index); + // Keep track of libraries already imported in this context + LibrarySelection libSel = new LibrarySelection(list, index); + preferSet.add(libSel.get()); + return libSel; } static public LibrarySelection findLibraryByCode(String code) { @@ -985,15 +996,16 @@ static public LibrarySelection findLibraryByCode(String code) { if (incs.isEmpty()) { return null; } - return findLibraryByImport(incs.get(0)); + return findLibraryByImport(incs.get(0), null); } - static public List findLibrariesByCode(String code) + static public List findLibrariesByCode(String code, + Set preferSet) throws IOException { List libs = new ArrayList<>(); List incs = PdePreprocessor.findIncludes(code); for (String[] inc : incs) { - LibrarySelection lib = findLibraryByImport(inc); + LibrarySelection lib = findLibraryByImport(inc, preferSet); if (lib != null) { libs.add(lib); } @@ -1001,8 +1013,10 @@ static public List findLibrariesByCode(String code) return libs; } - static public List findLibrariesByCode(File file) throws IOException { - return findLibrariesByCode(FileUtils.readFileToString(file)); + static public List findLibrariesByCode(File file, + Set preferSet) + throws IOException { + return findLibrariesByCode(FileUtils.readFileToString(file), preferSet); } static public void initParameters(String args[]) throws IOException { diff --git a/arduino-core/src/processing/app/debug/Compiler.java b/arduino-core/src/processing/app/debug/Compiler.java index 932762a5ced..fce265b0b15 100644 --- a/arduino-core/src/processing/app/debug/Compiler.java +++ b/arduino-core/src/processing/app/debug/Compiler.java @@ -623,13 +623,13 @@ static public List findAllSources(File sourcePath, boolean recurse) { return allSources; } - static public List findRequiredLibs(File sourcePath, boolean recurse) { + static public List findRequiredLibs(File sourcePath, boolean recurse, Set preferSet) { List files = findAllSources(sourcePath, recurse); List result = new ArrayList<>(); for (File file : files) { List libSels = null; try { - libSels = BaseNoGui.findLibrariesByCode(file); + libSels = BaseNoGui.findLibrariesByCode(file, preferSet); } catch (IOException e) { continue; } @@ -1389,8 +1389,9 @@ public void preprocess(String buildPath, PdePreprocessor preprocessor) throws Ru importedLibraries = new LibraryList(); importedDuplicateHeaders = new ArrayList<>(); importedDuplicateLibraries = new ArrayList<>(); + Set preferSet = new HashSet(); for (String[] item : preprocessor.getExtraImports()) { - LibrarySelection libSel = BaseNoGui.findLibraryByImport(item); + LibrarySelection libSel = BaseNoGui.findLibraryByImport(item, preferSet); if (libSel != null) { UserLibrary lib = libSel.get(); if (!importedLibraries.contains(lib)) { diff --git a/arduino-core/src/processing/app/packages/UserLibrary.java b/arduino-core/src/processing/app/packages/UserLibrary.java index d8ef4d53137..caa3c310838 100644 --- a/arduino-core/src/processing/app/packages/UserLibrary.java +++ b/arduino-core/src/processing/app/packages/UserLibrary.java @@ -44,8 +44,10 @@ import java.util.List; import java.util.TreeMap; import java.util.Map; +import java.util.HashSet; import java.util.TreeSet; import java.util.SortedSet; +import java.util.Set; public class UserLibrary extends ContributedLibrary { @@ -418,7 +420,11 @@ public boolean changedSinceLastUpdateRec(int idx, SortedSet visited) { public List getRequiredLibs() { if (requiredLibs == null || changedSinceLastUpdate(0)) { - requiredLibs = Compiler.findRequiredLibs(getSrcFolder(), useRecursion()); + // Note: the "recursion" in useRecursion() refers to a strategy for + // finding files within an individual project + Set preferSet = new HashSet<>(); + preferSet.add(this); + requiredLibs = Compiler.findRequiredLibs(getSrcFolder(), useRecursion(), preferSet); requiredLibs.remove(this); } return requiredLibs; From 1454800be0d46128a5c8999cbfb79fb601a182e9 Mon Sep 17 00:00:00 2001 From: Martin Sidaway Date: Fri, 26 Jun 2015 11:44:24 +0100 Subject: [PATCH 14/15] Don't report duplicate libs imported from libs. --- arduino-core/src/processing/app/debug/Compiler.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/arduino-core/src/processing/app/debug/Compiler.java b/arduino-core/src/processing/app/debug/Compiler.java index fce265b0b15..bc22f125cb6 100644 --- a/arduino-core/src/processing/app/debug/Compiler.java +++ b/arduino-core/src/processing/app/debug/Compiler.java @@ -1404,10 +1404,12 @@ public void preprocess(String buildPath, PdePreprocessor preprocessor) throws Ru for (LibrarySelection libSelRec : lib.getRequiredLibsRec()) { if (!importedLibraries.contains(libSelRec.get())) { importedLibraries.add(libSelRec.get()); - if (libSelRec.getList().size() > 1) { - importedDuplicateHeaders.add("UNKNOWN"); // FIXME - importedDuplicateLibraries.add(libSelRec); - } + // This is probably just confusing. People can examine the + // tooltips if they need this information. + //if (libSelRec.getList().size() > 1) { + // importedDuplicateHeaders.add("UNKNOWN"); // FIXME + // importedDuplicateLibraries.add(libSelRec); + //} } } } From 200a01233c595efb2eb5b7414c2f301c6daf8f03 Mon Sep 17 00:00:00 2001 From: Martin Sidaway Date: Fri, 26 Jun 2015 12:05:24 +0100 Subject: [PATCH 15/15] Implement UserLibrary.hashCode() for preferSet. --- app/src/processing/app/syntax/SketchTextArea.java | 2 +- arduino-core/src/processing/app/debug/Compiler.java | 2 +- arduino-core/src/processing/app/packages/UserLibrary.java | 7 ++++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/src/processing/app/syntax/SketchTextArea.java b/app/src/processing/app/syntax/SketchTextArea.java index a8b83feb88b..cf362313915 100644 --- a/app/src/processing/app/syntax/SketchTextArea.java +++ b/app/src/processing/app/syntax/SketchTextArea.java @@ -462,7 +462,7 @@ private String getImportInfo(String line) { LibrarySelection libSel = null; if (!incs.isEmpty()) { // Note: does not have a valid preferSet, so answer may be inaccurate - libSel = BaseNoGui.findLibraryByImport(incs.get(0), new HashSet()); + libSel = BaseNoGui.findLibraryByImport(incs.get(0), new HashSet<>()); } String info = null; if (libSel != null) { diff --git a/arduino-core/src/processing/app/debug/Compiler.java b/arduino-core/src/processing/app/debug/Compiler.java index bc22f125cb6..b38a3ab0136 100644 --- a/arduino-core/src/processing/app/debug/Compiler.java +++ b/arduino-core/src/processing/app/debug/Compiler.java @@ -1389,7 +1389,7 @@ public void preprocess(String buildPath, PdePreprocessor preprocessor) throws Ru importedLibraries = new LibraryList(); importedDuplicateHeaders = new ArrayList<>(); importedDuplicateLibraries = new ArrayList<>(); - Set preferSet = new HashSet(); + Set preferSet = new HashSet<>(); for (String[] item : preprocessor.getExtraImports()) { LibrarySelection libSel = BaseNoGui.findLibraryByImport(item, preferSet); if (libSel != null) { diff --git a/arduino-core/src/processing/app/packages/UserLibrary.java b/arduino-core/src/processing/app/packages/UserLibrary.java index caa3c310838..f3b8dfce12c 100644 --- a/arduino-core/src/processing/app/packages/UserLibrary.java +++ b/arduino-core/src/processing/app/packages/UserLibrary.java @@ -238,6 +238,11 @@ public String getDepSpec() { return depSpec; } + @Override + public int hashCode() { + return getDepSpec().hashCode(); + } + public boolean matchesDepSpec(String spec) { String[] parts = spec.split(":", 2); return getGlobalName().equals(parts[0]); @@ -266,7 +271,7 @@ public boolean matchesDepSpecVersion(String spec) { } } - private boolean versionsOrdered(String ver1, String ver2) { + static private boolean versionsOrdered(String ver1, String ver2) { String[] c1 = ver1.split("\\."); String[] c2 = ver2.split("\\."); int i;