Skip to content

Storing Libraries in subfolders #1986

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Prev Previous commit
Next Next commit
Allow libraries to live in subdirectories
Before, libraries could only be stored directly under
e.g. Sketchbook/libraries. When a lot of libraries were present, this
could lead to an unwieldy list in the import menu and a mess on disk.

This commit changes the library scanning code to recursively scan the
contents of the various libraries directories. Each directory is scanned
for signs of a library (library.properties file, utility folder, any .h
files). If the directory doesn't look like a library, any subdirectories
are recursively scanned.

This commit updates the scanning code and the modifies the LibraryList
class so it can store a tree of libraries instead of just a flat list.

In the GUI all libraries are still shown in a flat list.
  • Loading branch information
matthijskooijman committed Oct 30, 2014
commit 0e2d2eddce28ca16f31ea293c52226deed9ccd26
120 changes: 67 additions & 53 deletions app/src/processing/app/Base.java
Original file line number Diff line number Diff line change
Expand Up @@ -1312,81 +1312,99 @@ public void rebuildExamplesMenu(JMenu menu) {
if (libraries != null && !libraries.isEmpty()) {
if (found)
menu.addSeparator();
for (Library lib : libraries)
for (Library lib : getLibraries())
addSketchesSubmenu(menu, lib, false);
}
} catch (IOException e) {
e.printStackTrace();
}
}

public LibraryList scanLibraries(List<File> folders) throws IOException {
LibraryList res = new LibraryList();
for (File folder : folders)
res.addOrReplaceAll(scanLibraries(folder));
return res;
public void updateLibraries() throws IOException {
// Calculate paths for libraries and examples
examplesFolder = getContentFile("examples");
toolsFolder = getContentFile("tools");

libraries = new LibraryList(null);
librariesFolders = new ArrayList<File>();

File ideLibs = getContentFile("libraries");
libraries.addSub(scanLibraries(ideLibs, _("Libraries for all boards"), true));
librariesFolders.add(ideLibs);

TargetPlatform targetPlatform = getTargetPlatform();
if (targetPlatform != null) {
File platformFolder = targetPlatform.getFolder();
File platformLibs = new File(platformFolder, "libraries");
librariesFolders.add(platformLibs);
libraries.addSub(scanLibraries(platformLibs, I18n.format(_("Libraries for {0}"), targetPlatform.getName()), true));

String core = getBoardPreferences().get("build.core");
TargetPlatform referencedPlatform = null;
if (core.contains(":")) {
String referencedCore = core.split(":")[0];
referencedPlatform = Base.getTargetPlatform(referencedCore, targetPlatform.getId());
if (referencedPlatform != null) {
File referencedPlatformFolder = referencedPlatform.getFolder();
File referencedLibs = new File(referencedPlatformFolder, "libraries");
librariesFolders.add(referencedLibs);
libraries.addSub(scanLibraries(referencedLibs, I18n.format(_("Libraries for {0}"), referencedPlatform.getName()), true));
}
}
}

File sketchbookLibs = getSketchbookLibrariesFolder();
librariesFolders.add(sketchbookLibs);
libraries.addSub(scanLibraries(sketchbookLibs, _("Libraries from your sketchbook"), true));
}

public LibraryList scanLibraries(File folder) throws IOException {
LibraryList res = new LibraryList();
public LibraryList scanLibraries(File folder, String name, boolean allow_legacy) throws IOException {
LibraryList res = new LibraryList(name != null ? name : folder.getName());

String list[] = folder.list(new OnlyDirs());
// if a bad folder or something like that, this might come back null
if (list == null)
return res;
File[] subfolders = folder.listFiles(new OnlyDirs());
if (subfolders != null) {
for (File subfolder : subfolders)
scanLibraryFolder(res, subfolder, allow_legacy);
}

for (String libName : list) {
File subfolder = new File(folder, libName);
res.sort();

return res;
}

public void scanLibraryFolder(LibraryList list, File folder, boolean allow_legacy) throws IOException {
if (Library.isLibrary(folder)) {
// This looks like a library, add it
try {
Library lib = Library.create(subfolder);
Library lib = Library.create(folder);
// (also replace previously found libs with the same name)
if (lib != null)
res.addOrReplace(lib);
list.addOrReplace(lib);
} catch (IOException e) {
System.out.println(I18n.format(_("Invalid library found in {0}: {1}"),
subfolder, e.getMessage()));
folder, e.getMessage()));
}
} else {
// This didn't look like a library, see if we can find libraries
// in the subdirectories
LibraryList sub = scanLibraries(folder, null, false);

if (!sub.isEmpty())
list.addSub(sub);
}
return res;
}

public void onBoardOrPortChange() {
TargetPlatform targetPlatform = getTargetPlatform();
if (targetPlatform == null)
return;

// Calculate paths for libraries and examples
examplesFolder = getContentFile("examples");
toolsFolder = getContentFile("tools");

File platformFolder = targetPlatform.getFolder();
librariesFolders = new ArrayList<File>();
librariesFolders.add(getContentFile("libraries"));
String core = getBoardPreferences().get("build.core");
if (core.contains(":")) {
String referencedCore = core.split(":")[0];
TargetPlatform referencedPlatform = Base.getTargetPlatform(referencedCore, targetPlatform.getId());
if (referencedPlatform != null) {
File referencedPlatformFolder = referencedPlatform.getFolder();
librariesFolders.add(new File(referencedPlatformFolder, "libraries"));
}
}
librariesFolders.add(new File(platformFolder, "libraries"));
librariesFolders.add(getSketchbookLibrariesFolder());

// Scan for libraries in each library folder.
// Libraries located in the latest folders on the list can override
// other libraries with the same name.
// Scan for libraries
try {
libraries = scanLibraries(librariesFolders);
updateLibraries();
} catch (IOException e) {
showWarning(_("Error"), _("Error loading libraries"), e);
}

// Populate importToLibraryTable
importToLibraryTable = new HashMap<String, Library>();
for (Library lib : libraries) {
for (Library lib : getLibraries()) {
try {
String headers[] = headerListFromIncludePath(lib.getSrcFolder());
for (String header : headers) {
Expand Down Expand Up @@ -1785,11 +1803,7 @@ public void actionPerformed(ActionEvent e) {
}

protected void addLibraries(JMenu menu, LibraryList libs) throws IOException {

LibraryList list = new LibraryList(libs);
list.sort();

for (Library lib : list) {
for (Library lib : libs.getAll()) {
@SuppressWarnings("serial")
AbstractAction action = new AbstractAction(lib.getName()) {
public void actionPerformed(ActionEvent event) {
Expand Down Expand Up @@ -2084,8 +2098,8 @@ static public File createTempFolder(String name) {
}


static public LibraryList getLibraries() {
return libraries;
static public List<Library> getLibraries() {
return libraries.getAll();
}


Expand Down
78 changes: 55 additions & 23 deletions app/src/processing/app/packages/LibraryList.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,28 @@

import java.io.File;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Collection;
import java.util.Collections;

import processing.app.helpers.FileUtils;

@SuppressWarnings("serial")
public class LibraryList extends ArrayList<Library> {
public class LibraryList {
protected ArrayList<LibraryList> subs;
protected ArrayList<Library> libs;
protected String name;

public LibraryList(LibraryList libs) {
super(libs);
}

public LibraryList() {
public LibraryList(String name) {
super();
this.name = name;
this.subs = new ArrayList<LibraryList>();
this.libs = new ArrayList<Library>();
}

public Library getByName(String name) {
for (Library l : this)
for (Library l : libs)
if (l.getName().equals(name))
return l;
return null;
Expand All @@ -28,43 +32,71 @@ public Library getByName(String name) {
public void addOrReplace(Library lib) {
Library l = getByName(lib.getName());
if (l != null)
remove(l);
add(lib);
libs.remove(l);
libs.add(lib);
}

public void addOrReplaceAll(Collection<? extends Library> c) {
for (Library l : c)
addOrReplace(l);
}

public void addSub(LibraryList sub) {
subs.add(sub);
}

public List<LibraryList> getSubs() {
return subs;
}

public List<Library> getLibs() {
return libs;
}

public List<Library> getAll() {
ArrayList<Library> list = new ArrayList<Library>();

for (LibraryList sub : subs)
list.addAll(sub.getAll());

list.addAll(libs);

return list;
}

public void sort() {
Collections.sort(this, Library.CASE_INSENSITIVE_ORDER);
Collections.sort(libs, Library.CASE_INSENSITIVE_ORDER);
Collections.sort(subs, LibraryList.CASE_INSENSITIVE_ORDER);
}

public Library search(String name, String arch) {
for (Library lib : this) {
for (Library lib : libs) {
if (!lib.getName().equals(name))
continue;
if (!lib.supportsArchitecture(arch))
continue;
return lib;
}
for (LibraryList sub : subs) {
Library lib = sub.search(name, arch);
if (lib != null)
return lib;
}
return null;
}

public LibraryList filterByArchitecture(String reqArch) {
LibraryList res = new LibraryList();
for (Library lib : this)
if (lib.supportsArchitecture(reqArch))
res.add(lib);
return res;
public static final Comparator<LibraryList> CASE_INSENSITIVE_ORDER = new Comparator<LibraryList>() {
@Override
public int compare(LibraryList o1, LibraryList o2) {
return o1.getName().compareToIgnoreCase(o2.getName());
}
};

public String getName() {
return name;
}

public LibraryList filterLibrariesInSubfolder(File subFolder) {
LibraryList res = new LibraryList();
for (Library lib : this)
if (FileUtils.isSubDirectory(subFolder, lib.getFolder()))
res.add(lib);
return res;
public boolean isEmpty() {
return libs.isEmpty() && subs.isEmpty();
}
}