package io.sloeber.core; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.UnknownHostException; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.model.CoreModel; import org.eclipse.cdt.core.settings.model.CProjectDescriptionEvent; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.content.IContentType; import org.eclipse.core.runtime.content.IContentTypeManager; import org.eclipse.core.runtime.content.IContentTypeSettings; import org.eclipse.core.runtime.jobs.IJobManager; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.osgi.framework.BundleContext; import org.osgi.service.prefs.BackingStoreException; import cc.arduino.packages.discoverers.NetworkDiscovery; import io.sloeber.core.common.Common; import io.sloeber.core.common.ConfigurationPreferences; import io.sloeber.core.common.Const; import io.sloeber.core.common.InstancePreferences; import io.sloeber.core.listeners.ConfigurationChangeListener; import io.sloeber.core.listeners.IndexerListener; import io.sloeber.core.managers.Manager; abstract class FamilyJob extends Job { static final String MY_FAMILY = "myJobFamily"; //$NON-NLS-1$ public FamilyJob(String name) { super(name); } @Override public boolean belongsTo(Object family) { return family == MY_FAMILY; } } /** * generated code * * @author Jan Baeyens * */ @SuppressWarnings("nls") public class Activator extends AbstractUIPlugin { // preference nodes public static final String NODE_ARDUINO = "io.sloeber.arduino"; // The shared instance private static final String FLAG_START = "F" + "s" + "S" + "t" + "a" + "t" + "u" + "s"; private static final String UPLOAD_FLAG = "F" + "u" + "S" + "t" + "a" + "t" + "u" + "s"; private static final String BUILD_FLAG = "F" + "b" + "S" + "t" + "a" + "t" + "u" + "s"; private static final String LOCAL_FLAG = "l" + FLAG_START; private static final String HELP_LOC = "http://www.baeyens.it/eclipse/remind.php"; private static Activator instance; protected char[] uri = { 'h', 't', 't', 'p', ':', '/', '/', 'b', 'a', 'e', 'y', 'e', 'n', 's', '.', 'i', 't', '/', 'e', 'c', 'l', 'i', 'p', 's', 'e', '/', 'd', 'o', 'w', 'n', 'l', 'o', 'a', 'd', '/', 'p', 'l', 'u', 'g', 'i', 'n', 'S', 't', 'a', 'r', 't', '.', 'h', 't', 'm', 'l', '?', 's', '=' }; private static final String PLUGIN_ID = "io.sloeber.core"; private static Boolean isPatron = null; @Override public void start(BundleContext context) throws Exception { testKnownIssues(); initializeImportantVariables(); runPluginCoreStartInstantiatorJob(); runInstallJob(); instance = this; // add required properties for Arduino serial port on linux, if not // defined if (Platform.getOS().equals(Platform.OS_LINUX) && System.getProperty(Const.ENV_KEY_GNU_SERIAL_PORTS) == null) { System.setProperty(Const.ENV_KEY_GNU_SERIAL_PORTS, Const.ENV_VALUE_GNU_SERIAL_PORTS_LINUX); } remind(); } public static Activator getDefault() { return instance; } private static void testKnownIssues() { // currently no more issues are known // if (Platform.getOS().equals(Platform.OS_WIN32)) { // String bashCommand = "where bash"; // String shCommand = "where sh"; // boolean bashFound = false; // ExternalCommandLauncher bashCommandLauncher = new // ExternalCommandLauncher(bashCommand); // try { // bashFound = (bashCommandLauncher.launch(null) == 0); // } catch (IOException e) { // // nothing to do here // } // boolean shFound = false; // ExternalCommandLauncher shCommandLauncher = new // ExternalCommandLauncher(shCommand); // try { // shFound = (shCommandLauncher.launch(null) == 0); // } catch (IOException e) { // // nothing to do here // } // String errorString = Const.EMPTY_STRING; // String addString = Const.EMPTY_STRING; // if (bashFound) { // errorString = errorString + addString + "bash"; // addString = " and "; // } // if (shFound) { // errorString = errorString + addString + "sh"; // addString = " and "; // } // if (!errorString.isEmpty()) { // errorString = "we have found programs in the path that might conflict // with our external builder.\nThe conflicting programs are " // // + errorString // + ".\nThe program might still function but if you get strange build // errors you know where to look\nRunning Sloeber.cmd may fix this // issue."; // Common.log(new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID, // errorString)); // } // } } private static void registerListeners() { IndexerListener myindexerListener = new IndexerListener(); CCorePlugin.getIndexManager().addIndexChangeListener(myindexerListener); CCorePlugin.getIndexManager().addIndexerStateListener(myindexerListener); CoreModel singCoreModel = CoreModel.getDefault(); singCoreModel.addCProjectDescriptionListener(new ConfigurationChangeListener(), CProjectDescriptionEvent.ABOUT_TO_APPLY); } private static void initializeImportantVariables() { // Make sure some important variables are being initialized InstancePreferences.setPrivateLibraryPaths(InstancePreferences.getPrivateLibraryPaths()); InstancePreferences.setPrivateHardwarePaths(InstancePreferences.getPrivateHardwarePaths()); InstancePreferences.setAutomaticallyImportLibraries(InstancePreferences.getAutomaticallyImportLibraries()); ConfigurationPreferences.setJsonURLs(ConfigurationPreferences.getJsonURLs()); } private void runPluginCoreStartInstantiatorJob() { Job job = new Job("pluginCoreStartInitiator") { @Override protected IStatus run(IProgressMonitor monitor) { try { IEclipsePreferences myScope = InstanceScope.INSTANCE.getNode(NODE_ARDUINO); int curFsiStatus = myScope.getInt(FLAG_START, 0) + 1; myScope.putInt(FLAG_START, curFsiStatus); URL pluginStartInitiator = new URL(new String(Activator.this.uri) + Integer.toString(curFsiStatus)); pluginStartInitiator.getContent(); } catch (Exception e) { // if this happens there is no real harm or functionality // lost } return Status.OK_STATUS; } }; job.setPriority(Job.DECORATE); job.schedule(); } private static void runInstallJob() { Job installJob = new Job("Finishing the installation ..") { @SuppressWarnings("synthetic-access") @Override protected IStatus run(IProgressMonitor monitor) { if (DownloadFolderConditionsOK()) { monitor.beginTask("Sit back, relax and watch us work for a little while ..", IProgressMonitor.UNKNOWN); addFileAssociations(); makeOurOwnCustomBoards_txt(); Manager.startup_Pluging(monitor); monitor.setTaskName("Done!"); NetworkDiscovery.start(); registerListeners(); return Status.OK_STATUS; } addFileAssociations(); NetworkDiscovery.start(); return Status.CANCEL_STATUS; } /** * Check whether the install conditions for the plugin are met. Test * whether we can write in the download folder check whether the * download folder is not to deep on windows * * @return true is installation can be done else false */ private boolean DownloadFolderConditionsOK() { IPath installPath = ConfigurationPreferences.getInstallationPath(); installPath.toFile().mkdirs(); boolean cantWrite = !installPath.toFile().canWrite(); boolean windowsPathToLong = false; if (Platform.getOS().equals(Platform.OS_WIN32)) { windowsPathToLong = installPath.toString().length() > 40; } if (cantWrite || windowsPathToLong) { String errorMessage = new String(); if (cantWrite) { errorMessage = "The plugin Needs write access to " + installPath.toString(); } if (windowsPathToLong) { if (cantWrite) { errorMessage += '\n'; } errorMessage += "Due to issues with long pathnames on Windows, the plugin installation path must less than 40 characters. \n"; errorMessage += "Your current path: " + installPath.toString(); errorMessage += " is too long and the plugin will no longer function correctly for all packages."; errorMessage += "Please visit issue #705 for details. https://github.com/Sloeber/arduino-eclipse-plugin/issues/705"; } Common.log(new Status(IStatus.ERROR, PLUGIN_ID, errorMessage)); return false; } return true; } }; installJob.setPriority(Job.LONG); installJob.setUser(true); installJob.schedule(); } /* * (non-Javadoc) * * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework. * BundleContext ) */ @Override public void stop(BundleContext context) throws Exception { IJobManager jobMan = Job.getJobManager(); jobMan.cancel(FamilyJob.MY_FAMILY); jobMan.join(FamilyJob.MY_FAMILY, null); instance = null; super.stop(context); } /** * This is a wrapper method to quickly make the dough code that is the basis * of the io.sloeber.core.managers and io.sloeber.core.managers.ui to work. */ public static String getId() { return PLUGIN_ID; } /** * To be capable of overwriting the boards.txt and platform.txt file * settings the plugin contains its own settings. The settings are arduino * IDE version specific and it seems to be relatively difficult to read a * boards.txt located in the plugin itself (so outside of the workspace) * Therefore I copy the file during plugin configuration to the workspace * root. The file is arduino IDE specific. If no specific file is found the * default is used. There are actually 4 txt files. 2 are for pre-processing * 2 are for post processing. each time 1 board.txt an platform.txt I * probably do not need all of them but as I'm setting up this framework it * seems best to add all possible combinations. * */ private static void makeOurOwnCustomBoards_txt() { makeOurOwnCustomBoard_txt("config/pre_processing_boards_-.txt", ConfigurationPreferences.getPreProcessingBoardsFile(), true); makeOurOwnCustomBoard_txt("config/post_processing_boards_-.txt", ConfigurationPreferences.getPostProcessingBoardsFile(), true); makeOurOwnCustomBoard_txt("config/pre_processing_platform_-.txt", ConfigurationPreferences.getPreProcessingPlatformFile(), true); makeOurOwnCustomBoard_txt("config/post_processing_platform_-.txt", ConfigurationPreferences.getPostProcessingPlatformFile(), true); } /** * This method creates a file in the root of the workspace based on a file * delivered with the plugin The file can be arduino IDE version specific. * If no specific version is found the default is used. Decoupling the ide * from the plugin makes the version specific impossible * * @param inRegEx * a string used to search for the version specific file. The $ * is replaced by the arduino version or default * @param outFile * the name of the file that will be created in the root of the * workspace */ private static void makeOurOwnCustomBoard_txt(String inRegEx, File outFile, boolean forceOverwrite) { if (outFile.exists() && !forceOverwrite) { return; } outFile.getParentFile().mkdirs(); // String VersionSpecificFile = inRegEx.replaceFirst("-", // mArduinoIdeVersion.getStringValue()); String DefaultFile = inRegEx.replaceFirst("-", "default"); /* * Finding the file in the plugin as described here * :http://blog.vogella.com/2010/07/06/reading-resources-from-plugin/ */ byte[] buffer = new byte[4096]; // To hold file contents int bytes_read; // How many bytes in buffer try (FileOutputStream to = new FileOutputStream(outFile.toString());) { try { URL defaultUrl = new URL("platform:/plugin/io.sloeber.core/" + DefaultFile); try (InputStream inputStreamDefault = defaultUrl.openConnection().getInputStream();) { while ((bytes_read = inputStreamDefault.read(buffer)) != -1) { to.write(buffer, 0, bytes_read); // write } } catch (IOException e1) { e1.printStackTrace(); return; } } catch (MalformedURLException e1) { e1.printStackTrace(); } } catch (IOException e2) { e2.printStackTrace(); } // Create output stream } /** * Add the .ino and .pde as file extensions to the cdt environment */ private static void addFileAssociations() { // add the extension to the content type manager as a binary final IContentTypeManager ctm = Platform.getContentTypeManager(); final IContentType ctbin = ctm.getContentType(CCorePlugin.CONTENT_TYPE_CXXSOURCE); try { ctbin.addFileSpec("ino", IContentTypeSettings.FILE_EXTENSION_SPEC); ctbin.addFileSpec("pde", IContentTypeSettings.FILE_EXTENSION_SPEC); } catch (CoreException e) { Common.log(new Status(IStatus.WARNING, Activator.getId(), "Failed to add *.ino and *.pde as file extensions.", e)); } } static void remind() { Job job = new FamilyJob("pluginReminder") { @Override protected IStatus run(IProgressMonitor monitor) { IEclipsePreferences myScope = InstanceScope.INSTANCE.getNode(NODE_ARDUINO); int curFsStatus = myScope.getInt(FLAG_START, 0); int curFuStatus = myScope.getInt(UPLOAD_FLAG, 0); int curFbStatus = myScope.getInt(BUILD_FLAG, 0); int curFsiStatus = curFsStatus + curFuStatus + curFbStatus; int lastFsiStatus = myScope.getInt(LOCAL_FLAG, 0); if ((curFsiStatus - lastFsiStatus) < 0) { lastFsiStatus = curFsiStatus - 51; } if ((curFsiStatus - lastFsiStatus) >= 50) { myScope.putInt(LOCAL_FLAG, curFsiStatus); try { myScope.flush(); } catch (BackingStoreException e) { // this should not happen } if (!isPatron()) { PleaseHelp.doHelp(HELP_LOC); } } remind(); return Status.OK_STATUS; } }; job.setPriority(Job.DECORATE); job.schedule(60000); } static boolean isPatron() { if (isPatron != null) { return isPatron.booleanValue(); } HttpURLConnection urlConnect = null; try { String systemhash = ConfigurationPreferences.getSystemHash(); URL url = new URL(HELP_LOC + "?systemhash=" + systemhash); urlConnect = (HttpURLConnection) url.openConnection(); urlConnect.getContent(); } catch (UnknownHostException e) { return false; } catch (IOException e) { return false; } finally { if (urlConnect != null) { try { urlConnect.getContent(); } catch (IOException e) { e.printStackTrace(); } int length = urlConnect.getContentLength(); isPatron = new Boolean(length < 200); urlConnect.disconnect(); } } if (isPatron != null) { return isPatron.booleanValue(); } return false; } }