1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-06-17 22:23:10 +03:00

Merge branch 'master' into esp8266

* master: (414 commits)
  Don't export sketch if the underlying core does not support it. Fixes #3171
  RSyntaxTextArea: using a modified version, tracked at https://github.com/arduino/RSyntaxTextArea. Fixes #3099
  Updated keywords.txt
  New editor on MacOSX: since CMD+J is known as "jump to selection" and the editor has no such feature, CMD+J is disabled on mac. See #3098
  Old Preferences class remains for backwards compatibility as a delegate for PreferencesData
  New Preferences window: renders fine on every OS and it's easier to adapt using NetBeans as visual editor. Fixes #3140
  Remove spawn from exec command
  Removed redundant call to File.deleteIfExists()
  Removed buggy redundant check in FileUtils.deleteIfExists()
  Restored current line/current selected lines display on lower left of the IDE. Fixes #3134
  Updated cursor.ino
  New editor on MacOSX: restored CMD+E for finding selected text
  New editor on MacOSX: CMD+UP/DOWN moves cursor to start or end of sketch. See #3098
  New editor on MacOSX: CMD+BACKSPACE deletes current line until cursor position, ALT+BACKSPACE deletes previous word. See #3098
  ArduinoIDE is in the default package. Removed
  Fixes  #2969:
  Fix Uncategorized warning message
  New editor: ALT+ BACKSPACE deletes next word (OSX only). See #3098
  New editor: ALT+ UP/DOWN move current line only if "editor.advanced" (hidden pref) is true. Fixes #3101
  New editor: mark occurrences enable when "editor.advanced" (hidden pref) is true. Fixes #3102
  ...

Conflicts:
	.gitignore
	build/build.xml
	hardware/esp8266com/esp8266/libraries/ESP8266WiFi/keywords.txt
	hardware/esp8266com/esp8266/libraries/ESP8266WiFi/library.properties
	hardware/esp8266com/esp8266/libraries/ESP8266WiFi/src/ESP8266WiFi.h
	libraries/WiFi/README.adoc
	libraries/WiFi/src/WiFi.cpp
	libraries/WiFi/src/WiFiClient.cpp
	libraries/WiFi/src/WiFiClient.h
	libraries/WiFi/src/WiFiServer.cpp
	libraries/WiFi/src/WiFiUdp.cpp
This commit is contained in:
Ivan Grokhotkov
2015-05-18 14:54:06 +03:00
644 changed files with 37518 additions and 12166 deletions

View File

@ -0,0 +1,40 @@
/*
* This file is part of Arduino.
*
* Copyright 2015 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino;
public class DefaultUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.err.println(t);
e.printStackTrace();
}
}

View File

@ -0,0 +1,45 @@
/*
* This file is part of Arduino.
*
* Copyright 2015 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.contributions;
import cc.arduino.contributions.packages.DownloadableContribution;
import java.util.Comparator;
public class DownloadableContributionBuiltInAtTheBottomComparator implements Comparator<DownloadableContribution> {
@Override
public int compare(DownloadableContribution p1, DownloadableContribution p2) {
if (p1.isReadOnly() == p2.isReadOnly()) {
return 0;
}
return p1.isReadOnly() ? 1 : -1;
}
}

View File

@ -0,0 +1,50 @@
/*
* This file is part of Arduino.
*
* Copyright 2015 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.contributions;
import cc.arduino.contributions.packages.DownloadableContribution;
import java.util.Comparator;
public class DownloadableContributionVersionComparator implements Comparator<DownloadableContribution> {
private final VersionComparator versionComparator;
public DownloadableContributionVersionComparator() {
versionComparator = new VersionComparator();
}
@Override
public int compare(DownloadableContribution lib1, DownloadableContribution lib2) {
return versionComparator.compare(lib1.getParsedVersion(), lib2.getParsedVersion());
}
}

View File

@ -0,0 +1,121 @@
/*
* This file is part of Arduino.
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2015 Arduino LLC (http://www.arduino.cc/)
*/
package cc.arduino.contributions;
import org.apache.commons.compress.utils.IOUtils;
import org.bouncycastle.openpgp.*;
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider;
import java.io.*;
import java.util.Iterator;
public class GPGDetachedSignatureVerifier {
private String keyId;
public GPGDetachedSignatureVerifier() {
this("7F294291");
}
public GPGDetachedSignatureVerifier(String keyId) {
this.keyId = keyId;
}
public boolean verify(File signedFile, File signature, File publicKey) throws IOException, PGPException {
PGPPublicKey pgpPublicKey = readPublicKey(publicKey, keyId);
FileInputStream signatureInputStream = null;
FileInputStream signedFileInputStream = null;
try {
signatureInputStream = new FileInputStream(signature);
PGPObjectFactory pgpObjectFactory = new PGPObjectFactory(signatureInputStream, new BcKeyFingerprintCalculator());
Object nextObject;
try {
nextObject = pgpObjectFactory.nextObject();
if (!(nextObject instanceof PGPSignatureList)) {
return false;
}
} catch (IOException e) {
return false;
}
PGPSignatureList pgpSignatureList = (PGPSignatureList) nextObject;
assert pgpSignatureList.size() == 1;
PGPSignature pgpSignature = pgpSignatureList.get(0);
pgpSignature.init(new BcPGPContentVerifierBuilderProvider(), pgpPublicKey);
signedFileInputStream = new FileInputStream(signedFile);
pgpSignature.update(IOUtils.toByteArray(signedFileInputStream));
return pgpSignature.verify();
} finally {
if (signatureInputStream != null) {
signatureInputStream.close();
}
if (signedFileInputStream != null) {
signedFileInputStream.close();
}
}
}
private PGPPublicKey readPublicKey(File file, String keyId) throws IOException, PGPException {
InputStream keyIn = null;
try {
keyIn = new BufferedInputStream(new FileInputStream(file));
return readPublicKey(keyIn, keyId);
} finally {
if (keyIn != null) {
keyIn.close();
}
}
}
private PGPPublicKey readPublicKey(InputStream input, String keyId) throws IOException, PGPException {
PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(input), new BcKeyFingerprintCalculator());
Iterator keyRingIter = pgpPub.getKeyRings();
while (keyRingIter.hasNext()) {
PGPPublicKeyRing keyRing = (PGPPublicKeyRing) keyRingIter.next();
Iterator keyIter = keyRing.getPublicKeys();
while (keyIter.hasNext()) {
PGPPublicKey key = (PGPPublicKey) keyIter.next();
if (Long.toHexString(key.getKeyID()).toUpperCase().endsWith(keyId)) {
return key;
}
}
}
throw new IllegalArgumentException("Can't find encryption key in key ring.");
}
}

View File

@ -0,0 +1,16 @@
package cc.arduino.contributions;
import processing.app.I18n;
import static processing.app.I18n._;
public class SignatureVerificationFailedException extends Exception {
public SignatureVerificationFailedException(String filename) {
super(I18n.format(_("{0} file signature verification failed"), filename));
}
public SignatureVerificationFailedException(String filename, Throwable cause) {
super(I18n.format(_("{0} file signature verification failed"), filename), cause);
}
}

View File

@ -0,0 +1,71 @@
/*
* This file is part of Arduino.
*
* Copyright 2014 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.contributions;
import com.github.zafarkhaja.semver.Version;
import java.util.Comparator;
public class VersionComparator implements Comparator<String> {
@Override
public int compare(String a, String b) {
// null is always less than any other value
if (a == null && b == null)
return 0;
if (a == null)
return -1;
if (b == null)
return 1;
Version versionA = VersionHelper.valueOf(a);
Version versionB = VersionHelper.valueOf(b);
return versionA.compareTo(versionB);
}
public boolean greaterThan(String a, String b) {
// null is always less than any other value
if (a == null && b == null) {
return false;
}
if (a == null) {
return false;
}
if (b == null) {
return true;
}
Version versionA = VersionHelper.valueOf(a);
Version versionB = VersionHelper.valueOf(b);
return versionA.greaterThan(versionB);
}
}

View File

@ -0,0 +1,57 @@
/*
* This file is part of Arduino.
*
* Copyright 2015 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.contributions;
import com.github.zafarkhaja.semver.Version;
public class VersionHelper {
public static Version valueOf(String ver) {
if (ver == null) {
return null;
}
try {
String[] verParts = ver.split("\\.");
if (verParts.length < 3) {
if (verParts.length == 2) {
return Version.forIntegers(Integer.valueOf(verParts[0]), Integer.valueOf(verParts[1]));
} else {
return Version.forIntegers(Integer.valueOf(verParts[0]));
}
} else {
return Version.valueOf(ver);
}
} catch (Exception e) {
System.err.println("Invalid version found: " + ver);
return null;
}
}
}

View File

@ -0,0 +1,46 @@
/*
* This file is part of Arduino.
*
* Copyright 2015 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.contributions.filters;
import cc.arduino.contributions.packages.DownloadableContribution;
import com.google.common.base.Predicate;
public class BuiltInPredicate implements Predicate<DownloadableContribution> {
@Override
public boolean apply(DownloadableContribution input) {
return input.isReadOnly();
}
@Override
public boolean equals(Object obj) {
return obj instanceof BuiltInPredicate;
}
}

View File

@ -0,0 +1,47 @@
/*
* This file is part of Arduino.
*
* Copyright 2015 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.contributions.filters;
import cc.arduino.contributions.packages.DownloadableContribution;
import com.google.common.base.Predicate;
public class DownloadableContributionWithVersionPredicate implements Predicate<DownloadableContribution> {
private final String version;
public DownloadableContributionWithVersionPredicate(String version) {
this.version = version;
}
@Override
public boolean apply(DownloadableContribution contribution) {
return version.equals(contribution.getParsedVersion());
}
}

View File

@ -0,0 +1,46 @@
/*
* This file is part of Arduino.
*
* Copyright 2015 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.contributions.filters;
import cc.arduino.contributions.packages.DownloadableContribution;
import com.google.common.base.Predicate;
public class InstalledPredicate implements Predicate<DownloadableContribution> {
@Override
public boolean apply(DownloadableContribution input) {
return input.isInstalled();
}
@Override
public boolean equals(Object obj) {
return obj instanceof DownloadableContribution;
}
}

View File

@ -0,0 +1,153 @@
/*
* This file is part of Arduino.
*
* Copyright 2014 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.contributions.libraries;
import cc.arduino.contributions.packages.DownloadableContribution;
import processing.app.I18n;
import java.util.Comparator;
import java.util.List;
import static processing.app.I18n._;
public abstract class ContributedLibrary extends DownloadableContribution {
public abstract String getName();
public abstract String getMaintainer();
public abstract String getAuthor();
public abstract String getWebsite();
public abstract String getCategory();
public abstract void setCategory(String category);
public abstract String getLicense();
public abstract String getParagraph();
public abstract String getSentence();
public abstract List<String> getArchitectures();
public abstract List<String> getTypes();
public abstract List<ContributedLibraryReference> getRequires();
public static final Comparator<ContributedLibrary> CASE_INSENSITIVE_ORDER = new Comparator<ContributedLibrary>() {
@Override
public int compare(ContributedLibrary o1, ContributedLibrary o2) {
return o1.getName().compareToIgnoreCase(o2.getName());
}
};
/**
* Returns <b>true</b> if the library declares to support the specified
* architecture (through the "architectures" property field).
*
* @param reqArch
* @return
*/
public boolean supportsArchitecture(String reqArch) {
return getArchitectures().contains(reqArch) || getArchitectures().contains("*");
}
/**
* Returns <b>true</b> if the library declares to support at least one of the
* specified architectures.
*
* @param reqArchs A List of architectures to check
* @return
*/
public boolean supportsArchitecture(List<String> reqArchs) {
if (reqArchs.contains("*"))
return true;
for (String reqArch : reqArchs)
if (supportsArchitecture(reqArch))
return true;
return false;
}
@Override
public String toString() {
return I18n.format(_("Version {0}"), getParsedVersion());
}
public String info() {
String res = "";
res += " ContributedLibrary : " + getName() + "\n";
res += " author : " + getAuthor() + "\n";
res += " maintainer : " + getMaintainer() + "\n";
res += " version : " + getParsedVersion() + "\n";
res += " website : " + getUrl() + "\n";
res += " category : " + getCategory() + "\n";
res += " license : " + getLicense() + "\n";
res += " descrip : " + getSentence() + "\n";
if (getParagraph() != null && !getParagraph().isEmpty())
res += " " + getParagraph() + "\n";
res += " architectures : ";
if (getArchitectures() != null)
for (String a : getArchitectures()) {
res += a + ",";
}
res += "\n";
res += " requires :\n";
if (getRequires() != null)
for (ContributedLibraryReference r : getRequires()) {
res += " " + r;
}
res += "\n";
// DownloadableContribution
res += super.toString();
return res;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ContributedLibrary)) {
return false;
}
String thisVersion = getParsedVersion();
String otherVersion = ((ContributedLibrary) obj).getParsedVersion();
boolean versionEquals = thisVersion == otherVersion || (thisVersion != null && otherVersion != null && thisVersion.equals(otherVersion));
String thisName = getName();
String otherName = ((ContributedLibrary) obj).getName();
boolean nameEquals = thisName == null || otherName == null || thisName.equals(otherName);
return versionEquals && nameEquals;
}
}

View File

@ -0,0 +1,43 @@
/*
* This file is part of Arduino.
*
* Copyright 2014 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.contributions.libraries;
public abstract class ContributedLibraryReference {
public abstract String getName();
public abstract String getMaintainer();
public abstract String getVersion();
@Override
public String toString() {
return getName() + " " + getVersion() + " (" + getMaintainer() + ")";
}
}

View File

@ -0,0 +1,104 @@
/*
* This file is part of Arduino.
*
* Copyright 2014 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.contributions.libraries;
import cc.arduino.contributions.DownloadableContributionBuiltInAtTheBottomComparator;
import cc.arduino.contributions.filters.InstalledPredicate;
import cc.arduino.contributions.libraries.filters.LibraryWithNamePredicate;
import com.google.common.collect.Collections2;
import java.util.*;
public abstract class LibrariesIndex {
public abstract List<ContributedLibrary> getLibraries();
public List<ContributedLibrary> find(final String name) {
return new LinkedList<ContributedLibrary>(Collections2.filter(getLibraries(), new LibraryWithNamePredicate(name)));
}
public ContributedLibrary find(String name, String version) {
if (name == null || version == null) {
return null;
}
for (ContributedLibrary lib : find(name)) {
if (version.equals(lib.getParsedVersion())) {
return lib;
}
}
return null;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (ContributedLibrary library : getLibraries()) {
sb.append(library.toString());
}
return sb.toString();
}
public List<String> getCategories() {
List<String> categories = new LinkedList<String>();
for (ContributedLibrary lib : getLibraries()) {
if (lib.getCategory() != null && !categories.contains(lib.getCategory())) {
categories.add(lib.getCategory());
}
}
Collections.sort(categories);
return categories;
}
public List<String> getTypes() {
Collection<String> typesAccumulator = new HashSet<String>();
for (ContributedLibrary lib : getLibraries()) {
if (lib.getTypes() != null) {
typesAccumulator.addAll(lib.getTypes());
}
}
List<String> types = new LinkedList<String>(typesAccumulator);
Collections.sort(types);
return types;
}
public ContributedLibrary getInstalled(String name) {
List<ContributedLibrary> installedReleases = new LinkedList<ContributedLibrary>(Collections2.filter(find(name), new InstalledPredicate()));
Collections.sort(installedReleases, new DownloadableContributionBuiltInAtTheBottomComparator());
if (installedReleases.isEmpty()) {
return null;
}
return installedReleases.get(0);
}
}

View File

@ -0,0 +1,223 @@
/*
* This file is part of Arduino.
*
* Copyright 2014 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.contributions.libraries;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.module.mrbean.MrBeanModule;
import processing.app.BaseNoGui;
import processing.app.I18n;
import processing.app.helpers.FileUtils;
import processing.app.helpers.filefilters.OnlyDirs;
import processing.app.packages.LegacyUserLibrary;
import processing.app.packages.LibraryList;
import processing.app.packages.UserLibrary;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import static processing.app.I18n._;
public class LibrariesIndexer {
private LibrariesIndex index;
private final LibraryList installedLibraries = new LibraryList();
private final LibraryList installedLibrariesWithDuplicates = new LibraryList();
private List<File> librariesFolders;
private final File indexFile;
private final File stagingFolder;
private File sketchbookLibrariesFolder;
public LibrariesIndexer(File preferencesFolder) {
indexFile = new File(preferencesFolder, "library_index.json");
stagingFolder = new File(preferencesFolder, "staging" + File.separator +
"libraries");
}
public void parseIndex() throws IOException {
parseIndex(indexFile);
// TODO: resolve libraries inner references
}
private void parseIndex(File indexFile) throws IOException {
InputStream indexIn = null;
try {
indexIn = new FileInputStream(indexFile);
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new MrBeanModule());
mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
mapper.configure(DeserializationFeature.EAGER_DESERIALIZER_FETCH, true);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
index = mapper.readValue(indexIn, LibrariesIndex.class);
for (ContributedLibrary library : index.getLibraries()) {
if (library.getCategory() == null || "".equals(library.getCategory())) {
library.setCategory("Uncategorized");
}
}
} finally {
if (indexIn != null) {
indexIn.close();
}
}
}
public void setLibrariesFolders(List<File> _librariesFolders) {
librariesFolders = _librariesFolders;
rescanLibraries();
}
public void rescanLibraries() {
// Clear all installed flags
installedLibraries.clear();
installedLibrariesWithDuplicates.clear();
for (ContributedLibrary lib : index.getLibraries())
lib.setInstalled(false);
// Rescan libraries
for (File folder : librariesFolders)
scanInstalledLibraries(folder, folder.equals(sketchbookLibrariesFolder));
}
private void scanInstalledLibraries(File folder, boolean isSketchbook) {
File list[] = folder.listFiles(OnlyDirs.ONLY_DIRS);
// if a bad folder or something like that, this might come back null
if (list == null)
return;
for (File subfolder : list) {
if (!BaseNoGui.isSanitaryName(subfolder.getName())) {
String mess = I18n.format(_("The library \"{0}\" cannot be used.\n"
+ "Library names must contain only basic letters and numbers.\n"
+ "(ASCII only and no spaces, and it cannot start with a number)"),
subfolder.getName());
BaseNoGui.showMessage(_("Ignoring bad library name"), mess);
continue;
}
try {
scanLibrary(subfolder, isSketchbook);
} catch (IOException e) {
System.out.println(I18n.format(_("Invalid library found in {0}: {1}"), subfolder, e.getMessage()));
}
}
}
private void scanLibrary(File folder, boolean isSketchbook) throws IOException {
boolean readOnly = !FileUtils.isSubDirectory(sketchbookLibrariesFolder, folder);
// A library is considered "legacy" if it doesn't contains
// a file called "library.properties"
File check = new File(folder, "library.properties");
if (!check.exists() || !check.isFile()) {
// Create a legacy library and exit
LegacyUserLibrary lib = LegacyUserLibrary.create(folder);
lib.setReadOnly(readOnly);
installedLibraries.addOrReplace(lib);
if (isSketchbook) {
installedLibrariesWithDuplicates.add(lib);
} else {
installedLibrariesWithDuplicates.addOrReplace(lib);
}
return;
}
// Create a regular library
UserLibrary lib = UserLibrary.create(folder);
lib.setReadOnly(readOnly);
installedLibraries.addOrReplace(lib);
if (isSketchbook) {
installedLibrariesWithDuplicates.add(lib);
} else {
installedLibrariesWithDuplicates.addOrReplace(lib);
}
// Check if we can find the same library in the index
// and mark it as installed
ContributedLibrary foundLib = index.find(lib.getName(), lib.getParsedVersion());
if (foundLib != null) {
foundLib.setInstalled(true);
foundLib.setInstalledFolder(folder);
foundLib.setReadOnly(readOnly);
lib.setTypes(foundLib.getTypes());
}
if (lib.isReadOnly() && lib.getTypes() == null && !lib.getDeclaredTypes().isEmpty()) {
lib.setTypes(lib.getDeclaredTypes());
}
if (lib.getTypes() == null) {
lib.setTypes(Arrays.asList("Contributed"));
}
}
public LibrariesIndex getIndex() {
return index;
}
public LibraryList getInstalledLibraries() {
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() {
return stagingFolder;
}
/**
* Set the sketchbook library folder. <br />
* New libraries will be installed here. <br />
* Libraries not found on this folder will be marked as read-only.
*
* @param folder
*/
public void setSketchbookLibrariesFolder(File folder) {
this.sketchbookLibrariesFolder = folder;
}
public File getSketchbookLibrariesFolder() {
return sketchbookLibrariesFolder;
}
public File getIndexFile() {
return indexFile;
}
}

View File

@ -0,0 +1,172 @@
/*
* This file is part of Arduino.
*
* Copyright 2015 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.contributions.libraries;
import cc.arduino.contributions.packages.DownloadableContributionsDownloader;
import cc.arduino.utils.ArchiveExtractor;
import cc.arduino.utils.MultiStepProgress;
import cc.arduino.utils.Progress;
import processing.app.BaseNoGui;
import processing.app.I18n;
import processing.app.helpers.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import static processing.app.I18n._;
public class LibraryInstaller {
private static final String LIBRARY_INDEX_URL;
static {
String externalLibraryIndexUrl = System.getProperty("LIBRARY_INDEX_URL");
if (externalLibraryIndexUrl != null && !"".equals(externalLibraryIndexUrl)) {
LIBRARY_INDEX_URL = externalLibraryIndexUrl;
} else {
LIBRARY_INDEX_URL = "http://downloads.arduino.cc/libraries/library_index.json";
}
}
private final LibrariesIndexer indexer;
private final DownloadableContributionsDownloader downloader;
public LibraryInstaller(LibrariesIndexer _indexer) {
indexer = _indexer;
File stagingFolder = _indexer.getStagingFolder();
downloader = new DownloadableContributionsDownloader(stagingFolder) {
@Override
protected void onProgress(Progress progress) {
LibraryInstaller.this.onProgress(progress);
}
};
}
public void updateIndex() throws Exception {
final MultiStepProgress progress = new MultiStepProgress(2);
// Step 1: Download index
URL url = new URL(LIBRARY_INDEX_URL);
File outputFile = indexer.getIndexFile();
File tmpFile = new File(outputFile.getAbsolutePath() + ".tmp");
try {
downloader.download(url, tmpFile, progress,
_("Downloading libraries index..."));
} catch (InterruptedException e) {
// Download interrupted... just exit
return;
}
progress.stepDone();
// TODO: Check downloaded index
// Replace old index with the updated one
if (outputFile.exists())
outputFile.delete();
if (!tmpFile.renameTo(outputFile))
throw new Exception(
_("An error occurred while updating libraries index!"));
// Step 2: Rescan index
rescanLibraryIndex(progress);
}
public void install(ContributedLibrary lib, ContributedLibrary replacedLib) throws Exception {
if (lib.isInstalled()) {
System.out.println(I18n.format(_("Library is already installed: {0} version {1}"), lib.getName(), lib.getParsedVersion()));
return;
}
final MultiStepProgress progress = new MultiStepProgress(3);
// Step 1: Download library
try {
downloader.download(lib, progress, I18n.format(_("Downloading library: {0}"), lib.getName()));
} catch (InterruptedException e) {
// Download interrupted... just exit
return;
}
// TODO: Extract to temporary folders and move to the final destination only
// once everything is successfully unpacked. If the operation fails remove
// all the temporary folders and abort installation.
// Step 2: Unpack library on the correct location
progress.setStatus(I18n.format(_("Installing library: {0}"), lib.getName()));
onProgress(progress);
File libsFolder = indexer.getSketchbookLibrariesFolder();
File tmpFolder = FileUtils.createTempFolderIn(libsFolder);
try {
new ArchiveExtractor(BaseNoGui.getPlatform()).extract(lib.getDownloadedFile(), tmpFolder, 1);
} catch (Exception e) {
if (tmpFolder.exists())
FileUtils.recursiveDelete(tmpFolder);
}
progress.stepDone();
// Step 3: Remove replaced library and move installed one to the correct location
// TODO: Fix progress bar...
remove(replacedLib);
File destFolder = new File(libsFolder, lib.getName().replaceAll(" ", "_"));
tmpFolder.renameTo(destFolder);
progress.stepDone();
// Step 4: Rescan index
rescanLibraryIndex(progress);
}
public void remove(ContributedLibrary lib) throws IOException {
if (lib == null || lib.isReadOnly()) {
return;
}
final MultiStepProgress progress = new MultiStepProgress(2);
// Step 1: Remove library
progress.setStatus(I18n.format(_("Removing library: {0}"), lib.getName()));
onProgress(progress);
FileUtils.recursiveDelete(lib.getInstalledFolder());
progress.stepDone();
// Step 2: Rescan index
rescanLibraryIndex(progress);
}
private void rescanLibraryIndex(MultiStepProgress progress) {
progress.setStatus(_("Updating list of installed libraries"));
onProgress(progress);
indexer.rescanLibraries();
progress.stepDone();
}
protected void onProgress(Progress progress) {
// Empty
}
}

View File

@ -0,0 +1,48 @@
/*
* This file is part of Arduino.
*
* Copyright 2015 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.contributions.libraries.filters;
import cc.arduino.contributions.libraries.ContributedLibrary;
import com.google.common.base.Predicate;
public class LibraryWithNamePredicate implements Predicate<ContributedLibrary> {
private final String name;
public LibraryWithNamePredicate(String name) {
this.name = name;
}
@Override
public boolean apply(ContributedLibrary contributedLibrary) {
return name.equals(contributedLibrary.getName());
}
}

View File

@ -0,0 +1,23 @@
package cc.arduino.contributions.packages;
import java.util.Arrays;
import java.util.List;
public class Constants {
public static final String DEFAULT_INDEX_FILE_NAME = "package_index.json";
public static final List<String> PROTECTED_PACKAGE_NAMES = Arrays.asList("arduino", "Intel");
public static final String PACKAGE_INDEX_URL;
public static final String PREFERENCES_BOARDS_MANAGER_ADDITIONAL_URLS = "boardsmanager.additional.urls";
static {
String extenalPackageIndexUrl = System.getProperty("PACKAGE_INDEX_URL");
if (extenalPackageIndexUrl != null && !"".equals(extenalPackageIndexUrl)) {
PACKAGE_INDEX_URL = extenalPackageIndexUrl;
} else {
PACKAGE_INDEX_URL = "http://downloads.arduino.cc/packages/package_index.json";
}
}
}

View File

@ -0,0 +1,35 @@
/*
* This file is part of Arduino.
*
* Copyright 2014 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.contributions.packages;
public interface ContributedBoard {
String getName();
}

View File

@ -0,0 +1,7 @@
package cc.arduino.contributions.packages;
public abstract class ContributedHelp {
public abstract String getOnline();
}

View File

@ -0,0 +1,101 @@
/*
* This file is part of Arduino.
*
* Copyright 2014 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.contributions.packages;
import cc.arduino.contributions.VersionComparator;
import java.util.List;
public abstract class ContributedPackage {
public abstract String getName();
public abstract String getMaintainer();
public abstract String getWebsiteURL();
public abstract String getEmail();
public abstract List<ContributedPlatform> getPlatforms();
public abstract List<ContributedTool> getTools();
public abstract ContributedHelp getHelp();
public ContributedPlatform findPlatform(String architecture, String version) {
if (architecture == null || version == null) {
return null;
}
for (ContributedPlatform platform : getPlatforms()) {
if (platform.getArchitecture().equals(architecture) && version.equals(platform.getParsedVersion()))
return platform;
}
return null;
}
public ContributedTool findTool(String name, String version) {
for (ContributedTool tool : getTools()) {
if (tool.getName().equals(name) && tool.getVersion().equals(version))
return tool;
}
return null;
}
@Override
public String toString() {
String res;
res = "Package name : " + getName() + "\n";
res += " maintaner : " + getMaintainer() + " (" + getEmail() + ")\n";
if (getPlatforms() != null) {
for (ContributedPlatform plat : getPlatforms()) {
res += "\n Plaform : name : " + plat.getName();
if (plat.isInstalled()) {
res += "\n " + plat;
}
res += "\n category : " + plat.getCategory();
res += "\n architecture : " +
plat.getArchitecture() + " " + plat.getParsedVersion() + "\n";
if (plat.getToolsDependencies() != null)
for (ContributedToolReference t : plat.getToolsDependencies()) {
res += " tool dep : " + t.getName() + " " +
t.getVersion() + "\n";
}
if (plat.getBoards() != null)
for (ContributedBoard board : plat.getBoards())
res += " board : " + board.getName() +
"\n";
}
}
if (getTools() != null) {
for (ContributedTool tool : getTools())
res += tool + "\n";
}
return res;
}
}

View File

@ -0,0 +1,96 @@
/*
* This file is part of Arduino.
*
* Copyright 2014 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.contributions.packages;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
public abstract class ContributedPlatform extends DownloadableContribution {
public abstract String getName();
public abstract String getCategory();
public abstract void setCategory(String category);
public abstract String getArchitecture();
public abstract String getChecksum();
public abstract List<ContributedToolReference> getToolsDependencies();
public abstract List<ContributedBoard> getBoards();
public abstract ContributedHelp getHelp();
private List<ContributedTool> resolvedTools = null;
private ContributedPackage parentPackage;
public List<ContributedTool> getResolvedTools() {
if (resolvedTools == null) {
return null;
}
return new LinkedList<ContributedTool>(resolvedTools);
}
public void resolveToolsDependencies(Collection<ContributedPackage> packages) {
resolvedTools = new ArrayList<ContributedTool>();
// If there are no dependencies return empty list
if (getToolsDependencies() == null) {
return;
}
// For each tool dependency
for (ContributedToolReference dep : getToolsDependencies()) {
// Search the referenced tool
ContributedTool tool = dep.resolve(packages);
if (tool == null) {
System.err.println("Index error: could not find referenced tool " + dep);
}
resolvedTools.add(tool);
}
}
public ContributedPackage getParentPackage() {
return parentPackage;
}
public void setParentPackage(ContributedPackage parentPackage) {
this.parentPackage = parentPackage;
}
@Override
public String toString() {
return getParsedVersion();
}
}

View File

@ -0,0 +1,85 @@
/*
* This file is part of Arduino.
*
* Copyright 2014 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.contributions.packages;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import processing.app.debug.TargetPackage;
import processing.app.debug.TargetPlatform;
public class ContributedTargetPackage implements TargetPackage {
private final String id;
private final Map<String, TargetPlatform> platforms;
public ContributedTargetPackage(String _id) {
id = _id;
platforms = new HashMap<String, TargetPlatform>();
}
void addPlatform(TargetPlatform p) {
platforms.put(p.getId(), p);
}
boolean hasPlatforms() {
return platforms.size() > 0;
}
@Override
public String getId() {
return id;
}
@Override
public Map<String, TargetPlatform> getPlatforms() {
return platforms;
}
@Override
public Collection<TargetPlatform> platforms() {
return platforms.values();
}
@Override
public TargetPlatform get(String platform) {
return platforms.get(platform);
}
@Override
public boolean hasPlatform(TargetPlatform platform) {
return platforms.containsKey(platform.getId());
}
@Override
public String toString() {
return "TargetPackage: " + getId();
}
}

View File

@ -0,0 +1,45 @@
/*
* This file is part of Arduino.
*
* Copyright 2014 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.contributions.packages;
import java.io.File;
import processing.app.debug.LegacyTargetPlatform;
import processing.app.debug.TargetPackage;
import processing.app.debug.TargetPlatformException;
public class ContributedTargetPlatform extends LegacyTargetPlatform {
public ContributedTargetPlatform(String _name, File _folder,
TargetPackage parent,
ContributionsIndex index)
throws TargetPlatformException {
super(_name, _folder, parent);
}
}

View File

@ -0,0 +1,63 @@
/*
* This file is part of Arduino.
*
* Copyright 2014 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.contributions.packages;
import processing.app.BaseNoGui;
import java.util.List;
public abstract class ContributedTool {
public abstract String getName();
public abstract String getVersion();
public abstract List<HostDependentDownloadableContribution> getSystems();
public DownloadableContribution getDownloadableContribution() {
for (HostDependentDownloadableContribution c : getSystems()) {
if (c.isCompatible(BaseNoGui.getPlatform()))
return c;
}
return null;
}
@Override
public String toString() {
String res;
res = "Tool name : " + getName() + " " + getVersion() + "\n";
for (HostDependentDownloadableContribution sys : getSystems()) {
res += " sys";
res += sys.isCompatible(BaseNoGui.getPlatform()) ? "*" : " ";
res += " : " + sys + "\n";
}
return res;
}
}

View File

@ -0,0 +1,57 @@
/*
* This file is part of Arduino.
*
* Copyright 2014 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.contributions.packages;
import java.util.Collection;
public abstract class ContributedToolReference {
public abstract String getName();
public abstract String getVersion();
public abstract String getPackager();
public ContributedTool resolve(Collection<ContributedPackage> packages) {
for (ContributedPackage pack : packages) {
for (ContributedTool tool : pack.getTools())
if (tool.getName().equals(getName()) &&
tool.getVersion().equals(getVersion()) &&
pack.getName().equals(getPackager()))
return tool;
}
return null;
}
@Override
public String toString() {
return "name=" + getName() + " version=" + getVersion() + " packager=" +
getPackager();
}
}

View File

@ -0,0 +1,302 @@
/*
* This file is part of Arduino.
*
* Copyright 2014 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.contributions.packages;
import cc.arduino.contributions.GPGDetachedSignatureVerifier;
import cc.arduino.filters.FileExecutablePredicate;
import cc.arduino.utils.ArchiveExtractor;
import cc.arduino.utils.MultiStepProgress;
import cc.arduino.utils.Progress;
import com.google.common.collect.Collections2;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.Executor;
import processing.app.BaseNoGui;
import processing.app.I18n;
import processing.app.PreferencesData;
import processing.app.helpers.FileUtils;
import processing.app.helpers.filefilters.OnlyDirs;
import processing.app.tools.CollectStdOutStdErrExecutor;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.*;
import static processing.app.I18n._;
import static processing.app.I18n.format;
public class ContributionInstaller {
private final ContributionsIndexer indexer;
private final DownloadableContributionsDownloader downloader;
public ContributionInstaller(ContributionsIndexer contributionsIndexer) {
File stagingFolder = contributionsIndexer.getStagingFolder();
indexer = contributionsIndexer;
downloader = new DownloadableContributionsDownloader(stagingFolder) {
@Override
protected void onProgress(Progress progress) {
ContributionInstaller.this.onProgress(progress);
}
};
}
public List<String> install(ContributedPlatform platform) throws Exception {
List<String> errors = new LinkedList<String>();
if (platform.isInstalled()) {
throw new Exception("Platform is already installed!");
}
// Do not download already installed tools
List<ContributedTool> tools = new LinkedList<ContributedTool>(platform.getResolvedTools());
Iterator<ContributedTool> toolsIterator = tools.iterator();
while (toolsIterator.hasNext()) {
ContributedTool tool = toolsIterator.next();
DownloadableContribution downloadable = tool.getDownloadableContribution();
if (downloadable == null) {
throw new Exception(format(_("Tool {0} is not available for your operating system."), tool.getName()));
}
if (downloadable.isInstalled()) {
toolsIterator.remove();
}
}
// Calculate progress increases
MultiStepProgress progress = new MultiStepProgress((tools.size() + 1) * 2);
// Download all
try {
// Download platform
downloader.download(platform, progress, _("Downloading boards definitions."));
progress.stepDone();
// Download tools
int i = 1;
for (ContributedTool tool : tools) {
String msg = format(_("Downloading tools ({0}/{1})."), i, tools.size());
i++;
downloader.download(tool.getDownloadableContribution(), progress, msg);
progress.stepDone();
}
} catch (InterruptedException e) {
// Download interrupted... just exit
return errors;
}
ContributedPackage pack = platform.getParentPackage();
File packageFolder = new File(indexer.getPackagesFolder(), pack.getName());
// TODO: Extract to temporary folders and move to the final destination only
// once everything is successfully unpacked. If the operation fails remove
// all the temporary folders and abort installation.
// Unzip tools on the correct location
File toolsFolder = new File(packageFolder, "tools");
int i = 1;
for (ContributedTool tool : tools) {
progress.setStatus(format(_("Installing tools ({0}/{1})..."), i, tools.size()));
onProgress(progress);
i++;
DownloadableContribution toolContrib = tool.getDownloadableContribution();
File destFolder = new File(toolsFolder, tool.getName() + File.separator + tool.getVersion());
destFolder.mkdirs();
assert toolContrib.getDownloadedFile() != null;
new ArchiveExtractor(BaseNoGui.getPlatform()).extract(toolContrib.getDownloadedFile(), destFolder, 1);
try {
executePostInstallScriptIfAny(destFolder);
} catch (IOException e) {
errors.add(_("Error running post install script"));
}
toolContrib.setInstalled(true);
toolContrib.setInstalledFolder(destFolder);
progress.stepDone();
}
// Unpack platform on the correct location
progress.setStatus(_("Installing boards..."));
onProgress(progress);
File platformFolder = new File(packageFolder, "hardware" + File.separator + platform.getArchitecture());
File destFolder = new File(platformFolder, platform.getParsedVersion());
destFolder.mkdirs();
new ArchiveExtractor(BaseNoGui.getPlatform()).extract(platform.getDownloadedFile(), destFolder, 1);
platform.setInstalled(true);
platform.setInstalledFolder(destFolder);
progress.stepDone();
progress.setStatus(_("Installation completed!"));
onProgress(progress);
return errors;
}
private void executePostInstallScriptIfAny(File folder) throws IOException {
Collection<File> postInstallScripts = Collections2.filter(BaseNoGui.getPlatform().postInstallScripts(folder), new FileExecutablePredicate());
if (postInstallScripts.isEmpty()) {
String[] subfolders = folder.list(new OnlyDirs());
if (subfolders.length != 1) {
return;
}
executePostInstallScriptIfAny(new File(folder, subfolders[0]));
return;
}
File postInstallScript = postInstallScripts.iterator().next();
ByteArrayOutputStream stdout = new ByteArrayOutputStream();
ByteArrayOutputStream stderr = new ByteArrayOutputStream();
Executor executor = new CollectStdOutStdErrExecutor(stdout, stderr);
executor.setWorkingDirectory(folder);
executor.setExitValues(null);
int exitValue = executor.execute(new CommandLine(postInstallScript));
executor.setExitValues(new int[0]);
System.out.write(stdout.toByteArray());
System.err.write(stderr.toByteArray());
if (executor.isFailure(exitValue)) {
throw new IOException();
}
}
public List<String> remove(ContributedPlatform platform) {
if (platform == null || platform.isReadOnly()) {
return new LinkedList<String>();
}
List<String> errors = new LinkedList<String>();
FileUtils.recursiveDelete(platform.getInstalledFolder());
platform.setInstalled(false);
platform.setInstalledFolder(null);
// Check if the tools are no longer needed
for (ContributedTool tool : platform.getResolvedTools()) {
if (indexer.isContributedToolUsed(tool)) {
continue;
}
DownloadableContribution toolContrib = tool.getDownloadableContribution();
File destFolder = toolContrib.getInstalledFolder();
FileUtils.recursiveDelete(destFolder);
toolContrib.setInstalled(false);
toolContrib.setInstalledFolder(null);
// We removed the version folder (.../tools/TOOL_NAME/VERSION)
// now try to remove the containing TOOL_NAME folder
// (and silently fail if another version of the tool is installed)
try {
destFolder.getParentFile().delete();
} catch (SecurityException e) {
// ignore
}
}
return errors;
}
public List<String> updateIndex() throws Exception {
MultiStepProgress progress = new MultiStepProgress(1);
List<String> downloadedPackageIndexFilesAccumulator = new LinkedList<String>();
downloadIndexAndSignature(progress, downloadedPackageIndexFilesAccumulator, Constants.PACKAGE_INDEX_URL);
Set<String> packageIndexURLs = new HashSet<String>();
String additionalURLs = PreferencesData.get(Constants.PREFERENCES_BOARDS_MANAGER_ADDITIONAL_URLS, "");
if (!"".equals(additionalURLs)) {
packageIndexURLs.addAll(Arrays.asList(additionalURLs.split(",")));
}
for (String packageIndexURL : packageIndexURLs) {
downloadIndexAndSignature(progress, downloadedPackageIndexFilesAccumulator, packageIndexURL);
}
progress.stepDone();
return downloadedPackageIndexFilesAccumulator;
}
private void downloadIndexAndSignature(MultiStepProgress progress, List<String> downloadedPackagedIndexFilesAccumulator, String packageIndexUrl) throws Exception {
File packageIndex = download(progress, packageIndexUrl);
downloadedPackagedIndexFilesAccumulator.add(packageIndex.getName());
try {
File packageIndexSignature = download(progress, packageIndexUrl + ".sig");
boolean signatureVerified = new GPGDetachedSignatureVerifier().verify(packageIndex, packageIndexSignature, new File(BaseNoGui.getContentFile("lib"), "public.gpg.key"));
if (signatureVerified) {
downloadedPackagedIndexFilesAccumulator.add(packageIndexSignature.getName());
} else {
downloadedPackagedIndexFilesAccumulator.remove(packageIndex.getName());
packageIndex.delete();
packageIndexSignature.delete();
System.err.println(I18n.format(_("{0} file signature verification failed. File ignored."), packageIndexUrl));
}
} catch (Exception e) {
//ignore errors
}
}
private File download(MultiStepProgress progress, String packageIndexUrl) throws Exception {
String statusText = _("Downloading platforms index...");
URL url = new URL(packageIndexUrl);
String[] urlPathParts = url.getFile().split("/");
File outputFile = indexer.getIndexFile(urlPathParts[urlPathParts.length - 1]);
File tmpFile = new File(outputFile.getAbsolutePath() + ".tmp");
downloader.download(url, tmpFile, progress, statusText);
// Replace old index with the updated one
if (outputFile.exists()) {
if (!outputFile.delete()) {
throw new Exception("An error occurred while updating platforms index! I can't delete file " + outputFile);
}
}
if (!tmpFile.renameTo(outputFile)) {
throw new Exception("An error occurred while updating platforms index! I can't rename file " + tmpFile);
}
return outputFile;
}
protected void onProgress(Progress progress) {
// Empty
}
public void deleteUnknownFiles(List<String> downloadedPackageIndexFiles) {
File preferencesFolder = indexer.getIndexFile(".").getParentFile();
File[] additionalPackageIndexFiles = preferencesFolder.listFiles(new PackageIndexFilenameFilter(Constants.DEFAULT_INDEX_FILE_NAME));
if (additionalPackageIndexFiles == null) {
return;
}
for (File additionalPackageIndexFile : additionalPackageIndexFiles) {
if (!downloadedPackageIndexFiles.contains(additionalPackageIndexFile.getName())) {
additionalPackageIndexFile.delete();
}
}
}
}

View File

@ -0,0 +1,151 @@
/*
* This file is part of Arduino.
*
* Copyright 2014 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.contributions.packages;
import cc.arduino.contributions.DownloadableContributionBuiltInAtTheBottomComparator;
import cc.arduino.contributions.filters.DownloadableContributionWithVersionPredicate;
import cc.arduino.contributions.filters.InstalledPredicate;
import cc.arduino.contributions.packages.filters.PlatformArchitecturePredicate;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import java.util.*;
public abstract class ContributionsIndex {
public abstract List<ContributedPackage> getPackages();
public ContributedPackage findPackage(String packageName) {
for (ContributedPackage pack : getPackages()) {
if (pack.getName().equals(packageName))
return pack;
}
return null;
}
public List<ContributedPlatform> findPlatforms(String packageName, final String platformArch) {
if (packageName == null || platformArch == null) {
return null;
}
ContributedPackage aPackage = findPackage(packageName);
if (aPackage == null) {
return null;
}
Collection<ContributedPlatform> platforms = Collections2.filter(aPackage.getPlatforms(), new PlatformArchitecturePredicate(platformArch));
return Lists.newLinkedList(platforms);
}
public ContributedPlatform findPlatform(String packageName, final String platformArch, final String platformVersion) {
if (platformVersion == null) {
return null;
}
Collection<ContributedPlatform> platformsByName = findPlatforms(packageName, platformArch);
if (platformsByName == null) {
return null;
}
Collection<ContributedPlatform> platforms = Collections2.filter(platformsByName, new DownloadableContributionWithVersionPredicate(platformVersion));
if (platforms.isEmpty()) {
return null;
}
return platforms.iterator().next();
}
public ContributedPlatform getInstalled(String packageName, String platformArch) {
List<ContributedPlatform> installedPlatforms = new LinkedList<ContributedPlatform>(Collections2.filter(findPlatforms(packageName, platformArch), new InstalledPredicate()));
Collections.sort(installedPlatforms, new DownloadableContributionBuiltInAtTheBottomComparator());
if (installedPlatforms.isEmpty()) {
return null;
}
return installedPlatforms.get(0);
}
public List<ContributedPlatform> getPlatforms() {
return Lists.newLinkedList(Iterables.concat(Collections2.transform(getPackages(), new Function<ContributedPackage, List<ContributedPlatform>>() {
@Override
public List<ContributedPlatform> apply(ContributedPackage contributedPackage) {
return contributedPackage.getPlatforms();
}
})));
}
public ContributedTool findTool(String packageName, String name,
String version) {
ContributedPackage pack = findPackage(packageName);
if (pack == null)
return null;
return pack.findTool(name, version);
}
private final List<String> categories = new ArrayList<String>();
public List<String> getCategories() {
return categories;
}
public void fillCategories() {
categories.clear();
for (ContributedPackage pack : getPackages()) {
for (ContributedPlatform platform : pack.getPlatforms()) {
if (!categories.contains(platform.getCategory()))
categories.add(platform.getCategory());
}
}
}
public ContributedPackage getPackage(String packageName) {
for (ContributedPackage pack : getPackages()) {
if (pack.getName().equals(packageName)) {
return pack;
}
}
return null;
}
@Override
public String toString() {
String res = "";
res += "Categories: ";
for (String c : getCategories())
res += "'" + c + "' ";
res += "\n";
for (ContributedPackage pack : getPackages())
res += pack + "\n";
return res;
}
}

View File

@ -0,0 +1,403 @@
/*
* This file is part of Arduino.
*
* Copyright 2014 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.contributions.packages;
import cc.arduino.contributions.DownloadableContributionBuiltInAtTheBottomComparator;
import cc.arduino.contributions.GPGDetachedSignatureVerifier;
import cc.arduino.contributions.SignatureVerificationFailedException;
import cc.arduino.contributions.filters.BuiltInPredicate;
import cc.arduino.contributions.filters.InstalledPredicate;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.module.mrbean.MrBeanModule;
import com.google.common.base.Function;
import com.google.common.base.Predicates;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Multimaps;
import processing.app.BaseNoGui;
import processing.app.debug.TargetPackage;
import processing.app.debug.TargetPlatform;
import processing.app.debug.TargetPlatformException;
import processing.app.helpers.PreferencesMap;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import static processing.app.helpers.filefilters.OnlyDirs.ONLY_DIRS;
public class ContributionsIndexer {
private final File packagesFolder;
private final File stagingFolder;
private final File preferencesFolder;
private ContributionsIndex index;
public ContributionsIndexer(File preferencesFolder) {
this.preferencesFolder = preferencesFolder;
packagesFolder = new File(preferencesFolder, "packages");
stagingFolder = new File(preferencesFolder, "staging" + File.separator + "packages");
}
public void parseIndex() throws Exception {
File defaultIndexFile = getIndexFile(Constants.DEFAULT_INDEX_FILE_NAME);
if (!isSigned(defaultIndexFile)) {
throw new SignatureVerificationFailedException(Constants.DEFAULT_INDEX_FILE_NAME);
}
index = parseIndex(defaultIndexFile);
File[] indexFiles = preferencesFolder.listFiles(new TestPackageIndexFilenameFilter(new PackageIndexFilenameFilter(Constants.DEFAULT_INDEX_FILE_NAME)));
for (File indexFile : indexFiles) {
ContributionsIndex contributionsIndex = parseIndex(indexFile);
mergeContributions(contributionsIndex, indexFile);
}
List<ContributedPackage> packages = index.getPackages();
for (ContributedPackage pack : packages) {
for (ContributedPlatform platform : pack.getPlatforms()) {
// Set a reference to parent packages
platform.setParentPackage(pack);
// Resolve tools dependencies (works also as a check for file integrity)
platform.resolveToolsDependencies(packages);
}
}
index.fillCategories();
}
private void mergeContributions(ContributionsIndex contributionsIndex, File indexFile) {
boolean signed = isSigned(indexFile);
for (ContributedPackage contributedPackage : contributionsIndex.getPackages()) {
if (!signed) {
for (ContributedPlatform contributedPlatform : contributedPackage.getPlatforms()) {
contributedPlatform.setCategory("Contributed");
}
}
ContributedPackage targetPackage = index.getPackage(contributedPackage.getName());
if (targetPackage == null) {
index.getPackages().add(contributedPackage);
} else {
if (signed || !isPackageNameProtected(contributedPackage)) {
List<ContributedPlatform> platforms = contributedPackage.getPlatforms();
if (platforms == null) {
platforms = new LinkedList<ContributedPlatform>();
}
for (ContributedPlatform contributedPlatform : platforms) {
ContributedPlatform platform = targetPackage.findPlatform(contributedPlatform.getArchitecture(), contributedPlatform.getVersion());
if (platform != null) {
targetPackage.getPlatforms().remove(platform);
}
targetPackage.getPlatforms().add(contributedPlatform);
}
List<ContributedTool> tools = contributedPackage.getTools();
if (tools == null) {
tools = new LinkedList<ContributedTool>();
}
for (ContributedTool contributedTool : tools) {
ContributedTool tool = targetPackage.findTool(contributedTool.getName(), contributedTool.getVersion());
if (tool != null) {
targetPackage.getTools().remove(tool);
}
targetPackage.getTools().add(contributedTool);
}
}
}
}
}
private boolean isPackageNameProtected(ContributedPackage contributedPackage) {
return Constants.PROTECTED_PACKAGE_NAMES.contains(contributedPackage.getName());
}
private boolean isSigned(File indexFile) {
File signature = new File(indexFile.getParent(), indexFile.getName() + ".sig");
if (!signature.exists()) {
return false;
}
try {
return new GPGDetachedSignatureVerifier().verify(indexFile, signature, new File(BaseNoGui.getContentFile("lib"), "public.gpg.key"));
} catch (Exception e) {
BaseNoGui.showWarning(e.getMessage(), e.getMessage(), e);
return false;
}
}
private ContributionsIndex parseIndex(File indexFile) throws IOException {
InputStream inputStream = null;
try {
inputStream = new FileInputStream(indexFile);
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new MrBeanModule());
mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
mapper.configure(DeserializationFeature.EAGER_DESERIALIZER_FETCH, true);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return mapper.readValue(inputStream, ContributionsIndex.class);
} finally {
if (inputStream != null) {
inputStream.close();
}
}
}
public void syncWithFilesystem(File hardwareFolder) throws IOException {
syncBuiltInHardwareFolder(hardwareFolder);
syncLocalPackagesFolder();
}
public void syncBuiltInHardwareFolder(File hardwareFolder) throws IOException {
if (index == null) {
return;
}
for (File folder : hardwareFolder.listFiles(ONLY_DIRS)) {
ContributedPackage pack = index.findPackage(folder.getName());
if (pack != null) {
syncBuiltInPackageWithFilesystem(pack, folder);
File toolsFolder = new File(hardwareFolder, "tools");
if (toolsFolder.isDirectory()) {
for (File toolFolder : toolsFolder.listFiles(ONLY_DIRS)) {
File builtInToolsMetadata = new File(toolFolder, "builtin_tools_versions.txt");
if (builtInToolsMetadata.isFile()) {
PreferencesMap toolsMetadata = new PreferencesMap(builtInToolsMetadata).subTree(pack.getName());
for (Map.Entry<String, String> toolMetadata : toolsMetadata.entrySet()) {
syncToolWithFilesystem(pack, toolFolder, toolMetadata.getKey(), toolMetadata.getValue());
}
}
}
}
}
}
}
private void syncBuiltInPackageWithFilesystem(ContributedPackage pack, File hardwareFolder) throws IOException {
// Scan all hardware folders and mark as installed all the tools found.
for (File platformFolder : hardwareFolder.listFiles(ONLY_DIRS)) {
File platformTxt = new File(platformFolder, "platform.txt");
String version = new PreferencesMap(platformTxt).get("version");
ContributedPlatform platform = syncHardwareWithFilesystem(pack, platformFolder, platformFolder.getName(), version);
if (platform != null) {
platform.setReadOnly(true);
}
}
}
public void syncLocalPackagesFolder() {
if (!packagesFolder.isDirectory()) {
return;
}
if (index == null) {
return;
}
// Scan all hardware folders and mark as installed all the
// platforms found.
for (File folder : packagesFolder.listFiles(ONLY_DIRS)) {
ContributedPackage pack = index.findPackage(folder.getName());
if (pack != null) {
syncPackageWithFilesystem(pack, folder);
}
}
}
private void syncPackageWithFilesystem(ContributedPackage pack, File root) {
// Scan all hardware folders and mark as installed all the tools found.
File hardwareFolder = new File(root, "hardware");
if (hardwareFolder.isDirectory()) {
for (File platformFolder : hardwareFolder.listFiles(ONLY_DIRS)) {
for (File versionFolder : platformFolder.listFiles(ONLY_DIRS)) {
syncHardwareWithFilesystem(pack, versionFolder, platformFolder.getName(), versionFolder.getName());
}
}
}
// Scan all tools folders and mark as installed all the tools found.
File toolsFolder = new File(root, "tools");
if (toolsFolder.isDirectory()) {
for (File toolFolder : toolsFolder.listFiles(ONLY_DIRS)) {
for (File versionFolder : toolFolder.listFiles(ONLY_DIRS)) {
syncToolWithFilesystem(pack, versionFolder, toolFolder.getName(), versionFolder.getName());
}
}
}
}
private void syncToolWithFilesystem(ContributedPackage pack, File installationFolder, String toolName, String version) {
ContributedTool tool = pack.findTool(toolName, version);
if (tool == null) {
return;
}
DownloadableContribution contrib = tool.getDownloadableContribution();
if (contrib == null) {
System.err.println(tool + " seems to have no downloadable contributions for your operating system, but it is installed in\n" + installationFolder);
return;
}
contrib.setInstalled(true);
contrib.setInstalledFolder(installationFolder);
}
private ContributedPlatform syncHardwareWithFilesystem(ContributedPackage pack, File installationFolder, String architecture, String version) {
ContributedPlatform platform = pack.findPlatform(architecture, version);
if (platform != null) {
platform.setInstalled(true);
platform.setReadOnly(false);
platform.setInstalledFolder(installationFolder);
}
return platform;
}
@Override
public String toString() {
return index.toString();
}
public List<TargetPackage> createTargetPackages() throws TargetPlatformException {
List<TargetPackage> packages = new ArrayList<TargetPackage>();
if (index == null) {
return packages;
}
for (ContributedPackage aPackage : index.getPackages()) {
ContributedTargetPackage targetPackage = new ContributedTargetPackage(aPackage.getName());
List<ContributedPlatform> platforms = new LinkedList<ContributedPlatform>(Collections2.filter(aPackage.getPlatforms(), new InstalledPredicate()));
Collections.sort(platforms, new DownloadableContributionBuiltInAtTheBottomComparator());
for (ContributedPlatform platform : platforms) {
String arch = platform.getArchitecture();
File folder = platform.getInstalledFolder();
TargetPlatform targetPlatform = new ContributedTargetPlatform(arch, folder, targetPackage, index);
if (!targetPackage.hasPlatform(targetPlatform)) {
targetPackage.addPlatform(targetPlatform);
}
}
if (targetPackage.hasPlatforms()) {
packages.add(targetPackage);
}
}
return packages;
}
/**
* Check if a ContributedTool is currently in use by an installed platform
*
* @param tool
* @return
*/
public boolean isContributedToolUsed(ContributedTool tool) {
for (ContributedPackage pack : index.getPackages()) {
for (ContributedPlatform platform : pack.getPlatforms()) {
if (!platform.isInstalled())
continue;
for (ContributedTool requiredTool : platform.getResolvedTools()) {
if (requiredTool.equals(tool))
return true;
}
}
}
return false;
}
public Set<ContributedTool> getInstalledTools() {
Set<ContributedTool> tools = new HashSet<ContributedTool>();
if (index == null) {
return tools;
}
for (ContributedPackage pack : index.getPackages()) {
Collection<ContributedPlatform> platforms = Collections2.filter(pack.getPlatforms(), new InstalledPredicate());
ImmutableListMultimap<String, ContributedPlatform> platformsByName = Multimaps.index(platforms, new Function<ContributedPlatform, String>() {
@Override
public String apply(ContributedPlatform contributedPlatform) {
return contributedPlatform.getName();
}
});
for (Map.Entry<String, Collection<ContributedPlatform>> entry : platformsByName.asMap().entrySet()) {
Collection<ContributedPlatform> platformsWithName = entry.getValue();
if (platformsWithName.size() > 1) {
platformsWithName = Collections2.filter(platformsWithName, Predicates.not(new BuiltInPredicate()));
}
for (ContributedPlatform platform : platformsWithName) {
tools.addAll(platform.getResolvedTools());
}
}
}
return tools;
}
public ContributionsIndex getIndex() {
return index;
}
public File getPackagesFolder() {
return packagesFolder;
}
public File getStagingFolder() {
return stagingFolder;
}
public File getIndexFile(String name) {
return new File(preferencesFolder, name);
}
public List<ContributedPackage> getPackages() {
if (index == null) {
return new LinkedList<ContributedPackage>();
}
return index.getPackages();
}
public List<String> getCategories() {
if (index == null) {
return new LinkedList<String>();
}
return index.getCategories();
}
public ContributedPlatform getInstalled(String packageName, String platformArch) {
if (index == null) {
return null;
}
return index.getInstalled(packageName, platformArch);
}
}

View File

@ -0,0 +1,112 @@
/*
* This file is part of Arduino.
*
* Copyright 2014 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.contributions.packages;
import cc.arduino.contributions.VersionHelper;
import com.github.zafarkhaja.semver.Version;
import java.io.File;
public abstract class DownloadableContribution {
private boolean installed;
private File installedFolder;
private boolean downloaded;
private File downloadedFile;
public abstract String getUrl();
public abstract String getVersion();
public abstract String getChecksum();
public abstract long getSize();
public abstract String getArchiveFileName();
public boolean isDownloaded() {
return downloaded;
}
public void setDownloaded(boolean downloaded) {
this.downloaded = downloaded;
}
public File getDownloadedFile() {
return downloadedFile;
}
public void setDownloadedFile(File downloadedFile) {
this.downloadedFile = downloadedFile;
}
public boolean isInstalled() {
return installed;
}
public void setInstalled(boolean installed) {
this.installed = installed;
}
public File getInstalledFolder() {
return installedFolder;
}
public void setInstalledFolder(File installedFolder) {
this.installedFolder = installedFolder;
}
private boolean readOnly;
public boolean isReadOnly() {
return readOnly;
}
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
}
public String getParsedVersion() {
Version version = VersionHelper.valueOf(getVersion());
if (version == null) {
return null;
}
return version.toString();
}
@Override
public String toString() {
String res = "";
if (installed) {
res += "installed on " + installedFolder.getAbsolutePath() + " (" + getSize() + " bytes)";
}
return res;
}
}

View File

@ -0,0 +1,107 @@
/*
* This file is part of Arduino.
*
* Copyright 2014 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.contributions.packages;
import cc.arduino.utils.FileHash;
import cc.arduino.utils.Progress;
import cc.arduino.utils.network.FileDownloader;
import java.io.File;
import java.net.URL;
import java.util.Observable;
import java.util.Observer;
import static processing.app.I18n._;
import static processing.app.I18n.format;
public class DownloadableContributionsDownloader {
private final File stagingFolder;
public DownloadableContributionsDownloader(File _stagingFolder) {
stagingFolder = _stagingFolder;
}
public File download(DownloadableContribution contribution,
final Progress progress, final String statusText)
throws Exception {
URL url = new URL(contribution.getUrl());
final File outputFile = new File(stagingFolder, contribution.getArchiveFileName());
// Ensure the existence of staging folder
stagingFolder.mkdirs();
// Need to download or resume downloading?
if (!outputFile.isFile() || (outputFile.length() < contribution.getSize())) {
download(url, outputFile, progress, statusText);
}
// Test checksum
progress.setStatus(_("Verifying archive integrity..."));
onProgress(progress);
String checksum = contribution.getChecksum();
String algo = checksum.split(":")[0];
if (!FileHash.hash(outputFile, algo).equalsIgnoreCase(checksum)) {
throw new Exception(_("CRC doesn't match. File is corrupted."));
}
contribution.setDownloaded(true);
contribution.setDownloadedFile(outputFile);
return outputFile;
}
public void download(URL url, File tmpFile, final Progress progress,
final String statusText) throws Exception {
FileDownloader downloader = new FileDownloader(url, tmpFile);
downloader.addObserver(new Observer() {
@Override
public void update(Observable o, Object arg) {
FileDownloader me = (FileDownloader) o;
String msg = "";
if (me.getDownloadSize() != null) {
long downloaded = (me.getInitialSize() + me.getDownloaded()) / 1000;
long total = (me.getInitialSize() + me.getDownloadSize()) / 1000;
msg = format(_("Downloaded {0}kb of {1}kb."), downloaded, total);
}
progress.setStatus(statusText + " " + msg);
progress.setProgress(me.getProgress());
onProgress(progress);
}
});
downloader.download();
if (!downloader.isCompleted()) {
throw new Exception(format(_("Error downloading {0}"), url), downloader.getError());
}
}
protected void onProgress(Progress progress) {
// Empty
}
}

View File

@ -0,0 +1,72 @@
/*
* This file is part of Arduino.
*
* Copyright 2014 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.contributions.packages;
import processing.app.Platform;
public abstract class HostDependentDownloadableContribution extends DownloadableContribution {
public abstract String getHost();
@Override
public String toString() {
return getHost() + " " + super.toString();
}
public boolean isCompatible(Platform platform) {
String osName = platform.getOsName();
assert osName != null;
String osArch = platform.getOsArch();
assert osArch != null;
String host = getHost();
if (osName.contains("Linux")) {
if (osArch.contains("amd64")) {
return host.matches("x86_64-.*linux-gnu");
} else {
return host.matches("i[3456]86-.*linux-gnu");
}
}
if (osName.contains("Windows")) {
return host.matches("i[3456]86-.*mingw32") || host.matches("i[3456]86-.*cygwin");
}
if (osName.contains("Mac")) {
if (osArch.contains("x86_64")) {
return host.matches("x86_64-apple-darwin.*") || host.matches("i[3456]86-apple-darwin.*");
} else {
return host.matches("i[3456]86-apple-darwin.*");
}
}
return false;
}
}

View File

@ -0,0 +1,18 @@
package cc.arduino.contributions.packages;
import java.io.File;
import java.io.FilenameFilter;
public class PackageIndexFilenameFilter implements FilenameFilter {
private final String defaultPackageIndexFileName;
public PackageIndexFilenameFilter(String defaultPackageIndexFileName) {
this.defaultPackageIndexFileName = defaultPackageIndexFileName;
}
@Override
public boolean accept(File file, String name) {
return new File(file, name).isFile() && !defaultPackageIndexFileName.equals(name) && name.startsWith("package_") && name.endsWith("_index.json");
}
}

View File

@ -0,0 +1,27 @@
package cc.arduino.contributions.packages;
import java.io.File;
import java.io.FilenameFilter;
public class TestPackageIndexFilenameFilter implements FilenameFilter {
private final FilenameFilter parent;
public TestPackageIndexFilenameFilter(FilenameFilter parent) {
this.parent = parent;
}
public TestPackageIndexFilenameFilter() {
this(null);
}
@Override
public boolean accept(File file, String name) {
boolean result = false;
if (parent != null) {
result = parent.accept(file, name);
}
result = result || (new File(file, name).isFile() && name.startsWith("test_package_") && name.endsWith("_index.json"));
return result;
}
}

View File

@ -0,0 +1,48 @@
/*
* This file is part of Arduino.
*
* Copyright 2015 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.contributions.packages.filters;
import cc.arduino.contributions.packages.ContributedPlatform;
import com.google.common.base.Predicate;
public class PlatformArchitecturePredicate implements Predicate<ContributedPlatform> {
private final String platformArch;
public PlatformArchitecturePredicate(String platformArch) {
this.platformArch = platformArch;
}
@Override
public boolean apply(ContributedPlatform contributedPlatform) {
return platformArch.equals(contributedPlatform.getArchitecture());
}
}

View File

@ -0,0 +1,47 @@
package cc.arduino.files;
import processing.app.PreferencesData;
import processing.app.helpers.FileUtils;
import java.io.File;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
public class DeleteFilesOnShutdown implements Runnable {
public static final DeleteFilesOnShutdown INSTANCE = new DeleteFilesOnShutdown();
public static void add(File file) {
INSTANCE.addFile(file);
}
private final List<File> files;
public DeleteFilesOnShutdown() {
this.files = new LinkedList<File>();
}
public synchronized void addFile(File file) {
this.files.add(file);
}
@Override
public void run() {
boolean preserveTempFiles = PreferencesData.getBoolean("runtime.preserve.temp.files");
if (preserveTempFiles) {
return;
}
List<File> copyOfFiles;
synchronized (this) {
copyOfFiles = new LinkedList<File>(files);
}
Collections.reverse(copyOfFiles);
for (File file : copyOfFiles) {
if (file.exists() && file.canWrite()) {
FileUtils.recursiveDelete(file);
}
}
}
}

View File

@ -0,0 +1,43 @@
/*
* This file is part of Arduino.
*
* Copyright 2015 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.filters;
import com.google.common.base.Predicate;
import java.io.File;
public class FileExecutablePredicate implements Predicate<File> {
@Override
public boolean apply(File file) {
return file.isFile() && file.exists() && file.canRead() && file.canExecute();
}
}

View File

@ -29,36 +29,27 @@
package cc.arduino.packages;
import processing.app.helpers.PreferencesMap;
import java.util.List;
public interface Discovery {
/**
* Set discovery preferences
*
* @param options
*/
public void setPreferences(PreferencesMap options);
/**
* Start discovery service
*
* @throws Exception
*/
public void start() throws Exception;
void start() throws Exception;
/**
* Stop discovery service
*/
public void stop() throws Exception;
void stop() throws Exception;
/**
* Return the list of discovered ports.
*
* @return
*/
public List<BoardPort> discovery();
List<BoardPort> listDiscoveredBoards();
}

View File

@ -63,7 +63,7 @@ public class DiscoveryManager {
try {
d.stop();
} catch (Exception e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
e.printStackTrace(); //just printing as the JVM is terminating
}
}
}
@ -74,7 +74,7 @@ public class DiscoveryManager {
public List<BoardPort> discovery() {
List<BoardPort> res = new ArrayList<BoardPort>();
for (Discovery d : discoverers) {
res.addAll(d.discovery());
res.addAll(d.listDiscoveredBoards());
}
return res;
}

View File

@ -36,8 +36,9 @@ import processing.app.debug.TargetBoard;
public class UploaderFactory {
public Uploader newUploader(TargetBoard board, BoardPort port, boolean noUploadPort) {
if (noUploadPort)
return new SerialUploader(noUploadPort);
if (noUploadPort) {
return new SerialUploader(true);
}
if ("true".equals(board.getPreferences().get("upload.via_ssh")) && port != null && "network".equals(port.getProtocol())) {
return new SSHUploader(port);

View File

@ -31,9 +31,9 @@ package cc.arduino.packages.discoverers;
import cc.arduino.packages.BoardPort;
import cc.arduino.packages.Discovery;
import cc.arduino.packages.discoverers.network.BoardReachabilityFilter;
import cc.arduino.packages.discoverers.network.NetworkChecker;
import processing.app.BaseNoGui;
import processing.app.helpers.NetUtils;
import processing.app.helpers.PreferencesMap;
import processing.app.zeroconf.jmdns.ArduinoDNSTaskStarter;
@ -41,70 +41,57 @@ import javax.jmdns.*;
import javax.jmdns.impl.DNSTaskStarter;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;
public class NetworkDiscovery implements Discovery, ServiceListener, cc.arduino.packages.discoverers.network.NetworkTopologyListener {
private Timer timer;
private final List<BoardPort> ports;
private static final int MAX_TIME_AWAITING_FOR_PACKAGES = 5000;
private final List<BoardPort> boardPortsDiscoveredWithJmDNS;
private final Map<InetAddress, JmDNS> mappedJmDNSs;
private Timer networkCheckerTimer;
private Timer boardReachabilityFilterTimer;
private final List<BoardPort> reachableBoardPorts;
public NetworkDiscovery() {
DNSTaskStarter.Factory.setClassDelegate(new ArduinoDNSTaskStarter());
this.ports = new ArrayList<BoardPort>();
this.boardPortsDiscoveredWithJmDNS = new LinkedList<BoardPort>();
this.mappedJmDNSs = new Hashtable<InetAddress, JmDNS>();
this.reachableBoardPorts = new LinkedList<BoardPort>();
}
@Override
public List<BoardPort> discovery() {
List<BoardPort> boardPorts = clonePortsList();
Iterator<BoardPort> boardPortIterator = boardPorts.iterator();
while (boardPortIterator.hasNext()) {
try {
BoardPort board = boardPortIterator.next();
InetAddress inetAddress = InetAddress.getByName(board.getAddress());
int broadcastedPort = Integer.valueOf(board.getPrefs().get("port"));
List<Integer> ports = new LinkedList<Integer>();
ports.add(broadcastedPort);
//dirty code: allows non up to date yuns to be discovered. Newer yuns will broadcast port 22
if (broadcastedPort == 80) {
ports.add(0, 22);
}
boolean reachable = NetUtils.isReachable(inetAddress, ports);
if (!reachable) {
boardPortIterator.remove();
}
} catch (UnknownHostException e) {
boardPortIterator.remove();
}
}
return boardPorts;
}
private List<BoardPort> clonePortsList() {
synchronized (this) {
return new ArrayList<BoardPort>(this.ports);
public List<BoardPort> listDiscoveredBoards() {
synchronized (reachableBoardPorts) {
return new LinkedList<BoardPort>(reachableBoardPorts);
}
}
@Override
public void setPreferences(PreferencesMap options) {
public void setReachableBoardPorts(List<BoardPort> newReachableBoardPorts) {
synchronized (reachableBoardPorts) {
this.reachableBoardPorts.clear();
this.reachableBoardPorts.addAll(newReachableBoardPorts);
}
}
public List<BoardPort> getBoardPortsDiscoveredWithJmDNS() {
synchronized (boardPortsDiscoveredWithJmDNS) {
return new LinkedList<BoardPort>(boardPortsDiscoveredWithJmDNS);
}
}
@Override
public void start() throws IOException {
this.timer = new Timer(this.getClass().getName() + " timer");
new NetworkChecker(this, NetworkTopologyDiscovery.Factory.getInstance()).start(timer);
this.networkCheckerTimer = new Timer(NetworkChecker.class.getName());
new NetworkChecker(this, NetworkTopologyDiscovery.Factory.getInstance()).start(networkCheckerTimer);
this.boardReachabilityFilterTimer = new Timer(BoardReachabilityFilter.class.getName());
new BoardReachabilityFilter(this).start(boardReachabilityFilterTimer);
}
@Override
public void stop() throws IOException {
timer.purge();
this.networkCheckerTimer.purge();
this.boardReachabilityFilterTimer.purge();
// we don't close each JmDNS instance as it's too slow
}
@ -125,16 +112,27 @@ public class NetworkDiscovery implements Discovery, ServiceListener, cc.arduino.
@Override
public void serviceRemoved(ServiceEvent serviceEvent) {
String name = serviceEvent.getName();
synchronized (this) {
for (BoardPort port : ports) {
if (port.getBoardName().equals(name))
ports.remove(port);
synchronized (boardPortsDiscoveredWithJmDNS) {
for (BoardPort port : boardPortsDiscoveredWithJmDNS) {
if (port.getBoardName().equals(name)) {
boardPortsDiscoveredWithJmDNS.remove(port);
}
}
}
}
@Override
public void serviceResolved(ServiceEvent serviceEvent) {
int sleptFor = 0;
while (BaseNoGui.packages == null && sleptFor <= MAX_TIME_AWAITING_FOR_PACKAGES) {
try {
Thread.sleep(1000);
sleptFor += 1000;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
ServiceInfo info = serviceEvent.getInfo();
for (InetAddress inetAddress : info.getInet4Addresses()) {
String address = inetAddress.getHostAddress();
@ -147,12 +145,11 @@ public class NetworkDiscovery implements Discovery, ServiceListener, cc.arduino.
board = info.getPropertyString("board");
prefs.put("board", board);
prefs.put("distro_version", info.getPropertyString("distro_version"));
prefs.put("port", "" + info.getPort());
}
prefs.put("port", "" + info.getPort());
String label = name + " at " + address;
if (board != null) {
if (board != null && BaseNoGui.packages != null) {
String boardName = BaseNoGui.getPlatform().resolveDeviceByBoardID(BaseNoGui.packages, board);
if (boardName != null) {
label += " (" + boardName + ")";
@ -166,19 +163,21 @@ public class NetworkDiscovery implements Discovery, ServiceListener, cc.arduino.
port.setPrefs(prefs);
port.setLabel(label);
synchronized (this) {
synchronized (boardPortsDiscoveredWithJmDNS) {
removeDuplicateBoards(port);
ports.add(port);
boardPortsDiscoveredWithJmDNS.add(port);
}
}
}
private void removeDuplicateBoards(BoardPort newBoard) {
Iterator<BoardPort> iterator = ports.iterator();
while (iterator.hasNext()) {
BoardPort board = iterator.next();
if (newBoard.getAddress().equals(board.getAddress())) {
iterator.remove();
synchronized (boardPortsDiscoveredWithJmDNS) {
Iterator<BoardPort> iterator = boardPortsDiscoveredWithJmDNS.iterator();
while (iterator.hasNext()) {
BoardPort board = iterator.next();
if (newBoard.getAddress().equals(board.getAddress())) {
iterator.remove();
}
}
}
}

View File

@ -31,86 +31,49 @@ package cc.arduino.packages.discoverers;
import cc.arduino.packages.BoardPort;
import cc.arduino.packages.Discovery;
import processing.app.BaseNoGui;
import processing.app.Platform;
import processing.app.Serial;
import processing.app.debug.TargetBoard;
import processing.app.helpers.PreferencesMap;
import cc.arduino.packages.discoverers.serial.SerialBoardsLister;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import static processing.app.I18n._;
public class SerialDiscovery implements Discovery {
static {
//makes transifex happy
_("Uncertified");
private Timer serialBoardsListerTimer;
private final List<BoardPort> serialBoardPorts;
public SerialDiscovery() {
this.serialBoardPorts = new LinkedList<BoardPort>();
}
@Override
public List<BoardPort> discovery() {
Platform os = BaseNoGui.getPlatform();
String devicesListOutput = os.preListAllCandidateDevices();
public List<BoardPort> listDiscoveredBoards() {
return getSerialBoardPorts();
}
List<BoardPort> res = new ArrayList<BoardPort>();
List<String> ports = Serial.list();
for (String port : ports) {
Map<String, Object> boardData = os.resolveDeviceAttachedTo(port, BaseNoGui.packages, devicesListOutput);
BoardPort boardPort = new BoardPort();
boardPort.setAddress(port);
boardPort.setProtocol("serial");
String label = port;
PreferencesMap prefs = new PreferencesMap();
if (boardData != null) {
prefs.put("vid", boardData.get("vid").toString());
prefs.put("pid", boardData.get("pid").toString());
TargetBoard board = (TargetBoard) boardData.get("board");
if (board != null) {
String warningKey = "vid." + boardData.get("vid").toString() + ".warning";
String warning = board.getPreferences().get(warningKey);
prefs.put("warning", warning);
String boardName = board.getName();
if (boardName != null) {
if (warning != null) {
label += " (" + boardName + " - " + _(warning) + ")";
} else {
label += " (" + boardName + ")";
}
}
boardPort.setBoardName(boardName);
}
}
boardPort.setLabel(label);
boardPort.setPrefs(prefs);
res.add(boardPort);
public List<BoardPort> getSerialBoardPorts() {
synchronized (serialBoardPorts) {
return new LinkedList<BoardPort>(serialBoardPorts);
}
return res;
}
@Override
public void setPreferences(PreferencesMap options) {
public void setSerialBoardPorts(List<BoardPort> newSerialBoardPorts) {
synchronized (serialBoardPorts) {
serialBoardPorts.clear();
serialBoardPorts.addAll(newSerialBoardPorts);
}
}
@Override
public void start() {
this.serialBoardsListerTimer = new Timer(SerialBoardsLister.class.getName());
new SerialBoardsLister(this).start(serialBoardsListerTimer);
}
@Override
public void stop() {
this.serialBoardsListerTimer.purge();
}
}

View File

@ -0,0 +1,83 @@
/*
* This file is part of Arduino.
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2015 Arduino LLC (http://www.arduino.cc/)
*/
package cc.arduino.packages.discoverers.network;
import cc.arduino.packages.BoardPort;
import cc.arduino.packages.discoverers.NetworkDiscovery;
import processing.app.helpers.NetUtils;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;
public class BoardReachabilityFilter extends TimerTask {
private final NetworkDiscovery networkDiscovery;
public BoardReachabilityFilter(NetworkDiscovery networkDiscovery) {
this.networkDiscovery = networkDiscovery;
}
public void start(Timer timer) {
timer.schedule(this, 0, 3000);
}
@Override
public void run() {
List<BoardPort> boardPorts = networkDiscovery.getBoardPortsDiscoveredWithJmDNS();
Iterator<BoardPort> boardPortIterator = boardPorts.iterator();
while (boardPortIterator.hasNext()) {
try {
BoardPort board = boardPortIterator.next();
InetAddress inetAddress = InetAddress.getByName(board.getAddress());
int broadcastedPort = Integer.valueOf(board.getPrefs().get("port"));
List<Integer> ports = new LinkedList<Integer>();
ports.add(broadcastedPort);
//dirty code: allows non up to date yuns to be discovered. Newer yuns will broadcast port 22
if (broadcastedPort == 80) {
ports.add(0, 22);
}
boolean reachable = NetUtils.isReachable(inetAddress, ports);
if (!reachable) {
boardPortIterator.remove();
}
} catch (UnknownHostException e) {
boardPortIterator.remove();
}
}
networkDiscovery.setReachableBoardPorts(boardPorts);
}
}

View File

@ -0,0 +1,115 @@
/*
* This file is part of Arduino.
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2015 Arduino LLC (http://www.arduino.cc/)
*/
package cc.arduino.packages.discoverers.serial;
import cc.arduino.packages.BoardPort;
import cc.arduino.packages.discoverers.SerialDiscovery;
import processing.app.BaseNoGui;
import processing.app.Platform;
import processing.app.Serial;
import processing.app.debug.TargetBoard;
import processing.app.helpers.PreferencesMap;
import java.util.*;
public class SerialBoardsLister extends TimerTask {
private static final int MAX_TIME_AWAITING_FOR_PACKAGES = 5000;
private final SerialDiscovery serialDiscovery;
public SerialBoardsLister(SerialDiscovery serialDiscovery) {
this.serialDiscovery = serialDiscovery;
}
public void start(Timer timer) {
timer.schedule(this, 0, 3000);
}
@Override
public void run() {
int sleptFor = 0;
while (BaseNoGui.packages == null && sleptFor <= MAX_TIME_AWAITING_FOR_PACKAGES) {
try {
Thread.sleep(1000);
sleptFor += 1000;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Platform platform = BaseNoGui.getPlatform();
if (platform == null) {
return;
}
List<BoardPort> boardPorts = new LinkedList<BoardPort>();
List<String> ports = Serial.list();
String devicesListOutput = null;
if (!ports.isEmpty()) {
devicesListOutput = platform.preListAllCandidateDevices();
}
for (String port : ports) {
Map<String, Object> boardData = platform.resolveDeviceAttachedTo(port, BaseNoGui.packages, devicesListOutput);
BoardPort boardPort = new BoardPort();
boardPort.setAddress(port);
boardPort.setProtocol("serial");
String label = port;
PreferencesMap prefs = new PreferencesMap();
if (boardData != null) {
prefs.put("vid", boardData.get("vid").toString());
prefs.put("pid", boardData.get("pid").toString());
TargetBoard board = (TargetBoard) boardData.get("board");
if (board != null) {
String boardName = board.getName();
if (boardName != null) {
label += " (" + boardName + ")";
}
boardPort.setBoardName(boardName);
}
}
boardPort.setLabel(label);
boardPort.setPrefs(prefs);
boardPorts.add(boardPort);
}
serialDiscovery.setSerialBoardPorts(boardPorts);
}
}

View File

@ -119,7 +119,10 @@ public class SSHUploader extends Uploader {
private boolean runAVRDude(SSH ssh) throws IOException, JSchException {
TargetPlatform targetPlatform = BaseNoGui.getTargetPlatform();
PreferencesMap prefs = PreferencesData.getMap();
prefs.putAll(BaseNoGui.getBoardPreferences());
PreferencesMap boardPreferences = BaseNoGui.getBoardPreferences();
if (boardPreferences != null) {
prefs.putAll(boardPreferences);
}
prefs.putAll(targetPlatform.getTool(prefs.get("upload.tool")));
String additionalParams = verbose ? prefs.get("upload.params.verbose") : prefs.get("upload.params.quiet");

View File

@ -60,7 +60,10 @@ public class SerialUploader extends Uploader {
// FIXME: Preferences should be reorganized
TargetPlatform targetPlatform = BaseNoGui.getTargetPlatform();
PreferencesMap prefs = PreferencesData.getMap();
prefs.putAll(BaseNoGui.getBoardPreferences());
PreferencesMap boardPreferences = BaseNoGui.getBoardPreferences();
if (boardPreferences != null) {
prefs.putAll(boardPreferences);
}
String tool = prefs.getOrExcept("upload.tool");
if (tool.contains(":")) {
String[] split = tool.split(":", 2);
@ -116,7 +119,7 @@ public class SerialUploader extends Uploader {
if (verbose)
System.out.println(
I18n.format(_("Forcing reset using 1200bps open/close on port {0}"), uploadPort));
Serial.touchPort(uploadPort, 1200);
Serial.touchForCDCReset(uploadPort);
}
Thread.sleep(400);
if (waitForUploadPort) {
@ -242,13 +245,16 @@ public class SerialUploader extends Uploader {
}
PreferencesMap prefs = PreferencesData.getMap();
prefs.putAll(BaseNoGui.getBoardPreferences());
PreferencesMap boardPreferences = BaseNoGui.getBoardPreferences();
if (boardPreferences != null) {
prefs.putAll(boardPreferences);
}
PreferencesMap programmerPrefs = targetPlatform.getProgrammer(programmer);
if (programmerPrefs == null)
throw new RunnerException(
_("Please select a programmer from Tools->Programmer menu"));
prefs.putAll(targetPlatform.getTool(programmerPrefs.getOrExcept("program.tool")));
prefs.putAll(programmerPrefs);
prefs.putAll(targetPlatform.getTool(prefs.getOrExcept("program.tool")));
prefs.put("build.path", buildPath);
prefs.put("build.project_name", className);
@ -295,7 +301,10 @@ public class SerialUploader extends Uploader {
// Build configuration for the current programmer
PreferencesMap prefs = PreferencesData.getMap();
prefs.putAll(BaseNoGui.getBoardPreferences());
PreferencesMap boardPreferences = BaseNoGui.getBoardPreferences();
if (boardPreferences != null) {
prefs.putAll(boardPreferences);
}
prefs.putAll(programmerPrefs);
// Create configuration for bootloader tool

View File

@ -0,0 +1,303 @@
/*
* This file is part of Arduino.
*
* Copyright 2014 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.utils;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
import processing.app.I18n;
import processing.app.Platform;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import static processing.app.I18n._;
public class ArchiveExtractor {
private final Platform platform;
public ArchiveExtractor(Platform platform) {
assert platform != null;
this.platform = platform;
}
/**
* Extract <b>source</b> into <b>destFolder</b>. <b>source</b> file archive
* format is autodetected from file extension.
*
* @param archiveFile
* @param destFolder
* @throws IOException
*/
public void extract(File archiveFile, File destFolder) throws IOException, InterruptedException {
extract(archiveFile, destFolder, 0);
}
/**
* Extract <b>source</b> into <b>destFolder</b>. <b>source</b> file archive
* format is autodetected from file extension.
*
* @param archiveFile Archive file to extract
* @param destFolder Destination folder
* @param stripPath Number of path elements to strip from the paths contained in the
* archived files
* @throws IOException
*/
public void extract(File archiveFile, File destFolder, int stripPath) throws IOException, InterruptedException {
extract(archiveFile, destFolder, stripPath, false);
}
public void extract(File archiveFile, File destFolder, int stripPath, boolean overwrite) throws IOException, InterruptedException {
// Folders timestamps must be set at the end of archive extraction
// (because creating a file in a folder alters the folder's timestamp)
Map<File, Long> foldersTimestamps = new HashMap<File, Long>();
ArchiveInputStream in = null;
try {
// Create an ArchiveInputStream with the correct archiving algorithm
if (archiveFile.getName().endsWith("tar.bz2")) {
in = new TarArchiveInputStream(new BZip2CompressorInputStream(new FileInputStream(archiveFile)));
} else if (archiveFile.getName().endsWith("zip")) {
in = new ZipArchiveInputStream(new FileInputStream(archiveFile));
} else if (archiveFile.getName().endsWith("tar.gz")) {
in = new TarArchiveInputStream(new GzipCompressorInputStream(new FileInputStream(archiveFile)));
} else if (archiveFile.getName().endsWith("tar")) {
in = new TarArchiveInputStream(new FileInputStream(archiveFile));
} else {
throw new IOException("Archive format not supported.");
}
String pathPrefix = "";
Map<File, File> hardLinks = new HashMap<File, File>();
Map<File, Integer> hardLinksMode = new HashMap<File, Integer>();
Map<File, String> symLinks = new HashMap<File, String>();
Map<File, Long> symLinksModifiedTimes = new HashMap<File, Long>();
// Cycle through all the archive entries
while (true) {
ArchiveEntry entry = in.getNextEntry();
if (entry == null) {
break;
}
// Extract entry info
long size = entry.getSize();
String name = entry.getName();
boolean isDirectory = entry.isDirectory();
boolean isLink = false;
boolean isSymLink = false;
String linkName = null;
Integer mode = null;
long modifiedTime = entry.getLastModifiedDate().getTime();
{
// Skip MacOSX metadata
// http://superuser.com/questions/61185/why-do-i-get-files-like-foo-in-my-tarball-on-os-x
int slash = name.lastIndexOf('/');
if (slash == -1) {
if (name.startsWith("._")) {
continue;
}
} else {
if (name.substring(slash + 1).startsWith("._")) {
continue;
}
}
}
// Skip git metadata
// http://www.unix.com/unix-for-dummies-questions-and-answers/124958-file-pax_global_header-means-what.html
if (name.contains("pax_global_header")) {
continue;
}
if (entry instanceof TarArchiveEntry) {
TarArchiveEntry tarEntry = (TarArchiveEntry) entry;
mode = tarEntry.getMode();
isLink = tarEntry.isLink();
isSymLink = tarEntry.isSymbolicLink();
linkName = tarEntry.getLinkName();
}
// On the first archive entry, if requested, detect the common path
// prefix to be stripped from filenames
if (stripPath > 0 && pathPrefix.isEmpty()) {
int slash = 0;
while (stripPath > 0) {
slash = name.indexOf("/", slash);
if (slash == -1) {
throw new IOException("Invalid archive: it must contains a single root folder");
}
slash++;
stripPath--;
}
pathPrefix = name.substring(0, slash);
}
// Strip the common path prefix when requested
if (!name.startsWith(pathPrefix)) {
throw new IOException("Invalid archive: it must contains a single root folder while file " + name + " is outside " + pathPrefix);
}
name = name.substring(pathPrefix.length());
if (name.isEmpty()) {
continue;
}
File outputFile = new File(destFolder, name);
File outputLinkedFile = null;
if (isLink) {
if (!linkName.startsWith(pathPrefix)) {
throw new IOException("Invalid archive: it must contains a single root folder while file " + linkName + " is outside " + pathPrefix);
}
linkName = linkName.substring(pathPrefix.length());
outputLinkedFile = new File(destFolder, linkName);
}
if (isSymLink) {
// Symbolic links are referenced with relative paths
outputLinkedFile = new File(linkName);
if (outputLinkedFile.isAbsolute()) {
System.err.println(I18n.format(_("Warning: file {0} links to an absolute path {1}"), outputFile, outputLinkedFile));
System.err.println();
}
}
// Safety check
if (isDirectory) {
if (outputFile.isFile() && !overwrite) {
throw new IOException("Can't create folder " + outputFile + ", a file with the same name exists!");
}
} else {
// - isLink
// - isSymLink
// - anything else
if (outputFile.exists() && !overwrite) {
throw new IOException("Can't extract file " + outputFile + ", file already exists!");
}
}
// Extract the entry
if (isDirectory) {
if (!outputFile.exists() && !outputFile.mkdirs()) {
throw new IOException("Could not create folder: " + outputFile);
}
foldersTimestamps.put(outputFile, modifiedTime);
} else if (isLink) {
hardLinks.put(outputFile, outputLinkedFile);
hardLinksMode.put(outputFile, mode);
} else if (isSymLink) {
symLinks.put(outputFile, linkName);
symLinksModifiedTimes.put(outputFile, modifiedTime);
} else {
// Create the containing folder if not exists
if (!outputFile.getParentFile().isDirectory()) {
outputFile.getParentFile().mkdirs();
}
copyStreamToFile(in, size, outputFile);
outputFile.setLastModified(modifiedTime);
}
// Set file/folder permission
if (mode != null && !isSymLink && outputFile.exists()) {
platform.chmod(outputFile, mode);
}
}
for (Map.Entry<File, File> entry : hardLinks.entrySet()) {
if (entry.getKey().exists() && overwrite) {
entry.getKey().delete();
}
platform.link(entry.getValue(), entry.getKey());
Integer mode = hardLinksMode.get(entry.getKey());
if (mode != null) {
platform.chmod(entry.getKey(), mode);
}
}
for (Map.Entry<File, String> entry : symLinks.entrySet()) {
if (entry.getKey().exists() && overwrite) {
entry.getKey().delete();
}
platform.symlink(entry.getValue(), entry.getKey());
entry.getKey().setLastModified(symLinksModifiedTimes.get(entry.getKey()));
}
} finally {
if (in != null) {
in.close();
}
}
// Set folders timestamps
for (File folder : foldersTimestamps.keySet()) {
folder.setLastModified(foldersTimestamps.get(folder));
}
}
private static void copyStreamToFile(InputStream in, long size, File outputFile) throws IOException {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(outputFile);
// if size is not available, copy until EOF...
if (size == -1) {
byte buffer[] = new byte[4096];
int length;
while ((length = in.read(buffer)) != -1) {
fos.write(buffer, 0, length);
}
return;
}
// ...else copy just the needed amount of bytes
byte buffer[] = new byte[4096];
while (size > 0) {
int length = in.read(buffer);
if (length <= 0) {
throw new IOException("Error while extracting file " + outputFile.getAbsolutePath());
}
fos.write(buffer, 0, length);
size -= length;
}
} finally {
if (fos != null) {
fos.close();
}
}
}
}

View File

@ -0,0 +1,76 @@
/*
* This file is part of Arduino.
*
* Copyright 2014 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.utils;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class FileHash {
/**
* Calculate a message digest of a file using the algorithm specified. The
* result is a string containing the algorithm name followed by ":" and by the
* resulting hash in hex.
*
* @param file
* @param algorithm For example "SHA-256"
* @return The algorithm followed by ":" and the hash, for example:<br />
* "SHA-256:ee6796513086080cca078cbb383f543c5e508b647a71c9d6f39b7bca41071883"
* @throws IOException
* @throws NoSuchAlgorithmException
*/
public static String hash(File file, String algorithm) throws IOException, NoSuchAlgorithmException {
FileInputStream in = null;
try {
in = new FileInputStream(file);
byte buff[] = new byte[10240];
MessageDigest digest = MessageDigest.getInstance(algorithm);
while (in.available() > 0) {
int read = in.read(buff);
digest.update(buff, 0, read);
}
byte[] hash = digest.digest();
String res = "";
for (byte b : hash) {
int c = b & 0xFF;
if (c < 0x10)
res += "0";
res += Integer.toHexString(c);
}
return algorithm + ":" + res;
} finally {
if (in != null) {
in.close();
}
}
}
}

View File

@ -0,0 +1,74 @@
/*
* This file is part of Arduino.
*
* Copyright 2014 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.utils;
public class MultiStepProgress implements Progress {
private final double steps;
private double step;
private double stepProgress;
String status;
public MultiStepProgress(int _steps) {
steps = _steps;
step = 0.0;
stepProgress = 0.0;
}
public double getGlobalProgress() {
return (step + stepProgress) / steps;
}
public void stepDone() {
step += 100.0;
setProgress(0.0);
}
@Override
public void setProgress(double progress) {
stepProgress = progress;
}
@Override
public void setStatus(String _status) {
status = _status;
}
@Override
public double getProgress() {
return getGlobalProgress();
}
@Override
public String getStatus() {
return status;
}
}

View File

@ -0,0 +1,41 @@
/*
* This file is part of Arduino.
*
* Copyright 2014 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.utils;
public interface Progress {
void setProgress(double progress);
double getProgress();
void setStatus(String _status);
String getStatus();
}

View File

@ -0,0 +1,17 @@
package cc.arduino.utils;
import java.util.Comparator;
public class ReverseComparator<T> implements Comparator<T> {
private final Comparator<T> orig;
public ReverseComparator(Comparator<T> orig) {
this.orig = orig;
}
@Override
public int compare(T t, T t1) {
return -1 * orig.compare(t, t1);
}
}

View File

@ -0,0 +1,255 @@
/*
* This file is part of Arduino.
*
* Copyright 2014 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package cc.arduino.utils.network;
import org.apache.commons.codec.binary.Base64;
import processing.app.PreferencesData;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.Observable;
public class FileDownloader extends Observable {
public enum Status {
CONNECTING, //
CONNECTION_TIMEOUT_ERROR, //
DOWNLOADING, //
COMPLETE, //
CANCELLED, //
ERROR, //
}
private Status status;
private long initialSize;
private Long downloadSize = null;
private long downloaded;
private final URL downloadUrl;
private final File outputFile;
private InputStream stream = null;
private Exception error;
public FileDownloader(URL url, File file) {
downloadUrl = url;
outputFile = file;
downloaded = 0;
initialSize = 0;
}
public long getInitialSize() {
return initialSize;
}
public Long getDownloadSize() {
return downloadSize;
}
public void setDownloadSize(Long downloadSize) {
this.downloadSize = downloadSize;
setChanged();
notifyObservers();
}
public long getDownloaded() {
return downloaded;
}
private void setDownloaded(long downloaded) {
this.downloaded = downloaded;
setChanged();
notifyObservers();
}
public float getProgress() {
if (downloadSize == null)
return 0;
if (downloadSize == 0)
return 100;
return ((float) downloaded / downloadSize) * 100;
}
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
setChanged();
notifyObservers();
}
public void download() throws InterruptedException {
RandomAccessFile file = null;
try {
// Open file and seek to the end of it
file = new RandomAccessFile(outputFile, "rw");
initialSize = file.length();
file.seek(initialSize);
setStatus(Status.CONNECTING);
System.getProperties().remove("http.proxyHost");
System.getProperties().remove("http.proxyPort");
System.getProperties().remove("https.proxyHost");
System.getProperties().remove("https.proxyPort");
System.getProperties().remove("http.proxyUser");
System.getProperties().remove("http.proxyPassword");
if (PreferencesData.has("proxy.http.server") && PreferencesData.get("proxy.http.server") != null && !PreferencesData.get("proxy.http.server").equals("")) {
System.getProperties().put("http.proxyHost", PreferencesData.get("proxy.http.server"));
System.getProperties().put("http.proxyPort", PreferencesData.get("proxy.http.port"));
}
if (PreferencesData.has("proxy.https.server") && PreferencesData.get("proxy.https.server") != null && !PreferencesData.get("proxy.https.server").equals("")) {
System.getProperties().put("https.proxyHost", PreferencesData.get("proxy.https.server"));
System.getProperties().put("https.proxyPort", PreferencesData.get("proxy.https.port"));
}
if (PreferencesData.has("proxy.user") && PreferencesData.get("proxy.user") != null && !PreferencesData.get("proxy.user").equals("")) {
System.getProperties().put("http.proxyUser", PreferencesData.get("proxy.user"));
System.getProperties().put("http.proxyPassword", PreferencesData.get("proxy.password"));
System.getProperties().put("https.proxyUser", PreferencesData.get("proxy.user"));
System.getProperties().put("https.proxyPassword", PreferencesData.get("proxy.password"));
}
HttpURLConnection connection = (HttpURLConnection) downloadUrl.openConnection();
if (downloadUrl.getUserInfo() != null) {
String auth = "Basic " + new String(new Base64().encode(downloadUrl.getUserInfo().getBytes()));
connection.setRequestProperty("Authorization", auth);
}
connection.setRequestProperty("Range", "bytes=" + initialSize + "-");
connection.setConnectTimeout(5000);
setDownloaded(0);
// Connect
connection.connect();
int resp = connection.getResponseCode();
if (resp == HttpURLConnection.HTTP_MOVED_PERM || resp == HttpURLConnection.HTTP_MOVED_TEMP) {
String newUrl = connection.getHeaderField("Location");
// open the new connnection again
connection = (HttpURLConnection) new URL(newUrl).openConnection();
if (downloadUrl.getUserInfo() != null) {
String auth = "Basic " + new String(new Base64().encode(downloadUrl.getUserInfo().getBytes()));
connection.setRequestProperty("Authorization", auth);
}
connection.setRequestProperty("Range", "bytes=" + initialSize + "-");
connection.setConnectTimeout(5000);
connection.connect();
resp = connection.getResponseCode();
}
if (resp < 200 || resp >= 300) {
throw new IOException("Recevied invalid http status code from server: " + resp);
}
// Check for valid content length.
long len = connection.getContentLength();
if (len >= 0) {
setDownloadSize(len);
}
setStatus(Status.DOWNLOADING);
synchronized (this) {
stream = connection.getInputStream();
}
byte buffer[] = new byte[10240];
while (status == Status.DOWNLOADING) {
int read = stream.read(buffer);
if (read == -1)
break;
file.write(buffer, 0, read);
setDownloaded(getDownloaded() + read);
if (Thread.interrupted())
throw new InterruptedException();
}
if (getDownloadSize() != null) {
if (getDownloaded() < getDownloadSize())
throw new Exception("Incomplete download");
}
setStatus(Status.COMPLETE);
} catch (InterruptedException e) {
setStatus(Status.CANCELLED);
// lets InterruptedException go up to the caller
throw e;
} catch (SocketTimeoutException e) {
setStatus(Status.CONNECTION_TIMEOUT_ERROR);
setError(e);
} catch (Exception e) {
setStatus(Status.ERROR);
setError(e);
} finally {
if (file != null) {
try {
file.close();
} catch (Exception e) {
//ignore
}
}
synchronized (this) {
if (stream != null) {
try {
stream.close();
} catch (Exception e) {
//ignore
}
}
}
}
}
private void setError(Exception e) {
error = e;
}
public Exception getError() {
return error;
}
public boolean isCompleted() {
return status == Status.COMPLETE;
}
}

View File

@ -1,45 +1,53 @@
package processing.app;
import static processing.app.I18n._;
import cc.arduino.contributions.SignatureVerificationFailedException;
import cc.arduino.contributions.libraries.LibrariesIndexer;
import cc.arduino.contributions.packages.ContributedTool;
import cc.arduino.contributions.packages.ContributionsIndexer;
import cc.arduino.files.DeleteFilesOnShutdown;
import cc.arduino.packages.DiscoveryManager;
import cc.arduino.packages.Uploader;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.apache.commons.logging.impl.LogFactoryImpl;
import org.apache.commons.logging.impl.NoOpLog;
import processing.app.debug.Compiler;
import processing.app.debug.*;
import processing.app.helpers.*;
import processing.app.helpers.filefilters.OnlyDirs;
import processing.app.helpers.filefilters.OnlyFilesWithExtension;
import processing.app.legacy.PApplet;
import processing.app.packages.LibraryList;
import processing.app.packages.UserLibrary;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.*;
import java.net.URISyntaxException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.logging.impl.LogFactoryImpl;
import org.apache.commons.logging.impl.NoOpLog;
import cc.arduino.packages.DiscoveryManager;
import cc.arduino.packages.Uploader;
import processing.app.debug.Compiler;
import processing.app.debug.TargetBoard;
import processing.app.debug.TargetPackage;
import processing.app.debug.TargetPlatform;
import processing.app.debug.TargetPlatformException;
import processing.app.helpers.BasicUserNotifier;
import processing.app.helpers.CommandlineParser;
import processing.app.helpers.OSUtils;
import processing.app.helpers.PreferencesMap;
import processing.app.helpers.UserNotifier;
import processing.app.helpers.filefilters.OnlyDirs;
import processing.app.helpers.filefilters.OnlyFilesWithExtension;
import processing.app.legacy.PApplet;
import processing.app.packages.Library;
import processing.app.packages.LibraryList;
import static processing.app.I18n._;
public class BaseNoGui {
/** Version string to be used for build */
public static final int REVISION = 10601;
public static final int REVISION = 10605;
/** Extended version string displayed on GUI */
static String VERSION_NAME = "1.6.1";
public static final String VERSION_NAME = "1.6.5";
public static final String VERSION_NAME_LONG;
static {
String versionNameLong = VERSION_NAME;
File hourlyBuildTxt = new File(getContentFile("lib"), "hourlyBuild.txt");
if (hourlyBuildTxt.exists() && hourlyBuildTxt.canRead()) {
versionNameLong += " Hourly Build";
try {
versionNameLong += " " + FileUtils.readFileToString(hourlyBuildTxt).trim();
} catch (IOException e) {
//noop
}
}
VERSION_NAME_LONG = versionNameLong;
}
static File buildFolder;
@ -54,11 +62,12 @@ public class BaseNoGui {
static private File toolsFolder;
// maps #included files to their library folder
public static Map<String, Library> importToLibraryTable;
public static Map<String, LibraryList> importToLibraryTable;
// maps library name to their library folder
static private LibraryList libraries;
// XXX: Remove this field
static private List<File> librariesFolders;
static UserNotifier notifier = new BasicUserNotifier();
@ -68,9 +77,11 @@ public class BaseNoGui {
static Platform platform;
static File portableFolder = null;
static final String portableSketchbookFolder = "sketchbook";
public static ContributionsIndexer indexer;
static LibrariesIndexer librariesIndexer;
// Returns a File object for the given pathname. If the pathname
// is not absolute, it is interpreted relative to the current
// directory when starting the IDE (which is not the same as the
@ -139,7 +150,7 @@ public class BaseNoGui {
//File folder = new File(getTempFolder(), "build");
//if (!folder.exists()) folder.mkdirs();
buildFolder = createTempFolder("build");
buildFolder.deleteOnExit();
DeleteFilesOnShutdown.add(buildFolder);
}
}
return buildFolder;
@ -238,13 +249,6 @@ public class BaseNoGui {
return librariesFolders;
}
/**
* Return an InputStream for a file inside the Processing lib folder.
*/
static public InputStream getLibStream(String filename) throws IOException {
return new FileInputStream(new File(getContentFile("lib"), filename));
}
static public Platform getPlatform() {
return platform;
}
@ -403,9 +407,8 @@ public class BaseNoGui {
}
static public LibraryList getUserLibs() {
if (libraries == null)
return new LibraryList();
return libraries.filterLibrariesInSubfolder(getSketchbookFolder());
LibraryList libs = BaseNoGui.librariesIndexer.getInstalledLibraries();
return libs.filterLibrariesInSubfolder(getSketchbookFolder());
}
/**
@ -421,7 +424,7 @@ public class BaseNoGui {
return list;
}
static public void init(String[] args) {
static public void init(String[] args) throws Exception {
getPlatform().init();
String sketchbookPath = getSketchbookPath();
@ -508,7 +511,7 @@ public class BaseNoGui {
// - calls Sketch.build(verbose=false) that calls Sketch.ensureExistence(), set progressListener and calls Compiler.build()
// - calls Sketch.upload() (see later...)
if (!data.getFolder().exists()) showError(_("No sketch"), _("Can't find the sketch in the specified path"), null);
String suggestedClassName = Compiler.build(data, tempBuildFolder.getAbsolutePath(), tempBuildFolder, null, parser.isDoVerboseBuild());
String suggestedClassName = Compiler.build(data, tempBuildFolder.getAbsolutePath(), tempBuildFolder, null, parser.isDoVerboseBuild(), false);
if (suggestedClassName == null) showError(_("Error while verifying"), _("An error occurred while verifying the sketch"), null);
showMessage(_("Done compiling"), _("Done compiling"));
@ -553,7 +556,7 @@ public class BaseNoGui {
// if (!data.getFolder().exists()) showError(...);
// String ... = Compiler.build(data, tempBuildFolder.getAbsolutePath(), tempBuildFolder, null, verbose);
if (!data.getFolder().exists()) showError(_("No sketch"), _("Can't find the sketch in the specified path"), null);
String suggestedClassName = Compiler.build(data, tempBuildFolder.getAbsolutePath(), tempBuildFolder, null, parser.isDoVerboseBuild());
String suggestedClassName = Compiler.build(data, tempBuildFolder.getAbsolutePath(), tempBuildFolder, null, parser.isDoVerboseBuild(), false);
if (suggestedClassName == null) showError(_("Error while verifying"), _("An error occurred while verifying the sketch"), null);
showMessage(_("Done compiling"), _("Done compiling"));
} catch (Exception e) {
@ -582,13 +585,77 @@ public class BaseNoGui {
Logger.getLogger("javax.jmdns").setLevel(Level.OFF);
}
static public void initPackages() {
static public void initPackages() throws Exception {
indexer = new ContributionsIndexer(BaseNoGui.getSettingsFolder());
File indexFile = indexer.getIndexFile("package_index.json");
File defaultPackageJsonFile = new File(getContentFile("dist"), "package_index.json");
if (!indexFile.isFile() || (defaultPackageJsonFile.isFile() && defaultPackageJsonFile.lastModified() > indexFile.lastModified())) {
FileUtils.copyFile(defaultPackageJsonFile, indexFile);
} else if (!indexFile.isFile()) {
// Otherwise create an empty packages index
FileOutputStream out = null;
try {
out = new FileOutputStream(indexFile);
out.write("{ \"packages\" : [ ] }".getBytes());
out.close();
} finally {
if (out != null) {
out.close();
}
}
}
File indexSignatureFile = indexer.getIndexFile("package_index.json.sig");
File defaultPackageJsonSignatureFile = new File(getContentFile("dist"), "package_index.json.sig");
if (!indexSignatureFile.isFile() || (defaultPackageJsonSignatureFile.isFile() && defaultPackageJsonSignatureFile.lastModified() > indexSignatureFile.lastModified())) {
FileUtils.copyFile(defaultPackageJsonSignatureFile, indexSignatureFile);
}
try {
indexer.parseIndex();
} catch (JsonProcessingException e) {
FileUtils.deleteIfExists(indexFile);
FileUtils.deleteIfExists(indexSignatureFile);
throw e;
} catch (SignatureVerificationFailedException e) {
FileUtils.deleteIfExists(indexFile);
FileUtils.deleteIfExists(indexSignatureFile);
throw e;
}
indexer.syncWithFilesystem(getHardwareFolder());
packages = new HashMap<String, TargetPackage>();
loadHardware(getHardwareFolder());
loadHardware(getSketchbookHardwareFolder());
if (packages.size() == 0) {
System.out.println(_("No valid configured cores found! Exiting..."));
System.exit(3);
loadContributedHardware(indexer);
createToolPreferences(indexer);
librariesIndexer = new LibrariesIndexer(BaseNoGui.getSettingsFolder());
File librariesIndexFile = librariesIndexer.getIndexFile();
if (!librariesIndexFile.isFile()) {
File defaultLibraryJsonFile = new File(getContentFile("dist"), "library_index.json");
if (defaultLibraryJsonFile.isFile()) {
FileUtils.copyFile(defaultLibraryJsonFile, librariesIndexFile);
} else {
FileOutputStream out = null;
try {
// Otherwise create an empty packages index
out = new FileOutputStream(librariesIndexFile);
out.write("{ \"libraries\" : [ ] }".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (out != null) {
out.close();
}
}
}
}
try {
librariesIndexer.parseIndex();
} catch (JsonProcessingException e) {
FileUtils.deleteIfExists(librariesIndexFile);
throw e;
}
}
@ -649,9 +716,9 @@ public class BaseNoGui {
File subfolder = new File(folder, target);
try {
packages.put(target, new TargetPackage(target, subfolder));
packages.put(target, new LegacyTargetPackage(target, subfolder));
} catch (TargetPlatformException e) {
System.out.println("WARNING: Error loading hardware folder " + target);
System.out.println("WARNING: Error loading hardware folder " + new File(folder, target));
System.out.println(" " + e.getMessage());
}
}
@ -670,6 +737,8 @@ public class BaseNoGui {
if (args.length == 0)
showError(_("No parameters"), _("No command line parameters found"), null);
Runtime.getRuntime().addShutdownHook(new Thread(DeleteFilesOnShutdown.INSTANCE));
initPlatform();
initPortableFolder();
@ -683,9 +752,10 @@ public class BaseNoGui {
examplesFolder = getContentFile("examples");
toolsFolder = getContentFile("tools");
librariesFolders = new ArrayList<File>();
// Add IDE libraries folder
librariesFolders.add(getContentFile("libraries"));
// Add library folder for the current selected platform
TargetPlatform targetPlatform = getTargetPlatform();
if (targetPlatform != null) {
String core = getBoardPreferences().get("build.core", "arduino");
@ -694,35 +764,69 @@ public class BaseNoGui {
TargetPlatform referencedPlatform = getTargetPlatform(referencedCore, targetPlatform.getId());
if (referencedPlatform != null) {
File referencedPlatformFolder = referencedPlatform.getFolder();
librariesFolders.add(new File(referencedPlatformFolder, "libraries"));
// Add libraries folder for the referenced platform
File folder = new File(referencedPlatformFolder, "libraries");
librariesFolders.add(folder);
}
}
File platformFolder = targetPlatform.getFolder();
librariesFolders.add(new File(platformFolder, "libraries"));
librariesFolders.add(getSketchbookLibrariesFolder());
// Add libraries folder for the selected platform
File folder = new File(platformFolder, "libraries");
librariesFolders.add(folder);
}
// Add libraries folder for the sketchbook
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.
try {
scanAndUpdateLibraries(librariesFolders);
} catch (IOException e) {
showWarning(_("Error"), _("Error loading libraries"), e);
}
BaseNoGui.librariesIndexer.setSketchbookLibrariesFolder(getSketchbookLibrariesFolder());
BaseNoGui.librariesIndexer.setLibrariesFolders(librariesFolders);
BaseNoGui.librariesIndexer.rescanLibraries();
populateImportToLibraryTable();
}
static protected void loadContributedHardware(ContributionsIndexer indexer) throws TargetPlatformException {
for (TargetPackage pack : indexer.createTargetPackages()) {
packages.put(pack.getId(), pack);
}
}
static private void createToolPreferences(ContributionsIndexer indexer) {
// Remove previous runtime preferences
final String prefix = "runtime.tools.";
PreferencesData.removeAllKeysWithPrefix(prefix);
for (ContributedTool tool : indexer.getInstalledTools()) {
File installedFolder = tool.getDownloadableContribution().getInstalledFolder();
if (installedFolder != null) {
PreferencesData.set(prefix + tool.getName() + ".path", installedFolder.getAbsolutePath());
PreferencesData.set(prefix + tool.getName() + "-" + tool.getVersion() + ".path", installedFolder.getAbsolutePath());
}
}
}
static public void populateImportToLibraryTable() {
// Populate importToLibraryTable
importToLibraryTable = new HashMap<String, Library>();
for (Library lib : getLibraries()) {
// 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.
importToLibraryTable = new HashMap<String, LibraryList>();
for (UserLibrary lib : librariesIndexer.getInstalledLibraries()) {
try {
String headers[] = headerListFromIncludePath(lib.getSrcFolder());
for (String header : headers) {
Library old = importToLibraryTable.get(header);
if (old != null) {
LibraryList list = importToLibraryTable.get(header);
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
// with the same name. We must decide which library to
// use when a sketch has #include "name.h"
@ -737,61 +841,84 @@ public class BaseNoGui {
// for "libName", then for "oldName".
//
String name = header.substring(0, header.length() - 2); // name without ".h"
String oldName = old.getFolder().getName(); // just the library folder name
String libName = lib.getFolder().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
//System.out.println("name conflict: " + name);
//System.out.println(" old = " + oldName + " -> " + old.getFolder().getPath());
//System.out.println(" new = " + libName + " -> " + lib.getFolder().getPath());
//System.out.println(" old = " + oldName + " -> " + old.getInstalledFolder().getPath());
//System.out.println(" new = " + libName + " -> " + lib.getInstalledFolder().getPath());
String name_lc = name.toLowerCase();
String oldName_lc = oldName.toLowerCase();
String libName_lc = libName.toLowerCase();
// always favor a perfect name match
if (libName.equals(name)) {
} else if (oldName.equals(name)) {
continue;
useThisLib = false;
// check for "-master" appended (zip file from github)
} else if (libName.equals(name+"-master")) {
} else if (oldName.equals(name+"-master")) {
continue;
useThisLib = false;
// next, favor a match with other stuff appended
} else if (libName.startsWith(name)) {
} else if (oldName.startsWith(name)) {
continue;
useThisLib = false;
// otherwise, favor a match with stuff prepended
} else if (libName.endsWith(name)) {
} else if (oldName.endsWith(name)) {
continue;
useThisLib = false;
// as a last resort, match if stuff prepended and appended
} else if (libName.contains(name)) {
} else if (oldName.contains(name)) {
continue;
useThisLib = false;
// repeat all the above tests, with case insensitive matching
} else if (libName_lc.equals(name_lc)) {
} else if (oldName_lc.equals(name_lc)) {
continue;
useThisLib = false;
} else if (libName_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 (oldName_lc.startsWith(name_lc)) {
continue;
useThisLib = false;
} else if (libName_lc.endsWith(name_lc)) {
} else if (oldName_lc.endsWith(name_lc)) {
continue;
useThisLib = false;
} else if (libName_lc.contains(name_lc)) {
} else if (oldName_lc.contains(name_lc)) {
continue;
useThisLib = false;
} else {
// 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) {
showWarning(_("Error"), I18n
.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[]) {
@ -926,49 +1053,6 @@ public class BaseNoGui {
}
}
static public void scanAndUpdateLibraries(List<File> folders) throws IOException {
libraries = scanLibraries(folders);
}
static public LibraryList scanLibraries(List<File> folders) throws IOException {
LibraryList res = new LibraryList();
for (File folder : folders)
res.addOrReplaceAll(scanLibraries(folder));
return res;
}
static public LibraryList scanLibraries(File folder) throws IOException {
LibraryList res = new LibraryList();
String list[] = folder.list(new OnlyDirs());
// if a bad folder or something like that, this might come back null
if (list == null)
return res;
for (String libName : list) {
File subfolder = new File(folder, libName);
if (!isSanitaryName(libName)) {
String mess = I18n.format(_("The library \"{0}\" cannot be used.\n"
+ "Library names must contain only basic letters and numbers.\n"
+ "(ASCII only and no spaces, and it cannot start with a number)"),
libName);
showMessage(_("Ignoring bad library name"), mess);
continue;
}
try {
Library lib = Library.create(subfolder);
// (also replace previously found libs with the same name)
if (lib != null)
res.addOrReplace(lib);
} catch (IOException e) {
System.out.println(I18n.format(_("Invalid library found in {0}: {1}"),
subfolder, e.getMessage()));
}
}
return res;
}
static public void selectBoard(TargetBoard targetBoard) {
TargetPlatform targetPlatform = targetBoard.getContainerPlatform();
TargetPackage targetPackage = targetPlatform.getContainerPackage();

View File

@ -24,11 +24,11 @@ public class I18n {
// prompt text stuff
static String PROMPT_YES;
static String PROMPT_NO;
static String PROMPT_CANCEL;
static String PROMPT_OK;
static String PROMPT_BROWSE;
public static String PROMPT_YES;
public static String PROMPT_NO;
public static String PROMPT_CANCEL;
public static String PROMPT_OK;
public static String PROMPT_BROWSE;
static protected void init(String language) throws MissingResourceException {
String[] languageParts = language.split("_");

View File

@ -21,24 +21,19 @@
*/
package processing.app;
import static processing.app.I18n._;
import java.io.File;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.swing.UIManager;
import cc.arduino.packages.BoardPort;
import com.sun.jna.Library;
import com.sun.jna.Native;
import processing.app.debug.TargetBoard;
import processing.app.debug.TargetPackage;
import processing.app.debug.TargetPlatform;
import processing.app.legacy.PConstants;
import javax.swing.*;
import java.io.*;
import java.util.*;
import static processing.app.I18n._;
/**
* Used by Base for platform-specific tweaking, for instance finding the
@ -75,7 +70,7 @@ public class Platform {
}
public void init() {
public void init() throws IOException {
}
@ -169,6 +164,8 @@ public class Platform {
}
public String resolveDeviceByBoardID(Map<String, TargetPackage> packages, String boardId) {
assert packages != null;
assert boardId != null;
for (TargetPackage targetPackage : packages.values()) {
for (TargetPlatform targetPlatform : targetPackage.getPlatforms().values()) {
for (TargetBoard board : targetPlatform.getBoards().values()) {
@ -183,33 +180,6 @@ public class Platform {
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
public interface CLibrary extends Library {
CLibrary INSTANCE = (CLibrary)Native.loadLibrary("c", CLibrary.class);
int setenv(String name, String value, int overwrite);
String getenv(String name);
int unsetenv(String name);
int putenv(String string);
}
public void setenv(String variable, String value) {
CLibrary clib = CLibrary.INSTANCE;
clib.setenv(variable, value, 1);
}
public String getenv(String variable) {
CLibrary clib = CLibrary.INSTANCE;
return clib.getenv(variable);
}
public int unsetenv(String variable) {
CLibrary clib = CLibrary.INSTANCE;
return clib.unsetenv(variable);
}
public String getName() {
return PConstants.platformNames[PConstants.OTHER];
}
@ -227,4 +197,39 @@ public class Platform {
public List<BoardPort> filterPorts(List<BoardPort> ports, boolean aBoolean) {
return new LinkedList<BoardPort>(ports);
}
public void fixPrefsFilePermissions(File prefsFile) throws IOException, InterruptedException {
Process process = Runtime.getRuntime().exec(new String[]{"chmod", "600", prefsFile.getAbsolutePath()}, null, null);
process.waitFor();
}
public List<File> postInstallScripts(File folder) {
List<File> scripts = new LinkedList<File>();
scripts.add(new File(folder, "install_script.sh"));
scripts.add(new File(folder, "post_install.sh"));
return scripts;
}
public String getOsName() {
return System.getProperty("os.name");
}
public String getOsArch() {
return System.getProperty("os.arch");
}
public void symlink(String something, File somewhere) throws IOException, InterruptedException {
Process process = Runtime.getRuntime().exec(new String[]{"ln", "-s", something, somewhere.getAbsolutePath()}, null, somewhere.getParentFile());
process.waitFor();
}
public void link(File something, File somewhere) throws IOException, InterruptedException {
Process process = Runtime.getRuntime().exec(new String[]{"ln", something.getAbsolutePath(), somewhere.getAbsolutePath()}, null, null);
process.waitFor();
}
public void chmod(File file, int mode) throws IOException, InterruptedException {
Process process = Runtime.getRuntime().exec(new String[]{"chmod", Integer.toOctalString(mode), file.getAbsolutePath()}, null, null);
process.waitFor();
}
}

View File

@ -2,6 +2,7 @@ package processing.app;
import static processing.app.I18n._;
import java.awt.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
@ -9,8 +10,10 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Iterator;
import java.util.MissingResourceException;
import processing.app.helpers.PreferencesHelper;
import processing.app.helpers.PreferencesMap;
import processing.app.legacy.PApplet;
import processing.app.legacy.PConstants;
@ -18,7 +21,7 @@ import processing.app.legacy.PConstants;
public class PreferencesData {
static final String PREFS_FILE = "preferences.txt";
private static final String PREFS_FILE = "preferences.txt";
// data model
@ -29,18 +32,25 @@ public class PreferencesData {
static public void init(File file) {
if (file != null)
if (file != null) {
preferencesFile = file;
else
} else {
preferencesFile = BaseNoGui.getSettingsFile(PREFS_FILE);
}
try {
BaseNoGui.getPlatform().fixPrefsFilePermissions(preferencesFile);
} catch (Exception e) {
//ignore
}
// start by loading the defaults, in case something
// important was deleted from the user prefs
try {
prefs.load(BaseNoGui.getLibStream("preferences.txt"));
prefs.load(new File(BaseNoGui.getContentFile("lib"), PREFS_FILE));
} catch (IOException e) {
BaseNoGui.showError(null, _("Could not read default settings.\n" +
"You'll need to reinstall Arduino."), e);
"You'll need to reinstall Arduino."), e);
}
// set some runtime constants (not saved on preferences file)
@ -78,6 +88,10 @@ public class PreferencesData {
fixPreferences();
}
public static File getPreferencesFile() {
return preferencesFile;
}
private static void fixPreferences() {
String baud = get("serial.debug_rate");
if ("14400".equals(baud) || "28800".equals(baud)) {
@ -86,41 +100,6 @@ public class PreferencesData {
}
static public String[] loadStrings(InputStream input) {
try {
BufferedReader reader =
new BufferedReader(new InputStreamReader(input, "UTF-8"));
String lines[] = new String[100];
int lineCount = 0;
String line = null;
while ((line = reader.readLine()) != null) {
if (lineCount == lines.length) {
String temp[] = new String[lineCount << 1];
System.arraycopy(lines, 0, temp, 0, lineCount);
lines = temp;
}
lines[lineCount++] = line;
}
reader.close();
if (lineCount == lines.length) {
return lines;
}
// resize array to appropriate amount for these lines
String output[] = new String[lineCount];
System.arraycopy(lines, 0, output, 0, lineCount);
return output;
} catch (IOException e) {
e.printStackTrace();
//throw new RuntimeException("Error inside loadStrings()");
}
return null;
}
static protected void save() {
if (!doSave)
return;
@ -131,18 +110,30 @@ public class PreferencesData {
if (preferencesFile == null) return;
// Fix for 0163 to properly use Unicode when writing preferences.txt
PrintWriter writer = PApplet.createWriter(preferencesFile);
PrintWriter writer = null;
try {
writer = PApplet.createWriter(preferencesFile);
String[] keys = prefs.keySet().toArray(new String[0]);
Arrays.sort(keys);
for (String key: keys) {
if (key.startsWith("runtime."))
continue;
writer.println(key + "=" + prefs.get(key));
String[] keys = prefs.keySet().toArray(new String[0]);
Arrays.sort(keys);
for (String key : keys) {
if (key.startsWith("runtime."))
continue;
writer.println(key + "=" + prefs.get(key));
}
writer.flush();
} finally {
if (writer != null) {
writer.close();
}
}
writer.flush();
writer.close();
try {
BaseNoGui.getPlatform().fixPrefsFilePermissions(preferencesFile);
} catch (Exception e) {
//ignore
}
}
@ -194,6 +185,13 @@ public class PreferencesData {
return Integer.parseInt(get(attribute));
}
static public int getInteger(String attribute, int defaultValue) {
if (has(attribute)) {
return getInteger(attribute);
}
return defaultValue;
}
static public void setInteger(String key, int value) {
set(key, String.valueOf(value));
@ -205,10 +203,27 @@ public class PreferencesData {
return new PreferencesMap(prefs);
}
static public void removeAllKeysWithPrefix(String prefix) {
Iterator<String> keys = prefs.keySet().iterator();
while (keys.hasNext())
if (keys.next().startsWith(prefix))
keys.remove();
}
// Decide wether changed preferences will be saved. When value is
// false, Preferences.save becomes a no-op.
static public void setDoSave(boolean value)
{
doSave = value;
}
static public Font getFont(String attr) {
Font font = PreferencesHelper.getFont(prefs, attr);
if (font == null) {
String value = defaults.get(attr);
prefs.put(attr, value);
font = PreferencesHelper.getFont(prefs, attr);
}
return font;
}
}

View File

@ -88,11 +88,12 @@ public class Serial implements SerialPortEventListener {
BaseNoGui.getBoardPreferences().get("serial.disableDTR") == null);
}
public static boolean touchPort(String iname, int irate) throws SerialException {
public static boolean touchForCDCReset(String iname) throws SerialException {
SerialPort serialPort = new SerialPort(iname);
try {
serialPort.openPort();
serialPort.setParams(irate, 8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
serialPort.setParams(1200, 8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
serialPort.setDTR(false);
serialPort.closePort();
return true;
} catch (SerialPortException e) {

View File

@ -25,13 +25,7 @@ package processing.app.debug;
import static processing.app.I18n._;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -45,6 +39,9 @@ import cc.arduino.packages.BoardPort;
import cc.arduino.packages.Uploader;
import cc.arduino.packages.UploaderFactory;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.ExecuteStreamHandler;
import processing.app.BaseNoGui;
import processing.app.I18n;
import processing.app.PreferencesData;
@ -52,10 +49,12 @@ import processing.app.SketchCode;
import processing.app.SketchData;
import processing.app.helpers.*;
import processing.app.helpers.filefilters.OnlyDirs;
import processing.app.packages.Library;
import processing.app.packages.LibraryList;
import processing.app.preproc.PdePreprocessor;
import processing.app.legacy.PApplet;
import processing.app.packages.LegacyUserLibrary;
import processing.app.packages.UserLibrary;
import processing.app.tools.DoubleQuotedArgumentsOnWindowsCommandLine;
public class Compiler implements MessageConsumer {
@ -68,6 +67,7 @@ public class Compiler implements MessageConsumer {
private SketchData sketch;
private PreferencesMap prefs;
private boolean verbose;
private boolean saveHex;
private List<File> objectFiles;
@ -84,7 +84,7 @@ public class Compiler implements MessageConsumer {
private ProgressListener progressListener;
static public String build(SketchData data, String buildPath, File tempBuildFolder, ProgressListener progListener, boolean verbose) throws RunnerException, PreferencesMapException {
static public String build(SketchData data, String buildPath, File tempBuildFolder, ProgressListener progListener, boolean verbose, boolean save) throws RunnerException, PreferencesMapException {
if (SketchData.checkSketchFile(data.getPrimaryFile()) == null)
BaseNoGui.showError(_("Bad file selected"),
_("Bad sketch primary file or bad sketch directory structure"), null);
@ -113,9 +113,16 @@ public class Compiler implements MessageConsumer {
// compile the program. errors will happen as a RunnerException
// that will bubble up to whomever called build().
if (compiler.compile(verbose)) {
compiler.size(compiler.getBuildPreferences());
return primaryClassName;
try {
if (compiler.compile(verbose, save)) {
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;
}
@ -183,6 +190,9 @@ public class Compiler implements MessageConsumer {
sketch = _sketch;
prefs = createBuildPreferences(_buildPath, _primaryClassName);
// provide access to the source tree
prefs.put("build.source.path", _sketch.getFolder().getAbsolutePath());
// Start with an empty progress listener
progressListener = new ProgressListener() {
@Override
@ -311,10 +321,10 @@ public class Compiler implements MessageConsumer {
if (maxDataSize > 0) {
System.out
.println(I18n
.format(
_("Global variables use {0} bytes ({2}%%) of dynamic memory, leaving {3} bytes for local variables. Maximum is {1} bytes."),
dataSize, maxDataSize, dataSize * 100 / maxDataSize,
maxDataSize - dataSize));
.format(
_("Global variables use {0} bytes ({2}%%) of dynamic memory, leaving {3} bytes for local variables. Maximum is {1} bytes."),
dataSize, maxDataSize, dataSize * 100 / maxDataSize,
maxDataSize - dataSize));
} else {
System.out.println(I18n
.format(_("Global variables use {0} bytes of dynamic memory."), dataSize));
@ -341,11 +351,16 @@ public class Compiler implements MessageConsumer {
* @return true if successful.
* @throws RunnerException Only if there's a problem. Only then.
*/
public boolean compile(boolean _verbose) throws RunnerException, PreferencesMapException {
public boolean compile(boolean _verbose, boolean _save) throws RunnerException, PreferencesMapException {
preprocess(prefs.get("build.path"));
verbose = _verbose || PreferencesData.getBoolean("build.verbose");
saveHex = _save;
sketchIsCompiled = false;
// Hook runs at Start of Compilation
runActions("hooks.prebuild", prefs);
objectFiles = new ArrayList<File>();
// 0. include paths for core + all libraries
@ -354,11 +369,15 @@ public class Compiler implements MessageConsumer {
includeFolders.add(prefs.getFile("build.core.path"));
if (prefs.getFile("build.variant.path") != null)
includeFolders.add(prefs.getFile("build.variant.path"));
for (Library lib : importedLibraries) {
if (verbose)
for (UserLibrary lib : importedLibraries) {
if (verbose) {
String legacy = "";
if (lib instanceof LegacyUserLibrary)
legacy = "(legacy)";
System.out.println(I18n
.format(_("Using library {0} in folder: {1} {2}"), lib.getName(),
lib.getFolder(), lib.isLegacy() ? "(legacy)" : ""));
lib.getInstalledFolder(), legacy));
}
includeFolders.add(lib.getSrcFolder());
}
if (verbose)
@ -370,7 +389,7 @@ public class Compiler implements MessageConsumer {
String[] overrides = prefs.get("architecture.override_check").split(",");
archs.addAll(Arrays.asList(overrides));
}
for (Library lib : importedLibraries) {
for (UserLibrary lib : importedLibraries) {
if (!lib.supportsArchitecture(archs)) {
System.err.println(I18n
.format(_("WARNING: library {0} claims to run on {1} "
@ -382,26 +401,26 @@ public class Compiler implements MessageConsumer {
}
// 1. compile the sketch (already in the buildPath)
progressListener.progress(30);
progressListener.progress(20);
compileSketch(includeFolders);
sketchIsCompiled = true;
// 2. compile the libraries, outputting .o files to: <buildPath>/<library>/
// Doesn't really use configPreferences
progressListener.progress(40);
progressListener.progress(30);
compileLibraries(includeFolders);
// 3. compile the core, outputting .o files to <buildPath> and then
// collecting them into the core.a library file.
progressListener.progress(50);
progressListener.progress(40);
compileCore();
// 4. link it all together into the .elf file
progressListener.progress(60);
progressListener.progress(50);
compileLink();
// 5. run objcopy to generate output files
progressListener.progress(75);
progressListener.progress(60);
List<String> objcopyPatterns = new ArrayList<String>();
for (String key : prefs.keySet()) {
if (key.startsWith("recipe.objcopy.") && key.endsWith(".pattern"))
@ -412,10 +431,39 @@ public class Compiler implements MessageConsumer {
runRecipe(recipe);
}
// 7. save the hex file
if (saveHex) {
progressListener.progress(80);
saveHex();
}
progressListener.progress(90);
// Hook runs at End of Compilation
runActions("hooks.postbuild", prefs);
adviseDuplicateLibraries();
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,
String _primaryClassName)
throws RunnerException {
@ -647,7 +695,6 @@ public class Compiler implements MessageConsumer {
command = stringList.toArray(new String[stringList.size()]);
if (command.length == 0)
return;
int result = 0;
if (verbose) {
for (String c : command)
@ -655,30 +702,57 @@ public class Compiler implements MessageConsumer {
System.out.println();
}
Process process;
DefaultExecutor executor = new DefaultExecutor();
executor.setStreamHandler(new ExecuteStreamHandler() {
@Override
public void setProcessInputStream(OutputStream os) throws IOException {
}
@Override
public void setProcessErrorStream(InputStream is) throws IOException {
forwardToMessage(is);
}
@Override
public void setProcessOutputStream(InputStream is) throws IOException {
forwardToMessage(is);
}
private void forwardToMessage(InputStream is) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line;
while ((line = reader.readLine()) != null) {
message(line + "\n");
}
}
@Override
public void start() throws IOException {
}
@Override
public void stop() {
}
});
CommandLine commandLine = new DoubleQuotedArgumentsOnWindowsCommandLine(command[0]);
for (int i = 1; i < command.length; i++) {
commandLine.addArgument(command[i], false);
}
int result;
executor.setExitValues(null);
try {
process = ProcessUtils.exec(command);
result = executor.execute(commandLine);
} catch (IOException e) {
RunnerException re = new RunnerException(e.getMessage());
re.hideStackTrace();
throw re;
}
MessageSiphon in = new MessageSiphon(process.getInputStream(), this);
MessageSiphon err = new MessageSiphon(process.getErrorStream(), this);
// wait for the process to finish. if interrupted
// before waitFor returns, continue waiting
boolean compiling = true;
while (compiling) {
try {
in.join();
err.join();
result = process.waitFor();
//System.out.println("result is " + result);
compiling = false;
} catch (InterruptedException ignored) { }
}
executor.setExitValues(new int[0]);
// an error was queued up by message(), barf this back to compile(),
// which will barf it back to Editor. if you're having trouble
@ -720,10 +794,10 @@ public class Compiler implements MessageConsumer {
s = s.substring(0, i) + s.substring(i + (buildPath + File.separator).length());
}
}
// look for error line, which contains file name, line number,
// and at least the first line of the error message
String errorFormat = "([\\w\\d_]+.\\w+):(\\d+):\\s*error:\\s*(.*)\\s*";
String errorFormat = "(.+\\.\\w+):(\\d+)(:\\d+)*:\\s*error:\\s*(.*)\\s*";
String[] pieces = PApplet.match(s, errorFormat);
// if (pieces != null && exception == null) {
@ -732,56 +806,56 @@ public class Compiler implements MessageConsumer {
// }
if (pieces != null) {
String error = pieces[3], msg = "";
String error = pieces[pieces.length - 1], msg = "";
if (pieces[3].trim().equals("SPI.h: No such file or directory")) {
if (error.trim().equals("SPI.h: No such file or directory")) {
error = _("Please import the SPI library from the Sketch > Import Library menu.");
msg = _("\nAs of Arduino 0019, the Ethernet library depends on the SPI library." +
"\nYou appear to be using it or another library that depends on the SPI library.\n\n");
}
if (pieces[3].trim().equals("'BYTE' was not declared in this scope")) {
if (error.trim().equals("'BYTE' was not declared in this scope")) {
error = _("The 'BYTE' keyword is no longer supported.");
msg = _("\nAs of Arduino 1.0, the 'BYTE' keyword is no longer supported." +
"\nPlease use Serial.write() instead.\n\n");
}
if (pieces[3].trim().equals("no matching function for call to 'Server::Server(int)'")) {
if (error.trim().equals("no matching function for call to 'Server::Server(int)'")) {
error = _("The Server class has been renamed EthernetServer.");
msg = _("\nAs of Arduino 1.0, the Server class in the Ethernet library " +
"has been renamed to EthernetServer.\n\n");
}
if (pieces[3].trim().equals("no matching function for call to 'Client::Client(byte [4], int)'")) {
if (error.trim().equals("no matching function for call to 'Client::Client(byte [4], int)'")) {
error = _("The Client class has been renamed EthernetClient.");
msg = _("\nAs of Arduino 1.0, the Client class in the Ethernet library " +
"has been renamed to EthernetClient.\n\n");
}
if (pieces[3].trim().equals("'Udp' was not declared in this scope")) {
if (error.trim().equals("'Udp' was not declared in this scope")) {
error = _("The Udp class has been renamed EthernetUdp.");
msg = _("\nAs of Arduino 1.0, the Udp class in the Ethernet library " +
"has been renamed to EthernetUdp.\n\n");
}
if (pieces[3].trim().equals("'class TwoWire' has no member named 'send'")) {
if (error.trim().equals("'class TwoWire' has no member named 'send'")) {
error = _("Wire.send() has been renamed Wire.write().");
msg = _("\nAs of Arduino 1.0, the Wire.send() function was renamed " +
"to Wire.write() for consistency with other libraries.\n\n");
}
if (pieces[3].trim().equals("'class TwoWire' has no member named 'receive'")) {
if (error.trim().equals("'class TwoWire' has no member named 'receive'")) {
error = _("Wire.receive() has been renamed Wire.read().");
msg = _("\nAs of Arduino 1.0, the Wire.receive() function was renamed " +
"to Wire.read() for consistency with other libraries.\n\n");
}
if (pieces[3].trim().equals("'Mouse' was not declared in this scope")) {
if (error.trim().equals("'Mouse' was not declared in this scope")) {
error = _("'Mouse' only supported on the Arduino Leonardo");
//msg = _("\nThe 'Mouse' class is only supported on the Arduino Leonardo.\n\n");
}
if (pieces[3].trim().equals("'Keyboard' was not declared in this scope")) {
if (error.trim().equals("'Keyboard' was not declared in this scope")) {
error = _("'Keyboard' only supported on the Arduino Leonardo");
//msg = _("\nThe 'Keyboard' class is only supported on the Arduino Leonardo.\n\n");
}
@ -799,13 +873,15 @@ public class Compiler implements MessageConsumer {
SketchCode code = sketch.getCode(e.getCodeIndex());
String fileName = (code.isExtension("ino") || code.isExtension("pde")) ? code.getPrettyName() : code.getFileName();
int lineNum = e.getCodeLine() + 1;
s = fileName + ":" + lineNum + ": error: " + pieces[3] + msg;
s = fileName + ":" + lineNum + ": error: " + error + msg;
}
if (e != null) {
if (exception == null || exception.getMessage().equals(e.getMessage())) {
exception = e;
exception.hideStackTrace();
}
}
if (exception == null && e != null) {
exception = e;
exception.hideStackTrace();
}
}
if (s.contains("undefined reference to `SPIClass::begin()'") &&
@ -831,6 +907,8 @@ public class Compiler implements MessageConsumer {
dict.put("source_file", sourceFile.getAbsolutePath());
dict.put("object_file", objectFile.getAbsolutePath());
setupWarningFlags(dict);
String cmd = prefs.getOrExcept(recipe);
try {
return StringReplacer.formatAndSplit(cmd, dict, true);
@ -839,6 +917,19 @@ public class Compiler implements MessageConsumer {
}
}
private void setupWarningFlags(PreferencesMap dict) {
if (dict.containsKey("compiler.warning_level")) {
String key = "compiler.warning_flags." + dict.get("compiler.warning_level");
dict.put("compiler.warning_flags", dict.get(key));
} else {
dict.put("compiler.warning_flags", dict.get("compiler.warning_flags.none"));
}
if (dict.get("compiler.warning_flags") == null) {
dict.remove("compiler.warning_flags");
}
}
/////////////////////////////////////////////////////////////////////////////
private void createFolder(File folder) throws RunnerException {
@ -886,21 +977,21 @@ public class Compiler implements MessageConsumer {
// 2. compile the libraries, outputting .o files to:
// <buildPath>/<library>/
void compileLibraries(List<File> includeFolders) throws RunnerException, PreferencesMapException {
for (Library lib : importedLibraries) {
for (UserLibrary lib : importedLibraries) {
compileLibrary(lib, includeFolders);
}
}
private void compileLibrary(Library lib, List<File> includeFolders)
private void compileLibrary(UserLibrary lib, List<File> includeFolders)
throws RunnerException, PreferencesMapException {
File libFolder = lib.getSrcFolder();
File libBuildFolder = prefs.getFile(("build.path"), lib.getName());
if (lib.useRecursion()) {
// libBuildFolder == {build.path}/LibName
// libFolder == {lib.path}/src
recursiveCompileFilesInFolder(libBuildFolder, libFolder, includeFolders);
} else {
// libFolder == {lib.path}/
// utilityFolder == {lib.path}/utility
@ -908,11 +999,11 @@ public class Compiler implements MessageConsumer {
// utilityBuildFolder == {build.path}/LibName/utility
File utilityFolder = new File(libFolder, "utility");
File utilityBuildFolder = new File(libBuildFolder, "utility");
includeFolders.add(utilityFolder);
compileFilesInFolder(libBuildFolder, libFolder, includeFolders);
compileFilesInFolder(utilityBuildFolder, utilityFolder, includeFolders);
// other libraries should not see this library's utility/ folder
includeFolders.remove(utilityFolder);
}
@ -1031,6 +1122,8 @@ public class Compiler implements MessageConsumer {
dict.put("object_files", objectFileList);
dict.put("ide_version", "" + BaseNoGui.REVISION);
setupWarningFlags(dict);
String[] cmdArray;
String cmd = prefs.getOrExcept("recipe.c.combine.pattern");
try {
@ -1041,6 +1134,18 @@ public class Compiler implements MessageConsumer {
execAsynchronously(cmdArray);
}
void runActions(String recipeClass, PreferencesMap prefs) throws RunnerException, PreferencesMapException {
List<String> patterns = new ArrayList<String>();
for (String key : prefs.keySet()) {
if (key.startsWith("recipe."+recipeClass) && key.endsWith(".pattern"))
patterns.add(key);
}
Collections.sort(patterns);
for (String recipe : patterns) {
runRecipe(recipe);
}
}
void runRecipe(String recipe) throws RunnerException, PreferencesMapException {
PreferencesMap dict = new PreferencesMap(prefs);
dict.put("ide_version", "" + BaseNoGui.REVISION);
@ -1054,6 +1159,32 @@ public class Compiler implements MessageConsumer {
}
execAsynchronously(cmdArray);
}
//7. Save the .hex file
void saveHex() throws RunnerException {
if (!prefs.containsKey("recipe.output.tmp_file") || !prefs.containsKey("recipe.output.save_file")) {
System.err.println(_("Warning: This core does not support exporting sketches. Please consider upgrading it or contacting its author"));
return;
}
PreferencesMap dict = new PreferencesMap(prefs);
dict.put("ide_version", "" + BaseNoGui.REVISION);
try {
String compiledSketch = prefs.getOrExcept("recipe.output.tmp_file");
compiledSketch = StringReplacer.replaceFromMapping(compiledSketch, dict);
String copyOfCompiledSketch = prefs.getOrExcept("recipe.output.save_file");
copyOfCompiledSketch = StringReplacer.replaceFromMapping(copyOfCompiledSketch, dict);
File compiledSketchFile = new File(prefs.get("build.path"), compiledSketch);
File copyOfCompiledSketchFile = new File(sketch.getFolder(), copyOfCompiledSketch);
FileUtils.copyFile(compiledSketchFile, copyOfCompiledSketchFile);
} catch (Exception e) {
throw new RunnerException(e);
}
}
private static String prepareIncludes(List<File> includeFolders) {
String res = "";
@ -1120,12 +1251,12 @@ public class Compiler implements MessageConsumer {
// 2. run preproc on that code using the sugg class name
// to create a single .java file and write to buildpath
FileOutputStream outputStream = null;
try {
// Output file
File streamFile = new File(buildPath, sketch.getName() + ".cpp");
FileOutputStream outputStream = new FileOutputStream(streamFile);
outputStream = new FileOutputStream(streamFile);
preprocessor.write(outputStream);
outputStream.close();
} catch (FileNotFoundException fnfe) {
fnfe.printStackTrace();
String msg = _("Build folder disappeared or could not be written");
@ -1140,15 +1271,32 @@ public class Compiler implements MessageConsumer {
System.err.println(I18n.format(_("Uncaught exception type: {0}"), ex.getClass()));
ex.printStackTrace();
throw new RunnerException(ex.toString());
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
//noop
}
}
}
// grab the imports from the code just preproc'd
importedLibraries = new LibraryList();
importedDuplicateHeaders = new ArrayList<String>();
importedDuplicateLibraries = new ArrayList<LibraryList>();
for (String item : preprocessor.getExtraImports()) {
Library lib = BaseNoGui.importToLibraryTable.get(item);
if (lib != null && !importedLibraries.contains(lib)) {
importedLibraries.add(lib);
LibraryList list = BaseNoGui.importToLibraryTable.get(item);
if (list != null) {
UserLibrary lib = list.peekFirst();
if (lib != null && !importedLibraries.contains(lib)) {
importedLibraries.add(lib);
if (list.size() > 1) {
importedDuplicateHeaders.add(item);
importedDuplicateLibraries.add(list);
}
}
}
}
@ -1180,6 +1328,8 @@ public class Compiler implements MessageConsumer {
* List of library folders.
*/
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

View File

@ -0,0 +1,113 @@
/*
TargetPackage - Represents a hardware package
Part of the Arduino project - http://www.arduino.cc/
Copyright (c) 2014 Cristian Maglie
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app.debug;
import static processing.app.I18n._;
import static processing.app.I18n.format;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import processing.app.helpers.PreferencesMap;
public class LegacyTargetBoard implements TargetBoard {
private String id;
private PreferencesMap prefs;
private Map<String, PreferencesMap> menuOptions = new LinkedHashMap<String, PreferencesMap>();
private TargetPlatform containerPlatform;
/**
* Create a TargetBoard based on preferences passed as argument.
*
* @param _prefs
* @return
*/
public LegacyTargetBoard(String _id, PreferencesMap _prefs,
TargetPlatform parent) {
containerPlatform = parent;
id = _id;
prefs = new PreferencesMap(_prefs);
// Setup sub-menus
PreferencesMap menus = prefs.firstLevelMap().get("menu");
if (menus != null)
menuOptions = menus.firstLevelMap();
// Auto generate build.board if not set
if (!prefs.containsKey("build.board")) {
String board = containerPlatform.getId() + "_" + id;
board = board.toUpperCase();
prefs.put("build.board", board);
System.out
.println(format(_("Board {0}:{1}:{2} doesn''t define a ''build.board'' preference. Auto-set to: {3}"),
containerPlatform.getContainerPackage().getId(),
containerPlatform.getId(), id, board));
}
}
@Override
public String getName() {
return prefs.get("name");
}
@Override
public String getId() {
return id;
}
@Override
public PreferencesMap getPreferences() {
return prefs;
}
@Override
public boolean hasMenu(String menuId) {
return menuOptions.containsKey(menuId);
}
@Override
public PreferencesMap getMenuLabels(String menuId) {
return menuOptions.get(menuId).topLevelMap();
}
@Override
public String getMenuLabel(String menuId, String selectionId) {
return getMenuLabels(menuId).get(selectionId);
}
@Override
public Set<String> getMenuIds() {
return menuOptions.keySet();
}
@Override
public PreferencesMap getMenuPreferences(String menuId, String selectionId) {
return menuOptions.get(menuId).subTree(selectionId);
}
@Override
public TargetPlatform getContainerPlatform() {
return containerPlatform;
}
}

View File

@ -0,0 +1,89 @@
/*
TargetPackage - Represents a hardware package
Part of the Arduino project - http://www.arduino.cc/
Copyright (c) 2011 Cristian Maglie
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app.debug;
import static processing.app.I18n._;
import static processing.app.helpers.filefilters.OnlyDirs.ONLY_DIRS;
import java.io.File;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import processing.app.I18n;
public class LegacyTargetPackage implements TargetPackage {
private String id;
private Map<String, TargetPlatform> platforms;
public LegacyTargetPackage(String _id, File _folder) throws TargetPlatformException {
id = _id;
platforms = new LinkedHashMap<String, TargetPlatform>();
File[] folders = _folder.listFiles(ONLY_DIRS);
if (folders == null)
return;
for (File subFolder : folders) {
if (!subFolder.exists() || !subFolder.canRead())
continue;
String arch = subFolder.getName();
try {
TargetPlatform platform = new LegacyTargetPlatform(arch, subFolder, this);
platforms.put(arch, platform);
} catch (TargetPlatformException e) {
System.out.println(e.getMessage());
}
}
if (platforms.size() == 0) {
throw new TargetPlatformException(I18n
.format(_("No valid hardware definitions found in folder {0}."),
_folder.getName()));
}
}
@Override
public Map<String, TargetPlatform> getPlatforms() {
return platforms;
}
@Override
public Collection<TargetPlatform> platforms() {
return platforms.values();
}
@Override
public TargetPlatform get(String platform) {
return platforms.get(platform);
}
@Override
public boolean hasPlatform(TargetPlatform platform) {
return platforms.containsKey(platform.getId());
}
@Override
public String getId() {
return id;
}
}

View File

@ -0,0 +1,241 @@
/*
TargetPlatform - Represents a hardware platform
Part of the Arduino project - http://www.arduino.cc/
Copyright (c) 2009-2014 Arduino
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app.debug;
import processing.app.BaseNoGui;
import processing.app.I18n;
import processing.app.helpers.PreferencesMap;
import java.io.File;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import static processing.app.I18n._;
import static processing.app.I18n.format;
public class LegacyTargetPlatform implements TargetPlatform {
private String id;
private File folder;
private TargetPackage containerPackage;
protected PreferencesMap preferences = new PreferencesMap();
private Map<String, TargetBoard> boards = new LinkedHashMap<String, TargetBoard>();
private TargetBoard defaultBoard;
/**
* Contains preferences for every defined programmer
*/
private Map<String, PreferencesMap> programmers = new LinkedHashMap<String, PreferencesMap>();
/**
* Contains labels for top level menus
*/
private PreferencesMap customMenus = new PreferencesMap();
public LegacyTargetPlatform(String _name, File _folder, TargetPackage parent)
throws TargetPlatformException {
id = _name;
folder = _folder;
containerPackage = parent;
// If there is no boards.txt, this is not a valid 1.5 hardware folder
File boardsFile = new File(folder, "boards.txt");
if (!boardsFile.exists() || !boardsFile.canRead())
throw new TargetPlatformException(
format(_("Could not find boards.txt in {0}. Is it pre-1.5?"),
folder.getAbsolutePath()));
// Load boards
try {
Map<String, PreferencesMap> boardsPreferences = new PreferencesMap(
boardsFile).firstLevelMap();
// Create custom menus for this platform
PreferencesMap menus = boardsPreferences.get("menu");
if (menus != null)
customMenus = menus.topLevelMap();
boardsPreferences.remove("menu");
// Create boards
Set<String> boardIds = boardsPreferences.keySet();
for (String boardId : boardIds) {
PreferencesMap preferences = boardsPreferences.get(boardId);
TargetBoard board = new LegacyTargetBoard(boardId, preferences, this);
boards.put(boardId, board);
// Pick the first board as default
if (defaultBoard == null)
defaultBoard = board;
}
} catch (IOException e) {
throw new TargetPlatformException(format(_("Error loading {0}"),
boardsFile.getAbsolutePath()), e);
}
File platformsFile = new File(folder, "platform.txt");
try {
if (platformsFile.exists() && platformsFile.canRead()) {
preferences.load(platformsFile);
}
} catch (IOException e) {
throw new TargetPlatformException(
format(_("Error loading {0}"), platformsFile.getAbsolutePath()), e);
}
// Allow overriding values in platform.txt. This allows changing
// platform.txt (e.g. to use a system-wide toolchain), without
// having to modify platform.txt (which, when running from git,
// prevents files being marked as changed).
File localPlatformsFile = new File(folder, "platform.local.txt");
try {
if (localPlatformsFile.exists() && localPlatformsFile.canRead()) {
preferences.load(localPlatformsFile);
}
} catch (IOException e) {
throw new TargetPlatformException(
format(_("Error loading {0}"), localPlatformsFile.getAbsolutePath()), e);
}
if (!preferences.containsKey("rewriting") || !"disabled".equals(preferences.get("rewriting"))) {
try {
rewriteKeysOfOldPlatformsTxtAndWarnAboutIt();
} catch (IOException e) {
throw new TargetPlatformException(e);
}
}
File progFile = new File(folder, "programmers.txt");
try {
if (progFile.exists() && progFile.canRead()) {
PreferencesMap prefs = new PreferencesMap();
prefs.load(progFile);
programmers = prefs.firstLevelMap();
}
} catch (IOException e) {
throw new TargetPlatformException(format(_("Error loading {0}"),
progFile.getAbsolutePath()), e);
}
}
private void rewriteKeysOfOldPlatformsTxtAndWarnAboutIt() throws IOException {
File platformRewrite = new File(BaseNoGui.getHardwareFolder(), "platform.keys.rewrite.txt");
PreferencesMap platformRewriteProps = new PreferencesMap(platformRewrite);
PreferencesMap oldProps = platformRewriteProps.subTree("old");
PreferencesMap newProps = platformRewriteProps.subTree("new");
String platformName = preferences.get("name");
if (platformName == null) {
platformName = folder.getAbsolutePath();
}
for (Map.Entry<String, String> entry : oldProps.entrySet()) {
String preferencesKey = entry.getKey().substring(entry.getKey().indexOf(".") + 1);
if (preferences.containsKey(preferencesKey) && entry.getValue().equals(preferences.get(preferencesKey))) {
System.err.println(I18n.format(_("Warning: platform.txt from core '{0}' contains deprecated {1}, automatically converted to {2}. Consider upgrading this core."), platformName, preferencesKey + "=" + entry.getValue(), preferencesKey + "=" + newProps.get(entry.getKey())));
preferences.put(preferencesKey, newProps.get(entry.getKey()));
}
}
PreferencesMap addedProps = platformRewriteProps.subTree("added");
for (Map.Entry<String, String> entry : addedProps.entrySet()) {
String keyToAdd = entry.getKey();
String[] keyToAddParts = keyToAdd.split("\\.");
String keyToAddFirstLevel = keyToAddParts[0];
String keyToAddSecondLevel = keyToAddParts[0] + "." + keyToAddParts[1];
if (!preferences.subTree(keyToAddFirstLevel).isEmpty() && !preferences.subTree(keyToAddSecondLevel).isEmpty() && !preferences.containsKey(keyToAdd)) {
System.err.println(I18n.format(_("Warning: platform.txt from core '{0}' misses property {1}, automatically set to {2}. Consider upgrading this core."), platformName, keyToAdd, entry.getValue()));
preferences.put(keyToAdd, entry.getValue());
}
}
}
@Override
public String getId() {
return id;
}
@Override
public File getFolder() {
return folder;
}
@Override
public Map<String, TargetBoard> getBoards() {
return boards;
}
@Override
public PreferencesMap getCustomMenus() {
return customMenus;
}
@Override
public Set<String> getCustomMenuIds() {
return customMenus.keySet();
}
@Override
public Map<String, PreferencesMap> getProgrammers() {
return programmers;
}
@Override
public PreferencesMap getProgrammer(String programmer) {
return getProgrammers().get(programmer);
}
@Override
public PreferencesMap getTool(String tool) {
return getPreferences().subTree("tools").subTree(tool);
}
@Override
public PreferencesMap getPreferences() {
return preferences;
}
@Override
public TargetBoard getBoard(String boardId) {
if (boards.containsKey(boardId)) {
return boards.get(boardId);
}
return defaultBoard;
}
@Override
public TargetPackage getContainerPackage() {
return containerPackage;
}
@Override
public String toString() {
String res = "TargetPlatform: name=" + id + " boards={\n";
for (String boardId : boards.keySet())
res += " " + boardId + " = " + boards.get(boardId) + "\n";
return res + "}";
}
}

View File

@ -1,76 +1,51 @@
/*
TargetBoard - Represents a hardware board
Part of the Arduino project - http://www.arduino.cc/
Copyright (c) 2014 Cristian Maglie
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.app.debug;
import static processing.app.I18n._;
import static processing.app.I18n.format;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import processing.app.helpers.PreferencesMap;
public class TargetBoard {
private String id;
private PreferencesMap prefs;
private Map<String, PreferencesMap> menuOptions = new LinkedHashMap<String, PreferencesMap>();
private TargetPlatform containerPlatform;
/**
* Create a TargetBoard based on preferences passed as argument.
*
* @param _prefs
* @return
*/
public TargetBoard(String _id, PreferencesMap _prefs, TargetPlatform parent) {
containerPlatform = parent;
id = _id;
prefs = new PreferencesMap(_prefs);
// Setup sub-menus
PreferencesMap menus = prefs.firstLevelMap().get("menu");
if (menus != null)
menuOptions = menus.firstLevelMap();
// Auto generate build.board if not set
if (!prefs.containsKey("build.board")) {
String board = containerPlatform.getId() + "_" + id;
board = board.toUpperCase();
prefs.put("build.board", board);
System.out
.println(format(
_("Board {0}:{1}:{2} doesn''t define a ''build.board'' preference. Auto-set to: {3}"),
containerPlatform.getContainerPackage().getId(),
containerPlatform.getId(), id, board));
}
}
public interface TargetBoard {
/**
* Get the name of the board.
*
* @return
*/
public String getName() {
return prefs.get("name");
}
public String getName();
/**
* Get the identifier of the board
*
* @return
*/
public String getId() {
return id;
}
public String getId();
/**
* Get the full preferences map of the board with a given identifier
* Get the full preferences map of the board
*
* @return
*/
public PreferencesMap getPreferences() {
return prefs;
}
public PreferencesMap getPreferences();
/**
* Check if the board has a sub menu.
@ -79,9 +54,7 @@ public class TargetBoard {
* The menu ID to check
* @return
*/
public boolean hasMenu(String menuId) {
return menuOptions.containsKey(menuId);
}
public boolean hasMenu(String menuId);
/**
* Returns the options available on a specific menu
@ -90,9 +63,7 @@ public class TargetBoard {
* The menu ID
* @return
*/
public PreferencesMap getMenuLabels(String menuId) {
return menuOptions.get(menuId).topLevelMap();
}
public PreferencesMap getMenuLabels(String menuId);
/**
* Returns the label of the specified option in the specified menu
@ -103,13 +74,9 @@ public class TargetBoard {
* The option ID
* @return
*/
public String getMenuLabel(String menuId, String selectionId) {
return getMenuLabels(menuId).get(selectionId);
}
public String getMenuLabel(String menuId, String selectionId);
public Set<String> getMenuIds() {
return menuOptions.keySet();
}
public Set<String> getMenuIds();
/**
* Returns the configuration parameters to override (as a PreferenceMap) when
@ -121,12 +88,8 @@ public class TargetBoard {
* The option ID
* @return
*/
public PreferencesMap getMenuPreferences(String menuId, String selectionId) {
return menuOptions.get(menuId).subTree(selectionId);
}
public PreferencesMap getMenuPreferences(String menuId, String selectionId);
public TargetPlatform getContainerPlatform() {
return containerPlatform;
}
public TargetPlatform getContainerPlatform();
}

View File

@ -1,9 +1,8 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
TargetPackage - Represents a hardware package
Part of the Arduino project - http://www.arduino.cc/
Copyright (c) 2011 Cristian Maglie
Copyright (c) 2014 Cristian Maglie
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -21,61 +20,18 @@
*/
package processing.app.debug;
import static processing.app.I18n._;
import java.io.File;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import processing.app.I18n;
import processing.app.helpers.filefilters.OnlyDirs;
public interface TargetPackage {
public class TargetPackage {
String getId();
private String id;
Map<String, TargetPlatform> getPlatforms();
Map<String, TargetPlatform> platforms = new LinkedHashMap<String, TargetPlatform>();
Collection<TargetPlatform> platforms();
public TargetPackage(String _id, File _folder) throws TargetPlatformException {
id = _id;
TargetPlatform get(String platform);
File[] folders = _folder.listFiles(new OnlyDirs());
if (folders == null)
return;
for (File subFolder : folders) {
if (!subFolder.exists() || !subFolder.canRead())
continue;
String arch = subFolder.getName();
try {
TargetPlatform platform = new TargetPlatform(arch, subFolder, this);
platforms.put(arch, platform);
} catch (TargetPlatformException e) {
System.out.println(e.getMessage());
}
}
if (platforms.size() == 0) {
throw new TargetPlatformException(I18n
.format(_("No valid hardware definitions found in folder {0}."),
_folder.getName()));
}
}
public Map<String, TargetPlatform> getPlatforms() {
return platforms;
}
public Collection<TargetPlatform> platforms() {
return platforms.values();
}
public TargetPlatform get(String platform) {
return platforms.get(platform);
}
public String getId() {
return id;
}
boolean hasPlatform(TargetPlatform platform);
}

View File

@ -1,9 +1,8 @@
/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
TargetPlatform - Represents a hardware platform
Part of the Arduino project - http://www.arduino.cc/
Copyright (c) 2009 David A. Mellis
Copyright (c) 2014 Arduino
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -21,174 +20,78 @@
*/
package processing.app.debug;
import static processing.app.I18n._;
import static processing.app.I18n.format;
import java.io.File;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import processing.app.helpers.PreferencesMap;
public class TargetPlatform {
public interface TargetPlatform {
private String id;
private File folder;
private TargetPackage containerPackage;
public String getId();
public File getFolder();
/**
* Contains preferences for every defined board
* Get TargetBoards under this TargetPlatform into a Map that maps the board
* id with the corresponding TargetBoard
*
* @return a Map<String, TargetBoard>
*/
private Map<String, TargetBoard> boards = new LinkedHashMap<String, TargetBoard>();
private TargetBoard defaultBoard;
public Map<String, TargetBoard> getBoards();
public PreferencesMap getCustomMenus();
/**
* Contains preferences for every defined programmer
* Return ids for top level menus
*
* @return a Set<String> with the ids of the top level custom menus
*/
private Map<String, PreferencesMap> programmers = new LinkedHashMap<String, PreferencesMap>();
public Set<String> getCustomMenuIds();
/**
* Contains preferences for platform
* Get preferences for all programmers
*
* @return
*/
private PreferencesMap preferences = new PreferencesMap();
public Map<String, PreferencesMap> getProgrammers();
/**
* Contains labels for top level menus
* Get preferences for a specific programmer
*
* @param programmer
* @return
*/
private PreferencesMap customMenus = new PreferencesMap();
public PreferencesMap getProgrammer(String programmer);
public TargetPlatform(String _name, File _folder, TargetPackage parent)
throws TargetPlatformException {
/**
* Get preferences for a specific tool
*
* @param tool
* @return
*/
public PreferencesMap getTool(String tool);
id = _name;
folder = _folder;
containerPackage = parent;
/**
* Return TargetPlatform preferences
*
* @return
*/
public PreferencesMap getPreferences();
// If there is no boards.txt, this is not a valid 1.5 hardware folder
File boardsFile = new File(folder, "boards.txt");
if (!boardsFile.exists() || !boardsFile.canRead())
throw new TargetPlatformException(
format(_("Could not find boards.txt in {0}. Is it pre-1.5?"),
folder.getAbsolutePath()));
/**
* Get a target board
*
* @param boardId
* @return
*/
public TargetBoard getBoard(String boardId);
// Load boards
try {
Map<String, PreferencesMap> boardsPreferences = new PreferencesMap(
boardsFile).firstLevelMap();
/**
* Get the TargetPackage that contains this TargetPlatform
*
* @return
*/
public TargetPackage getContainerPackage();
// Create custom menus for this platform
PreferencesMap menus = boardsPreferences.get("menu");
if (menus != null)
customMenus = menus.topLevelMap();
boardsPreferences.remove("menu");
// Create boards
Set<String> boardIds = boardsPreferences.keySet();
for (String boardId : boardIds) {
PreferencesMap preferences = boardsPreferences.get(boardId);
TargetBoard board = new TargetBoard(boardId, preferences, this);
boards.put(boardId, board);
// Pick the first board as default
if (defaultBoard == null)
defaultBoard = board;
}
} catch (IOException e) {
throw new TargetPlatformException(format(_("Error loading {0}"),
boardsFile.getAbsolutePath()), e);
}
File platformsFile = new File(folder, "platform.txt");
try {
if (platformsFile.exists() && platformsFile.canRead()) {
preferences.load(platformsFile);
}
} catch (IOException e) {
throw new TargetPlatformException(
format(_("Error loading {0}"), platformsFile.getAbsolutePath()), e);
}
// Allow overriding values in platform.txt. This allows changing
// platform.txt (e.g. to use a system-wide toolchain), without
// having to modify platform.txt (which, when running from git,
// prevents files being marked as changed).
File localPlatformsFile = new File(folder, "platform.local.txt");
try {
if (localPlatformsFile.exists() && localPlatformsFile.canRead()) {
preferences.load(localPlatformsFile);
}
} catch (IOException e) {
throw new TargetPlatformException(
format(_("Error loading {0}"), localPlatformsFile.getAbsolutePath()), e);
}
File progFile = new File(folder, "programmers.txt");
try {
if (progFile.exists() && progFile.canRead()) {
PreferencesMap prefs = new PreferencesMap();
prefs.load(progFile);
programmers = prefs.firstLevelMap();
}
} catch (IOException e) {
throw new TargetPlatformException(format(_("Error loading {0}"), progFile
.getAbsolutePath()), e);
}
}
public String getId() {
return id;
}
public File getFolder() {
return folder;
}
public Map<String, TargetBoard> getBoards() {
return boards;
}
public PreferencesMap getCustomMenus() {
return customMenus;
}
public Set<String> getCustomMenuIds() {
return customMenus.keySet();
}
public Map<String, PreferencesMap> getProgrammers() {
return programmers;
}
public PreferencesMap getProgrammer(String programmer) {
return getProgrammers().get(programmer);
}
public PreferencesMap getTool(String tool) {
return getPreferences().subTree("tools").subTree(tool);
}
public PreferencesMap getPreferences() {
return preferences;
}
public TargetBoard getBoard(String boardId) {
if (boards.containsKey(boardId)) {
return boards.get(boardId);
}
return defaultBoard;
}
public TargetPackage getContainerPackage() {
return containerPackage;
}
@Override
public String toString() {
String res = "TargetPlatform: name=" + id + " boards={\n";
for (String boardId : boards.keySet())
res += " " + boardId + " = " + boards.get(boardId) + "\n";
return res + "}";
}
}

View File

@ -1,13 +1,5 @@
package processing.app.helpers;
import static processing.app.I18n._;
import java.io.File;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import processing.app.BaseNoGui;
import processing.app.I18n;
import processing.app.PreferencesData;
@ -16,34 +8,59 @@ import processing.app.debug.TargetPackage;
import processing.app.debug.TargetPlatform;
import processing.app.legacy.PApplet;
import java.io.File;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import static processing.app.I18n._;
public class CommandlineParser {
protected static enum ACTION { GUI, NOOP, VERIFY, UPLOAD, GET_PREF };
private enum ACTION {
GUI, NOOP, VERIFY("--verify"), UPLOAD("--upload"), GET_PREF("--get-pref"), INSTALL_BOARD("--install-boards"), INSTALL_LIBRARY("--install-library");
private final String value;
ACTION() {
this.value = null;
}
ACTION(String value) {
this.value = value;
}
}
private ACTION action = ACTION.GUI;
private boolean doVerboseBuild = false;
private boolean doVerboseUpload = false;
private boolean doUseProgrammer = false;
private boolean preserveTempFiles;
private boolean noUploadPort = false;
private boolean forceSavePrefs = false;
private String getPref = null;
private String getPref;
private String boardToInstall;
private String libraryToInstall;
private List<String> filenames = new LinkedList<String>();
public static CommandlineParser newCommandlineParser(String[] args) {
return new CommandlineParser(args);
}
private CommandlineParser(String[] args) {
parseArguments(args);
checkAction();
}
private void parseArguments(String[] args) {
// Map of possible actions and corresponding options
final Map<String, ACTION> actions = new HashMap<String, ACTION>();
actions.put("--verify", ACTION.VERIFY);
actions.put("--upload", ACTION.UPLOAD);
actions.put("--get-pref", ACTION.GET_PREF);
actions.put("--install-boards", ACTION.INSTALL_BOARD);
actions.put("--install-library", ACTION.INSTALL_LIBRARY);
// Check if any files were passed in on the command line
for (int i = 0; i < args.length; i++) {
@ -56,10 +73,25 @@ public class CommandlineParser {
}
if (a == ACTION.GET_PREF) {
i++;
if (i >= args.length)
BaseNoGui.showError(null, _("Argument required for --get-pref"), 3);
if (i >= args.length) {
BaseNoGui.showError(null, I18n.format(_("Argument required for {0}"), a.value), 3);
}
getPref = args[i];
}
if (a == ACTION.INSTALL_BOARD) {
i++;
if (i >= args.length) {
BaseNoGui.showError(null, I18n.format(_("Argument required for {0}"), a.value), 3);
}
boardToInstall = args[i];
}
if (a == ACTION.INSTALL_LIBRARY) {
i++;
if (i >= args.length) {
BaseNoGui.showError(null, I18n.format(_("Argument required for {0}"), a.value), 3);
}
libraryToInstall = args[i];
}
action = a;
continue;
}
@ -74,6 +106,12 @@ public class CommandlineParser {
action = ACTION.NOOP;
continue;
}
if (args[i].equals("--preserve-temp-files")) {
preserveTempFiles = true;
if (action == ACTION.GUI)
action = ACTION.NOOP;
continue;
}
if (args[i].equals("--verbose-build")) {
doVerboseBuild = true;
if (action == ACTION.GUI)
@ -164,7 +202,7 @@ public class CommandlineParser {
filenames.add(args[i]);
}
}
private void checkAction() {
if ((action == ACTION.UPLOAD || action == ACTION.VERIFY) && filenames.size() != 1)
BaseNoGui.showError(null, _("Must specify exactly one sketch file"), 3);
@ -179,7 +217,7 @@ public class CommandlineParser {
private void processBoardArgument(String selectBoard) {
// No board selected? Nothing to do
if (selectBoard == null)
return;
return;
String[] split = selectBoard.split(":", 4);
@ -251,27 +289,27 @@ public class CommandlineParser {
public List<String> getFilenames() {
return filenames;
}
public boolean isGetPrefMode() {
return action == ACTION.GET_PREF;
}
public boolean isGuiMode() {
return action == ACTION.GUI;
}
public boolean isNoOpMode() {
return action == ACTION.NOOP;
}
public boolean isUploadMode() {
return action == ACTION.UPLOAD;
}
public boolean isVerifyMode() {
return action == ACTION.VERIFY;
}
public boolean isVerifyOrUploadMode() {
return isVerifyMode() || isUploadMode();
}
@ -284,4 +322,23 @@ public class CommandlineParser {
return noUploadPort;
}
public boolean isInstallBoard() {
return action == ACTION.INSTALL_BOARD;
}
public boolean isInstallLibrary() {
return action == ACTION.INSTALL_LIBRARY;
}
public String getBoardToInstall() {
return this.boardToInstall;
}
public String getLibraryToInstall() {
return libraryToInstall;
}
public boolean isPreserveTempFiles() {
return preserveTempFiles;
}
}

View File

@ -73,17 +73,28 @@ public class FileUtils {
}
public static void recursiveDelete(File file) {
if (file == null)
if (file == null) {
return;
}
if (file.isDirectory()) {
for (File current : file.listFiles())
File[] files = file.listFiles();
if (files == null) {
return;
}
for (File current : files) {
recursiveDelete(current);
}
}
file.delete();
}
public static File createTempFolder() throws IOException {
File tmpFolder = new File(System.getProperty("java.io.tmpdir"), "arduino_" + new Random().nextInt(1000000));
return createTempFolderIn(new File(System.getProperty("java.io.tmpdir")));
}
public static File createTempFolderIn(File parent) throws IOException {
File tmpFolder = new File(parent, "arduino_"
+ new Random().nextInt(1000000));
if (!tmpFolder.mkdir()) {
throw new IOException("Unable to create temp folder " + tmpFolder);
}
@ -249,5 +260,21 @@ public class FileUtils {
return result;
}
public static File newFile(File parent, String... parts) {
File result = parent;
for (String part : parts) {
result = new File(result, part);
}
return result;
}
public static boolean deleteIfExists(File file) {
if (file == null) {
return true;
}
return file.delete();
}
}

View File

@ -11,7 +11,7 @@ public abstract class NetUtils {
private static boolean isReachableByEcho(InetAddress address) {
try {
return address.isReachable(100);
return address.isReachable(300);
} catch (IOException e) {
return false;
}
@ -38,7 +38,7 @@ public abstract class NetUtils {
Socket socket = null;
try {
socket = new Socket();
socket.connect(new InetSocketAddress(address, port), 300);
socket.connect(new InetSocketAddress(address, port), 1000);
return true;
} catch (IOException e) {
return false;

View File

@ -21,18 +21,14 @@
*/
package processing.app.helpers;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import processing.app.legacy.PApplet;
import java.io.*;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import processing.app.legacy.PApplet;
@SuppressWarnings("serial")
public class PreferencesMap extends LinkedHashMap<String, String> {
@ -71,7 +67,15 @@ public class PreferencesMap extends LinkedHashMap<String, String> {
* @throws IOException
*/
public void load(File file) throws IOException {
load(new FileInputStream(file));
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(file);
load(fileInputStream);
} finally {
if (fileInputStream != null) {
fileInputStream.close();
}
}
}
protected String processPlatformSuffix(String key, String suffix, boolean isCurrentPlatform) {
@ -311,7 +315,7 @@ public class PreferencesMap extends LinkedHashMap<String, String> {
* insensitive compared), <b>false</b> in any other case
*/
public boolean getBoolean(String key) {
return new Boolean(get(key));
return Boolean.valueOf(get(key));
}
/**

View File

@ -26,4 +26,18 @@ public class StringUtils {
String regex = pattern.replace("?", ".?").replace("*", ".*?");
return input.matches(regex);
}
/**
* Returns the string without trailing whitespace characters
*
* @param s
* @return
*/
public static String rtrim(String s) {
int i = s.length() - 1;
while (i >= 0 && Character.isWhitespace(s.charAt(i))) {
i--;
}
return s.substring(0, i + 1);
}
}

View File

@ -32,11 +32,17 @@ import java.io.FilenameFilter;
*/
public class OnlyDirs implements FilenameFilter {
public boolean accept(File dir, String name) {
if (name.charAt(0) == '.')
return false;
if (name.equals("CVS"))
return false;
return new File(dir, name).isDirectory();
}
public boolean accept(File dir, String name) {
if (name.charAt(0) == '.')
return false;
if (name.equals("CVS"))
return false;
return new File(dir, name).isDirectory();
}
/**
* An handy pre-instantiated object
*/
public static final OnlyDirs ONLY_DIRS = new OnlyDirs();
}

View File

@ -29,13 +29,15 @@ public class OnlyFilesWithExtension implements FilenameFilter {
String extensions[];
public OnlyFilesWithExtension(String... ext) {
extensions = ext;
this.extensions = ext;
}
public boolean accept(File dir, String name) {
for (String ext : extensions)
if (name.endsWith(ext))
for (String ext : extensions) {
if (name.endsWith(ext)) {
return true;
}
}
return false;
}

View File

@ -1,16 +1,6 @@
package processing.app.legacy;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.io.*;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.StringTokenizer;
@ -276,15 +266,26 @@ public class PApplet {
}
static public String[] loadStrings(File file) {
InputStream is = createInput(file);
if (is != null) return loadStrings(is);
return null;
InputStream is = null;
try {
is = createInput(file);
if (is != null) return loadStrings(is);
return null;
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
// noop
}
}
}
}
static public String[] loadStrings(InputStream input) {
BufferedReader reader = null;
try {
BufferedReader reader =
new BufferedReader(new InputStreamReader(input, "UTF-8"));
reader = new BufferedReader(new InputStreamReader(input, "UTF-8"));
String lines[] = new String[100];
int lineCount = 0;
@ -297,7 +298,6 @@ public class PApplet {
}
lines[lineCount++] = line;
}
reader.close();
if (lineCount == lines.length) {
return lines;
@ -311,6 +311,15 @@ public class PApplet {
} catch (IOException e) {
e.printStackTrace();
//throw new RuntimeException("Error inside loadStrings()");
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
//ignore
}
}
}
return null;
}
@ -321,14 +330,29 @@ public class PApplet {
static public void saveStrings(File file, String strings[]) {
saveStrings(createOutput(file), strings);
OutputStream outputStream = null;
try {
outputStream = createOutput(file);
saveStrings(outputStream, strings);
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
//noop
}
}
}
}
static public void saveStrings(OutputStream output, String strings[]) {
PrintWriter writer = createWriter(output);
for (int i = 0; i < strings.length; i++) {
writer.println(strings[i]);
if (writer == null) {
return;
}
for (String string : strings) {
writer.println(string);
}
writer.flush();
writer.close();

View File

@ -0,0 +1,151 @@
/*
* This file is part of Arduino.
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2015 Arduino LLC (http://www.arduino.cc/)
*/
package processing.app.linux;
import javax.swing.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class GTKLookAndFeelFixer {
/*
* All functions of this class courtesy of Klaus Reimer
* https://www.ailis.de/~k/archives/67-Workaround-for-borderless-Java-Swing-menus-on-Linux.html
*/
/*
* This is free and unencumbered software released into the public domain.
*
* Anyone is free to copy, modify, publish, use, compile, sell, or
* distribute this software, either in source code form or as a compiled
* binary, for any purpose, commercial or non-commercial, and by any
* means.
*
* In jurisdictions that recognize copyright laws, the author or authors
* of this software dedicate any and all copyright interest in the
* software to the public domain. We make this dedication for the benefit
* of the public at large and to the detriment of our heirs and
* successors. We intend this dedication to be an overt act of
* relinquishment in perpetuity of all present and future rights to this
* software under copyright law.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* For more information, please refer to <http://unlicense.org/>
*/
/**
* Swing menus are looking pretty bad on Linux when the GTK LaF is used (See
* bug #6925412). It will most likely never be fixed anytime soon so this
* method provides a workaround for it. It uses reflection to change the GTK
* style objects of Swing so popup menu borders have a minimum thickness of
* 1 and menu separators have a minimum vertical thickness of 1.
*/
public static void installGtkPopupBugWorkaround() {
// Get current look-and-feel implementation class
LookAndFeel laf = UIManager.getLookAndFeel();
Class<?> lafClass = laf.getClass();
// Do nothing when not using the problematic LaF
if (!lafClass.getName().equals("com.sun.java.swing.plaf.gtk.GTKLookAndFeel")) {
return;
}
// We do reflection from here on. Failure is silently ignored. The
// workaround is simply not installed when something goes wrong here
try {
// Access the GTK style factory
Field field = lafClass.getDeclaredField("styleFactory");
boolean accessible = field.isAccessible();
field.setAccessible(true);
Object styleFactory = field.get(laf);
field.setAccessible(accessible);
// Fix the horizontal and vertical thickness of popup menu style
Object style = getGtkStyle(styleFactory, new JPopupMenu(), "POPUP_MENU");
fixGtkThickness(style, "yThickness");
fixGtkThickness(style, "xThickness");
// Fix the vertical thickness of the popup menu separator style
style = getGtkStyle(styleFactory, new JSeparator(), "POPUP_MENU_SEPARATOR");
fixGtkThickness(style, "yThickness");
} catch (Exception e) {
// Silently ignored. Workaround can't be applied.
}
}
/**
* Called internally by installGtkPopupBugWorkaround to fix the thickness
* of a GTK style field by setting it to a minimum value of 1.
*
* @param style The GTK style object.
* @param fieldName The field name.
* @throws Exception When reflection fails.
*/
private static void fixGtkThickness(Object style, String fieldName) throws Exception {
Field field = style.getClass().getDeclaredField(fieldName);
boolean accessible = field.isAccessible();
field.setAccessible(true);
field.setInt(style, Math.max(1, field.getInt(style)));
field.setAccessible(accessible);
}
/**
* Called internally by installGtkPopupBugWorkaround. Returns a specific
* GTK style object.
*
* @param styleFactory The GTK style factory.
* @param component The target component of the style.
* @param regionName The name of the target region of the style.
* @return The GTK style.
* @throws Exception When reflection fails.
*/
private static Object getGtkStyle(Object styleFactory, JComponent component, String regionName) throws Exception {
// Create the region object
Class<?> regionClass = Class.forName("javax.swing.plaf.synth.Region");
Field field = regionClass.getField(regionName);
Object region = field.get(regionClass);
// Get and return the style
Class<?> styleFactoryClass = styleFactory.getClass();
Method method = styleFactoryClass.getMethod("getStyle", JComponent.class, regionClass);
boolean accessible = method.isAccessible();
method.setAccessible(true);
Object style = method.invoke(styleFactory, component, region);
method.setAccessible(accessible);
return style;
}
}

View File

@ -26,10 +26,12 @@ import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.Executor;
import processing.app.PreferencesData;
import processing.app.debug.TargetPackage;
import processing.app.tools.ExternalProcessExecutor;
import processing.app.legacy.PConstants;
import processing.app.tools.CollectStdOutExecutor;
import java.io.*;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Map;
@ -42,17 +44,7 @@ public class Platform extends processing.app.Platform {
// TODO Need to be smarter here since KDE people ain't gonna like that GTK.
// It may even throw a weird exception at 'em for their trouble.
public void setLookAndFeel() throws Exception {
// Linux is by default even uglier than metal (Motif?).
// Actually, i'm using native menus, so they're even uglier
// and Motif-looking (Lesstif?). Ick. Need to fix this.
//String lfname = UIManager.getCrossPlatformLookAndFeelClassName();
//UIManager.setLookAndFeel(lfname);
// For 0120, trying out the gtk+ look and feel as the default.
// This is available in Java 1.4.2 and later, and it can't possibly
// be any worse than Metal. (Ocean might also work, but that's for
// Java 1.5, and we aren't going there yet)
//UIManager.setLookAndFeel("com.sun.java.swing.plaf.gtk.GTKLookAndFeel");
GTKLookAndFeelFixer.installGtkPopupBugWorkaround();
}
@ -130,8 +122,9 @@ public class Platform extends processing.app.Platform {
@Override
public Map<String, Object> resolveDeviceAttachedTo(String serial, Map<String, TargetPackage> packages, String devicesListOutput) {
assert packages != null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Executor executor = new ExternalProcessExecutor(baos);
Executor executor = new CollectStdOutExecutor(baos);
try {
CommandLine toDevicePath = CommandLine.parse("udevadm info -q path -n " + serial);

View File

@ -26,12 +26,12 @@ import cc.arduino.packages.BoardPort;
import com.apple.eio.FileManager;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.Executor;
import org.apache.commons.lang3.StringUtils;
import processing.app.debug.TargetPackage;
import processing.app.tools.ExternalProcessExecutor;
import processing.app.legacy.PApplet;
import processing.app.legacy.PConstants;
import processing.app.tools.CollectStdOutExecutor;
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.lang.reflect.Method;
@ -45,12 +45,9 @@ import java.util.List;
*/
public class Platform extends processing.app.Platform {
private String osArch;
public void setLookAndFeel() throws Exception {
// Use the Quaqua L & F on OS X to make JFileChooser less awful
UIManager.setLookAndFeel("ch.randelshofer.quaqua.QuaquaLookAndFeel");
// undo quaqua trying to fix the margins, since we've already
// hacked that in, bit by bit, over the years
UIManager.put("Component.visualMargin", new Insets(1, 1, 1, 1));
}
public Platform() {
@ -59,35 +56,18 @@ public class Platform extends processing.app.Platform {
Toolkit.getDefaultToolkit();
}
public void init() {
public void init() throws IOException {
System.setProperty("apple.laf.useScreenMenuBar", "true");
/*
try {
String name = "processing.app.macosx.ThinkDifferent";
Class osxAdapter = ClassLoader.getSystemClassLoader().loadClass(name);
Class[] defArgs = { Base.class };
Method registerMethod = osxAdapter.getDeclaredMethod("register", defArgs);
if (registerMethod != null) {
Object[] args = { this };
registerMethod.invoke(osxAdapter, args);
}
} catch (NoClassDefFoundError e) {
// This will be thrown first if the OSXAdapter is loaded on a system without the EAWT
// because OSXAdapter extends ApplicationAdapter in its def
System.err.println("This version of Mac OS X does not support the Apple EAWT." +
"Application Menu handling has been disabled (" + e + ")");
discoverRealOsArch();
}
} catch (ClassNotFoundException e) {
// This shouldn't be reached; if there's a problem with the OSXAdapter
// we should get the above NoClassDefFoundError first.
System.err.println("This version of Mac OS X does not support the Apple EAWT. " +
"Application Menu handling has been disabled (" + e + ")");
} catch (Exception e) {
System.err.println("Exception while loading BaseOSX:");
e.printStackTrace();
}
*/
private void discoverRealOsArch() throws IOException {
CommandLine uname = CommandLine.parse("uname -m");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
CollectStdOutExecutor executor = new CollectStdOutExecutor(baos);
executor.execute(uname);
osArch = StringUtils.trim(new String(baos.toByteArray()));
}
@ -115,7 +95,7 @@ public class Platform extends processing.app.Platform {
public void openURL(String url) throws Exception {
if (PApplet.javaVersion < 1.6f) {
if (url.startsWith("http://")) {
if (url.startsWith("http")) {
// formerly com.apple.eio.FileManager.openURL(url);
// but due to deprecation, instead loading dynamically
try {
@ -139,7 +119,7 @@ public class Platform extends processing.app.Platform {
// for Java 1.6, replacing with java.awt.Desktop.browse()
// and java.awt.Desktop.open()
if (url.startsWith("http://")) { // browse to a location
if (url.startsWith("http")) { // browse to a location
Method browseMethod =
desktopClass.getMethod("browse", new Class[] { URI.class });
browseMethod.invoke(desktop, new Object[] { new URI(url) });
@ -211,6 +191,7 @@ public class Platform extends processing.app.Platform {
@Override
public Map<String, Object> resolveDeviceAttachedTo(String serial, Map<String, TargetPackage> packages, String devicesListOutput) {
assert packages != null;
if (devicesListOutput == null) {
return super.resolveDeviceAttachedTo(serial, packages, devicesListOutput);
}
@ -231,7 +212,7 @@ public class Platform extends processing.app.Platform {
@Override
public String preListAllCandidateDevices() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Executor executor = new ExternalProcessExecutor(baos);
Executor executor = new CollectStdOutExecutor(baos);
try {
CommandLine toDevicePath = CommandLine.parse("/usr/sbin/system_profiler SPUSBDataType");
@ -250,11 +231,16 @@ public class Platform extends processing.app.Platform {
List<BoardPort> filteredPorts = new LinkedList<BoardPort>();
for (BoardPort port : ports) {
if (!port.getAddress().startsWith("/dev/cu.")) {
if (!port.getAddress().startsWith("/dev/tty.")) {
filteredPorts.add(port);
}
}
return filteredPorts;
}
@Override
public String getOsArch() {
return osArch;
}
}

View File

@ -0,0 +1,126 @@
/*
* This file is part of Arduino.
*
* Copyright 2014 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package processing.app.packages;
import java.io.File;
import java.util.Arrays;
import java.util.List;
import cc.arduino.contributions.libraries.ContributedLibraryReference;
public class LegacyUserLibrary extends UserLibrary {
private String name;
public static LegacyUserLibrary create(File libFolder) {
// construct an old style library
LegacyUserLibrary res = new LegacyUserLibrary();
res.setInstalledFolder(libFolder);
res.setInstalled(true);
res.layout = LibraryLayout.FLAT;
res.name = libFolder.getName();
return res;
}
@Override
public String getName() {
return name;
}
@Override
public List<String> getArchitectures() {
return Arrays.asList("*");
}
@Override
public String getAuthor() {
return null;
}
@Override
public String getParagraph() {
return null;
}
@Override
public String getSentence() {
return null;
}
@Override
public String getWebsite() {
return null;
}
@Override
public String getCategory() {
return "Uncategorized";
}
@Override
public String getLicense() {
return null;
}
@Override
public String getVersion() {
return null;
}
@Override
public String getMaintainer() {
return null;
}
@Override
public String getChecksum() {
return null;
}
@Override
public long getSize() {
return 0;
}
@Override
public String getUrl() {
return null;
}
@Override
public List<ContributedLibraryReference> getRequires() {
return null;
}
@Override
public String toString() {
return "LegacyLibrary:" + name + "\n";
}
}

View File

@ -1,14 +1,42 @@
/*
* This file is part of Arduino.
*
* Copyright 2014 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package processing.app.packages;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import processing.app.helpers.FileUtils;
@SuppressWarnings("serial")
public class LibraryList extends ArrayList<Library> {
public class LibraryList extends LinkedList<UserLibrary> {
public LibraryList(LibraryList libs) {
super(libs);
@ -18,53 +46,46 @@ public class LibraryList extends ArrayList<Library> {
super();
}
public Library getByName(String name) {
for (Library l : this)
public LibraryList(List<UserLibrary> ideLibs) {
super(ideLibs);
}
public UserLibrary getByName(String name) {
for (UserLibrary l : this)
if (l.getName().equals(name))
return l;
return null;
}
public void addOrReplace(Library lib) {
Library l = getByName(lib.getName());
if (l != null)
remove(l);
public void addOrReplace(UserLibrary lib) {
remove(lib);
add(lib);
}
public void addOrReplaceAll(Collection<? extends Library> c) {
for (Library l : c)
addOrReplace(l);
public void remove(UserLibrary lib) {
UserLibrary l = getByName(lib.getName());
if (l != null)
super.remove(l);
}
public void sort() {
Collections.sort(this, Library.CASE_INSENSITIVE_ORDER);
}
public Library search(String name, String arch) {
for (Library lib : this) {
if (!lib.getName().equals(name))
continue;
if (!lib.supportsArchitecture(arch))
continue;
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;
Collections.sort(this, UserLibrary.CASE_INSENSITIVE_ORDER);
}
public LibraryList filterLibrariesInSubfolder(File subFolder) {
LibraryList res = new LibraryList();
for (Library lib : this)
if (FileUtils.isSubDirectory(subFolder, lib.getFolder()))
for (UserLibrary lib : this) {
if (FileUtils.isSubDirectory(subFolder, lib.getInstalledFolder())) {
res.add(lib);
}
}
return res;
}
public boolean hasLibrary(UserLibrary lib) {
for (UserLibrary l : this)
if (l == lib) return true;
return false;
}
}

View File

@ -1,16 +1,46 @@
/*
* This file is part of Arduino.
*
* Copyright 2014 Arduino LLC (http://www.arduino.cc/)
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package processing.app.packages;
import cc.arduino.contributions.libraries.ContributedLibrary;
import cc.arduino.contributions.libraries.ContributedLibraryReference;
import processing.app.helpers.FileUtils;
import processing.app.helpers.PreferencesMap;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import processing.app.helpers.FileUtils;
import processing.app.helpers.PreferencesMap;
public class Library {
public class UserLibrary extends ContributedLibrary {
private String name;
private String version;
@ -18,44 +48,23 @@ public class Library {
private String maintainer;
private String sentence;
private String paragraph;
private String url;
private String website;
private String category;
private String license;
private List<String> architectures;
private File folder;
private boolean isLegacy;
private enum LibraryLayout { FLAT, RECURSIVE };
private LibraryLayout layout;
private List<String> types;
private List<String> declaredTypes;
private static final List<String> MANDATORY_PROPERTIES = Arrays
.asList(new String[] { "name", "version", "author", "maintainer",
"sentence", "paragraph", "url" });
.asList(new String[]{"name", "version", "author", "maintainer",
"sentence", "paragraph", "url"});
private static final List<String> CATEGORIES = Arrays.asList(new String[] {
"Display", "Communication", "Signal Input/Output", "Sensors",
"Device Control", "Timing", "Data Storage", "Data Processing", "Other",
"Uncategorized" });
private static final List<String> CATEGORIES = Arrays.asList(new String[]{
"Display", "Communication", "Signal Input/Output", "Sensors",
"Device Control", "Timing", "Data Storage", "Data Processing", "Other",
"Uncategorized"});
/**
* Scans inside a folder and create a Library object out of it. Automatically
* detects legacy libraries. Automatically fills metadata from
* library.properties file if found.
*
* @param libFolder
* @return
*/
static public Library create(File libFolder) throws IOException {
// A library is considered "new" if it contains a file called
// "library.properties"
File check = new File(libFolder, "library.properties");
if (!check.exists() || !check.isFile())
return createLegacyLibrary(libFolder);
else
return createLibrary(libFolder);
}
private static Library createLibrary(File libFolder) throws IOException {
public static UserLibrary create(File libFolder) throws IOException {
// Parse metadata
File propertiesFile = new File(libFolder, "library.properties");
PreferencesMap properties = new PreferencesMap();
@ -66,16 +75,16 @@ public class Library {
// Compatibility with 1.5 rev.1 libraries:
// "email" field changed to "maintainer"
if (!properties.containsKey("maintainer") &&
properties.containsKey("email"))
if (!properties.containsKey("maintainer") && properties.containsKey("email")) {
properties.put("maintainer", properties.get("email"));
}
// Compatibility with 1.5 rev.1 libraries:
// "arch" folder no longer supported
File archFolder = new File(libFolder, "arch");
if (archFolder.isDirectory())
throw new IOException("'arch' folder is no longer supported! See "
+ "http://goo.gl/gfFJzU for more information");
+ "http://goo.gl/gfFJzU for more information");
// Check mandatory properties
for (String p : MANDATORY_PROPERTIES)
@ -93,7 +102,7 @@ public class Library {
File utilFolder = new File(libFolder, "utility");
if (utilFolder.exists() && utilFolder.isDirectory()) {
throw new IOException(
"Library can't use both 'src' and 'utility' folders.");
"Library can't use both 'src' and 'utility' folders.");
}
} else {
// Layout with source code on library's root and "utility" folders
@ -104,8 +113,7 @@ public class Library {
for (File file : libFolder.listFiles()) {
if (file.isDirectory()) {
if (FileUtils.isSCCSOrHiddenFile(file)) {
System.out.println("WARNING: Spurious " + file.getName() +
" folder in '" + properties.get("name") + "' library");
System.out.println("WARNING: Spurious " + file.getName() + " folder in '" + properties.get("name") + "' library");
continue;
}
}
@ -123,110 +131,88 @@ public class Library {
if (category == null)
category = "Uncategorized";
if (!CATEGORIES.contains(category)) {
category = "Uncategorized";
System.out.println("WARNING: Category '" + category + "' in library " +
properties.get("name") + " is not valid. Setting to 'Uncategorized'");
properties.get("name") + " is not valid. Setting to 'Uncategorized'");
category = "Uncategorized";
}
String license = properties.get("license");
if (license == null)
if (license == null) {
license = "Unspecified";
}
Library res = new Library();
res.folder = libFolder;
String types = properties.get("types");
if (types == null) {
types = "Contributed";
}
List<String> typesList = new LinkedList<String>();
for (String type : types.split(",")) {
typesList.add(type.trim());
}
UserLibrary res = new UserLibrary();
res.setInstalledFolder(libFolder);
res.setInstalled(true);
res.name = properties.get("name").trim();
res.version = properties.get("version").trim();
res.author = properties.get("author").trim();
res.maintainer = properties.get("maintainer").trim();
res.sentence = properties.get("sentence").trim();
res.paragraph = properties.get("paragraph").trim();
res.url = properties.get("url").trim();
res.website = properties.get("url").trim();
res.category = category.trim();
res.license = license.trim();
res.architectures = archs;
res.isLegacy = false;
res.layout = layout;
res.declaredTypes = typesList;
return res;
}
private static Library createLegacyLibrary(File libFolder) {
// construct an old style library
Library res = new Library();
res.folder = libFolder;
res.layout = LibraryLayout.FLAT;
res.name = libFolder.getName();
res.architectures = Arrays.asList("*");
res.isLegacy = true;
return res;
}
/**
* Returns <b>true</b> if the library declares to support the specified
* architecture (through the "architectures" property field).
*
* @param reqArch
* @return
*/
public boolean supportsArchitecture(String reqArch) {
return architectures.contains(reqArch) || architectures.contains("*");
}
/**
* Returns <b>true</b> if the library declares to support at least one of the
* specified architectures.
*
* @param reqArchs
* A List of architectures to check
* @return
*/
public boolean supportsArchitecture(List<String> reqArchs) {
if (reqArchs.contains("*"))
return true;
for (String reqArch : reqArchs)
if (supportsArchitecture(reqArch))
return true;
return false;
}
public static final Comparator<Library> CASE_INSENSITIVE_ORDER = new Comparator<Library>() {
@Override
public int compare(Library o1, Library o2) {
return o1.getName().compareToIgnoreCase(o2.getName());
}
};
@Override
public String getName() {
return name;
}
public File getFolder() {
return folder;
}
@Override
public List<String> getArchitectures() {
return architectures;
}
@Override
public String getAuthor() {
return author;
}
@Override
public String getParagraph() {
return paragraph;
}
@Override
public String getSentence() {
return sentence;
}
public String getUrl() {
return url;
@Override
public String getWebsite() {
return website;
}
@Override
public String getCategory() {
return category;
}
@Override
public List<String> getTypes() {
return types;
}
public void setTypes(List<String> types) {
this.types = types;
}
@Override
public String getLicense() {
return license;
}
@ -235,44 +221,82 @@ public class Library {
return CATEGORIES;
}
@Override
public void setCategory(String category) {
this.category = category;
}
@Override
public String getVersion() {
return version;
}
@Override
public String getMaintainer() {
return maintainer;
}
@Override
public String getChecksum() {
return null;
}
@Override
public long getSize() {
return 0;
}
@Override
public String getUrl() {
return null;
}
@Override
public String getArchiveFileName() {
return null;
}
@Override
public List<ContributedLibraryReference> getRequires() {
return null;
}
public List<String> getDeclaredTypes() {
return declaredTypes;
}
protected enum LibraryLayout {
FLAT, RECURSIVE
}
protected LibraryLayout layout;
public File getSrcFolder() {
switch (layout) {
case FLAT:
return getInstalledFolder();
case RECURSIVE:
return new File(getInstalledFolder(), "src");
default:
return null; // Keep compiler happy :-(
}
}
public boolean useRecursion() {
return (layout == LibraryLayout.RECURSIVE);
}
public File getSrcFolder() {
switch (layout) {
case FLAT:
return folder;
case RECURSIVE:
return new File(folder, "src");
default:
return null; // Keep compiler happy :-(
}
}
public boolean isLegacy() {
return isLegacy;
}
@Override
public String toString() {
String res = "Library:";
res += " (name=" + name + ")";
res += " (version=" + version + ")";
res += " (author=" + author + ")";
res += " (maintainer=" + maintainer + ")";
res += " (sentence=" + sentence + ")";
res += " (paragraph=" + paragraph + ")";
res += " (url=" + url + ")";
res += " (architectures=" + architectures + ")";
String res = "Library: " + name + "\n";
res += " (version=" + version + ")\n";
res += " (author=" + author + ")\n";
res += " (maintainer=" + maintainer + ")\n";
res += " (sentence=" + sentence + ")\n";
res += " (paragraph=" + paragraph + ")\n";
res += " (url=" + website + ")\n";
res += " (architectures=" + architectures + ")\n";
return res;
}
}

View File

@ -42,6 +42,9 @@ import java.util.regex.*;
* Class that orchestrates preprocessing p5 syntax into straight Java.
*/
public class PdePreprocessor {
private static final String IMPORT_REGEX = "^\\s*#include\\s*[<\"](\\S+)[\">]";
// stores number of built user-defined function prototypes
public int prototypeCount = 0;
@ -94,10 +97,9 @@ public class PdePreprocessor {
}
//String importRegexp = "(?:^|\\s|;)(import\\s+)(\\S+)(\\s*;)";
String importRegexp = "^\\s*#include\\s*[<\"](\\S+)[\">]";
programImports = new ArrayList<String>();
String[][] pieces = PApplet.matchAll(program, importRegexp);
String[][] pieces = PApplet.matchAll(program, IMPORT_REGEX);
if (pieces != null)
for (int i = 0; i < pieces.length; i++)
@ -121,6 +123,19 @@ public class PdePreprocessor {
return headerCount + prototypeCount;
}
public static List<String> findIncludes(String code){
String[][] pieces = PApplet.matchAll(code, IMPORT_REGEX);
ArrayList programImports = new ArrayList<String>();
if (pieces != null)
for (int i = 0; i < pieces.length; i++)
programImports.add(pieces[i][1]); // the package name
return programImports;
}
static String substituteUnicode(String program) {
// check for non-ascii chars (these will be/must be in unicode format)

View File

@ -10,9 +10,9 @@ import java.io.OutputStream;
/**
* Handy process executor, collecting stdout into a given OutputStream
*/
public class ExternalProcessExecutor extends DefaultExecutor {
public class CollectStdOutExecutor extends DefaultExecutor {
public ExternalProcessExecutor(final OutputStream os) {
public CollectStdOutExecutor(final OutputStream stdout) {
this.setStreamHandler(new ExecuteStreamHandler() {
@Override
public void setProcessInputStream(OutputStream outputStream) throws IOException {
@ -27,7 +27,7 @@ public class ExternalProcessExecutor extends DefaultExecutor {
byte[] buf = new byte[4096];
int bytes = -1;
while ((bytes = inputStream.read(buf)) != -1) {
os.write(buf, 0, bytes);
stdout.write(buf, 0, bytes);
}
}

View File

@ -0,0 +1,49 @@
package processing.app.tools;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.ExecuteStreamHandler;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Handy process executor, collecting stdout and stderr into given OutputStreams
*/
public class CollectStdOutStdErrExecutor extends DefaultExecutor {
public CollectStdOutStdErrExecutor(final OutputStream stdout, final OutputStream stderr) {
this.setStreamHandler(new ExecuteStreamHandler() {
@Override
public void setProcessInputStream(OutputStream outputStream) throws IOException {
}
@Override
public void setProcessErrorStream(InputStream inputStream) throws IOException {
byte[] buf = new byte[4096];
int bytes = -1;
while ((bytes = inputStream.read(buf)) != -1) {
stderr.write(buf, 0, bytes);
}
}
@Override
public void setProcessOutputStream(InputStream inputStream) throws IOException {
byte[] buf = new byte[4096];
int bytes = -1;
while ((bytes = inputStream.read(buf)) != -1) {
stdout.write(buf, 0, bytes);
}
}
@Override
public void start() throws IOException {
}
@Override
public void stop() {
}
});
}
}

View File

@ -0,0 +1,35 @@
package processing.app.tools;
import org.apache.commons.exec.CommandLine;
import processing.app.helpers.OSUtils;
import java.io.File;
public class DoubleQuotedArgumentsOnWindowsCommandLine extends CommandLine {
public DoubleQuotedArgumentsOnWindowsCommandLine(String executable) {
super(executable);
}
public DoubleQuotedArgumentsOnWindowsCommandLine(File executable) {
super(executable);
}
public DoubleQuotedArgumentsOnWindowsCommandLine(CommandLine other) {
super(other);
}
@Override
public CommandLine addArgument(String argument, boolean handleQuoting) {
// Brutal hack to workaround windows command line parsing.
// http://stackoverflow.com/questions/5969724/java-runtime-exec-fails-to-escape-characters-properly
// http://msdn.microsoft.com/en-us/library/a1y7w461.aspx
// http://bugs.sun.com/view_bug.do?bug_id=6468220
// http://bugs.sun.com/view_bug.do?bug_id=6518827
if (argument.contains("\"") && OSUtils.isWindows()) {
argument = argument.replace("\"", "\\\"");
}
return super.addArgument(argument, handleQuoting);
}
}

View File

@ -22,23 +22,21 @@
package processing.app.windows;
import com.sun.jna.Library;
import com.sun.jna.Native;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.Executor;
import processing.app.PreferencesData;
import processing.app.debug.TargetPackage;
import processing.app.legacy.PApplet;
import processing.app.legacy.PConstants;
import processing.app.tools.ExternalProcessExecutor;
import processing.app.tools.CollectStdOutExecutor;
import processing.app.windows.Registry.REGISTRY_ROOT_KEY;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@ -55,7 +53,7 @@ public class Platform extends processing.app.Platform {
"\\arduino.exe \"%1\"";
static final String DOC = "Arduino.Document";
public void init() {
public void init() throws IOException {
super.init();
checkAssociations();
@ -244,7 +242,7 @@ public class Platform extends processing.app.Platform {
// "Access is denied" in both cygwin and the "dos" prompt.
//Runtime.getRuntime().exec("cmd /c " + currentDir + "\\reference\\" +
// referenceFile + ".html");
if (url.startsWith("http://")) {
if (url.startsWith("http")) {
// open dos prompt, give it 'start' command, which will
// open the url properly. start by itself won't work since
// it appears to need cmd
@ -281,36 +279,6 @@ public class Platform extends processing.app.Platform {
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
// Code partially thanks to Richard Quirk from:
// http://quirkygba.blogspot.com/2009/11/setting-environment-variables-in-java.html
static WinLibC clib = (WinLibC) Native.loadLibrary("msvcrt", WinLibC.class);
public interface WinLibC extends Library {
//WinLibC INSTANCE = (WinLibC) Native.loadLibrary("msvcrt", WinLibC.class);
//libc = Native.loadLibrary("msvcrt", WinLibC.class);
public int _putenv(String name);
}
public void setenv(String variable, String value) {
//WinLibC clib = WinLibC.INSTANCE;
clib._putenv(variable + "=" + value);
}
public String getenv(String variable) {
return System.getenv(variable);
}
public int unsetenv(String variable) {
//WinLibC clib = WinLibC.INSTANCE;
//clib._putenv(variable + "=");
//return 0;
return clib._putenv(variable + "=");
}
@Override
public String getName() {
return PConstants.platformNames[PConstants.WINDOWS];
@ -318,6 +286,7 @@ public class Platform extends processing.app.Platform {
@Override
public Map<String, Object> resolveDeviceAttachedTo(String serial, Map<String, TargetPackage> packages, String devicesListOutput) {
assert packages != null;
if (devicesListOutput == null) {
return super.resolveDeviceAttachedTo(serial, packages, devicesListOutput);
}
@ -338,7 +307,7 @@ public class Platform extends processing.app.Platform {
@Override
public String preListAllCandidateDevices() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Executor executor = new ExternalProcessExecutor(baos);
Executor executor = new CollectStdOutExecutor(baos);
try {
String listComPorts = new File(System.getProperty("user.dir"), "hardware/tools/listComPorts.exe").getCanonicalPath();
@ -350,4 +319,24 @@ public class Platform extends processing.app.Platform {
return super.preListAllCandidateDevices();
}
}
@Override
public void fixPrefsFilePermissions(File prefsFile) throws IOException {
//noop
}
public List<File> postInstallScripts(File folder) {
List<File> scripts = new LinkedList<File>();
scripts.add(new File(folder, "post_install.bat"));
return scripts;
}
public void symlink(File something, File somewhere) throws IOException, InterruptedException {
}
public void link(File something, File somewhere) throws IOException, InterruptedException {
}
public void chmod(File file, int mode) throws IOException, InterruptedException {
}
}