mirror of
https://github.com/arduino/Arduino.git
synced 2025-02-13 07:54:20 +01:00
997 lines
35 KiB
Java
997 lines
35 KiB
Java
package processing.app;
|
|
|
|
import cc.arduino.Constants;
|
|
import cc.arduino.contributions.GPGDetachedSignatureVerifier;
|
|
import cc.arduino.contributions.SignatureVerificationFailedException;
|
|
import cc.arduino.contributions.VersionComparator;
|
|
import cc.arduino.contributions.libraries.LibrariesIndexer;
|
|
import cc.arduino.contributions.packages.ContributedPlatform;
|
|
import cc.arduino.contributions.packages.ContributedTool;
|
|
import cc.arduino.contributions.packages.ContributionsIndexer;
|
|
import cc.arduino.packages.DiscoveryManager;
|
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
import org.apache.commons.compress.utils.IOUtils;
|
|
import org.apache.commons.logging.impl.LogFactoryImpl;
|
|
import org.apache.commons.logging.impl.NoOpLog;
|
|
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 cc.arduino.files.DeleteFilesOnShutdown;
|
|
import processing.app.helpers.FileUtils;
|
|
|
|
import java.beans.PropertyChangeListener;
|
|
import java.beans.PropertyChangeSupport;
|
|
import java.io.File;
|
|
import java.io.FileWriter;
|
|
import java.io.IOException;
|
|
import java.util.*;
|
|
import java.util.logging.Level;
|
|
import java.util.logging.Logger;
|
|
|
|
import cc.arduino.packages.BoardPort;
|
|
|
|
import static processing.app.I18n.tr;
|
|
import static processing.app.helpers.filefilters.OnlyDirs.ONLY_DIRS;
|
|
|
|
public class BaseNoGui {
|
|
|
|
/** Version string to be used for build */
|
|
public static final int REVISION = 10803;
|
|
/** Extended version string displayed on GUI */
|
|
public static final String VERSION_NAME = "1.8.3";
|
|
public static final String VERSION_NAME_LONG;
|
|
|
|
// Current directory to use for relative paths specified on the
|
|
// commandline
|
|
static String currentDirectory = System.getProperty("user.dir");
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
File windowsStoreConfig = new File(getContentFile("lib"), "windowsStore.txt");
|
|
if (windowsStoreConfig.exists()) {
|
|
try {
|
|
PreferencesMap conf = new PreferencesMap(windowsStoreConfig);
|
|
PreferencesData.setBoolean("runtime.is-windows-store-app", true);
|
|
PreferencesData.set("runtime.windows-store-app.id", conf.get("appid"));
|
|
versionNameLong += " (Windows Store " + conf.get("version") + ")";
|
|
} catch (IOException e1) {
|
|
e1.printStackTrace();
|
|
}
|
|
}
|
|
|
|
VERSION_NAME_LONG = versionNameLong;
|
|
}
|
|
|
|
private static DiscoveryManager discoveryManager;
|
|
|
|
// these are static because they're used by Sketch
|
|
static private File examplesFolder;
|
|
static private File toolsFolder;
|
|
|
|
// maps #included files to their library folder
|
|
public static Map<String, LibraryList> importToLibraryTable;
|
|
|
|
// XXX: Remove this field
|
|
static private List<File> librariesFolders;
|
|
|
|
static UserNotifier notifier = new BasicUserNotifier();
|
|
|
|
static public Map<String, TargetPackage> packages;
|
|
|
|
static Platform platform;
|
|
|
|
static File portableFolder = null;
|
|
static final String portableSketchbookFolder = "sketchbook";
|
|
|
|
public static ContributionsIndexer indexer;
|
|
public static LibrariesIndexer librariesIndexer;
|
|
|
|
private static String boardManagerLink = "";
|
|
|
|
private static File buildCache;
|
|
|
|
// 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
|
|
// current working directory!).
|
|
static public File absoluteFile(String path) {
|
|
if (path == null) return null;
|
|
|
|
File file = new File(path);
|
|
if (!file.isAbsolute()) {
|
|
file = new File(currentDirectory, path);
|
|
}
|
|
return file;
|
|
}
|
|
|
|
/**
|
|
* Get the number of lines in a file by counting the number of newline
|
|
* characters inside a String (and adding 1).
|
|
*/
|
|
static public int countLines(String what) {
|
|
int count = 1;
|
|
for (char c : what.toCharArray()) {
|
|
if (c == '\n') count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static public PreferencesMap getBoardPreferences() {
|
|
TargetBoard board = getTargetBoard();
|
|
if (board == null)
|
|
return null;
|
|
String boardId = board.getId();
|
|
|
|
PreferencesMap prefs = new PreferencesMap(board.getPreferences());
|
|
|
|
String extendedName = prefs.get("name");
|
|
for (String menuId : board.getMenuIds()) {
|
|
if (!board.hasMenu(menuId))
|
|
continue;
|
|
|
|
// Get "custom_[MENU_ID]" preference (for example "custom_cpu")
|
|
String entry = PreferencesData.get("custom_" + menuId);
|
|
if (entry != null && entry.startsWith(boardId)) {
|
|
|
|
String selectionId = entry.substring(boardId.length() + 1);
|
|
prefs.putAll(board.getMenuPreferences(menuId, selectionId));
|
|
|
|
// Update the name with the extended configuration
|
|
extendedName += ", " + board.getMenuLabel(menuId, selectionId);
|
|
}
|
|
}
|
|
prefs.put("name", extendedName);
|
|
|
|
// Resolve tools needed for this board
|
|
List<ContributedTool> requiredTools = new ArrayList<>();
|
|
|
|
// Add all tools dependencies specified in package index
|
|
ContributedPlatform p = indexer.getContributedPlaform(getTargetPlatform());
|
|
if (p != null)
|
|
requiredTools.addAll(p.getResolvedTools());
|
|
|
|
// Add all tools dependencies from the (possibily) referenced core
|
|
String core = prefs.get("build.core");
|
|
if (core != null && core.contains(":")) {
|
|
String split[] = core.split(":");
|
|
TargetPlatform referenced = BaseNoGui.getCurrentTargetPlatformFromPackage(split[0]);
|
|
if (referenced != null) {
|
|
ContributedPlatform referencedPlatform = indexer.getContributedPlaform(referenced);
|
|
if (referencedPlatform != null)
|
|
requiredTools.addAll(referencedPlatform.getResolvedTools());
|
|
} else {
|
|
String msg = tr("The current selected board needs the core '{0}' that is not installed.");
|
|
System.out.println(I18n.format(msg, core));
|
|
}
|
|
}
|
|
|
|
String prefix = "runtime.tools.";
|
|
for (ContributedTool tool : requiredTools) {
|
|
File folder = tool.getDownloadableContribution(getPlatform()).getInstalledFolder();
|
|
if (folder == null) {
|
|
continue;
|
|
}
|
|
String toolPath = folder.getAbsolutePath();
|
|
prefs.put(prefix + tool.getName() + ".path", toolPath);
|
|
PreferencesData.set(prefix + tool.getName() + ".path", toolPath);
|
|
PreferencesData.set(prefix + tool.getName() + "-" + tool.getVersion() + ".path", toolPath);
|
|
}
|
|
return prefs;
|
|
}
|
|
|
|
static public File getContentFile(String name) {
|
|
String appDir = System.getProperty("APP_DIR");
|
|
if (appDir == null || appDir.length() == 0) {
|
|
appDir = currentDirectory;
|
|
}
|
|
File installationFolder = new File(appDir);
|
|
return new File(installationFolder, name);
|
|
}
|
|
|
|
static public TargetPlatform getCurrentTargetPlatformFromPackage(String pack) {
|
|
return getTargetPlatform(pack, PreferencesData.get("target_platform"));
|
|
}
|
|
|
|
static public File getDefaultSketchbookFolder() {
|
|
if (getPortableFolder() != null)
|
|
return new File(getPortableFolder(), getPortableSketchbookFolder());
|
|
|
|
File sketchbookFolder = null;
|
|
try {
|
|
sketchbookFolder = getPlatform().getDefaultSketchbookFolder();
|
|
} catch (Exception e) { }
|
|
|
|
return sketchbookFolder;
|
|
}
|
|
|
|
public static DiscoveryManager getDiscoveryManager() {
|
|
if (discoveryManager == null) {
|
|
discoveryManager = new DiscoveryManager();
|
|
}
|
|
return discoveryManager;
|
|
}
|
|
|
|
static public File getExamplesFolder() {
|
|
return examplesFolder;
|
|
}
|
|
|
|
static public String getExamplesPath() {
|
|
return examplesFolder.getAbsolutePath();
|
|
}
|
|
|
|
static public File getHardwareFolder() {
|
|
// calculate on the fly because it's needed by Preferences.init() to find
|
|
// the boards.txt and programmers.txt preferences files (which happens
|
|
// before the other folders / paths get cached).
|
|
return getContentFile("hardware");
|
|
}
|
|
|
|
static public String getHardwarePath() {
|
|
return getHardwareFolder().getAbsolutePath();
|
|
}
|
|
|
|
static public List<File> getLibrariesPath() {
|
|
return librariesFolders;
|
|
}
|
|
|
|
static public Platform getPlatform() {
|
|
return platform;
|
|
}
|
|
|
|
static public File getPortableFolder() {
|
|
return portableFolder;
|
|
}
|
|
|
|
static public String getPortableSketchbookFolder() {
|
|
return portableSketchbookFolder;
|
|
}
|
|
|
|
static public File getCachePath() {
|
|
if (buildCache == null) {
|
|
try {
|
|
buildCache = FileUtils.createTempFolder("arduino_cache_");
|
|
DeleteFilesOnShutdown.add(buildCache);
|
|
} catch (IOException e) {
|
|
return null;
|
|
}
|
|
}
|
|
return buildCache;
|
|
}
|
|
|
|
/**
|
|
* Convenience method to get a File object for the specified filename inside
|
|
* the settings folder.
|
|
* For now, only used by Preferences to get the preferences.txt file.
|
|
* @param filename A file inside the settings folder.
|
|
* @return filename wrapped as a File object inside the settings folder
|
|
*/
|
|
static public File getSettingsFile(String filename) {
|
|
return new File(getSettingsFolder(), filename);
|
|
}
|
|
|
|
static public File getSettingsFolder() {
|
|
if (getPortableFolder() != null)
|
|
return getPortableFolder();
|
|
|
|
File settingsFolder = null;
|
|
|
|
String preferencesPath = PreferencesData.get("settings.path");
|
|
if (preferencesPath != null) {
|
|
settingsFolder = absoluteFile(preferencesPath);
|
|
|
|
} else {
|
|
try {
|
|
settingsFolder = getPlatform().getSettingsFolder();
|
|
} catch (Exception e) {
|
|
showError(tr("Problem getting data folder"),
|
|
tr("Error getting the Arduino data folder."), e);
|
|
}
|
|
}
|
|
|
|
// create the folder if it doesn't exist already
|
|
if (!settingsFolder.exists()) {
|
|
if (!settingsFolder.mkdirs()) {
|
|
showError(tr("Settings issues"),
|
|
tr("Arduino cannot run because it could not\n" +
|
|
"create a folder to store your settings."), null);
|
|
}
|
|
}
|
|
return settingsFolder;
|
|
}
|
|
|
|
static public File getSketchbookFolder() {
|
|
String sketchBookPath = PreferencesData.get("sketchbook.path");
|
|
if (getPortableFolder() != null && !new File(sketchBookPath).isAbsolute()) {
|
|
return new File(getPortableFolder(), sketchBookPath);
|
|
}
|
|
return absoluteFile(sketchBookPath);
|
|
}
|
|
|
|
static public File getSketchbookHardwareFolder() {
|
|
return new File(getSketchbookFolder(), "hardware");
|
|
}
|
|
|
|
static public File getSketchbookLibrariesFolder() {
|
|
File libdir = new File(getSketchbookFolder(), "libraries");
|
|
if (!libdir.exists()) {
|
|
FileWriter freadme = null;
|
|
try {
|
|
libdir.mkdirs();
|
|
freadme = new FileWriter(new File(libdir, "readme.txt"));
|
|
freadme.write(tr("For information on installing libraries, see: " +
|
|
"http://www.arduino.cc/en/Guide/Libraries\n"));
|
|
} catch (Exception e) {
|
|
} finally {
|
|
IOUtils.closeQuietly(freadme);
|
|
}
|
|
}
|
|
return libdir;
|
|
}
|
|
|
|
static public String getSketchbookPath() {
|
|
// Get the sketchbook path, and make sure it's set properly
|
|
String sketchbookPath = PreferencesData.get("sketchbook.path");
|
|
|
|
// If a value is at least set, first check to see if the folder exists.
|
|
// If it doesn't, warn the user that the sketchbook folder is being reset.
|
|
if (sketchbookPath != null) {
|
|
File sketchbookFolder;
|
|
if (getPortableFolder() != null && !new File(sketchbookPath).isAbsolute()) {
|
|
sketchbookFolder = new File(getPortableFolder(), sketchbookPath);
|
|
} else {
|
|
sketchbookFolder = absoluteFile(sketchbookPath);
|
|
}
|
|
if (!sketchbookFolder.exists()) {
|
|
showWarning(tr("Sketchbook folder disappeared"),
|
|
tr("The sketchbook folder no longer exists.\n" +
|
|
"Arduino will switch to the default sketchbook\n" +
|
|
"location, and create a new sketchbook folder if\n" +
|
|
"necessary. Arduino will then stop talking about\n" +
|
|
"himself in the third person."), null);
|
|
sketchbookPath = null;
|
|
}
|
|
}
|
|
|
|
return sketchbookPath;
|
|
}
|
|
|
|
public static TargetBoard getTargetBoard() {
|
|
TargetPlatform targetPlatform = getTargetPlatform();
|
|
if (targetPlatform == null)
|
|
return null;
|
|
String boardId = PreferencesData.get("board");
|
|
return targetPlatform.getBoard(boardId);
|
|
}
|
|
|
|
/**
|
|
* Returns a specific TargetPackage
|
|
*
|
|
* @param packageName
|
|
* @return
|
|
*/
|
|
static public TargetPackage getTargetPackage(String packageName) {
|
|
return packages.get(packageName);
|
|
}
|
|
|
|
/**
|
|
* Returns the currently selected TargetPlatform.
|
|
*
|
|
* @return
|
|
*/
|
|
static public TargetPlatform getTargetPlatform() {
|
|
String packageName = PreferencesData.get("target_package");
|
|
String platformName = PreferencesData.get("target_platform");
|
|
return getTargetPlatform(packageName, platformName);
|
|
}
|
|
|
|
/**
|
|
* Returns a specific TargetPlatform searching Package/Platform
|
|
*
|
|
* @param packageName
|
|
* @param platformName
|
|
* @return
|
|
*/
|
|
static public TargetPlatform getTargetPlatform(String packageName,
|
|
String platformName) {
|
|
TargetPackage p = packages.get(packageName);
|
|
if (p == null)
|
|
return null;
|
|
return p.get(platformName);
|
|
}
|
|
|
|
static public File getToolsFolder() {
|
|
return toolsFolder;
|
|
}
|
|
|
|
static public String getToolsPath() {
|
|
return toolsFolder.getAbsolutePath();
|
|
}
|
|
|
|
static public LibraryList getUserLibs() {
|
|
LibraryList libs = BaseNoGui.librariesIndexer.getInstalledLibraries();
|
|
return libs.filterLibrariesInSubfolder(getSketchbookFolder());
|
|
}
|
|
|
|
static public String getBoardManagerLink() {
|
|
return boardManagerLink;
|
|
}
|
|
|
|
protected static PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(BaseNoGui.class);
|
|
|
|
public static void setBoardManagerLink(String temp) {
|
|
boardManagerLink = temp;
|
|
propertyChangeSupport.firePropertyChange("boardManagerLink", "", boardManagerLink);
|
|
}
|
|
|
|
public static void addPropertyChangeListener(PropertyChangeListener listener) {
|
|
propertyChangeSupport.addPropertyChangeListener(listener);
|
|
}
|
|
|
|
/**
|
|
* Given a folder, return a list of the header files in that folder (but not
|
|
* the header files in its sub-folders, as those should be included from
|
|
* within the header files at the top-level).
|
|
*/
|
|
static public String[] headerListFromIncludePath(File path) throws IOException {
|
|
String[] list = path.list(new OnlyFilesWithExtension(".h", ".hh", ".hpp"));
|
|
if (list == null) {
|
|
throw new IOException();
|
|
}
|
|
return list;
|
|
}
|
|
|
|
protected static void dumpPrefs(CommandlineParser parser) {
|
|
if (parser.getGetPref() != null) {
|
|
String value = PreferencesData.get(parser.getGetPref(), null);
|
|
if (value != null) {
|
|
System.out.println(value);
|
|
System.exit(0);
|
|
} else {
|
|
System.exit(4);
|
|
}
|
|
} else {
|
|
System.out.println("#PREFDUMP#");
|
|
PreferencesMap prefs = PreferencesData.getMap();
|
|
for (Map.Entry<String, String> entry : prefs.entrySet()) {
|
|
System.out.println(entry.getKey() + "=" + entry.getValue());
|
|
}
|
|
System.exit(0);
|
|
}
|
|
}
|
|
|
|
static public void initLogger() {
|
|
System.setProperty(LogFactoryImpl.LOG_PROPERTY, NoOpLog.class.getCanonicalName());
|
|
Logger.getLogger("javax.jmdns").setLevel(Level.OFF);
|
|
}
|
|
|
|
static public void initPackages() throws Exception {
|
|
indexer = new ContributionsIndexer(getSettingsFolder(), getHardwareFolder(), getPlatform(),
|
|
new GPGDetachedSignatureVerifier());
|
|
|
|
try {
|
|
indexer.parseIndex();
|
|
} catch (JsonProcessingException | SignatureVerificationFailedException e) {
|
|
File indexFile = indexer.getIndexFile(Constants.DEFAULT_INDEX_FILE_NAME);
|
|
File indexSignatureFile = indexer.getIndexFile(Constants.DEFAULT_INDEX_FILE_NAME + ".sig");
|
|
FileUtils.deleteIfExists(indexFile);
|
|
FileUtils.deleteIfExists(indexSignatureFile);
|
|
throw e;
|
|
}
|
|
indexer.syncWithFilesystem();
|
|
|
|
packages = new LinkedHashMap<>();
|
|
loadHardware(getHardwareFolder());
|
|
loadContributedHardware(indexer);
|
|
loadHardware(getSketchbookHardwareFolder());
|
|
createToolPreferences(indexer.getInstalledTools(), true);
|
|
|
|
librariesIndexer = new LibrariesIndexer(getSettingsFolder());
|
|
try {
|
|
librariesIndexer.parseIndex();
|
|
} catch (JsonProcessingException e) {
|
|
File librariesIndexFile = librariesIndexer.getIndexFile();
|
|
FileUtils.deleteIfExists(librariesIndexFile);
|
|
}
|
|
|
|
if (discoveryManager == null) {
|
|
discoveryManager = new DiscoveryManager();
|
|
}
|
|
}
|
|
|
|
static protected void initPlatform() {
|
|
try {
|
|
Class<?> platformClass = Class.forName("processing.app.Platform");
|
|
if (OSUtils.isMacOS()) {
|
|
platformClass = Class.forName("processing.app.macosx.Platform");
|
|
} else if (OSUtils.isWindows()) {
|
|
platformClass = Class.forName("processing.app.windows.Platform");
|
|
} else if (OSUtils.isLinux()) {
|
|
platformClass = Class.forName("processing.app.linux.Platform");
|
|
}
|
|
platform = (Platform) platformClass.newInstance();
|
|
} catch (Exception e) {
|
|
showError(tr("Problem Setting the Platform"),
|
|
tr("An unknown error occurred while trying to load\n" +
|
|
"platform-specific code for your machine."), e);
|
|
}
|
|
}
|
|
|
|
static public void initPortableFolder() {
|
|
// Portable folder
|
|
portableFolder = getContentFile("portable");
|
|
if (!portableFolder.exists()) {
|
|
portableFolder = null;
|
|
}
|
|
}
|
|
|
|
static public void initVersion() {
|
|
// help 3rd party installers find the correct hardware path
|
|
PreferencesData.set("last.ide." + VERSION_NAME + ".hardwarepath", getHardwarePath());
|
|
PreferencesData.set("last.ide." + VERSION_NAME + ".daterun", "" + (new Date()).getTime() / 1000);
|
|
}
|
|
|
|
/**
|
|
* Return true if the name is valid for a Processing sketch.
|
|
*/
|
|
static public boolean isSanitaryName(String name) {
|
|
return sanitizeName(name).equals(name);
|
|
}
|
|
|
|
static protected void loadHardware(File folder) {
|
|
if (!folder.isDirectory()) {
|
|
return;
|
|
}
|
|
|
|
String list[] = folder.list(new OnlyDirs());
|
|
|
|
// if a bad folder or something like that, this might come back null
|
|
if (list == null) {
|
|
return;
|
|
}
|
|
|
|
// alphabetize list, since it's not always alpha order
|
|
// replaced hella slow bubble sort with this feller for 0093
|
|
Arrays.sort(list, String.CASE_INSENSITIVE_ORDER);
|
|
|
|
for (String target : list) {
|
|
// Skip reserved 'tools' folder.
|
|
if (target.equals("tools")) {
|
|
continue;
|
|
}
|
|
File subfolder = new File(folder, target);
|
|
|
|
TargetPackage targetPackage;
|
|
if (packages.containsKey(target)) {
|
|
targetPackage = packages.get(target);
|
|
} else {
|
|
targetPackage = new LegacyTargetPackage(target);
|
|
packages.put(target, targetPackage);
|
|
}
|
|
try {
|
|
loadTargetPackage(targetPackage, subfolder);
|
|
} catch (TargetPlatformException e) {
|
|
System.out.println("WARNING: Error loading hardware folder " + new File(folder, target));
|
|
System.out.println(" " + e.getMessage());
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void loadTargetPackage(TargetPackage targetPackage, File _folder) throws TargetPlatformException {
|
|
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 p = new LegacyTargetPlatform(arch, subFolder, targetPackage);
|
|
targetPackage.getPlatforms().put(arch, p);
|
|
} catch (TargetPlatformException e) {
|
|
System.err.println(e.getMessage());
|
|
}
|
|
}
|
|
|
|
if (targetPackage.getPlatforms().size() == 0) {
|
|
throw new TargetPlatformException(I18n.format(tr("No valid hardware definitions found in folder {0}."), _folder.getName()));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Grab the contents of a file as a string.
|
|
*/
|
|
static public String loadFile(File file) throws IOException {
|
|
String[] contents = PApplet.loadStrings(file);
|
|
if (contents == null) return null;
|
|
return PApplet.join(contents, "\n");
|
|
}
|
|
|
|
public static void checkInstallationFolder() {
|
|
if (isIDEInstalledIntoSettingsFolder()) {
|
|
showError(tr("Incorrect IDE installation folder"), tr("Your copy of the IDE is installed in a subfolder of your settings folder.\nPlease move the IDE to another folder."), 10);
|
|
}
|
|
if (isIDEInstalledIntoSketchbookFolder()) {
|
|
showError(tr("Incorrect IDE installation folder"), tr("Your copy of the IDE is installed in a subfolder of your sketchbook.\nPlease move the IDE to another folder."), 10);
|
|
}
|
|
}
|
|
|
|
public static boolean isIDEInstalledIntoSketchbookFolder() {
|
|
return PreferencesData.has("sketchbook.path") && FileUtils.isSubDirectory(new File(PreferencesData.get("sketchbook.path")), new File(PreferencesData.get("runtime.ide.path")));
|
|
}
|
|
|
|
public static boolean isIDEInstalledIntoSettingsFolder() {
|
|
try {
|
|
return FileUtils.isSubDirectory(BaseNoGui.getPlatform().getSettingsFolder(), new File(PreferencesData.get("runtime.ide.path")));
|
|
} catch (Exception e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static public void onBoardOrPortChange() {
|
|
examplesFolder = getContentFile("examples");
|
|
toolsFolder = getContentFile("tools");
|
|
librariesFolders = new ArrayList<>();
|
|
|
|
// Add IDE libraries folder
|
|
librariesFolders.add(getContentFile("libraries"));
|
|
|
|
TargetPlatform targetPlatform = getTargetPlatform();
|
|
if (targetPlatform != null) {
|
|
String core = getBoardPreferences().get("build.core", "arduino");
|
|
if (core.contains(":")) {
|
|
String referencedCore = core.split(":")[0];
|
|
TargetPlatform referencedPlatform = getTargetPlatform(referencedCore, targetPlatform.getId());
|
|
if (referencedPlatform != null) {
|
|
File referencedPlatformFolder = referencedPlatform.getFolder();
|
|
// Add libraries folder for the referenced platform
|
|
File folder = new File(referencedPlatformFolder, "libraries");
|
|
librariesFolders.add(folder);
|
|
}
|
|
}
|
|
File platformFolder = targetPlatform.getFolder();
|
|
// 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.
|
|
librariesIndexer.setSketchbookLibrariesFolder(getSketchbookLibrariesFolder());
|
|
librariesIndexer.setLibrariesFolders(librariesFolders);
|
|
librariesIndexer.rescanLibraries();
|
|
|
|
populateImportToLibraryTable();
|
|
}
|
|
|
|
static protected void loadContributedHardware(ContributionsIndexer idx) {
|
|
for (TargetPackage pack : idx.createTargetPackages()) {
|
|
packages.put(pack.getId(), pack);
|
|
}
|
|
}
|
|
|
|
public static void createToolPreferences(Collection<ContributedTool> installedTools, boolean removeOldKeys) {
|
|
String prefix = "runtime.tools.";
|
|
if (removeOldKeys) {
|
|
PreferencesData.removeAllKeysWithPrefix(prefix);
|
|
}
|
|
|
|
Map<String, String> latestVersions = new HashMap<>();
|
|
VersionComparator comparator = new VersionComparator();
|
|
for (ContributedTool tool : installedTools) {
|
|
File installedFolder = tool.getDownloadableContribution(getPlatform()).getInstalledFolder();
|
|
String toolPath;
|
|
if (installedFolder != null) {
|
|
toolPath = installedFolder.getAbsolutePath();
|
|
} else {
|
|
toolPath = Constants.PREF_REMOVE_PLACEHOLDER;
|
|
}
|
|
String toolName = tool.getName();
|
|
String toolVersion = tool.getVersion();
|
|
PreferencesData.set(prefix + toolName + "-" + toolVersion + ".path", toolPath);
|
|
PreferencesData.set(prefix + tool.getPackager() + "-" + toolName + "-" + toolVersion + ".path", toolPath);
|
|
// In the generic tool property put the path of the latest version if more are available
|
|
try {
|
|
if (!latestVersions.containsKey(toolName) || comparator.greaterThan(toolVersion, latestVersions.get(toolName))) {
|
|
latestVersions.put(toolName, toolVersion);
|
|
PreferencesData.set(prefix + toolName + ".path", toolPath);
|
|
}
|
|
} catch (Exception e) {
|
|
// Ignore invalid versions
|
|
}
|
|
}
|
|
}
|
|
|
|
static public void populateImportToLibraryTable() {
|
|
// Populate importToLibraryTable. Each header filename maps to
|
|
// a list of libraries. Compiler.java will use only the first
|
|
// library on each list. The others are used only to advise
|
|
// user of ambiguously matched and duplicate libraries.
|
|
importToLibraryTable = new HashMap<>();
|
|
for (UserLibrary lib : librariesIndexer.getInstalledLibraries()) {
|
|
try {
|
|
String headers[] = headerListFromIncludePath(lib.getSrcFolder());
|
|
for (String header : headers) {
|
|
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"
|
|
//
|
|
// When all other factors are equal, "libName" is
|
|
// used in preference to "oldName", because getLibraries()
|
|
// gives the library list in order from less specific to
|
|
// more specific locations.
|
|
//
|
|
// But often one library is more clearly the user's
|
|
// intention to use. Many cases are tested, always first
|
|
// for "libName", then for "oldName".
|
|
//
|
|
String name = header.substring(0, header.length() - 2); // name without ".h"
|
|
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.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)) {
|
|
useThisLib = false;
|
|
// check for "-master" appended (zip file from github)
|
|
} else if (libName.equals(name+"-master")) {
|
|
} else if (oldName.equals(name+"-master")) {
|
|
useThisLib = false;
|
|
// next, favor a match with other stuff appended
|
|
} else if (libName.startsWith(name)) {
|
|
} else if (oldName.startsWith(name)) {
|
|
useThisLib = false;
|
|
// otherwise, favor a match with stuff prepended
|
|
} else if (libName.endsWith(name)) {
|
|
} else if (oldName.endsWith(name)) {
|
|
useThisLib = false;
|
|
// as a last resort, match if stuff prepended and appended
|
|
} else if (libName.contains(name)) {
|
|
} else if (oldName.contains(name)) {
|
|
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)) {
|
|
useThisLib = false;
|
|
} else if (libName_lc.equals(name_lc+"-master")) {
|
|
} else if (oldName_lc.equals(name_lc+"-master")) {
|
|
useThisLib = false;
|
|
} else if (libName_lc.startsWith(name_lc)) {
|
|
} else if (oldName_lc.startsWith(name_lc)) {
|
|
useThisLib = false;
|
|
} else if (libName_lc.endsWith(name_lc)) {
|
|
} else if (oldName_lc.endsWith(name_lc)) {
|
|
useThisLib = false;
|
|
} else if (libName_lc.contains(name_lc)) {
|
|
} else if (oldName_lc.contains(name_lc)) {
|
|
useThisLib = false;
|
|
} else {
|
|
// none of these tests matched, so just default to "libName".
|
|
}
|
|
if (useThisLib) {
|
|
list.addFirst(lib);
|
|
} else {
|
|
list.addLast(lib);
|
|
}
|
|
}
|
|
}
|
|
} catch (IOException e) {
|
|
showWarning(tr("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[]) throws Exception {
|
|
String preferencesFile = null;
|
|
|
|
// Do a first pass over the commandline arguments, the rest of them
|
|
// will be processed by the Base constructor. Note that this loop
|
|
// does not look at the last element of args, to prevent crashing
|
|
// when no parameter was specified to an option. Later, Base() will
|
|
// then show an error for these.
|
|
for (int i = 0; i < args.length - 1; i++) {
|
|
if (args[i].equals("--preferences-file")) {
|
|
++i;
|
|
preferencesFile = args[i];
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// run static initialization that grabs all the prefs
|
|
PreferencesData.init(absoluteFile(preferencesFile));
|
|
}
|
|
|
|
/**
|
|
* Produce a sanitized name that fits our standards for likely to work.
|
|
* <p/>
|
|
* Java classes have a wider range of names that are technically allowed
|
|
* (supposedly any Unicode name) than what we support. The reason for
|
|
* going more narrow is to avoid situations with text encodings and
|
|
* converting during the process of moving files between operating
|
|
* systems, i.e. uploading from a Windows machine to a Linux server,
|
|
* or reading a FAT32 partition in OS X and using a thumb drive.
|
|
* <p/>
|
|
* This helper function replaces everything but A-Z, a-z, and 0-9 with
|
|
* underscores. Also disallows starting the sketch name with a digit.
|
|
*/
|
|
static public String sanitizeName(String origName) {
|
|
char c[] = origName.toCharArray();
|
|
StringBuffer buffer = new StringBuffer();
|
|
|
|
// can't lead with a digit, so start with an underscore
|
|
if ((c[0] >= '0') && (c[0] <= '9')) {
|
|
buffer.append('_');
|
|
}
|
|
for (int i = 0; i < c.length; i++) {
|
|
if (((c[i] >= '0') && (c[i] <= '9')) ||
|
|
((c[i] >= 'a') && (c[i] <= 'z')) ||
|
|
((c[i] >= 'A') && (c[i] <= 'Z')) ||
|
|
((i > 0) && (c[i] == '-')) ||
|
|
((i > 0) && (c[i] == '.'))) {
|
|
buffer.append(c[i]);
|
|
} else {
|
|
buffer.append('_');
|
|
}
|
|
}
|
|
// let's not be ridiculous about the length of filenames.
|
|
// in fact, Mac OS 9 can handle 255 chars, though it can't really
|
|
// deal with filenames longer than 31 chars in the Finder.
|
|
// but limiting to that for sketches would mean setting the
|
|
// upper-bound on the character limit here to 25 characters
|
|
// (to handle the base name + ".class")
|
|
if (buffer.length() > 63) {
|
|
buffer.setLength(63);
|
|
}
|
|
return buffer.toString();
|
|
}
|
|
|
|
/**
|
|
* Save the content of a String into a file
|
|
* - Save the content into a temp file
|
|
* - Find the canonical path of the file (if it's a symlink, follow it)
|
|
* - Remove the original file
|
|
* - Move temp file to original path
|
|
* This ensures that the file is not getting truncated if the disk is full
|
|
*/
|
|
static public void saveFile(String str, File file) throws IOException {
|
|
File temp = File.createTempFile(file.getName(), null, file.getParentFile());
|
|
PApplet.saveStrings(temp, new String[] { str });
|
|
|
|
try {
|
|
file = file.getCanonicalFile();
|
|
} catch (IOException e) {
|
|
}
|
|
|
|
if (file.exists()) {
|
|
boolean result = file.delete();
|
|
if (!result) {
|
|
throw new IOException(
|
|
I18n.format(
|
|
tr("Could not remove old version of {0}"),
|
|
file.getAbsolutePath()));
|
|
}
|
|
}
|
|
boolean result = temp.renameTo(file);
|
|
if (!result) {
|
|
throw new IOException(
|
|
I18n.format(
|
|
tr("Could not replace {0}"),
|
|
file.getAbsolutePath()));
|
|
}
|
|
}
|
|
|
|
static public void selectBoard(TargetBoard targetBoard) {
|
|
TargetPlatform targetPlatform = targetBoard.getContainerPlatform();
|
|
TargetPackage targetPackage = targetPlatform.getContainerPackage();
|
|
|
|
PreferencesData.set("target_package", targetPackage.getId());
|
|
PreferencesData.set("target_platform", targetPlatform.getId());
|
|
PreferencesData.set("board", targetBoard.getId());
|
|
|
|
File platformFolder = targetPlatform.getFolder();
|
|
PreferencesData.set("runtime.platform.path", platformFolder.getAbsolutePath());
|
|
PreferencesData.set("runtime.hardware.path", platformFolder.getParentFile().getAbsolutePath());
|
|
}
|
|
|
|
public static void selectSerialPort(String port) {
|
|
PreferencesData.set("serial.port", port);
|
|
BoardPort boardPort = getDiscoveryManager().find(port, true);
|
|
if (boardPort != null) {
|
|
PreferencesData.set("serial.port.iserial", boardPort.getPrefs().get("iserial"));
|
|
}
|
|
String portFile = port;
|
|
if (port.startsWith("/dev/")) {
|
|
portFile = portFile.substring(5);
|
|
}
|
|
PreferencesData.set("serial.port.file", portFile);
|
|
}
|
|
|
|
static public void showError(String title, String message, int exit_code) {
|
|
showError(title, message, null, exit_code);
|
|
}
|
|
|
|
static public void showError(String title, String message, Throwable e) {
|
|
notifier.showError(title, message, e, 1);
|
|
}
|
|
|
|
/**
|
|
* Show an error message that's actually fatal to the program.
|
|
* This is an error that can't be recovered. Use showWarning()
|
|
* for errors that allow P5 to continue running.
|
|
*/
|
|
static public void showError(String title, String message, Throwable e, int exit_code) {
|
|
notifier.showError(title, message, e, exit_code);
|
|
}
|
|
|
|
/**
|
|
* "No cookie for you" type messages. Nothing fatal or all that
|
|
* much of a bummer, but something to notify the user about.
|
|
*/
|
|
static public void showMessage(String title, String message) {
|
|
notifier.showMessage(title, message);
|
|
}
|
|
|
|
/**
|
|
* Non-fatal error message with optional stack trace side dish.
|
|
*/
|
|
static public void showWarning(String title, String message, Exception e) {
|
|
notifier.showWarning(title, message, e);
|
|
}
|
|
|
|
}
|