1 package processing.app; 2 3 import cc.arduino.Constants; 4 import cc.arduino.contributions.GPGDetachedSignatureVerifier; 5 import cc.arduino.contributions.SignatureVerificationFailedException; 6 import cc.arduino.contributions.VersionComparator; 7 import cc.arduino.contributions.libraries.LibrariesIndexer; 8 import cc.arduino.contributions.packages.ContributedPlatform; 9 import cc.arduino.contributions.packages.ContributedTool; 10 import cc.arduino.contributions.packages.ContributionsIndexer; 11 import cc.arduino.packages.DiscoveryManager; 12 import com.fasterxml.jackson.core.JsonProcessingException; 13 import org.apache.commons.compress.utils.IOUtils; 14 import org.apache.commons.logging.impl.LogFactoryImpl; 15 import org.apache.commons.logging.impl.NoOpLog; 16 import processing.app.debug.*; 17 import processing.app.helpers.*; 18 import processing.app.helpers.filefilters.OnlyDirs; 19 import processing.app.helpers.filefilters.OnlyFilesWithExtension; 20 import processing.app.legacy.PApplet; 21 import processing.app.packages.LibraryList; 22 import processing.app.packages.UserLibrary; 23 24 import cc.arduino.files.DeleteFilesOnShutdown; 25 import processing.app.helpers.FileUtils; 26 27 import java.beans.PropertyChangeListener; 28 import java.beans.PropertyChangeSupport; 29 import java.io.File; 30 import java.io.FileWriter; 31 import java.io.IOException; 32 import java.util.*; 33 import java.util.logging.Level; 34 import java.util.logging.Logger; 35 36 import cc.arduino.packages.BoardPort; 37 38 import static processing.app.I18n.tr; 39 import static processing.app.helpers.filefilters.OnlyDirs.ONLY_DIRS; 40 41 public class BaseNoGui { 42 43 /** Version string to be used for build */ 44 public static final int REVISION = 10802; 45 /** Extended version string displayed on GUI */ 46 public static final String VERSION_NAME = "1.8.2"; 47 public static final String VERSION_NAME_LONG; 48 49 // Current directory to use for relative paths specified on the 50 // commandline 51 static String currentDirectory = System.getProperty("user.dir"); 52 53 static { 54 String versionNameLong = VERSION_NAME; 55 File hourlyBuildTxt = new File(getContentFile("lib"), "hourlyBuild.txt"); 56 if (hourlyBuildTxt.exists() && hourlyBuildTxt.canRead()) { 57 versionNameLong += " Hourly Build"; 58 try { 59 versionNameLong += " " + FileUtils.readFileToString(hourlyBuildTxt).trim(); 60 } catch (IOException e) { 61 //noop 62 } 63 } 64 65 File windowsStoreConfig = new File(getContentFile("lib"), "windowsStore.txt"); 66 if (windowsStoreConfig.exists()) { 67 try { 68 PreferencesMap conf = new PreferencesMap(windowsStoreConfig); 69 PreferencesData.setBoolean("runtime.is-windows-store-app", true); 70 PreferencesData.set("runtime.windows-store-app.id", conf.get("appid")); 71 versionNameLong += " (Windows Store " + conf.get("version") + ")"; 72 } catch (IOException e1) { 73 e1.printStackTrace(); 74 } 75 } 76 77 VERSION_NAME_LONG = versionNameLong; 78 } 79 80 private static DiscoveryManager discoveryManager; 81 82 // these are static because they're used by Sketch 83 static private File examplesFolder; 84 static private File toolsFolder; 85 86 // maps #included files to their library folder 87 public static Map<String, LibraryList> importToLibraryTable; 88 89 // XXX: Remove this field 90 static private List<File> librariesFolders; 91 92 static UserNotifier notifier = new BasicUserNotifier(); 93 94 static public Map<String, TargetPackage> packages; 95 96 static Platform platform; 97 98 static File portableFolder = null; 99 static final String portableSketchbookFolder = "sketchbook"; 100 101 public static ContributionsIndexer indexer; 102 public static LibrariesIndexer librariesIndexer; 103 104 private static String boardManagerLink = ""; 105 106 private static File buildCache; 107 108 // Returns a File object for the given pathname. If the pathname 109 // is not absolute, it is interpreted relative to the current 110 // directory when starting the IDE (which is not the same as the 111 // current working directory!). absoluteFile(String path)112 static public File absoluteFile(String path) { 113 if (path == null) return null; 114 115 File file = new File(path); 116 if (!file.isAbsolute()) { 117 file = new File(currentDirectory, path); 118 } 119 return file; 120 } 121 122 /** 123 * Get the number of lines in a file by counting the number of newline 124 * characters inside a String (and adding 1). 125 */ countLines(String what)126 static public int countLines(String what) { 127 int count = 1; 128 for (char c : what.toCharArray()) { 129 if (c == '\n') count++; 130 } 131 return count; 132 } 133 getBoardPreferences()134 static public PreferencesMap getBoardPreferences() { 135 TargetBoard board = getTargetBoard(); 136 if (board == null) 137 return null; 138 String boardId = board.getId(); 139 140 PreferencesMap prefs = new PreferencesMap(board.getPreferences()); 141 142 String extendedName = prefs.get("name"); 143 for (String menuId : board.getMenuIds()) { 144 if (!board.hasMenu(menuId)) 145 continue; 146 147 // Get "custom_[MENU_ID]" preference (for example "custom_cpu") 148 String entry = PreferencesData.get("custom_" + menuId); 149 if (entry != null && entry.startsWith(boardId)) { 150 151 String selectionId = entry.substring(boardId.length() + 1); 152 prefs.putAll(board.getMenuPreferences(menuId, selectionId)); 153 154 // Update the name with the extended configuration 155 extendedName += ", " + board.getMenuLabel(menuId, selectionId); 156 } 157 } 158 prefs.put("name", extendedName); 159 160 // Resolve tools needed for this board 161 List<ContributedTool> requiredTools = new ArrayList<>(); 162 163 // Add all tools dependencies specified in package index 164 ContributedPlatform p = indexer.getContributedPlaform(getTargetPlatform()); 165 if (p != null) 166 requiredTools.addAll(p.getResolvedTools()); 167 168 // Add all tools dependencies from the (possibily) referenced core 169 String core = prefs.get("build.core"); 170 if (core != null && core.contains(":")) { 171 String split[] = core.split(":"); 172 TargetPlatform referenced = BaseNoGui.getCurrentTargetPlatformFromPackage(split[0]); 173 if (referenced != null) { 174 ContributedPlatform referencedPlatform = indexer.getContributedPlaform(referenced); 175 if (referencedPlatform != null) 176 requiredTools.addAll(referencedPlatform.getResolvedTools()); 177 } else { 178 String msg = tr("The current selected board needs the core '{0}' that is not installed."); 179 System.out.println(I18n.format(msg, core)); 180 } 181 } 182 183 String prefix = "runtime.tools."; 184 for (ContributedTool tool : requiredTools) { 185 File folder = tool.getDownloadableContribution(getPlatform()).getInstalledFolder(); 186 if (folder == null) { 187 continue; 188 } 189 String toolPath = folder.getAbsolutePath(); 190 prefs.put(prefix + tool.getName() + ".path", toolPath); 191 PreferencesData.set(prefix + tool.getName() + ".path", toolPath); 192 PreferencesData.set(prefix + tool.getName() + "-" + tool.getVersion() + ".path", toolPath); 193 } 194 return prefs; 195 } 196 getContentFile(String name)197 static public File getContentFile(String name) { 198 String appDir = System.getProperty("APP_DIR"); 199 if (appDir == null || appDir.length() == 0) { 200 appDir = currentDirectory; 201 } 202 File installationFolder = new File(appDir); 203 return new File(installationFolder, name); 204 } 205 getCurrentTargetPlatformFromPackage(String pack)206 static public TargetPlatform getCurrentTargetPlatformFromPackage(String pack) { 207 return getTargetPlatform(pack, PreferencesData.get("target_platform")); 208 } 209 getDefaultSketchbookFolder()210 static public File getDefaultSketchbookFolder() { 211 if (getPortableFolder() != null) 212 return new File(getPortableFolder(), getPortableSketchbookFolder()); 213 214 File sketchbookFolder = null; 215 try { 216 sketchbookFolder = getPlatform().getDefaultSketchbookFolder(); 217 } catch (Exception e) { } 218 219 return sketchbookFolder; 220 } 221 getDiscoveryManager()222 public static DiscoveryManager getDiscoveryManager() { 223 if (discoveryManager == null) { 224 discoveryManager = new DiscoveryManager(); 225 } 226 return discoveryManager; 227 } 228 getExamplesFolder()229 static public File getExamplesFolder() { 230 return examplesFolder; 231 } 232 getExamplesPath()233 static public String getExamplesPath() { 234 return examplesFolder.getAbsolutePath(); 235 } 236 getHardwareFolder()237 static public File getHardwareFolder() { 238 // calculate on the fly because it's needed by Preferences.init() to find 239 // the boards.txt and programmers.txt preferences files (which happens 240 // before the other folders / paths get cached). 241 return getContentFile("hardware"); 242 } 243 getHardwarePath()244 static public String getHardwarePath() { 245 return getHardwareFolder().getAbsolutePath(); 246 } 247 getLibrariesPath()248 static public List<File> getLibrariesPath() { 249 return librariesFolders; 250 } 251 getPlatform()252 static public Platform getPlatform() { 253 return platform; 254 } 255 getPortableFolder()256 static public File getPortableFolder() { 257 return portableFolder; 258 } 259 getPortableSketchbookFolder()260 static public String getPortableSketchbookFolder() { 261 return portableSketchbookFolder; 262 } 263 getCachePath()264 static public File getCachePath() { 265 if (buildCache == null) { 266 try { 267 buildCache = FileUtils.createTempFolder("arduino_cache_"); 268 DeleteFilesOnShutdown.add(buildCache); 269 } catch (IOException e) { 270 return null; 271 } 272 } 273 return buildCache; 274 } 275 276 /** 277 * Convenience method to get a File object for the specified filename inside 278 * the settings folder. 279 * For now, only used by Preferences to get the preferences.txt file. 280 * @param filename A file inside the settings folder. 281 * @return filename wrapped as a File object inside the settings folder 282 */ getSettingsFile(String filename)283 static public File getSettingsFile(String filename) { 284 return new File(getSettingsFolder(), filename); 285 } 286 getSettingsFolder()287 static public File getSettingsFolder() { 288 if (getPortableFolder() != null) 289 return getPortableFolder(); 290 291 File settingsFolder = null; 292 293 String preferencesPath = PreferencesData.get("settings.path"); 294 if (preferencesPath != null) { 295 settingsFolder = absoluteFile(preferencesPath); 296 297 } else { 298 try { 299 settingsFolder = getPlatform().getSettingsFolder(); 300 } catch (Exception e) { 301 showError(tr("Problem getting data folder"), 302 tr("Error getting the Arduino data folder."), e); 303 } 304 } 305 306 // create the folder if it doesn't exist already 307 if (!settingsFolder.exists()) { 308 if (!settingsFolder.mkdirs()) { 309 showError(tr("Settings issues"), 310 tr("Arduino cannot run because it could not\n" + 311 "create a folder to store your settings."), null); 312 } 313 } 314 return settingsFolder; 315 } 316 getSketchbookFolder()317 static public File getSketchbookFolder() { 318 String sketchBookPath = PreferencesData.get("sketchbook.path"); 319 if (getPortableFolder() != null && !new File(sketchBookPath).isAbsolute()) { 320 return new File(getPortableFolder(), sketchBookPath); 321 } 322 return absoluteFile(sketchBookPath); 323 } 324 getSketchbookHardwareFolder()325 static public File getSketchbookHardwareFolder() { 326 return new File(getSketchbookFolder(), "hardware"); 327 } 328 getSketchbookLibrariesFolder()329 static public File getSketchbookLibrariesFolder() { 330 File libdir = new File(getSketchbookFolder(), "libraries"); 331 if (!libdir.exists()) { 332 FileWriter freadme = null; 333 try { 334 libdir.mkdirs(); 335 freadme = new FileWriter(new File(libdir, "readme.txt")); 336 freadme.write(tr("For information on installing libraries, see: " + 337 "http://www.arduino.cc/en/Guide/Libraries\n")); 338 } catch (Exception e) { 339 } finally { 340 IOUtils.closeQuietly(freadme); 341 } 342 } 343 return libdir; 344 } 345 getSketchbookPath()346 static public String getSketchbookPath() { 347 // Get the sketchbook path, and make sure it's set properly 348 String sketchbookPath = PreferencesData.get("sketchbook.path"); 349 350 // If a value is at least set, first check to see if the folder exists. 351 // If it doesn't, warn the user that the sketchbook folder is being reset. 352 if (sketchbookPath != null) { 353 File sketchbookFolder; 354 if (getPortableFolder() != null && !new File(sketchbookPath).isAbsolute()) { 355 sketchbookFolder = new File(getPortableFolder(), sketchbookPath); 356 } else { 357 sketchbookFolder = absoluteFile(sketchbookPath); 358 } 359 if (!sketchbookFolder.exists()) { 360 showWarning(tr("Sketchbook folder disappeared"), 361 tr("The sketchbook folder no longer exists.\n" + 362 "Arduino will switch to the default sketchbook\n" + 363 "location, and create a new sketchbook folder if\n" + 364 "necessary. Arduino will then stop talking about\n" + 365 "himself in the third person."), null); 366 sketchbookPath = null; 367 } 368 } 369 370 return sketchbookPath; 371 } 372 getTargetBoard()373 public static TargetBoard getTargetBoard() { 374 TargetPlatform targetPlatform = getTargetPlatform(); 375 if (targetPlatform == null) 376 return null; 377 String boardId = PreferencesData.get("board"); 378 return targetPlatform.getBoard(boardId); 379 } 380 381 /** 382 * Returns a specific TargetPackage 383 * 384 * @param packageName 385 * @return 386 */ getTargetPackage(String packageName)387 static public TargetPackage getTargetPackage(String packageName) { 388 return packages.get(packageName); 389 } 390 391 /** 392 * Returns the currently selected TargetPlatform. 393 * 394 * @return 395 */ getTargetPlatform()396 static public TargetPlatform getTargetPlatform() { 397 String packageName = PreferencesData.get("target_package"); 398 String platformName = PreferencesData.get("target_platform"); 399 return getTargetPlatform(packageName, platformName); 400 } 401 402 /** 403 * Returns a specific TargetPlatform searching Package/Platform 404 * 405 * @param packageName 406 * @param platformName 407 * @return 408 */ getTargetPlatform(String packageName, String platformName)409 static public TargetPlatform getTargetPlatform(String packageName, 410 String platformName) { 411 TargetPackage p = packages.get(packageName); 412 if (p == null) 413 return null; 414 return p.get(platformName); 415 } 416 getToolsFolder()417 static public File getToolsFolder() { 418 return toolsFolder; 419 } 420 getToolsPath()421 static public String getToolsPath() { 422 return toolsFolder.getAbsolutePath(); 423 } 424 getUserLibs()425 static public LibraryList getUserLibs() { 426 LibraryList libs = BaseNoGui.librariesIndexer.getInstalledLibraries(); 427 return libs.filterLibrariesInSubfolder(getSketchbookFolder()); 428 } 429 getBoardManagerLink()430 static public String getBoardManagerLink() { 431 return boardManagerLink; 432 } 433 434 protected static PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(BaseNoGui.class); 435 setBoardManagerLink(String temp)436 public static void setBoardManagerLink(String temp) { 437 boardManagerLink = temp; 438 propertyChangeSupport.firePropertyChange("boardManagerLink", "", boardManagerLink); 439 } 440 addPropertyChangeListener(PropertyChangeListener listener)441 public static void addPropertyChangeListener(PropertyChangeListener listener) { 442 propertyChangeSupport.addPropertyChangeListener(listener); 443 } 444 445 /** 446 * Given a folder, return a list of the header files in that folder (but not 447 * the header files in its sub-folders, as those should be included from 448 * within the header files at the top-level). 449 */ headerListFromIncludePath(File path)450 static public String[] headerListFromIncludePath(File path) throws IOException { 451 String[] list = path.list(new OnlyFilesWithExtension(".h", ".hh", ".hpp")); 452 if (list == null) { 453 throw new IOException(); 454 } 455 return list; 456 } 457 dumpPrefs(CommandlineParser parser)458 protected static void dumpPrefs(CommandlineParser parser) { 459 if (parser.getGetPref() != null) { 460 String value = PreferencesData.get(parser.getGetPref(), null); 461 if (value != null) { 462 System.out.println(value); 463 System.exit(0); 464 } else { 465 System.exit(4); 466 } 467 } else { 468 System.out.println("#PREFDUMP#"); 469 PreferencesMap prefs = PreferencesData.getMap(); 470 for (Map.Entry<String, String> entry : prefs.entrySet()) { 471 System.out.println(entry.getKey() + "=" + entry.getValue()); 472 } 473 System.exit(0); 474 } 475 } 476 initLogger()477 static public void initLogger() { 478 System.setProperty(LogFactoryImpl.LOG_PROPERTY, NoOpLog.class.getCanonicalName()); 479 Logger.getLogger("javax.jmdns").setLevel(Level.OFF); 480 } 481 initPackages()482 static public void initPackages() throws Exception { 483 indexer = new ContributionsIndexer(getSettingsFolder(), getHardwareFolder(), getPlatform(), 484 new GPGDetachedSignatureVerifier()); 485 486 try { 487 indexer.parseIndex(); 488 } catch (JsonProcessingException | SignatureVerificationFailedException e) { 489 File indexFile = indexer.getIndexFile(Constants.DEFAULT_INDEX_FILE_NAME); 490 File indexSignatureFile = indexer.getIndexFile(Constants.DEFAULT_INDEX_FILE_NAME + ".sig"); 491 FileUtils.deleteIfExists(indexFile); 492 FileUtils.deleteIfExists(indexSignatureFile); 493 throw e; 494 } 495 indexer.syncWithFilesystem(); 496 497 packages = new LinkedHashMap<>(); 498 loadHardware(getHardwareFolder()); 499 loadContributedHardware(indexer); 500 loadHardware(getSketchbookHardwareFolder()); 501 createToolPreferences(indexer.getInstalledTools(), true); 502 503 librariesIndexer = new LibrariesIndexer(getSettingsFolder()); 504 try { 505 librariesIndexer.parseIndex(); 506 } catch (JsonProcessingException e) { 507 File librariesIndexFile = librariesIndexer.getIndexFile(); 508 FileUtils.deleteIfExists(librariesIndexFile); 509 } 510 511 if (discoveryManager == null) { 512 discoveryManager = new DiscoveryManager(); 513 } 514 } 515 initPlatform()516 static protected void initPlatform() { 517 try { 518 Class<?> platformClass = Class.forName("processing.app.Platform"); 519 if (OSUtils.isMacOS()) { 520 platformClass = Class.forName("processing.app.macosx.Platform"); 521 } else if (OSUtils.isWindows()) { 522 platformClass = Class.forName("processing.app.windows.Platform"); 523 } else if (OSUtils.isLinux()) { 524 platformClass = Class.forName("processing.app.linux.Platform"); 525 } 526 platform = (Platform) platformClass.newInstance(); 527 } catch (Exception e) { 528 showError(tr("Problem Setting the Platform"), 529 tr("An unknown error occurred while trying to load\n" + 530 "platform-specific code for your machine."), e); 531 } 532 } 533 initPortableFolder()534 static public void initPortableFolder() { 535 // Portable folder 536 portableFolder = getContentFile("portable"); 537 if (!portableFolder.exists()) { 538 portableFolder = null; 539 } 540 } 541 initVersion()542 static public void initVersion() { 543 // help 3rd party installers find the correct hardware path 544 PreferencesData.set("last.ide." + VERSION_NAME + ".hardwarepath", getHardwarePath()); 545 PreferencesData.set("last.ide." + VERSION_NAME + ".daterun", "" + (new Date()).getTime() / 1000); 546 } 547 548 /** 549 * Return true if the name is valid for a Processing sketch. 550 */ isSanitaryName(String name)551 static public boolean isSanitaryName(String name) { 552 return sanitizeName(name).equals(name); 553 } 554 loadHardware(File folder)555 static protected void loadHardware(File folder) { 556 if (!folder.isDirectory()) { 557 return; 558 } 559 560 String list[] = folder.list(new OnlyDirs()); 561 562 // if a bad folder or something like that, this might come back null 563 if (list == null) { 564 return; 565 } 566 567 // alphabetize list, since it's not always alpha order 568 // replaced hella slow bubble sort with this feller for 0093 569 Arrays.sort(list, String.CASE_INSENSITIVE_ORDER); 570 571 for (String target : list) { 572 // Skip reserved 'tools' folder. 573 if (target.equals("tools")) { 574 continue; 575 } 576 File subfolder = new File(folder, target); 577 578 TargetPackage targetPackage; 579 if (packages.containsKey(target)) { 580 targetPackage = packages.get(target); 581 } else { 582 targetPackage = new LegacyTargetPackage(target); 583 packages.put(target, targetPackage); 584 } 585 try { 586 loadTargetPackage(targetPackage, subfolder); 587 } catch (TargetPlatformException e) { 588 System.out.println("WARNING: Error loading hardware folder " + new File(folder, target)); 589 System.out.println(" " + e.getMessage()); 590 } 591 } 592 } 593 loadTargetPackage(TargetPackage targetPackage, File _folder)594 private static void loadTargetPackage(TargetPackage targetPackage, File _folder) throws TargetPlatformException { 595 File[] folders = _folder.listFiles(ONLY_DIRS); 596 if (folders == null) { 597 return; 598 } 599 600 for (File subFolder : folders) { 601 if (!subFolder.exists() || !subFolder.canRead()) { 602 continue; 603 } 604 String arch = subFolder.getName(); 605 try { 606 TargetPlatform p = new LegacyTargetPlatform(arch, subFolder, targetPackage); 607 targetPackage.getPlatforms().put(arch, p); 608 } catch (TargetPlatformException e) { 609 System.err.println(e.getMessage()); 610 } 611 } 612 613 if (targetPackage.getPlatforms().size() == 0) { 614 throw new TargetPlatformException(I18n.format(tr("No valid hardware definitions found in folder {0}."), _folder.getName())); 615 } 616 } 617 618 /** 619 * Grab the contents of a file as a string. 620 */ loadFile(File file)621 static public String loadFile(File file) throws IOException { 622 String[] contents = PApplet.loadStrings(file); 623 if (contents == null) return null; 624 return PApplet.join(contents, "\n"); 625 } 626 checkInstallationFolder()627 public static void checkInstallationFolder() { 628 if (isIDEInstalledIntoSettingsFolder()) { 629 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); 630 } 631 if (isIDEInstalledIntoSketchbookFolder()) { 632 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); 633 } 634 } 635 isIDEInstalledIntoSketchbookFolder()636 public static boolean isIDEInstalledIntoSketchbookFolder() { 637 return PreferencesData.has("sketchbook.path") && FileUtils.isSubDirectory(new File(PreferencesData.get("sketchbook.path")), new File(PreferencesData.get("runtime.ide.path"))); 638 } 639 isIDEInstalledIntoSettingsFolder()640 public static boolean isIDEInstalledIntoSettingsFolder() { 641 try { 642 return FileUtils.isSubDirectory(BaseNoGui.getPlatform().getSettingsFolder(), new File(PreferencesData.get("runtime.ide.path"))); 643 } catch (Exception e) { 644 return false; 645 } 646 } 647 onBoardOrPortChange()648 static public void onBoardOrPortChange() { 649 examplesFolder = getContentFile("examples"); 650 toolsFolder = getContentFile("tools"); 651 librariesFolders = new ArrayList<>(); 652 653 // Add IDE libraries folder 654 librariesFolders.add(getContentFile("libraries")); 655 656 TargetPlatform targetPlatform = getTargetPlatform(); 657 if (targetPlatform != null) { 658 String core = getBoardPreferences().get("build.core", "arduino"); 659 if (core.contains(":")) { 660 String referencedCore = core.split(":")[0]; 661 TargetPlatform referencedPlatform = getTargetPlatform(referencedCore, targetPlatform.getId()); 662 if (referencedPlatform != null) { 663 File referencedPlatformFolder = referencedPlatform.getFolder(); 664 // Add libraries folder for the referenced platform 665 File folder = new File(referencedPlatformFolder, "libraries"); 666 librariesFolders.add(folder); 667 } 668 } 669 File platformFolder = targetPlatform.getFolder(); 670 // Add libraries folder for the selected platform 671 File folder = new File(platformFolder, "libraries"); 672 librariesFolders.add(folder); 673 } 674 675 // Add libraries folder for the sketchbook 676 librariesFolders.add(getSketchbookLibrariesFolder()); 677 678 // Scan for libraries in each library folder. 679 // Libraries located in the latest folders on the list can override 680 // other libraries with the same name. 681 librariesIndexer.setSketchbookLibrariesFolder(getSketchbookLibrariesFolder()); 682 librariesIndexer.setLibrariesFolders(librariesFolders); 683 librariesIndexer.rescanLibraries(); 684 685 populateImportToLibraryTable(); 686 } 687 loadContributedHardware(ContributionsIndexer idx)688 static protected void loadContributedHardware(ContributionsIndexer idx) { 689 for (TargetPackage pack : idx.createTargetPackages()) { 690 packages.put(pack.getId(), pack); 691 } 692 } 693 createToolPreferences(Collection<ContributedTool> installedTools, boolean removeOldKeys)694 public static void createToolPreferences(Collection<ContributedTool> installedTools, boolean removeOldKeys) { 695 String prefix = "runtime.tools."; 696 if (removeOldKeys) { 697 PreferencesData.removeAllKeysWithPrefix(prefix); 698 } 699 700 Map<String, String> latestVersions = new HashMap<>(); 701 VersionComparator comparator = new VersionComparator(); 702 for (ContributedTool tool : installedTools) { 703 File installedFolder = tool.getDownloadableContribution(getPlatform()).getInstalledFolder(); 704 String toolPath; 705 if (installedFolder != null) { 706 toolPath = installedFolder.getAbsolutePath(); 707 } else { 708 toolPath = Constants.PREF_REMOVE_PLACEHOLDER; 709 } 710 String toolName = tool.getName(); 711 String toolVersion = tool.getVersion(); 712 PreferencesData.set(prefix + toolName + "-" + toolVersion + ".path", toolPath); 713 PreferencesData.set(prefix + tool.getPackager() + "-" + toolName + "-" + toolVersion + ".path", toolPath); 714 // In the generic tool property put the path of the latest version if more are available 715 try { 716 if (!latestVersions.containsKey(toolName) || comparator.greaterThan(toolVersion, latestVersions.get(toolName))) { 717 latestVersions.put(toolName, toolVersion); 718 PreferencesData.set(prefix + toolName + ".path", toolPath); 719 } 720 } catch (Exception e) { 721 // Ignore invalid versions 722 } 723 } 724 } 725 populateImportToLibraryTable()726 static public void populateImportToLibraryTable() { 727 // Populate importToLibraryTable. Each header filename maps to 728 // a list of libraries. Compiler.java will use only the first 729 // library on each list. The others are used only to advise 730 // user of ambiguously matched and duplicate libraries. 731 importToLibraryTable = new HashMap<>(); 732 for (UserLibrary lib : librariesIndexer.getInstalledLibraries()) { 733 try { 734 String headers[] = headerListFromIncludePath(lib.getSrcFolder()); 735 for (String header : headers) { 736 LibraryList list = importToLibraryTable.get(header); 737 if (list == null) { 738 // This is the first library found with this header 739 list = new LibraryList(); 740 list.addFirst(lib); 741 importToLibraryTable.put(header, list); 742 } else { 743 UserLibrary old = list.peekFirst(); 744 boolean useThisLib = true; 745 // This is the case where 2 libraries have a .h header 746 // with the same name. We must decide which library to 747 // use when a sketch has #include "name.h" 748 // 749 // When all other factors are equal, "libName" is 750 // used in preference to "oldName", because getLibraries() 751 // gives the library list in order from less specific to 752 // more specific locations. 753 // 754 // But often one library is more clearly the user's 755 // intention to use. Many cases are tested, always first 756 // for "libName", then for "oldName". 757 // 758 String name = header.substring(0, header.length() - 2); // name without ".h" 759 String oldName = old.getInstalledFolder().getName(); // just the library folder name 760 String libName = lib.getInstalledFolder().getName(); // just the library folder name 761 //System.out.println("name conflict: " + name); 762 //System.out.println(" old = " + oldName + " -> " + old.getInstalledFolder().getPath()); 763 //System.out.println(" new = " + libName + " -> " + lib.getInstalledFolder().getPath()); 764 String name_lc = name.toLowerCase(); 765 String oldName_lc = oldName.toLowerCase(); 766 String libName_lc = libName.toLowerCase(); 767 // always favor a perfect name match 768 if (libName.equals(name)) { 769 } else if (oldName.equals(name)) { 770 useThisLib = false; 771 // check for "-master" appended (zip file from github) 772 } else if (libName.equals(name+"-master")) { 773 } else if (oldName.equals(name+"-master")) { 774 useThisLib = false; 775 // next, favor a match with other stuff appended 776 } else if (libName.startsWith(name)) { 777 } else if (oldName.startsWith(name)) { 778 useThisLib = false; 779 // otherwise, favor a match with stuff prepended 780 } else if (libName.endsWith(name)) { 781 } else if (oldName.endsWith(name)) { 782 useThisLib = false; 783 // as a last resort, match if stuff prepended and appended 784 } else if (libName.contains(name)) { 785 } else if (oldName.contains(name)) { 786 useThisLib = false; 787 // repeat all the above tests, with case insensitive matching 788 } else if (libName_lc.equals(name_lc)) { 789 } else if (oldName_lc.equals(name_lc)) { 790 useThisLib = false; 791 } else if (libName_lc.equals(name_lc+"-master")) { 792 } else if (oldName_lc.equals(name_lc+"-master")) { 793 useThisLib = false; 794 } else if (libName_lc.startsWith(name_lc)) { 795 } else if (oldName_lc.startsWith(name_lc)) { 796 useThisLib = false; 797 } else if (libName_lc.endsWith(name_lc)) { 798 } else if (oldName_lc.endsWith(name_lc)) { 799 useThisLib = false; 800 } else if (libName_lc.contains(name_lc)) { 801 } else if (oldName_lc.contains(name_lc)) { 802 useThisLib = false; 803 } else { 804 // none of these tests matched, so just default to "libName". 805 } 806 if (useThisLib) { 807 list.addFirst(lib); 808 } else { 809 list.addLast(lib); 810 } 811 } 812 } 813 } catch (IOException e) { 814 showWarning(tr("Error"), I18n 815 .format("Unable to list header files in {0}", lib.getSrcFolder()), e); 816 } 817 } 818 // repeat for ALL libraries, to pick up duplicates not visible normally. 819 // any new libraries found here are NEVER used, but they are added to the 820 // end of already-found headers, to allow Compiler to report them if 821 // the sketch tries to use them. 822 for (UserLibrary lib : librariesIndexer.getInstalledLibrariesWithDuplicates()) { 823 try { 824 String headers[] = headerListFromIncludePath(lib.getSrcFolder()); 825 for (String header : headers) { 826 LibraryList list = importToLibraryTable.get(header); 827 if (list != null) { 828 if (!(list.hasLibrary(lib))) { 829 list.addLast(lib); 830 //System.out.println(" duplicate lib: " + lib.getInstalledFolder().getPath()); 831 } 832 } 833 } 834 } catch (IOException e) { 835 } 836 } 837 } 838 initParameters(String args[])839 static public void initParameters(String args[]) throws Exception { 840 String preferencesFile = null; 841 842 // Do a first pass over the commandline arguments, the rest of them 843 // will be processed by the Base constructor. Note that this loop 844 // does not look at the last element of args, to prevent crashing 845 // when no parameter was specified to an option. Later, Base() will 846 // then show an error for these. 847 for (int i = 0; i < args.length - 1; i++) { 848 if (args[i].equals("--preferences-file")) { 849 ++i; 850 preferencesFile = args[i]; 851 continue; 852 } 853 } 854 855 // run static initialization that grabs all the prefs 856 PreferencesData.init(absoluteFile(preferencesFile)); 857 } 858 859 /** 860 * Produce a sanitized name that fits our standards for likely to work. 861 * <p/> 862 * Java classes have a wider range of names that are technically allowed 863 * (supposedly any Unicode name) than what we support. The reason for 864 * going more narrow is to avoid situations with text encodings and 865 * converting during the process of moving files between operating 866 * systems, i.e. uploading from a Windows machine to a Linux server, 867 * or reading a FAT32 partition in OS X and using a thumb drive. 868 * <p/> 869 * This helper function replaces everything but A-Z, a-z, and 0-9 with 870 * underscores. Also disallows starting the sketch name with a digit. 871 */ sanitizeName(String origName)872 static public String sanitizeName(String origName) { 873 char c[] = origName.toCharArray(); 874 StringBuffer buffer = new StringBuffer(); 875 876 // can't lead with a digit, so start with an underscore 877 if ((c[0] >= '0') && (c[0] <= '9')) { 878 buffer.append('_'); 879 } 880 for (int i = 0; i < c.length; i++) { 881 if (((c[i] >= '0') && (c[i] <= '9')) || 882 ((c[i] >= 'a') && (c[i] <= 'z')) || 883 ((c[i] >= 'A') && (c[i] <= 'Z')) || 884 ((i > 0) && (c[i] == '-')) || 885 ((i > 0) && (c[i] == '.'))) { 886 buffer.append(c[i]); 887 } else { 888 buffer.append('_'); 889 } 890 } 891 // let's not be ridiculous about the length of filenames. 892 // in fact, Mac OS 9 can handle 255 chars, though it can't really 893 // deal with filenames longer than 31 chars in the Finder. 894 // but limiting to that for sketches would mean setting the 895 // upper-bound on the character limit here to 25 characters 896 // (to handle the base name + ".class") 897 if (buffer.length() > 63) { 898 buffer.setLength(63); 899 } 900 return buffer.toString(); 901 } 902 903 /** 904 * Save the content of a String into a file 905 * - Save the content into a temp file 906 * - Find the canonical path of the file (if it's a symlink, follow it) 907 * - Remove the original file 908 * - Move temp file to original path 909 * This ensures that the file is not getting truncated if the disk is full 910 */ saveFile(String str, File file)911 static public void saveFile(String str, File file) throws IOException { 912 File temp = File.createTempFile(file.getName(), null, file.getParentFile()); 913 PApplet.saveStrings(temp, new String[] { str }); 914 915 try { 916 file = file.getCanonicalFile(); 917 } catch (IOException e) { 918 } 919 920 if (file.exists()) { 921 boolean result = file.delete(); 922 if (!result) { 923 throw new IOException( 924 I18n.format( 925 tr("Could not remove old version of {0}"), 926 file.getAbsolutePath())); 927 } 928 } 929 boolean result = temp.renameTo(file); 930 if (!result) { 931 throw new IOException( 932 I18n.format( 933 tr("Could not replace {0}"), 934 file.getAbsolutePath())); 935 } 936 } 937 selectBoard(TargetBoard targetBoard)938 static public void selectBoard(TargetBoard targetBoard) { 939 TargetPlatform targetPlatform = targetBoard.getContainerPlatform(); 940 TargetPackage targetPackage = targetPlatform.getContainerPackage(); 941 942 PreferencesData.set("target_package", targetPackage.getId()); 943 PreferencesData.set("target_platform", targetPlatform.getId()); 944 PreferencesData.set("board", targetBoard.getId()); 945 946 File platformFolder = targetPlatform.getFolder(); 947 PreferencesData.set("runtime.platform.path", platformFolder.getAbsolutePath()); 948 PreferencesData.set("runtime.hardware.path", platformFolder.getParentFile().getAbsolutePath()); 949 } 950 selectSerialPort(String port)951 public static void selectSerialPort(String port) { 952 PreferencesData.set("serial.port", port); 953 BoardPort boardPort = getDiscoveryManager().find(port, true); 954 if (boardPort != null) { 955 PreferencesData.set("serial.port.iserial", boardPort.getPrefs().get("iserial")); 956 } 957 String portFile = port; 958 if (port.startsWith("/dev/")) { 959 portFile = portFile.substring(5); 960 } 961 PreferencesData.set("serial.port.file", portFile); 962 } 963 showError(String title, String message, int exit_code)964 static public void showError(String title, String message, int exit_code) { 965 showError(title, message, null, exit_code); 966 } 967 showError(String title, String message, Throwable e)968 static public void showError(String title, String message, Throwable e) { 969 notifier.showError(title, message, e, 1); 970 } 971 972 /** 973 * Show an error message that's actually fatal to the program. 974 * This is an error that can't be recovered. Use showWarning() 975 * for errors that allow P5 to continue running. 976 */ showError(String title, String message, Throwable e, int exit_code)977 static public void showError(String title, String message, Throwable e, int exit_code) { 978 notifier.showError(title, message, e, exit_code); 979 } 980 981 /** 982 * "No cookie for you" type messages. Nothing fatal or all that 983 * much of a bummer, but something to notify the user about. 984 */ showMessage(String title, String message)985 static public void showMessage(String title, String message) { 986 notifier.showMessage(title, message); 987 } 988 989 /** 990 * Non-fatal error message with optional stack trace side dish. 991 */ showWarning(String title, String message, Exception e)992 static public void showWarning(String title, String message, Exception e) { 993 notifier.showWarning(title, message, e); 994 } 995 996 } 997