From 275e9bba423f523b3ce6677916cd5f6d8fcf9645 Mon Sep 17 00:00:00 2001 From: jan Date: Wed, 21 Aug 2024 01:33:05 +0200 Subject: [PATCH 001/115] Excluded resources from build should not provide include directive #1672 When a library is added to a project the link to the source is added to the include directive for the build command. When the library is excluded the include directive should not be on the build command. It was ... so I fixed it and then the rename regression test failed. --- .../src/io/sloeber/core/Activator.java | 2 +- .../core/api/ISloeberConfiguration.java | 3 +- .../core/internal/SloeberConfiguration.java | 126 +++++++++++++++--- .../ConfigurationChangeListener.java | 19 ++- .../toolchain/ArduinoLanguageProvider.java | 11 +- .../src/io/sloeber/core/tools/Helpers.java | 2 +- 6 files changed, 137 insertions(+), 26 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/core/Activator.java b/io.sloeber.core/src/io/sloeber/core/Activator.java index afa7859e..9bdda40c 100644 --- a/io.sloeber.core/src/io/sloeber/core/Activator.java +++ b/io.sloeber.core/src/io/sloeber/core/Activator.java @@ -191,7 +191,7 @@ private static void registerListeners() { CCorePlugin.getIndexManager().addIndexerStateListener(myindexerListener); CoreModel singCoreModel = CoreModel.getDefault(); - singCoreModel.addCProjectDescriptionListener(myConfigurationChangeListener,CProjectDescriptionEvent.ABOUT_TO_APPLY); + singCoreModel.addCProjectDescriptionListener(myConfigurationChangeListener,CProjectDescriptionEvent.ABOUT_TO_APPLY|CProjectDescriptionEvent.APPLIED); diff --git a/io.sloeber.core/src/io/sloeber/core/api/ISloeberConfiguration.java b/io.sloeber.core/src/io/sloeber/core/api/ISloeberConfiguration.java index 9d1a7bc4..d5a8f37c 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/ISloeberConfiguration.java +++ b/io.sloeber.core/src/io/sloeber/core/api/ISloeberConfiguration.java @@ -58,7 +58,7 @@ public static ISloeberConfiguration getConfig(IAutoBuildConfigurationDescription return (ISloeberConfiguration) autoBuildConfig.getAutoBuildConfigurationExtensionDescription(); } - IFolder getArduinoCodeFolder(); + IFolder getArduinoConfigurationFolder(); IFolder getArduinoCoreFolder(); @@ -120,5 +120,6 @@ public static ISloeberConfiguration getConfig(IAutoBuildConfigurationDescription Map getUsedLibraries(); void setLibraries(Set selectedLibraries); + IFolder getArduinoRootFolder(); } diff --git a/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java b/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java index d61d969b..517d304d 100644 --- a/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java +++ b/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java @@ -11,6 +11,7 @@ import static io.sloeber.core.api.Common.*; import static io.sloeber.core.api.Const.*; +import org.eclipse.cdt.core.settings.model.CSourceEntry; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.core.settings.model.ICSourceEntry; import org.eclipse.cdt.core.settings.model.extension.CConfigurationData; @@ -36,6 +37,7 @@ import io.sloeber.core.Activator; import io.sloeber.core.Messages; import io.sloeber.core.api.BoardDescription; +import io.sloeber.core.api.Common; import io.sloeber.core.api.CompileDescription; import io.sloeber.core.api.ConfigurationPreferences; import io.sloeber.core.api.IArduinoLibraryVersion; @@ -167,25 +169,30 @@ public IProject getProject() { } @Override - public IFolder getArduinoCodeFolder() { - String cdtConfDescName = getAutoBuildDescription().getCdtConfigurationDescription().getName(); + public IFolder getArduinoRootFolder() { IProject project = getProject(); - return project.getFolder(SLOEBER_ARDUINO_FOLDER_NAME).getFolder(cdtConfDescName); + return project.getFolder(SLOEBER_ARDUINO_FOLDER_NAME); + } + + @Override + public IFolder getArduinoConfigurationFolder() { + String cdtConfDescName = getCDTConfName(); + return getArduinoRootFolder().getFolder(cdtConfDescName); } @Override public IFolder getArduinoCoreFolder() { - return getArduinoCodeFolder().getFolder(SLOEBER_CODE_FOLDER_NAME); + return getArduinoConfigurationFolder().getFolder(SLOEBER_CODE_FOLDER_NAME); } @Override public IFolder getArduinoVariantFolder() { - return getArduinoCodeFolder().getFolder(SLOEBER_VARIANT_FOLDER_NAME); + return getArduinoConfigurationFolder().getFolder(SLOEBER_VARIANT_FOLDER_NAME); } @Override public IFolder getArduinoLibraryFolder() { - return getArduinoCodeFolder().getFolder(SLOEBER_LIBRARY_FOLDER_NAME); + return getArduinoConfigurationFolder().getFolder(SLOEBER_LIBRARY_FOLDER_NAME); } @Override @@ -202,12 +209,17 @@ private void configureWhenDirty() { } - private void LinkToCore() { + /* Update the links to the Arduino stuff + * Return true if the project needs a refresh + */ + private boolean LinkToCore() { if (!ResourcesPlugin.getWorkspace().isTreeLocked()) { if (projectNeedsUpdate()) { updateArduinoCodeLinks(); + return true; } } + return false; } @@ -288,7 +300,7 @@ private boolean getEnvVarsNonExpanding() { private IPath getCodeLocation() { IProject project = getProject(); - IPath arduinoPath=project.getFolder(SLOEBER_ARDUINO_FOLDER_NAME).getFullPath(); + IPath arduinoPath=getArduinoRootFolder().getFullPath(); ICSourceEntry[] sourceEntries = getAutoBuildDesc().getCdtConfigurationDescription().getSourceEntries(); for (ICSourceEntry curEntry : sourceEntries) { IPath entryPath=curEntry.getFullPath(); @@ -435,17 +447,24 @@ public IAutoBuildConfigurationDescription getAutoBuildDesc() { * */ private void updateArduinoCodeLinks() { - IPath corePath = myBoardDescription.getActualCoreCodePath(); + IFolder arduinoVariantFolder = getArduinoVariantFolder(); + IFolder arduinoCodeFolder = getArduinoCoreFolder(); + NullProgressMonitor monitor=new NullProgressMonitor(); + try { + arduinoVariantFolder.delete(true, monitor); + arduinoCodeFolder.delete(true, monitor); + } catch (CoreException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + IPath corePath = myBoardDescription.getActualCoreCodePath(); if (corePath != null) { - Helpers.LinkFolderToFolder(corePath, getArduinoCoreFolder()); - IPath variantPath = myBoardDescription.getActualVariantPath(); - if ((variantPath == null) || (!variantPath.toFile().exists())) { - // remove the existing link - Helpers.removeCodeFolder(arduinoVariantFolder); - } else { - Helpers.LinkFolderToFolder(variantPath, arduinoVariantFolder); - } + Helpers.LinkFolderToFolder(corePath, arduinoCodeFolder); + } + IPath variantPath = myBoardDescription.getActualVariantPath(); + if (variantPath != null) { + Helpers.LinkFolderToFolder(variantPath, arduinoVariantFolder); } } @@ -603,9 +622,80 @@ public LinkedHashMap getPostbuildSteps() { return ret; } - public void apply() { + /** + * Because SloeberConfiguration are copied and can be changed at all times + * but there is only 1 disk representation we can not update the disk + * at the time the SloeberConfiguration is changed + * + * When a configuration becomes "the active" configuration this method will + * create the necessary resources on disk. + * This apply is when this configuration is new. + */ + public void aboutToApplyConfigChange() { + updateSourceEntries(); configureWhenDirty() ; + + } + + public void appliedConfigChange() { LinkToCore(); } + /** + * Look at the source entries to see whether this is a rename + * If it is a rename update the source entries and update the arduino/[configName] + */ + private void updateSourceEntries() { + + IFolder curArduinoConfigurationfolder=getArduinoConfigurationFolder(); + IFolder oldArduinoConfigurationfolder=null; + + //update the source entries + + ICSourceEntry[] orgSourceEntries = getAutoBuildDesc().getCdtConfigurationDescription().getSourceEntries(); + ICSourceEntry[] newSourceEntries=new ICSourceEntry[orgSourceEntries.length]; + for ( int curItem=0; curItem getSettingEntries(ICConfigurationDescription return null; } List ret = new LinkedList<>(); - ISloeberConfiguration autoConfDesc= ISloeberConfiguration.getConfig(cfgDescription); + IAutoBuildConfigurationDescription autoBuildConfData=IAutoBuildConfigurationDescription.getConfig(cfgDescription); + ISloeberConfiguration autoConfDesc= ISloeberConfiguration.getConfig(autoBuildConfData); + ICSourceEntry[] mySrcEntries = IAutoBuildConfigurationDescription.getResolvedSourceEntries(autoBuildConfData); Set includeFolders = autoConfDesc.getIncludeFolders(); int flags = ICSettingEntry.READONLY | ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED; for(IFolder curFolder:includeFolders) { + boolean isExcluded = mySrcEntries == null ? false + : CDataUtil.isExcluded(curFolder.getProjectRelativePath(), mySrcEntries); + if (isExcluded) { + continue; + } ret.add(CDataUtil.getPooledEntry(new CIncludePathEntry(curFolder, flags))); } return LanguageSettingsStorage.getPooledList(ret); diff --git a/io.sloeber.core/src/io/sloeber/core/tools/Helpers.java b/io.sloeber.core/src/io/sloeber/core/tools/Helpers.java index bc2b2932..2f2240e5 100644 --- a/io.sloeber.core/src/io/sloeber/core/tools/Helpers.java +++ b/io.sloeber.core/src/io/sloeber/core/tools/Helpers.java @@ -265,7 +265,7 @@ public static void createNewFolder(IFolder newFolder, IPath linklocation) throws if (linklocation != null) { URI relativeLinklocation = newFolder.getProject().getPathVariableManager() .convertToRelative(URIUtil.toURI(linklocation), false, null); - newFolder.createLink(relativeLinklocation, IResource.REPLACE | IResource.ALLOW_MISSING_LOCAL, null); + newFolder.createLink(relativeLinklocation, IResource.REPLACE | IResource.ALLOW_MISSING_LOCAL |IResource.BACKGROUND_REFRESH , null); } else { newFolder.create(0, false, null); } From 67077782780482ddf5853a468dab5e1b4ec9eacd Mon Sep 17 00:00:00 2001 From: jan Date: Wed, 21 Aug 2024 01:33:44 +0200 Subject: [PATCH 002/115] Archives were not correctly placed on the command line --- io.sloeber.core/plugin.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io.sloeber.core/plugin.xml b/io.sloeber.core/plugin.xml index 8309f00e..563c4359 100644 --- a/io.sloeber.core/plugin.xml +++ b/io.sloeber.core/plugin.xml @@ -240,7 +240,6 @@ name="%tool.archiver.name" toolType="o->ar"> From 7f839491465f8ad6dc6e8f17afa58b757d1aea5d Mon Sep 17 00:00:00 2001 From: jan Date: Thu, 22 Aug 2024 02:57:50 +0200 Subject: [PATCH 003/115] Configs with libs can now be renamed #1674 --- .../io/sloeber/core/api/LibraryManager.java | 17 +++ .../core/internal/SloeberConfiguration.java | 125 ++++++++++++++---- 2 files changed, 119 insertions(+), 23 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/core/api/LibraryManager.java b/io.sloeber.core/src/io/sloeber/core/api/LibraryManager.java index 95ab3ad9..573ec37d 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/LibraryManager.java +++ b/io.sloeber.core/src/io/sloeber/core/api/LibraryManager.java @@ -18,6 +18,7 @@ import java.util.TreeMap; import java.util.TreeSet; +import org.eclipse.core.resources.IFolder; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; @@ -450,4 +451,20 @@ private static Map getLibrariesHarware(BoardDesc return ret; } + public static IArduinoLibraryVersion getLibraryVersionFromLocation(IFolder libFolder,BoardDescription boardDescriptor) { + // TODO Auto-generated method stub + + if (boardDescriptor != null) { + if(boardDescriptor.getReferencedCoreLibraryPath().isPrefixOf(libFolder.getLocation())) { + return getLibrariesHarware(boardDescriptor).get(libFolder.getName()); + } + } + + if(ConfigurationPreferences.getInstallationPathLibraries().isPrefixOf(libFolder.getLocation())) { + return getLibrariesdManaged().get(libFolder.getName()); + } + + return getLibrariesPrivate().get(libFolder.getName()); + } + } \ No newline at end of file diff --git a/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java b/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java index 517d304d..fa048d1e 100644 --- a/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java +++ b/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java @@ -42,6 +42,7 @@ import io.sloeber.core.api.ConfigurationPreferences; import io.sloeber.core.api.IArduinoLibraryVersion; import io.sloeber.core.api.ISloeberConfiguration; +import io.sloeber.core.api.LibraryManager; import io.sloeber.core.api.OtherDescription; import io.sloeber.core.tools.Helpers; import io.sloeber.core.tools.uploaders.UploadSketchWrapper; @@ -495,6 +496,70 @@ public Set getIncludeFolders() { return ret; } + /** + * Get all the libraries that are linked to on disk + * + * @return + * @throws CoreException + */ + private Map getLibrariesFromLinks() throws CoreException { + Map ret = new HashMap<>(); + IFolder libFolder = getArduinoLibraryFolder(); + for (IResource curResource : libFolder.members()) { + if (curResource instanceof IFolder) { + IFolder curFolder = (IFolder) curResource; + IArduinoLibraryVersion curLib = myLibraries.get(curFolder.getName()); + if (curLib != null){ + //We knbow the lib so it is ok + ret.put(curLib.getName(),curLib); + continue; + } + + curLib=LibraryManager.getLibraryVersionFromLocation(curFolder,getBoardDescription()); + if (curLib != null){ + ret.put(curLib.getName(),curLib); + } + } + } + return ret; + } + + + /** + * remove the links from the libraries on disk + * + */ + private void removeLibraryLinks() { + IProgressMonitor monitor = new NullProgressMonitor(); + IFolder libFolder = getArduinoLibraryFolder(); + for (String curLib : myLibraries.keySet()) { + IFolder curLibFolder=libFolder.getFolder(curLib); + if (curLibFolder.exists()) { + try { + curLibFolder.delete(true, monitor); + } catch (CoreException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + } + + /** + * remove the links from the libraries on disk + * + */ + private void linkLibrariesToFolder() { + IFolder libFolder = getArduinoLibraryFolder(); + for (IArduinoLibraryVersion curLib : myLibraries.values()) { + IFolder curLibFolder=libFolder.getFolder(curLib.getName()); + Helpers.linkDirectory(curLib.getInstallPath(), curLibFolder); + } + } + + + + @Override public void reAttachLibraries() { IProgressMonitor monitor = new NullProgressMonitor(); @@ -632,20 +697,33 @@ public LinkedHashMap getPostbuildSteps() { * This apply is when this configuration is new. */ public void aboutToApplyConfigChange() { - updateSourceEntries(); + try { + myLibraries =getLibrariesFromLinks(); + } catch (CoreException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if(updateSourceEntries()) { + //the config hasbeen renamed; + // remove the library links + removeLibraryLinks(); + } configureWhenDirty() ; } public void appliedConfigChange() { LinkToCore(); + linkLibrariesToFolder(); } /** * Look at the source entries to see whether this is a rename * If it is a rename update the source entries and update the arduino/[configName] + * + * return true if the config has been renamed otherwise false */ - private void updateSourceEntries() { + private boolean updateSourceEntries() { IFolder curArduinoConfigurationfolder=getArduinoConfigurationFolder(); IFolder oldArduinoConfigurationfolder=null; @@ -667,30 +745,31 @@ private void updateSourceEntries() { newSourceEntries[curItem]=orgSourceEntries[curItem]; } } - if(oldArduinoConfigurationfolder!=null) { - try { - if(oldArduinoConfigurationfolder.exists()) { - NullProgressMonitor monitor= new NullProgressMonitor(); - IFolder deleteFolder=oldArduinoConfigurationfolder.getFolder(SLOEBER_VARIANT_FOLDER_NAME); - if(deleteFolder.exists()) { - deleteFolder.delete(true, monitor); - } - deleteFolder=oldArduinoConfigurationfolder.getFolder(SLOEBER_CODE_FOLDER_NAME); - if(deleteFolder.exists()) { - deleteFolder.delete(true, monitor ); - } - if(oldArduinoConfigurationfolder.exists()) { - oldArduinoConfigurationfolder.delete(true, monitor ); - } + if (oldArduinoConfigurationfolder == null) { + return false; + } + try { + if (oldArduinoConfigurationfolder.exists()) { + NullProgressMonitor monitor = new NullProgressMonitor(); + IFolder deleteFolder = oldArduinoConfigurationfolder.getFolder(SLOEBER_VARIANT_FOLDER_NAME); + if (deleteFolder.exists()) { + deleteFolder.delete(true, monitor); + } + deleteFolder = oldArduinoConfigurationfolder.getFolder(SLOEBER_CODE_FOLDER_NAME); + if (deleteFolder.exists()) { + deleteFolder.delete(true, monitor); + } + if (oldArduinoConfigurationfolder.exists()) { + oldArduinoConfigurationfolder.delete(true, monitor); } - getAutoBuildDesc().getCdtConfigurationDescription().setSourceEntries(newSourceEntries); - - } catch (Exception e) { - Common.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID,"Failed to modify configuration for rename",e)); } - } + getAutoBuildDesc().getCdtConfigurationDescription().setSourceEntries(newSourceEntries); - } + } catch (Exception e) { + Common.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, "Failed to modify configuration for rename", e)); + } + return true; + } private String getCDTConfName() { return getAutoBuildDescription().getCdtConfigurationDescription().getName(); From 428ae7167e3c1b9b8e7b4818d388583808a96ea7 Mon Sep 17 00:00:00 2001 From: jan Date: Fri, 23 Aug 2024 15:37:01 +0200 Subject: [PATCH 004/115] fix for package_tl7788_index.json --- io.sloeber.core/src/io/sloeber/core/txt/WorkAround.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/core/txt/WorkAround.java b/io.sloeber.core/src/io/sloeber/core/txt/WorkAround.java index 5f8a543c..753a8e62 100644 --- a/io.sloeber.core/src/io/sloeber/core/txt/WorkAround.java +++ b/io.sloeber.core/src/io/sloeber/core/txt/WorkAround.java @@ -45,7 +45,7 @@ public class WorkAround { // Each time this class is touched consider changing the String below to enforce // updates - private static final String FIRST_SLOEBER_WORKAROUND_LINE = "#Sloeber created TXT file V3.00.test 24 "; + private static final String FIRST_SLOEBER_WORKAROUND_LINE = "#Sloeber created TXT file V3.00.test 28 "; private static Map USB_replacers; @@ -403,7 +403,7 @@ private static String platformApplyStandardWorkArounds(String inPlatformTxt) { // the fix below seems no longer needed but is still on august 2021 // Arduino treats core differently so we need to change the location of directly // referenced files this manifests only in the combine recipe - String changed = origRecipe.replaceAll("(\\{build\\.path})(/core)?/sys", "$1/core/core/sys") + " "; + String changed = origRecipe.replaceAll("(\\{build\\.path})(/core)?/sys", "$1/"+SLOEBER_ARDUINO_FOLDER_NAME+SLACH+"{ConfigName}/core/sys") + " "; changed = changed.replace(" \"{build.path}/{archive_file}\" ", " {ARCHIVES} "); changed = changed.replace(" \"{archive_file_path}\" ", " {ARCHIVES} "); From ae020d46da9138e9e7a81876ae0879dbd84d8201 Mon Sep 17 00:00:00 2001 From: jan Date: Fri, 23 Aug 2024 15:37:35 +0200 Subject: [PATCH 005/115] Fix for the case the library folder does not exist --- .../src/io/sloeber/core/internal/SloeberConfiguration.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java b/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java index fa048d1e..87d7d766 100644 --- a/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java +++ b/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java @@ -505,6 +505,9 @@ public Set getIncludeFolders() { private Map getLibrariesFromLinks() throws CoreException { Map ret = new HashMap<>(); IFolder libFolder = getArduinoLibraryFolder(); + if(!libFolder.exists()) { + return ret; + } for (IResource curResource : libFolder.members()) { if (curResource instanceof IFolder) { IFolder curFolder = (IFolder) curResource; From 86a874bd6904e3cacc34b0c0cbeaaf2456573704 Mon Sep 17 00:00:00 2001 From: jan Date: Fri, 23 Aug 2024 22:13:46 +0200 Subject: [PATCH 006/115] Test ran successfull on +1400 boards --- ...teAndCompileDefaultInoOnAllBoardsTest.java | 48 ++++++++++++++----- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java index 88416f83..6876c867 100644 --- a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java +++ b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java @@ -12,7 +12,10 @@ import java.util.List; import java.util.stream.Stream; +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.dom.IPDOMManager; import org.eclipse.core.runtime.IPath; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -33,10 +36,30 @@ public class CreateAndCompileDefaultInoOnAllBoardsTest { private static final boolean closeFailedProjects = false; private static int myTotalFails = 0; - private static int maxFails = 50; - private static int mySkipTestsAtStart = 0; + private static final int maxFails = 50; + private static final int mySkipTestsAtStart = 0; + + @BeforeAll + static public void beforeAll() throws Exception { + Shared.setCloseFailedProjects(closeFailedProjects); + // make sure all plugin installation is done + Shared.waitForAllJobsToFinish(); + Shared.setUseParralBuildProjects(Boolean.TRUE); + CCorePlugin.getIndexManager().setDefaultIndexerId( IPDOMManager.ID_NO_INDEXER ); + // build the Arduino way + Preferences.setUseArduinoToolSelection(true); + Preferences.setUseBonjour(false); + installAdditionalBoards(); + } private static final String[] packageUrlsToIgnoreonAllOSes = { + + //This package contains an additional avr folder for the tiny. + //This should work. Bug=Sloeber starts from disk not from boardsmanager + //therefore doesn't find the json file + //this test excludes the other hardwares but I can't be bothered + "http://www.leonardomiliani.com/repository/package_leonardomiliani.com_index.json", + // There is a newer version "https://raw.githubusercontent.com/ElektorLabs/arduino/master/package_elektor-labs.com_ide-1.6.5_index.json", // Third party url implies this is outdated (it also doesn't work) @@ -69,15 +92,20 @@ public class CreateAndCompileDefaultInoOnAllBoardsTest { // confirmed 2020 03 09 version 25 12 17 "https://raw.githubusercontent.com/avandalen/SAM15x15/master/package_avdweb_nl_index.json", - //no longer supported causes issues with USB_MANUFACTOR + //no longer supported "https://raw.githubusercontent.com/mikaelpatel/Cosa/master/package_cosa_index.json", + "https://engimusing.github.io/arduinoIDE/package_engimusing_modules_index.json", //Seems no longer supported json file download fails "https://raw.githubusercontent.com/MaximIntegratedMicros/arduino-collateral/master/package_maxim_index.json", - + //another fail to download json "https://www.mattairtech.com/software/arduino/package_MattairTech_index.json", + //malformed json file (confirmed by arduino IDE + "https://raw.githubusercontent.com/DFRobot/DFRobotDuinoBoard/master/package_dfrobot_index.json", + "https://raw.githubusercontent.com/DFRobot/DFRobotDuinoBoard/master/package_dfrobot_iot_mainboard.json", + // uses busybox on windows so command line issues and on Linux the all in one // archive build fails "https://github.com/tenbaht/sduino/raw/master/package_sduino_stm8_index.json", }; @@ -107,8 +135,8 @@ public class CreateAndCompileDefaultInoOnAllBoardsTest { // confirmed 2020 03 09 version 4.0.0 "SmartEverything Bee (Native USB Port)", - // issue #1152 (confirmed 2020 03 07 ) - "Engimusing EFM32WG840", "Engimusing EFM32WG842", "Engimusing EFM32WG842F64", + // confirmed 2024 08 23 not to work in arduino IDE + "Kitten Syringe nRF52833 (NSFW)", "D-duino-32", // confirmed 2020 03 09 "SparkFun Blynk Board", // (confirmed 2020 03 07 ) @@ -271,13 +299,7 @@ public class CreateAndCompileDefaultInoOnAllBoardsTest { }; public static Stream allBoards() throws Exception { - Shared.setCloseFailedProjects(closeFailedProjects); - // make sure all plugin installation is done - Shared.waitForAllJobsToFinish(); - // build the Arduino way - Preferences.setUseArduinoToolSelection(true); - Preferences.setUseBonjour(false); - installAdditionalBoards(); + List boards = new ArrayList<>(); for (File curBoardFile : BoardsManager.getAllBoardsFiles()) { From bf4331cd006136300c183c826165ce51e2e1221a Mon Sep 17 00:00:00 2001 From: jan Date: Sat, 24 Aug 2024 01:11:53 +0200 Subject: [PATCH 007/115] Add extension post and prebuild commands to the generated makefile --- .../providers/MakefileGenerator.java | 127 +++++++----------- 1 file changed, 46 insertions(+), 81 deletions(-) diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/MakefileGenerator.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/MakefileGenerator.java index 08af9ed2..3c751d5d 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/MakefileGenerator.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/MakefileGenerator.java @@ -6,6 +6,7 @@ import java.text.MessageFormat; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Set; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; @@ -309,29 +310,7 @@ protected StringBuffer topMakeGetRMCommand(Set myDependencyMacros) { return (buffer.append(NEWLINE)); } - protected String topMakeGetPreBuildStep() { - String prebuildStep = myAutoBuildConfData.getPrebuildStep(); - // JABA issue927 adding recipe.hooks.sketch.prebuild.NUMBER.pattern as cdt - // prebuild command if needed - // ICConfigurationDescription confDesc = - // ManagedBuildManager.getDescriptionForConfiguration(config); - // String sketchPrebuild = - // io.sloeber.core.common.Common.getBuildEnvironmentVariable(confDesc, - // "sloeber.prebuild", - // new String(), false); - String sketchPrebuild = getVariableValue("sloeber.prebuild", EMPTY_STRING, true, myAutoBuildConfData); //$NON-NLS-1$ - if (!sketchPrebuild.isEmpty()) { - if (!prebuildStep.isEmpty()) { - prebuildStep = prebuildStep + NEWLINE+TAB + sketchPrebuild; - } else { - prebuildStep = sketchPrebuild; - } - } - // end off JABA issue927 - // try to resolve the build macros in the prebuild step - prebuildStep = resolve(prebuildStep, EMPTY_STRING, WHITESPACE, myAutoBuildConfData); - return prebuildStep.trim(); - } + protected StringBuffer topMakeGetIncludeDependencies() { StringBuffer buffer = new StringBuffer(); @@ -339,22 +318,32 @@ protected StringBuffer topMakeGetIncludeDependencies() { // Add the comment for the "All" target buffer.append(COMMENT_START).append(MakefileGenerator_comment_build_alltarget).append(NEWLINE); - String prebuildStep = topMakeGetPreBuildStep(); - String postbuildStep = resolve(myAutoBuildConfData.getPostbuildStep(), EMPTY_STRING, WHITESPACE, - myAutoBuildConfData); - if (prebuildStep.isBlank() && postbuildStep.isBlank()) { + LinkedHashMap prebuildSteps = myAutoBuildConfData.getPrebuildSteps(); + LinkedHashMap postbuildSteps = myAutoBuildConfData.getPostbuildSteps(); + if (prebuildSteps.size()== 0 && postbuildSteps.size()==0) { buffer.append(TARGET_ALL).append(COLON).append(WHITESPACE).append(MAINBUILD).append(NEWLINE); } else { + // add prebuild announce data + String preannouncebuildStep = myAutoBuildConfData.getPreBuildAnouncement(); + if (preannouncebuildStep.length() > 0) { + buffer.append(TAB).append(DASH).append(AT_SYMBOL).append(escapedEcho(preannouncebuildStep)); + } + // Add the prebuild recipes buffer.append(TARGET_ALL).append(COLON).append(NEWLINE); - if (!prebuildStep.isBlank()) { - buffer.append(TAB).append(MAKE).append(WHITESPACE).append(NO_PRINT_DIR).append(WHITESPACE) - .append(PREBUILD).append(NEWLINE); + for (String curRecipe : prebuildSteps.values()) { + buffer.append(TAB).append(WHITESPACE).append(curRecipe).append(NEWLINE); } + // Add the make all command buffer.append(TAB).append(MAKE).append(WHITESPACE).append(NO_PRINT_DIR).append(WHITESPACE).append(MAINBUILD) .append(NEWLINE); - if (!postbuildStep.isBlank()) { - buffer.append(TAB).append(MAKE).append(WHITESPACE).append(NO_PRINT_DIR).append(WHITESPACE) - .append(POSTBUILD).append(NEWLINE); + // add postbuild announce data + String postannouncebuildStep = myAutoBuildConfData.getPostBuildAnouncement(); + if (postannouncebuildStep.length() > 0) { + buffer.append(TAB).append(DASH).append(AT_SYMBOL).append(escapedEcho(postannouncebuildStep)); + } + // Add the postbuild recipes + for (String curRecipe : postbuildSteps.values()) { + buffer.append(TAB).append(WHITESPACE).append(curRecipe).append(NEWLINE); } } @@ -396,31 +385,31 @@ protected StringBuffer topMakeGetTargets() { } buffer.append(NEWLINE).append(NEWLINE); - String prebuildStep = topMakeGetPreBuildStep(); - if (prebuildStep.length() > 0) { - - String preannouncebuildStep = myAutoBuildConfData.getPreBuildAnouncement(); - buffer.append(PREBUILD).append(COLON).append(NEWLINE); - if (preannouncebuildStep.length() > 0) { - buffer.append(TAB).append(DASH).append(AT_SYMBOL).append(escapedEcho(preannouncebuildStep)); - } - buffer.append(TAB).append(DASH).append(prebuildStep).append(NEWLINE); - buffer.append(TAB).append(DASH).append(AT_SYMBOL).append(ECHO_BLANK_LINE).append(NEWLINE); - } +// String prebuildStep = topMakeGetPreBuildStep(); +// if (prebuildStep.length() > 0) { +// +// String preannouncebuildStep = myAutoBuildConfData.getPreBuildAnouncement(); +// buffer.append(PREBUILD).append(COLON).append(NEWLINE); +// if (preannouncebuildStep.length() > 0) { +// buffer.append(TAB).append(DASH).append(AT_SYMBOL).append(escapedEcho(preannouncebuildStep)); +// } +// buffer.append(TAB).append(DASH).append(prebuildStep).append(NEWLINE); +// buffer.append(TAB).append(DASH).append(AT_SYMBOL).append(ECHO_BLANK_LINE).append(NEWLINE); +// } - String postbuildStep = myAutoBuildConfData.getPostbuildStep(); - postbuildStep = resolve(postbuildStep, EMPTY_STRING, WHITESPACE, myAutoBuildConfData); - postbuildStep = postbuildStep.trim(); - // Add the postbuild step, if specified - if (postbuildStep.length() > 0) { - String postannouncebuildStep = myAutoBuildConfData.getPostBuildAnouncement(); - buffer.append(POSTBUILD).append(COLON).append(NEWLINE); - if (postannouncebuildStep.length() > 0) { - buffer.append(TAB).append(DASH).append(AT_SYMBOL).append(escapedEcho(postannouncebuildStep)); - } - buffer.append(TAB).append(DASH).append(postbuildStep).append(NEWLINE); - buffer.append(TAB).append(DASH).append(AT_SYMBOL).append(ECHO_BLANK_LINE).append(NEWLINE); - } +// String postbuildStep = myAutoBuildConfData.getPostbuildStep(); +// postbuildStep = resolve(postbuildStep, EMPTY_STRING, WHITESPACE, myAutoBuildConfData); +// postbuildStep = postbuildStep.trim(); +// // Add the postbuild step, if specified +// if (postbuildStep.length() > 0) { +// String postannouncebuildStep = myAutoBuildConfData.getPostBuildAnouncement(); +// buffer.append(POSTBUILD).append(COLON).append(NEWLINE); +// if (postannouncebuildStep.length() > 0) { +// buffer.append(TAB).append(DASH).append(AT_SYMBOL).append(escapedEcho(postannouncebuildStep)); +// } +// buffer.append(TAB).append(DASH).append(postbuildStep).append(NEWLINE); +// buffer.append(TAB).append(DASH).append(AT_SYMBOL).append(ECHO_BLANK_LINE).append(NEWLINE); +// } return buffer; } @@ -562,34 +551,10 @@ public StringBuffer getRecipesInMakeFileStyle(IAutoBuildMakeRule makeRule) { .append(escapedEcho(MakefileGenerator_message_start_file + WHITESPACE + OUT_MACRO)); buffer.append(TAB).append(AT_SYMBOL).append(escapedEcho(tool.getAnnouncement())); - // // JABA add sketch.prebuild and postbuild if needed - // //TOFIX this should not be here - // if ("sloeber.ino".equals(fileName)) { //$NON-NLS-1$ - // - // // String sketchPrebuild = - // io.sloeber.core.common.Common.getBuildEnvironmentVariable(confDesc, - // // "sloeber.sketch.prebuild", new String(), true); //$NON-NLS-1$ - // // String sketchPostBuild = - // io.sloeber.core.common.Common.getBuildEnvironmentVariable(confDesc, - // // "sloeber.sketch.postbuild", new String(), true); //$NON-NLS-1$ - // String sketchPrebuild = resolve("sloeber.sketch.prebuild", EMPTY_STRING, - // WHITESPACE, autoBuildConfData); - // String sketchPostBuild = resolve("sloeber.sketch.postbuild", EMPTY_STRING, - // WHITESPACE,autoBuildConfData); - // if (!sketchPrebuild.isEmpty()) { - // buffer.append(TAB).append(sketchPrebuild); - // } - // buffer.append(TAB).append(buildCmd).append(NEWLINE); - // if (!sketchPostBuild.isEmpty()) { - // buffer.append(TAB).append(sketchPostBuild); - // } - // } else { for (String resolvedCommand : makeRule.getRecipes(myBuildRoot, myAutoBuildConfData)) { buffer.append(TAB).append(resolvedCommand); buffer.append(NEWLINE); } - // } - // // end JABA add sketch.prebuild and postbuild if needed buffer.append(NEWLINE); buffer.append(TAB).append(AT_SYMBOL) From 7e46346a4ee764475f9b9db2f7fdb65be7db8bd0 Mon Sep 17 00:00:00 2001 From: jan Date: Sat, 24 Aug 2024 23:10:50 +0200 Subject: [PATCH 008/115] fix the discovery regression missing includes #1672 --- .../src/io/sloeber/core/txt/WorkAround.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/core/txt/WorkAround.java b/io.sloeber.core/src/io/sloeber/core/txt/WorkAround.java index 753a8e62..5395d3e7 100644 --- a/io.sloeber.core/src/io/sloeber/core/txt/WorkAround.java +++ b/io.sloeber.core/src/io/sloeber/core/txt/WorkAround.java @@ -45,7 +45,7 @@ public class WorkAround { // Each time this class is touched consider changing the String below to enforce // updates - private static final String FIRST_SLOEBER_WORKAROUND_LINE = "#Sloeber created TXT file V3.00.test 28 "; + private static final String FIRST_SLOEBER_WORKAROUND_LINE = "#Sloeber created TXT file V3.00.test 25 "; private static Map USB_replacers; @@ -351,9 +351,9 @@ private static String platformApplyStandardWorkArounds(String inPlatformTxt) { changed = changed + "{sloeber.extra.compile} {sloeber.extra.c.compile} {sloeber.extra.all}"; String codan = changed.replace(RECIPE_C_to_O, RECIPE_C_O_CODAN); codan = codan.replace(" -o ", " "); - codan = codan.replace(" {FLAGS} ", " "); + //codan = codan.replace(" {FLAGS} ", " "); codan = codan.replace(" {OUTPUT} ", " "); - codan = codan.replace(" {compiler.cpreprocessor.flags} ", " "); + //codan = codan.replace(" {compiler.cpreprocessor.flags} ", " "); codan = codan.replace(" {INPUTS} ", DISCOVERY_PARAMETERS.replace("${", "{") + BLANK); platformTXT = platformTXT.replace(origRecipe, changed + NEWLINE + codan); @@ -368,9 +368,9 @@ private static String platformApplyStandardWorkArounds(String inPlatformTxt) { changed = changed + "{sloeber.extra.compile} {sloeber.extra.cpp.compile} {sloeber.extra.all}"; String codan = changed.replace(RECIPE_CPP_to_O, RECIPE_CPP_O_CODAN); codan = codan.replace(" -o ", " "); - codan = codan.replace(" {FLAGS} ", " "); + //codan = codan.replace(" {FLAGS} ", " "); codan = codan.replace(" {OUTPUT} ", " "); - codan = codan.replace(" {compiler.cpreprocessor.flags} ", " "); + //codan = codan.replace(" {compiler.cpreprocessor.flags} ", " "); codan = codan.replace(" {INPUTS} ", DISCOVERY_PARAMETERS.replace("${", "{") + BLANK); platformTXT = platformTXT.replace(origRecipe, changed + NEWLINE + codan); From b5ff8d9cd21e207b0c5cdeb00af605477e34597c Mon Sep 17 00:00:00 2001 From: jan Date: Sat, 24 Aug 2024 23:44:13 +0200 Subject: [PATCH 009/115] Cater for null case reported by #1666 --- io.sloeber.core/src/io/sloeber/core/api/LibraryManager.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/io.sloeber.core/src/io/sloeber/core/api/LibraryManager.java b/io.sloeber.core/src/io/sloeber/core/api/LibraryManager.java index 573ec37d..9933eba4 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/LibraryManager.java +++ b/io.sloeber.core/src/io/sloeber/core/api/LibraryManager.java @@ -455,7 +455,8 @@ public static IArduinoLibraryVersion getLibraryVersionFromLocation(IFolder libFo // TODO Auto-generated method stub if (boardDescriptor != null) { - if(boardDescriptor.getReferencedCoreLibraryPath().isPrefixOf(libFolder.getLocation())) { + IPath libPath=boardDescriptor.getReferencedCoreLibraryPath(); + if(libPath!=null && libPath.isPrefixOf(libFolder.getLocation())) { return getLibrariesHarware(boardDescriptor).get(libFolder.getName()); } } From cbe23900b7db8c75a917e98c9d436b03662554b0 Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 25 Aug 2024 18:35:45 +0200 Subject: [PATCH 010/115] Seems I used a very old package url for esp32 for tests --- io.sloeber.tests/src/io/sloeber/providers/ESP32.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io.sloeber.tests/src/io/sloeber/providers/ESP32.java b/io.sloeber.tests/src/io/sloeber/providers/ESP32.java index 74ef5c1d..5ce017e2 100644 --- a/io.sloeber.tests/src/io/sloeber/providers/ESP32.java +++ b/io.sloeber.tests/src/io/sloeber/providers/ESP32.java @@ -14,7 +14,7 @@ public class ESP32 extends MCUBoard { private static final String provider = "esp32"; private static final String architectureName = "esp32"; private static final String jsonFileName = "package_esp32_index.json"; - public static final String packageURL = "https://dl.espressif.com/dl/package_esp32_index.json"; + public static final String packageURL = "https://espressif.github.io/arduino-esp32/package_esp32_index.json"; public static final String esp32ID = "esp32"; public static MCUBoard esp32() { From 685ab31f6b46243f7603cda943c3905ecb2e1785 Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 25 Aug 2024 18:41:03 +0200 Subject: [PATCH 011/115] a implementation for #1675 and #1670 Not sure they will work properly. Needs some testing --- .../src/io/sloeber/core/Activator.java | 1 - .../io/sloeber/core/api/BoardsManager.java | 211 ++++++------------ .../core/api/ConfigurationPreferences.java | 2 +- .../src/io/sloeber/core/api/Const.java | 1 + .../ThirdPartyHardwareSelectionPage.java | 12 +- 5 files changed, 83 insertions(+), 144 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/core/Activator.java b/io.sloeber.core/src/io/sloeber/core/Activator.java index 9bdda40c..829e6bb5 100644 --- a/io.sloeber.core/src/io/sloeber/core/Activator.java +++ b/io.sloeber.core/src/io/sloeber/core/Activator.java @@ -221,7 +221,6 @@ private static void initializeImportantVariables() { InstancePreferences.setPrivateLibraryPaths(InstancePreferences.getPrivateLibraryPaths()); InstancePreferences.setPrivateHardwarePaths(InstancePreferences.getPrivateHardwarePaths()); InstancePreferences.setAutomaticallyImportLibraries(InstancePreferences.getAutomaticallyImportLibraries()); - BoardsManager.setJsonURLs(BoardsManager.getJsonURLs()); } private void runPluginCoreStartInstantiatorJob() { diff --git a/io.sloeber.core/src/io/sloeber/core/api/BoardsManager.java b/io.sloeber.core/src/io/sloeber/core/api/BoardsManager.java index 63880717..edb4b5cb 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/BoardsManager.java +++ b/io.sloeber.core/src/io/sloeber/core/api/BoardsManager.java @@ -11,6 +11,8 @@ import java.io.Reader; import java.net.MalformedURLException; import java.net.URL; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; @@ -22,11 +24,9 @@ import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; -import org.eclipse.cdt.core.parser.util.StringUtil; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; @@ -62,16 +62,13 @@ * */ public class BoardsManager { - private static String stringSplitter = "\n";//$NON-NLS-1$ - private static final String KEY_MANAGER_JSON_URLS_V3 = "Arduino Manager board Urls"; //$NON-NLS-1$ - private static final String KEY_MANAGER_ARDUINO_LIBRARY_JSON_URL = "https://downloads.arduino.cc/libraries/library_index.json"; //$NON-NLS-1$ - private static final String KEY_MANAGER_JSON_URLS = "Manager jsons"; //$NON-NLS-1$ - private static final String DEFAULT_JSON_URLS = "https://downloads.arduino.cc/packages/package_index.json\n" //$NON-NLS-1$ - + "https://raw.githubusercontent.com/jantje/hardware/master/package_jantje_index.json\n" //$NON-NLS-1$ - + "https://raw.githubusercontent.com/jantje/ArduinoLibraries/master/library_jantje_index.json\n" //$NON-NLS-1$ - + "https://arduino.esp8266.com/stable/package_esp8266com_index.json\n" //$NON-NLS-1$ - + "https://www.pjrc.com/teensy/package_teensy_index.json\n" //$NON-NLS-1$ - + KEY_MANAGER_ARDUINO_LIBRARY_JSON_URL; + private static final String THIRD_PARTY_URL_FILE="sloeber_third_party_url.txt"; //$NON-NLS-1$ + private static final String[] DEFAULT_JSON_URLS = {"https://downloads.arduino.cc/packages/package_index.json", //$NON-NLS-1$ + "https://raw.githubusercontent.com/jantje/hardware/master/package_jantje_index.json", //$NON-NLS-1$ + "https://raw.githubusercontent.com/jantje/ArduinoLibraries/master/library_jantje_index.json", //$NON-NLS-1$ + "https://arduino.esp8266.com/stable/package_esp8266com_index.json", //$NON-NLS-1$ + "https://www.pjrc.com/teensy/package_teensy_index.json", //$NON-NLS-1$ + "https://downloads.arduino.cc/libraries/library_index.json"};//$NON-NLS-1$ protected static List packageIndices; private static boolean myHasbeenLogged = false; @@ -131,7 +128,11 @@ static private BoardDescription getNewestBoardIDFromBoardsManager(String jsonFil return boardid; } - public static void addPackageURLs(HashSet packageUrlsToAdd, boolean forceDownload) { + public static void addPackageURLs(Collection packageUrlsToAdd, boolean forceDownload) { + if (!isReady()) { + Common.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); + return; + } HashSet originalJsonUrls = new HashSet<>(Arrays.asList(getJsonURLList())); packageUrlsToAdd.addAll(originalJsonUrls); @@ -139,9 +140,9 @@ public static void addPackageURLs(HashSet packageUrlsToAdd, boolean forc loadJsons(forceDownload); } - public static void setPackageURLs(HashSet packageUrls, boolean forceDownload) { + public static void setPackageURLs(Collection packageUrls, boolean forceDownload) { if (!isReady()) { - Common.log(new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); + Common.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); return; } setJsonURLs(packageUrls); @@ -152,7 +153,7 @@ public static void setPackageURLs(HashSet packageUrls, boolean forceDown * installs a subset of the latest platforms It skips the first * platforms And stops at platforms. To install the 5 first latest * platforms installsubsetOfLatestPlatforms(0,5) - * + * * @param fromIndex * the platforms at the start to skip * @param toIndex @@ -161,7 +162,7 @@ public static void setPackageURLs(HashSet packageUrls, boolean forceDown public static void installsubsetOfLatestPlatforms(int fromIndex, int toIndex) { String DEPRECATED = "DEPRECATED"; //$NON-NLS-1$ if (!isReady()) { - Common.log(new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); + Common.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); return; } envVarsNeedUpdating = true; @@ -199,7 +200,7 @@ public static void installAllLatestPlatforms() { public static void installLatestPlatform(String JasonName, String packageName, String architectureName) { if (!isReady()) { - Common.log(new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); + Common.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); return; } envVarsNeedUpdating = true; @@ -215,7 +216,7 @@ public static void installLatestPlatform(String JasonName, String packageName, S } } } - Common.log(new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID, + Common.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, "failed to find " + JasonName + " " + packageName + " " + architectureName)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } @@ -276,7 +277,7 @@ private static IStatus install(ArduinoPlatformVersion platformVersion, IProgress WorkAround.applyKnownWorkArounds(platformVersion); - System.out.println("done installing platform " + platformVersion.toString()); //$NON-NLS-1$ + System.out.println("done installing platform " + platformVersion.toString()); //$NON-NLS-1$ return mstatus; } @@ -308,10 +309,10 @@ public static File[] getAllBoardsFiles() { TreeSet boardFiles = new TreeSet<>(); for (String CurFolder : hardwareFolders) { - searchFiles(new File(CurFolder), boardFiles, Const.BOARDS_FILE_NAME, 6); + searchFiles(new File(CurFolder), boardFiles, BOARDS_FILE_NAME, 6); } if (boardFiles.size() == 0) { - Common.log(new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID, + Common.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, Helpers_No_boards_txt_found.replace(FILE_TAG, String.join("\n", hardwareFolders)), null)); //$NON-NLS-1$ return null; } @@ -323,7 +324,7 @@ private static void searchFiles(File folder, TreeSet Hardwarelists, String File[] a = folder.listFiles(); if (a == null) { if (!myHasbeenLogged) { - Common.log(new Status(IStatus.INFO, Const.CORE_PLUGIN_ID, + Common.log(new Status(IStatus.INFO, CORE_PLUGIN_ID, Helpers_Error_The_folder_is_empty.replace(FOLDER_TAG, folder.toString()), null)); myHasbeenLogged = true; } @@ -331,7 +332,10 @@ private static void searchFiles(File folder, TreeSet Hardwarelists, String } for (File f : a) { if (f.isDirectory()) { - searchFiles(f, Hardwarelists, Filename, depth - 1); + //ignore folders named tools + if(!f.getName().equals(TOOLS)) { + searchFiles(f, Hardwarelists, Filename, depth - 1); + } } else if (f.getName().equals(Filename)) { Hardwarelists.add(f); } @@ -352,7 +356,7 @@ private static String[] getHardwarePaths() { public static IStatus updatePlatforms(List platformsToInstall, List platformsToRemove, IProgressMonitor monitor, MultiStatus status) { if (!isReady()) { - status.add(new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID, BoardsManagerIsBussy, null)); + status.add(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, null)); return status; } //TODO updating the jsons after selecting what to install seems dangerous to me; check to delete @@ -405,7 +409,7 @@ public static TreeMap getAllmenus() { public static void setPrivateHardwarePaths(String[] hardWarePaths) { if (!isReady()) { - Common.log(new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); + Common.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); return; } InstancePreferences.setPrivateHardwarePaths(hardWarePaths); @@ -509,91 +513,55 @@ protected static File getLocalFileName(String url, boolean show_error) { return packagePath.toFile(); } - public static String getDefaultJsonURLs() { + public static String[] getDefaultJsonURLs() { return DEFAULT_JSON_URLS; } - public static String getJsonUrlsKey() { - return KEY_MANAGER_JSON_URLS; - } - - public static void setJsonURLs(String urls) { - setString(KEY_MANAGER_JSON_URLS, urls); - } - private static void saveJsonURLs(String urls[]) { - setString(KEY_MANAGER_JSON_URLS, StringUtil.join(urls, stringSplitter)); - } - - public static void setJsonURLs(HashSet urls) { - setString(KEY_MANAGER_JSON_URLS, StringUtil.join(urls, stringSplitter)); + private static void setJsonURLs(Collection urls) { + IPath myThirdPartyURLStoragePath=getThirdPartyURLStoragePath(); + try { + if(myThirdPartyURLStoragePath!=null ) { + Files.write(myThirdPartyURLStoragePath.toPath(),urls, Charset.forName(StandardCharsets.UTF_8.name())); + } + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } } public static String[] getJsonURLList() { - return getJsonURLs().replace("\r", new String()).split(stringSplitter); //$NON-NLS-1$ - } - - public static String getJsonURLs() { - // I added some code here to get easier from V3 to V4 - // the library json url is now managed as the boards url's so it also - // needs to be added to the json url's - // this is doen in the default but people who have installed other - // boards or do not move to the default (which is by default) - // wil not see libraries - // to fix this I changed the storage name and if the new storage name is - // empty I read the ol one and add the lib - String ret = getString(KEY_MANAGER_JSON_URLS, DEFAULT_JSON_URLS); - if (DEFAULT_JSON_URLS.equals(ret)) { - ret = getString(KEY_MANAGER_JSON_URLS_V3, DEFAULT_JSON_URLS); - if (!DEFAULT_JSON_URLS.equals(ret)) { - ret += System.lineSeparator() + KEY_MANAGER_ARDUINO_LIBRARY_JSON_URL; - setString(KEY_MANAGER_JSON_URLS, ret); - removeKey(KEY_MANAGER_JSON_URLS_V3); - } - } - return ret; + IPath myThirdPartyURLStoragePath=getThirdPartyURLStoragePath(); + try { + if(myThirdPartyURLStoragePath!=null && myThirdPartyURLStoragePath.toFile().exists()) { + ListthirdPartyURLs = Files.readAllLines(myThirdPartyURLStoragePath.toPath(), Charset.forName(StandardCharsets.UTF_8.name())); + if(thirdPartyURLs.size()>0) { + return thirdPartyURLs.toArray(new String[thirdPartyURLs.size()]); + } + } + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + //The new way of storing the thirdparty urls's failed + //try the Sloeber V3 way for downwards compatibility + String[] sloeberV4Storage= getString("Manager jsons", EMPTY_STRING).replace("\r", new String()).split("\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + if(sloeberV4Storage.length>3) { + return sloeberV4Storage; + } + //Everything failed; This is probably a new install; return the defaults; + return DEFAULT_JSON_URLS; + + } + + private static IPath getThirdPartyURLStoragePath() { + return sloeberHomePath.append(SLOEBER_HOME_SUB_FOLDER).append(THIRD_PARTY_URL_FILE); } - /** - * Completely replace the list with jsons with a new list - * - * @param newJsonUrls - */ - public static void setJsonURLs(String[] newJsonUrls) { - if (!isReady()) { - Common.log(new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); - return; - } - - String curJsons[] = getJsonURLList(); - HashSet origJsons = new HashSet<>(Arrays.asList(curJsons)); - HashSet currentSelectedJsons = new HashSet<>(Arrays.asList(newJsonUrls)); - origJsons.removeAll(currentSelectedJsons); - // remove the files from disk which were in the old lst but not in the - // new one - for (String curJson : origJsons) { - try { - File localFile = getLocalFileName(curJson, false); - if (localFile.exists()) { - localFile.delete(); - } - } catch (@SuppressWarnings("unused") Exception e) { - // ignore - } - } - // save to configurationsettings before calling LoadIndices - saveJsonURLs(newJsonUrls); - // reload the indices (this will remove all potential remaining - // references - // existing files do not need to be refreshed as they have been - // refreshed at startup - // new files will be added - loadJsons(false); - } public static void removeAllInstalledPlatforms() { if (!isReady()) { - Common.log(new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); + Common.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); return; } try { @@ -617,9 +585,9 @@ public static Map getEnvironmentVariables() { return myWorkbenchEnvironmentVariables; } myWorkbenchEnvironmentVariables.clear(); - ArduinoPlatformVersion latestAvrPlatform = getNewestInstalledPlatform(Const.VENDOR_ARDUINO, Const.AVR); - ArduinoPlatformVersion latestSamdPlatform = getNewestInstalledPlatform(Const.VENDOR_ARDUINO, Const.SAMD); - ArduinoPlatformVersion latestSamPlatform = getNewestInstalledPlatform(Const.VENDOR_ARDUINO, Const.SAM); + ArduinoPlatformVersion latestAvrPlatform = getNewestInstalledPlatform(VENDOR_ARDUINO, AVR); + ArduinoPlatformVersion latestSamdPlatform = getNewestInstalledPlatform(VENDOR_ARDUINO, SAMD); + ArduinoPlatformVersion latestSamPlatform = getNewestInstalledPlatform(VENDOR_ARDUINO, SAM); if (latestSamdPlatform != null) { myWorkbenchEnvironmentVariables.putAll(getEnvVarPlatformFileTools(latestSamdPlatform)); @@ -651,7 +619,7 @@ private static Map getEnvVarPlatformFileTools(ArduinoPlatformVer /** * given a vendor and a architecture provide the newest installed platform * version - * + * * @param vendor * @param architecture * @return the found platformVersion or null if none found @@ -827,48 +795,13 @@ static public ArduinoPackage getPackage(String packageName) { return null; } - /** - * This method removes the json files from disk and removes memory references to - * these files or their content - * - * @param packageUrlsToRemove - */ - public static void removePackageURLs(Set packageUrlsToRemove) { - if (!isReady()) { - Common.log(new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); - return; - } - // remove the files from memory - Set activeUrls = new HashSet<>(Arrays.asList(getJsonURLList())); - - activeUrls.removeAll(packageUrlsToRemove); - - setJsonURLs(activeUrls.toArray((String[]) null)); - - // remove the files from disk - for (String curJson : packageUrlsToRemove) { - File localFile = getLocalFileName(curJson, true); - if (localFile != null) { - if (localFile.exists()) { - localFile.delete(); - } - } - } - - // reload the indices (this will remove all potential remaining - // references - // existing files do not need to be refreshed as they have been - // refreshed at startup - loadJsons(false); - - } /** * Remove all packages that have a more recent version */ public static void onlyKeepLatestPlatforms() { if (!isReady()) { - Common.log(new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); + Common.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); return; } List allPackages = getPackages(); diff --git a/io.sloeber.core/src/io/sloeber/core/api/ConfigurationPreferences.java b/io.sloeber.core/src/io/sloeber/core/api/ConfigurationPreferences.java index dd7110ca..dd2264b3 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/ConfigurationPreferences.java +++ b/io.sloeber.core/src/io/sloeber/core/api/ConfigurationPreferences.java @@ -71,7 +71,7 @@ public static void setString(String key, String value) { } public static IPath getInstallationPath() { - return Common.sloeberHomePath.append("arduinoPlugin"); //$NON-NLS-1$ + return Common.sloeberHomePath.append(SLOEBER_HOME_SUB_FOLDER); } public static IPath getInstallationPathLibraries() { diff --git a/io.sloeber.core/src/io/sloeber/core/api/Const.java b/io.sloeber.core/src/io/sloeber/core/api/Const.java index 34fe564c..d7ada4c5 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Const.java +++ b/io.sloeber.core/src/io/sloeber/core/api/Const.java @@ -79,6 +79,7 @@ public class Const extends AutoBuildConstants { public static final String LOCAL = "local"; // Folder and file Information + public static final String SLOEBER_HOME_SUB_FOLDER ="arduinoPlugin"; public static final String ARDUINO_HARDWARE_FOLDER_NAME = HARDWARE; public static final String ARDUINO_CODE_FOLDER_NAME = CORE; public static final String ARDUINO_VARIANTS_FOLDER_NAME = VARIANTS; diff --git a/io.sloeber.ui/src/io/sloeber/ui/preferences/ThirdPartyHardwareSelectionPage.java b/io.sloeber.ui/src/io/sloeber/ui/preferences/ThirdPartyHardwareSelectionPage.java index 569a7116..0e48add7 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/preferences/ThirdPartyHardwareSelectionPage.java +++ b/io.sloeber.ui/src/io/sloeber/ui/preferences/ThirdPartyHardwareSelectionPage.java @@ -3,6 +3,9 @@ import static io.sloeber.ui.Activator.*; +import java.util.Arrays; +import java.util.HashSet; + import org.eclipse.cdt.core.parser.util.StringUtil; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; @@ -40,15 +43,18 @@ public ThirdPartyHardwareSelectionPage() { @Override public boolean performOk() { - BoardsManager.setJsonURLs(this.urlsText.getText().split(System.lineSeparator())); - Preferences.setUpdateJsonFiles(this.upDateJsons.getBooleanValue()); + boolean updateFiles=this.upDateJsons.getBooleanValue(); + Preferences.setUpdateJsonFiles(updateFiles); + HashSet toSetList = new HashSet<>(Arrays.asList(urlsText.getText().split(System.lineSeparator()))); + BoardsManager.setPackageURLs(toSetList,updateFiles); + return super.performOk(); } @Override protected void performDefaults() { super.performDefaults(); - this.urlsText.setText(BoardsManager.getDefaultJsonURLs()); + this.urlsText.setText(String.join(System.lineSeparator(), BoardsManager.getDefaultJsonURLs())); } @Override From 419b03e89396991a4d4a384ea19cc063b7be90d8 Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 25 Aug 2024 18:42:26 +0200 Subject: [PATCH 012/115] Externalized string and added debug; console does not work properly I'm trying to get the console to work properly for this. That is a set enabled (currently always on) and have the console at the fingertips of the user (does not seem to work right now) --- .../io/sloeber/autoBuild/core/Messages.java | 14 ++++---- .../autoBuild/core/messages.properties | 12 +++---- .../AutoBuildLanguageSettingsProvider.java | 36 +++++++++---------- 3 files changed, 30 insertions(+), 32 deletions(-) diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/core/Messages.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/core/Messages.java index 6e7fffdd..da0e8076 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/core/Messages.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/core/Messages.java @@ -20,7 +20,6 @@ public class Messages extends NLS { public static String ManagedMakeBuilder_message_no_build; public static String ManagedMakeBuilder_message_error; public static String ManagedMakeBuilder_message_error_build; - // public static String ManagedMakeBuilder_message_error_refresh; public static String ManagedMakeBuilder_message_undefined_build_command; public static String ManagedMakeBuilder_message_finished; public static String ManagedMakeBuilder_message_cancelled; @@ -122,7 +121,6 @@ public class Messages extends NLS { public static String GeneratedMakefileBuilder_cleanSelectedFiles; public static String FolderInfo_4; public static String GnuLinkOutputNameProvider_0; - public static String CommonBuilder_1; public static String CommonBuilder_2; public static String CommonBuilder_6; public static String CommonBuilder_7; @@ -140,18 +138,22 @@ public class Messages extends NLS { public static String ResourceChangeHandler2_0; public static String ToolInfo_0; public static String ToolInfo_1; - public static String AbstractBuiltinSpecsDetector_AddScannerDiscoveryMarkers; public static String AbstractBuiltinSpecsDetector_ClearingMarkers; public static String AbstractBuiltinSpecsDetector_DiscoverBuiltInSettingsJobName; - public static String AbstractBuiltinSpecsDetector_RunningScannerDiscovery; - public static String AbstractBuiltinSpecsDetector_ScannerDiscoveryMarkerLocationPreferences; - public static String AbstractBuiltinSpecsDetector_ScannerDiscoveryMarkerLocationProperties; public static String AbstractBuiltinSpecsDetector_ScannerDiscoveryTaskTitle; public static String AbstractBuiltinSpecsDetector_SerializingResults; public static String ExternalBuilderName; public static String InternalBuilderName; public static String InternalBuildRunner_NoNeedToRun; + + public static String ScannerDiscoveryMarkerLocationPreferences; + + public static String ScannerDiscoveryMarkerLocationProperties; + + public static String AddScannerDiscoveryMarkers; + + public static String RunningScannerDiscovery; static { // initialize resource bundle NLS.initializeMessages(BUNDLE_NAME, Messages.class); diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/core/messages.properties b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/core/messages.properties index 0e54f6ea..f7ec54e3 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/core/messages.properties +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/core/messages.properties @@ -150,7 +150,6 @@ CfgScannerConfigUtil_ErrorNotSupported=Only type {0} is supported in this method GeneratedMakefileBuilder_cleanSelectedFiles=Cleaning Selected Files FolderInfo_4=converter invocation failed GnuLinkOutputNameProvider_0=tool parent must be one of configuration, toolchain, or resource configuration -CommonBuilder_1=customized builder created for builder that does not support customization CommonBuilder_2=request for building non active configuration for the builder that does not support this CommonBuilder_6=Time consumed: {0} ms. \u0020 CommonBuilder_7=Info: Parallel threads used: {0} @@ -171,17 +170,16 @@ ToolInfo_0=conversion failure ToolInfo_1=the tool is removed #Language settings providers messages -AbstractBuiltinSpecsDetector_AddScannerDiscoveryMarkers=Adding Scanner Discovery markers +AddScannerDiscoveryMarkers=Adding Scanner Discovery markers AbstractBuiltinSpecsDetector_ClearingMarkers=Clearing markers for {0} AbstractBuiltinSpecsDetector_DiscoverBuiltInSettingsJobName=Discover compiler built-in language settings -AbstractBuiltinSpecsDetector_RunningScannerDiscovery=Running scanner discovery: {0} -AbstractBuiltinSpecsDetector_ScannerDiscoveryMarkerLocationPreferences=Preferences, C++/Build/Settings/Discovery, [{0}] options -AbstractBuiltinSpecsDetector_ScannerDiscoveryMarkerLocationProperties=Project Properties, C++ Preprocessor Include.../Providers, [{0}] options +RunningScannerDiscovery=Running scanner discovery: {0} +ScannerDiscoveryMarkerLocationPreferences=Preferences, C++/Build/Settings/Discovery, [{0}] options +ScannerDiscoveryMarkerLocationProperties=Project Properties, C++ Preprocessor Include.../Providers, [{0}] options AbstractBuiltinSpecsDetector_ScannerDiscoveryTaskTitle=CDT Scanner Discovery AbstractBuiltinSpecsDetector_SerializingResults=Serializing results - ExternalBuilderName=Make builder InternalBuilderName=Internal builder -InternalBuildRunner_NoNeedToRun=No need to run +InternalBuildRunner_NoNeedToRun=No need to run diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/internal/AutoBuildLanguageSettingsProvider.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/internal/AutoBuildLanguageSettingsProvider.java index d4cf99ea..d6f2741a 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/internal/AutoBuildLanguageSettingsProvider.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/internal/AutoBuildLanguageSettingsProvider.java @@ -19,6 +19,7 @@ import java.io.File; import java.io.IOException; import java.net.URL; +import java.text.MessageFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; @@ -65,6 +66,7 @@ import io.sloeber.autoBuild.api.AutoBuildCommon; import io.sloeber.autoBuild.api.IAutoBuildConfigurationDescription; import io.sloeber.autoBuild.core.Activator; +import io.sloeber.autoBuild.core.Messages; import io.sloeber.autoBuild.extensionPoint.providers.InternalBuildRunner; import io.sloeber.autoBuild.schema.api.IOption; @@ -74,8 +76,8 @@ public class AutoBuildLanguageSettingsProvider extends AbstractExecutableExtensionBase implements ILanguageSettingsBroadcastingProvider { - private static final String SCANNER_DISCOVERY_CONSOLE = "org.eclipse.cdt.managedbuilder.ScannerDiscoveryConsole"; //$NON-NLS-1$ - private static final String SCANNER_DISCOVERY_GLOBAL_CONSOLE = "org.eclipse.cdt.managedbuilder.ScannerDiscoveryGlobalConsole"; //$NON-NLS-1$ + private static final String SCANNER_DISCOVERY_CONSOLE = "io.Sloeber.autoBuild.ScannerDiscoveryConsole"; //$NON-NLS-1$ + private static final String SCANNER_DISCOVERY_GLOBAL_CONSOLE = "io.Sloeber.autoBuild.ScannerDiscoveryGlobalConsole"; //$NON-NLS-1$ private static final String DEFAULT_CONSOLE_ICON = "icons/obj16/inspect_sys.gif"; //$NON-NLS-1$ private static final String GMAKE_ERROR_PARSER_ID = "org.eclipse.cdt.core.GmakeErrorParser"; //$NON-NLS-1$ @@ -101,10 +103,10 @@ public void addMarker(IResource rc, int lineNumber, String errorDesc, int severi @Override public void addMarker(final ProblemMarkerInfo problemMarkerInfo) { final String providerId = getId(); + final String providerName=getName(); // Add markers in a job to avoid deadlocks Job markerJob = new Job( - // ManagedMakeMessages.getResourceString( - "AbstractBuiltinSpecsDetector.AddScannerDiscoveryMarkers") { //$NON-NLS-1$ + Messages.AddScannerDiscoveryMarkers) { @Override protected IStatus run(IProgressMonitor monitor) { // Avoid duplicates as different languages can generate identical errors @@ -133,14 +135,12 @@ protected IStatus run(IProgressMonitor monitor) { marker.setAttribute(SDMarkerGenerator.ATTR_PROVIDER, providerId); if (problemMarkerInfo.file instanceof IWorkspaceRoot) { - String msgPreferences = // ManagedMakeMessages.getFormattedString( - "AbstractBuiltinSpecsDetector.ScannerDiscoveryMarkerLocationPreferences"; - // , //$NON-NLS-1$ providerName); + String msgPreferences = MessageFormat.format( + Messages.ScannerDiscoveryMarkerLocationPreferences, providerName); marker.setAttribute(IMarker.LOCATION, msgPreferences); } else { - String msgProperties = // ManagedMakeMessages.getFormattedString( - "AbstractBuiltinSpecsDetector.ScannerDiscoveryMarkerLocationProperties"; - // //$NON-NLS-1$ providerName); + String msgProperties = MessageFormat.format( + Messages.ScannerDiscoveryMarkerLocationProperties, providerName); marker.setAttribute(IMarker.LOCATION, msgProperties); } } catch (CoreException e) { @@ -298,10 +298,8 @@ private List runForLanguage(String currentLanguageId, St try (AutoBuildRunnerHelper buildRunnerHelper = new AutoBuildRunnerHelper(currentProject);) { SubMonitor subMonitor = SubMonitor.convert(monitor, - // ManagedMakeMessages.getFormattedString( - "AbstractBuiltinSpecsDetector.RunningScannerDiscovery", //$NON-NLS-1$ - // getName()), - 100); + MessageFormat.format( + Messages.RunningScannerDiscovery, getName()), 100); IConsole console; if (isConsoleEnabled) { @@ -331,11 +329,11 @@ private List runForLanguage(String currentLanguageId, St buildRunnerHelper.prepareStreams(epm, parsers, console, subMonitor.split(TICKS_OUTPUT_PARSING)); - buildRunnerHelper.greeting(// ManagedMakeMessages - // .getFormattedString( - "AbstractBuiltinSpecsDetector.RunningScannerDiscovery"//$NON-NLS-1$ - // , getName()) + buildRunnerHelper.greeting(MessageFormat.format( + Messages.RunningScannerDiscovery , getName()) ); + + System.out.println(currentCommandResolved); InternalBuildRunner.launchCommand(currentCommandResolved, autoConf, monitor, buildRunnerHelper); @@ -412,7 +410,7 @@ public void startup(ICConfigurationDescription cfgDescription, IWorkingDirectory @Override public boolean processLine(String line) { - // System.out.println(line); + System.out.println(line); for (Entry curMatcher : outputMatchers.entrySet()) { Pattern pat = curMatcher.getKey(); From 7c9c628c2c9e15d9df64310647ff161d9564b6cd Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 25 Aug 2024 21:46:21 +0200 Subject: [PATCH 013/115] refactor rename fields to myXX format Also deleted a unused method --- .../core/api/Json/ArduinoInstallable.java | 20 +++--- .../core/api/Json/ArduinoPlatformVersion.java | 70 +++++++++---------- .../api/Json/ArduinpPlatformToolSystem.java | 8 +-- 3 files changed, 46 insertions(+), 52 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoInstallable.java b/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoInstallable.java index 75204b03..149d8fd2 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoInstallable.java +++ b/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoInstallable.java @@ -4,31 +4,31 @@ public abstract class ArduinoInstallable { - protected String archiveFileName; - protected String url; - protected String checksum; - protected String size; - protected String name; + protected String myArchiveFileName; + protected String myURL; + protected String myChecksum; + protected String mySize; + protected String myName; abstract public IPath getInstallPath(); public String getArchiveFileName() { - return archiveFileName; + return myArchiveFileName; } public String getUrl() { - return url; + return myURL; } public String getChecksum() { - return checksum; + return myChecksum; } public String getSize() { - return size; + return mySize; } public String getName() { - return name; + return myName; } } diff --git a/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoPlatformVersion.java b/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoPlatformVersion.java index 7aed2b05..7d632e7d 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoPlatformVersion.java +++ b/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoPlatformVersion.java @@ -12,7 +12,6 @@ import java.io.File; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import org.eclipse.core.runtime.IPath; @@ -25,12 +24,12 @@ public class ArduinoPlatformVersion extends ArduinoInstallable implements Comparable { - private String architecture; - private VersionNumber version; - private String category; + private String myArchitecture; + private VersionNumber myVersion; + private String myCategory; - private List boards = new ArrayList<>(); - private List toolsDependencies = new ArrayList<>();; + private List myBoards = new ArrayList<>(); + private List myToolDependencies = new ArrayList<>(); private ArduinoPlatform myParent; @@ -40,20 +39,20 @@ public ArduinoPlatformVersion(JsonElement json, ArduinoPlatform parent) { JsonObject jsonObject = json.getAsJsonObject(); try { - name = getSafeString(jsonObject, "name"); - architecture = getSafeString(jsonObject, "architecture"); - version = getSafeVersion(jsonObject, "version"); - category = getSafeString(jsonObject, "category"); - url = getSafeString(jsonObject, "url"); - archiveFileName = getSafeString(jsonObject, "archiveFileName"); - checksum = getSafeString(jsonObject, "checksum"); - size = getSafeString(jsonObject, "size"); + myName = getSafeString(jsonObject, "name"); + myArchitecture = getSafeString(jsonObject, "architecture"); + myVersion = getSafeVersion(jsonObject, "version"); + myCategory = getSafeString(jsonObject, "category"); + myURL = getSafeString(jsonObject, "url"); + myArchiveFileName = getSafeString(jsonObject, "archiveFileName"); + myChecksum = getSafeString(jsonObject, "checksum"); + mySize = getSafeString(jsonObject, "size"); for (JsonElement curElement : jsonObject.get("boards").getAsJsonArray()) { - boards.add(getSafeString(curElement.getAsJsonObject(), "name")); + myBoards.add(getSafeString(curElement.getAsJsonObject(), "name")); } if (jsonObject.get("toolsDependencies") != null) { for (JsonElement curElement : jsonObject.get("toolsDependencies").getAsJsonArray()) { - toolsDependencies.add(new ArduinoPlatformTooldDependency(curElement, this)); + myToolDependencies.add(new ArduinoPlatformTooldDependency(curElement, this)); } } } catch (Exception e) { @@ -66,19 +65,19 @@ public ArduinoPlatform getParent() { } public String getArchitecture() { - return architecture; + return myArchitecture; } public VersionNumber getVersion() { - return version; + return myVersion; } public String getCategory() { - return category; + return myCategory; } public List getToolsDependencies() { - return toolsDependencies; + return myToolDependencies; } public boolean isInstalled() { @@ -95,22 +94,17 @@ public File getPlatformFile() { @Override public IPath getInstallPath() { - return getParent().getInstallPath().append(this.version.toString()); + return getParent().getInstallPath().append(this.myVersion.toString()); } - public List getIncludePath() { - IPath installPath = getInstallPath(); - return Arrays.asList(installPath.append("cores/{build.core}"), //$NON-NLS-1$ - installPath.append(ARDUINO_VARIANTS_FOLDER_NAME + "/{build.variant}")); //$NON-NLS-1$ - } @Override public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + ((this.name == null) ? 0 : this.name.hashCode()); + result = prime * result + ((this.myName == null) ? 0 : this.myName.hashCode()); result = prime * result + ((this.myParent == null) ? 0 : this.myParent.hashCode()); - result = prime * result + ((this.version == null) ? 0 : this.version.hashCode()); + result = prime * result + ((this.myVersion == null) ? 0 : this.myVersion.hashCode()); return result; } @@ -123,30 +117,30 @@ public boolean equals(Object obj) { if (getClass() != obj.getClass()) return false; ArduinoPlatformVersion other = (ArduinoPlatformVersion) obj; - if (this.name == null) { - if (other.name != null) + if (this.myName == null) { + if (other.myName != null) return false; - } else if (!this.name.equals(other.name)) + } else if (!this.myName.equals(other.myName)) return false; if (this.myParent == null) { if (other.myParent != null) return false; } else if (!this.myParent.equals(other.myParent)) return false; - if (this.version == null) { - if (other.version != null) + if (this.myVersion == null) { + if (other.myVersion != null) return false; - } else if (!this.version.equals(other.version)) + } else if (!this.myVersion.equals(other.myVersion)) return false; return true; } public List getBoardNames() { - return boards; + return myBoards; } public String getID() { - return version.toString(); + return myVersion.toString(); } public String getConcattenatedBoardNames() { @@ -155,12 +149,12 @@ public String getConcattenatedBoardNames() { @Override public int compareTo(ArduinoPlatformVersion o) { - return name.compareTo(o.getName()); + return myName.compareTo(o.getName()); } @Override public String toString() { - return name + SPACE + architecture + '(' + version + ')'; + return myName + SPACE + myArchitecture + '(' + myVersion + ')'; } } diff --git a/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinpPlatformToolSystem.java b/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinpPlatformToolSystem.java index 58b675bf..3f561df5 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinpPlatformToolSystem.java +++ b/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinpPlatformToolSystem.java @@ -27,10 +27,10 @@ public ArduinpPlatformToolSystem(JsonElement json, ArduinoPlatformToolVersion to try { host = getSafeString(jsonObject, "host"); - archiveFileName = getSafeString(jsonObject, "archiveFileName"); - url = getSafeString(jsonObject, "url"); - checksum = getSafeString(jsonObject, "checksum"); - size = getSafeString(jsonObject, "size"); + myArchiveFileName = getSafeString(jsonObject, "archiveFileName"); + myURL = getSafeString(jsonObject, "url"); + myChecksum = getSafeString(jsonObject, "checksum"); + mySize = getSafeString(jsonObject, "size"); } catch (Exception e) { throw new JsonParseException("failed to parse Tool json " + e.getMessage(),e); } From 9b1422a7549c8b35bdbaaae278f34b327e04c849 Mon Sep 17 00:00:00 2001 From: jan Date: Mon, 26 Aug 2024 15:32:15 +0200 Subject: [PATCH 014/115] Refactor rename/move to get some controll over the code --- io.sloeber.autoBuild/build.properties | 1 - io.sloeber.autoBuild/plugin.xml | 2 +- .../AutoBuildLanguageSettingsProvider.java | 14 +- io.sloeber.core/META-INF/MANIFEST.MF | 17 +- .../api}/ArduinoInstallable.java | 2 +- .../api/BoardDescription.java | 33 ++-- .../api/BoardsManager.java | 146 +++++++++--------- .../arduinoFramework/api/IArduinoLibrary.java | 60 +++++++ .../api/IArduinoLibraryIndex.java | 31 ++++ .../api/IArduinoLibraryVersion.java | 24 ++- .../arduinoFramework/api/IArduinoPackage.java | 69 +++++++++ .../api/IArduinoPlatform.java | 42 +++++ .../api/IArduinoPlatformPackageIndex.java | 21 +++ .../api/IArduinoPlatformVersion.java | 34 ++++ .../api/IExample.java | 2 +- .../api/LibraryManager.java | 53 ++++--- .../internal}/ArduinoLibrary.java | 49 ++++-- .../internal}/ArduinoLibraryIndex.java | 30 ++-- .../internal}/ArduinoLibraryVersion.java | 21 ++- .../internal}/ArduinoPackage.java | 82 ++++++---- .../internal}/ArduinoPlatform.java | 50 +++--- .../ArduinoPlatformPackageIndex.java | 29 +++- .../internal}/ArduinoPlatformTool.java | 7 +- .../internal}/ArduinoPlatformToolVersion.java | 14 +- .../ArduinoPlatformTooldDependency.java | 9 +- .../internal}/ArduinoPlatformVersion.java | 42 +++-- .../internal}/ArduinpPlatformToolSystem.java | 6 +- .../internal}/GsonConverter.java | 2 +- .../internal}/Node.java | 2 +- .../src/io/sloeber/core/Activator.java | 2 +- .../io/sloeber/core/api/CodeDescription.java | 4 + .../src/io/sloeber/core/api/Defaults.java | 1 - .../core/api/IInstallLibraryHandler.java | 2 + .../core/api/ISloeberConfiguration.java | 2 + .../sloeber/core/api/LaunchConfiguration.java | 1 - .../io/sloeber/core/api/SloeberProject.java | 65 ++++---- .../core/builder/SloeberBuilderExtension.java | 2 +- .../core/communication/ArduinoSerial.java | 2 +- .../core/core/DefaultInstallHandler.java | 2 +- .../core/internal/ArduinoHardwareLibrary.java | 54 ++++++- .../ArduinoPrivateLibraryVersion.java | 51 +++++- .../src/io/sloeber/core/internal/Example.java | 4 +- .../core/internal/SloeberConfiguration.java | 6 +- .../core/listeners/IndexerListener.java | 4 +- .../SloeberConfigurationVariableSupplier.java | 3 +- .../io/sloeber/core/tools/FileModifiers.java | 8 +- .../io/sloeber/core/tools/PackageManager.java | 3 +- .../tools/uploaders/UploadSketchWrapper.java | 2 +- .../src/io/sloeber/core/txt/Programmers.java | 2 +- .../src/io/sloeber/core/txt/WorkAround.java | 4 +- .../src/io/sloeber/core/BuildTests.java | 10 +- .../src/io/sloeber/core/CompileAndUpload.java | 2 +- ...leArduinoIDEExamplesOnAVRHardwareTest.java | 7 +- ...CompileArduinoIDEExamplesOnTeensyTest.java | 6 +- ...ArduinoIDEExamplesonJantjesBoardsTest.java | 8 +- ...teAndCompileDefaultInoOnAllBoardsTest.java | 6 +- .../core/CreateAndCompileExamplesTest.java | 9 +- .../CreateAndCompileLibraryExamplesTest.java | 9 +- .../src/io/sloeber/core/Example.java | 2 +- .../src/io/sloeber/core/Shared.java | 8 +- .../src/io/sloeber/providers/Adafruit.java | 4 +- .../src/io/sloeber/providers/Arduino.java | 16 +- .../src/io/sloeber/providers/ESP32.java | 4 +- .../src/io/sloeber/providers/ESP8266.java | 4 +- .../src/io/sloeber/providers/Jantje.java | 4 +- .../src/io/sloeber/providers/MCUBoard.java | 16 +- .../src/io/sloeber/providers/Teensy.java | 4 +- .../src/io/sloeber/ui/Activator.java | 2 +- .../io/sloeber/ui/Import_Libraries_Page.java | 4 +- .../sloeber/ui/actions/AddLibraryAction.java | 2 +- .../ui/actions/AddSourceFolderAction.java | 2 +- .../ui/actions/BurnBootloaderHandler.java | 2 +- .../sloeber/ui/actions/NewSketchHandler.java | 2 +- .../ui/actions/OpenSerialMonitorHandler.java | 2 +- .../ui/actions/ProgramProjectHandler.java | 2 +- .../ui/actions/UploadProjectHandler.java | 2 +- .../ui/listeners/MyLibraryInstallHandler.java | 2 +- .../ui/preferences/LibrarySelectionPage.java | 57 ++++--- .../ui/preferences/PlatformSelectionPage.java | 141 +++++++++-------- .../ui/preferences/PreferencePage.java | 4 +- .../ThirdPartyHardwareSelectionPage.java | 2 +- .../properties/BoardSelectionPage.java | 4 +- .../ui/wizard/newsketch/NewSketchWizard.java | 2 +- .../newsketch/NewSketchWizardBoardPage.java | 2 +- .../NewSketchWizardCodeSelectionPage.java | 4 +- .../ui/wizard/newsketch/SampleSelector.java | 6 +- 86 files changed, 984 insertions(+), 497 deletions(-) rename io.sloeber.autoBuild/src/io/sloeber/autoBuild/{internal => extensionPoint/providers}/AutoBuildLanguageSettingsProvider.java (95%) rename io.sloeber.core/src/io/sloeber/{core/api/Json => arduinoFramework/api}/ArduinoInstallable.java (89%) rename io.sloeber.core/src/io/sloeber/{core => arduinoFramework}/api/BoardDescription.java (97%) rename io.sloeber.core/src/io/sloeber/{core => arduinoFramework}/api/BoardsManager.java (84%) create mode 100644 io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoLibrary.java create mode 100644 io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoLibraryIndex.java rename io.sloeber.core/src/io/sloeber/{core => arduinoFramework}/api/IArduinoLibraryVersion.java (60%) create mode 100644 io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoPackage.java create mode 100644 io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoPlatform.java create mode 100644 io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoPlatformPackageIndex.java create mode 100644 io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoPlatformVersion.java rename io.sloeber.core/src/io/sloeber/{core => arduinoFramework}/api/IExample.java (88%) rename io.sloeber.core/src/io/sloeber/{core => arduinoFramework}/api/LibraryManager.java (89%) rename io.sloeber.core/src/io/sloeber/{core/api/Json => arduinoFramework/internal}/ArduinoLibrary.java (75%) rename io.sloeber.core/src/io/sloeber/{core/api/Json => arduinoFramework/internal}/ArduinoLibraryIndex.java (76%) rename io.sloeber.core/src/io/sloeber/{core/api/Json => arduinoFramework/internal}/ArduinoLibraryVersion.java (91%) rename io.sloeber.core/src/io/sloeber/{core/api/Json => arduinoFramework/internal}/ArduinoPackage.java (75%) rename io.sloeber.core/src/io/sloeber/{core/api/Json => arduinoFramework/internal}/ArduinoPlatform.java (67%) rename io.sloeber.core/src/io/sloeber/{core/api/Json => arduinoFramework/internal}/ArduinoPlatformPackageIndex.java (80%) rename io.sloeber.core/src/io/sloeber/{core/api/Json => arduinoFramework/internal}/ArduinoPlatformTool.java (94%) rename io.sloeber.core/src/io/sloeber/{core/api/Json => arduinoFramework/internal}/ArduinoPlatformToolVersion.java (89%) rename io.sloeber.core/src/io/sloeber/{core/api/Json => arduinoFramework/internal}/ArduinoPlatformTooldDependency.java (78%) rename io.sloeber.core/src/io/sloeber/{core/api/Json => arduinoFramework/internal}/ArduinoPlatformVersion.java (83%) rename io.sloeber.core/src/io/sloeber/{core/api/Json => arduinoFramework/internal}/ArduinpPlatformToolSystem.java (95%) rename io.sloeber.core/src/io/sloeber/{core/Gson => arduinoFramework/internal}/GsonConverter.java (93%) rename io.sloeber.core/src/io/sloeber/{core/api/Json => arduinoFramework/internal}/Node.java (85%) diff --git a/io.sloeber.autoBuild/build.properties b/io.sloeber.autoBuild/build.properties index 9bc5fc8c..15072b46 100644 --- a/io.sloeber.autoBuild/build.properties +++ b/io.sloeber.autoBuild/build.properties @@ -10,6 +10,5 @@ bin.includes = .,\ icons/ jars.compile.order = . src.includes = schema/,\ - src/,\ template/,\ icons/ diff --git a/io.sloeber.autoBuild/plugin.xml b/io.sloeber.autoBuild/plugin.xml index fea1e11b..169fce6c 100644 --- a/io.sloeber.autoBuild/plugin.xml +++ b/io.sloeber.autoBuild/plugin.xml @@ -2072,7 +2072,7 @@ diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/internal/AutoBuildLanguageSettingsProvider.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/AutoBuildLanguageSettingsProvider.java similarity index 95% rename from io.sloeber.autoBuild/src/io/sloeber/autoBuild/internal/AutoBuildLanguageSettingsProvider.java rename to io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/AutoBuildLanguageSettingsProvider.java index d6f2741a..f7d86012 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/internal/AutoBuildLanguageSettingsProvider.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/AutoBuildLanguageSettingsProvider.java @@ -12,7 +12,7 @@ * Andrew Gvozdev - initial API and implementation *******************************************************************************/ -package io.sloeber.autoBuild.internal; +package io.sloeber.autoBuild.extensionPoint.providers; import static io.sloeber.autoBuild.helpers.api.AutoBuildConstants.*; @@ -67,7 +67,7 @@ import io.sloeber.autoBuild.api.IAutoBuildConfigurationDescription; import io.sloeber.autoBuild.core.Activator; import io.sloeber.autoBuild.core.Messages; -import io.sloeber.autoBuild.extensionPoint.providers.InternalBuildRunner; +import io.sloeber.autoBuild.internal.AutoBuildRunnerHelper; import io.sloeber.autoBuild.schema.api.IOption; /** @@ -75,6 +75,7 @@ */ public class AutoBuildLanguageSettingsProvider extends AbstractExecutableExtensionBase implements ILanguageSettingsBroadcastingProvider { + private static final boolean LOG_TO_STD_OUT =false; private static final String SCANNER_DISCOVERY_CONSOLE = "io.Sloeber.autoBuild.ScannerDiscoveryConsole"; //$NON-NLS-1$ private static final String SCANNER_DISCOVERY_GLOBAL_CONSOLE = "io.Sloeber.autoBuild.ScannerDiscoveryGlobalConsole"; //$NON-NLS-1$ @@ -332,8 +333,9 @@ private List runForLanguage(String currentLanguageId, St buildRunnerHelper.greeting(MessageFormat.format( Messages.RunningScannerDiscovery , getName()) ); - - System.out.println(currentCommandResolved); + if(LOG_TO_STD_OUT) { + System.out.println(currentCommandResolved); + } InternalBuildRunner.launchCommand(currentCommandResolved, autoConf, monitor, buildRunnerHelper); @@ -410,7 +412,9 @@ public void startup(ICConfigurationDescription cfgDescription, IWorkingDirectory @Override public boolean processLine(String line) { - System.out.println(line); + if(LOG_TO_STD_OUT) { + System.out.println(line); + } for (Entry curMatcher : outputMatchers.entrySet()) { Pattern pat = curMatcher.getKey(); diff --git a/io.sloeber.core/META-INF/MANIFEST.MF b/io.sloeber.core/META-INF/MANIFEST.MF index 1fddc2e6..1d3de98d 100644 --- a/io.sloeber.core/META-INF/MANIFEST.MF +++ b/io.sloeber.core/META-INF/MANIFEST.MF @@ -32,12 +32,7 @@ Require-Bundle: Export-Package: cc.arduino.packages;x-internal:=true, cc.arduino.packages.discoverers;x-internal:=true, cc.arduino.packages.ssh;x-internal:=true, - com.jcraft.jsch, - com.jcraft.jsch.jce;uses:="com.jcraft.jsch", - com.jcraft.jsch.jcraft;uses:="com.jcraft.jsch", - com.jcraft.jsch.jgss;uses:="com.jcraft.jsch", - io.sloeber.core;x-friends:="io.sloeber.tests", - io.sloeber.core.Gson;uses:="com.google.gson,io.sloeber.core.api", + io.sloeber.arduinoFramework.api, io.sloeber.core.api; uses:="io.sloeber.core.common, org.eclipse.debug.core.model, @@ -48,20 +43,12 @@ Export-Package: cc.arduino.packages;x-internal:=true, org.eclipse.cdt.core.settings.model, org.eclipse.core.resources, io.sloeber.core.api.Json", - io.sloeber.core.api.Json;uses:="org.eclipse.core.runtime,com.google.gson,io.sloeber.core.api", - io.sloeber.core.builder;x-internal:=true, io.sloeber.core.common;x-friends:="io.sloeber.tests", io.sloeber.core.communication;x-internal:=true, io.sloeber.core.core;x-internal:=true, - io.sloeber.core.eclipseIntegrations;uses:="org.eclipse.core.variables", - io.sloeber.core.internal;x-friends:="io.sloeber.tests", - io.sloeber.core.listeners;x-internal:=true, - io.sloeber.core.managers;x-internal:=true, + io.sloeber.core.internal, io.sloeber.core.natures;x-internal:=true, io.sloeber.core.templates;x-internal:=true, - io.sloeber.core.toolchain;x-internal:=true, - io.sloeber.core.tools;x-friends:="io.sloeber.tests", - io.sloeber.core.tools.uploaders;x-internal:=true, io.sloeber.core.txt;x-friends:="io.sloeber.tests" Import-Package: io.sloeber.autoBuild.schema.api, org.apache.commons.io;version="2.13.0", diff --git a/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoInstallable.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/ArduinoInstallable.java similarity index 89% rename from io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoInstallable.java rename to io.sloeber.core/src/io/sloeber/arduinoFramework/api/ArduinoInstallable.java index 149d8fd2..034288cd 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoInstallable.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/ArduinoInstallable.java @@ -1,4 +1,4 @@ -package io.sloeber.core.api.Json; +package io.sloeber.arduinoFramework.api; import org.eclipse.core.runtime.IPath; diff --git a/io.sloeber.core/src/io/sloeber/core/api/BoardDescription.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java similarity index 97% rename from io.sloeber.core/src/io/sloeber/core/api/BoardDescription.java rename to io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java index 039955cf..108e3da6 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/BoardDescription.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java @@ -1,4 +1,4 @@ -package io.sloeber.core.api; +package io.sloeber.arduinoFramework.api; import static io.sloeber.core.Messages.*; import static io.sloeber.core.api.Common.*; @@ -26,11 +26,14 @@ import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.InstanceScope; +import io.sloeber.arduinoFramework.internal.ArduinoPlatformTooldDependency; import io.sloeber.autoBuild.api.IAutoBuildConfigurationDescription; import io.sloeber.autoBuild.helpers.api.KeyValueTree; -import io.sloeber.core.api.Json.ArduinoPlatform; -import io.sloeber.core.api.Json.ArduinoPlatformTooldDependency; -import io.sloeber.core.api.Json.ArduinoPlatformVersion; +import io.sloeber.core.api.Common; +import io.sloeber.core.api.ConfigurationPreferences; +import io.sloeber.core.api.Const; +import io.sloeber.core.api.Preferences; +import io.sloeber.core.api.VersionNumber; import io.sloeber.core.tools.KeyValue; import io.sloeber.core.txt.BoardTxtFile; import io.sloeber.core.txt.PlatformTxtFile; @@ -54,9 +57,9 @@ public class BoardDescription { private String myBoardsVariant = null; private String myUploadTool = null; - private ArduinoPlatformVersion myReferencedPlatformVariant = null; - private ArduinoPlatformVersion myReferencedPlatformCore = null; - private ArduinoPlatformVersion myReferencedPlatformUpload = null; + private IArduinoPlatformVersion myReferencedPlatformVariant = null; + private IArduinoPlatformVersion myReferencedPlatformCore = null; + private IArduinoPlatformVersion myReferencedPlatformUpload = null; private boolean isDirty = true; @@ -281,7 +284,7 @@ public static List makeBoardDescriptors(File boardFile) { * @param options * if null default options are taken */ - BoardDescription(File boardsFile, String boardID, Map options) { + public BoardDescription(File boardsFile, String boardID, Map options) { myBoardID = boardID; myUserSelectedBoardsTxtFile = boardsFile; mySloeberBoardTxtFile = new BoardTxtFile(resolvePathEnvironmentString(myUserSelectedBoardsTxtFile)); @@ -295,9 +298,9 @@ public BoardDescription() { myUserSelectedBoardsTxtFile = new File(myStorageNode.get(KEY_LAST_USED_BOARDS_FILE, EMPTY)); if (!myUserSelectedBoardsTxtFile.exists()) { - ArduinoPlatformVersion platform = BoardsManager.getNewestInstalledPlatform(VENDOR_ARDUINO, AVR); + IArduinoPlatformVersion platform = BoardsManager.getNewestInstalledPlatform(VENDOR_ARDUINO, AVR); if (platform == null) { - List platforms = BoardsManager.getInstalledPlatforms(); + List platforms = BoardsManager.getInstalledPlatforms(); //If you crash on the next line no platform have been installed platform = platforms.get(0); } @@ -644,11 +647,11 @@ public IPath getreferencedCoreHardwarePath() { */ public IPath getArduinoPlatformPath() { updateWhenDirty(); - ArduinoPlatform platform = BoardsManager.getPlatform(VENDOR_ARDUINO, getArchitecture()); + IArduinoPlatform platform = BoardsManager.getPlatform(VENDOR_ARDUINO, getArchitecture()); if (platform == null) { return null; } - ArduinoPlatformVersion platformVersion = platform.getNewestInstalled(); + IArduinoPlatformVersion platformVersion = platform.getNewestInstalled(); if (platformVersion == null) { return null; } @@ -938,14 +941,14 @@ private Map getEnVarPlatformInfo() { } IPath referencingPlatformPath = getreferencingPlatformPath(); - ArduinoPlatformVersion referencingPlatform = BoardsManager.getPlatform(referencingPlatformPath); + IArduinoPlatformVersion referencingPlatform = BoardsManager.getPlatform(referencingPlatformPath); if (referencingPlatform == null) { // This is the case for private hardware //there is no need to specify tool path as they do not use them return ret; } - ArduinoPlatformVersion latestArduinoPlatform = BoardsManager.getNewestInstalledPlatform(Const.VENDOR_ARDUINO, + IArduinoPlatformVersion latestArduinoPlatform = BoardsManager.getNewestInstalledPlatform(Const.VENDOR_ARDUINO, referencingPlatform.getArchitecture()); if (latestArduinoPlatform != null) { ret.putAll(getEnvVarPlatformFileTools(latestArduinoPlatform)); @@ -978,7 +981,7 @@ private Map getEnVarPlatformInfo() { * @param platformVersion * @return environment variables pointing to the tools used by the platform */ - private static Map getEnvVarPlatformFileTools(ArduinoPlatformVersion platformVersion) { + private static Map getEnvVarPlatformFileTools(IArduinoPlatformVersion platformVersion) { HashMap vars = new HashMap<>(); for (ArduinoPlatformTooldDependency tool : platformVersion.getToolsDependencies()) { IPath installPath = tool.getInstallPath(); diff --git a/io.sloeber.core/src/io/sloeber/core/api/BoardsManager.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java similarity index 84% rename from io.sloeber.core/src/io/sloeber/core/api/BoardsManager.java rename to io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java index edb4b5cb..b15bb7f2 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/BoardsManager.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java @@ -1,4 +1,4 @@ -package io.sloeber.core.api; +package io.sloeber.arduinoFramework.api; import static io.sloeber.core.Messages.*; import static io.sloeber.core.api.Common.*; @@ -22,6 +22,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.TreeMap; @@ -37,16 +38,17 @@ import com.google.gson.Gson; +import io.sloeber.arduinoFramework.internal.ArduinoPlatformPackageIndex; +import io.sloeber.arduinoFramework.internal.ArduinoPlatformTool; +import io.sloeber.arduinoFramework.internal.ArduinoPlatformToolVersion; +import io.sloeber.arduinoFramework.internal.ArduinoPlatformTooldDependency; import io.sloeber.core.Activator; import io.sloeber.core.Messages; -import io.sloeber.core.api.Json.ArduinoInstallable; -import io.sloeber.core.api.Json.ArduinoPackage; -import io.sloeber.core.api.Json.ArduinoPlatform; -import io.sloeber.core.api.Json.ArduinoPlatformPackageIndex; -import io.sloeber.core.api.Json.ArduinoPlatformTool; -import io.sloeber.core.api.Json.ArduinoPlatformToolVersion; -import io.sloeber.core.api.Json.ArduinoPlatformTooldDependency; -import io.sloeber.core.api.Json.ArduinoPlatformVersion; +import io.sloeber.core.api.Common; +import io.sloeber.core.api.ConfigurationPreferences; +import io.sloeber.core.api.Defaults; +import io.sloeber.core.api.SloeberProject; +import io.sloeber.core.api.VersionNumber; import io.sloeber.core.common.InstancePreferences; import io.sloeber.core.managers.InstallProgress; import io.sloeber.core.tools.MyMultiStatus; @@ -111,17 +113,17 @@ static public BoardDescription getBoardDescription(String jsonFileName, String p static private BoardDescription getNewestBoardIDFromBoardsManager(String jsonFileName, String packageName, String architectureID, String boardID, Map options) { - ArduinoPackage thePackage = getPackage(jsonFileName, packageName); + IArduinoPackage thePackage = getPackage(jsonFileName, packageName); if (thePackage == null) { System.err.println("failed to find package:" + packageName); //$NON-NLS-1$ return null; } - ArduinoPlatform platform = thePackage.getPlatform(architectureID); + IArduinoPlatform platform = thePackage.getPlatform(architectureID); if (platform == null) { System.err.println("failed to find architecture ID " + architectureID + " in package:" + packageName); //$NON-NLS-1$ //$NON-NLS-2$ return null; } - ArduinoPlatformVersion platformVersion = platform.getNewestVersion(); + IArduinoPlatformVersion platformVersion = platform.getNewestVersion(); java.io.File boardsFile = platformVersion.getBoardsFile(); BoardDescription boardid = new BoardDescription(boardsFile, boardID, options); @@ -168,13 +170,13 @@ public static void installsubsetOfLatestPlatforms(int fromIndex, int toIndex) { envVarsNeedUpdating = true; int currPlatformIndex = 1; NullProgressMonitor monitor = new NullProgressMonitor(); - List allPackages = getPackages(); - for (ArduinoPackage curPackage : allPackages) { - Collection latestPlatforms = curPackage.getPlatforms(); - for (ArduinoPlatform curPlatform : latestPlatforms) { + List allPackages = getPackages(); + for (IArduinoPackage curPackage : allPackages) { + Collection latestPlatforms = curPackage.getPlatforms(); + for (IArduinoPlatform curPlatform : latestPlatforms) { if (!curPlatform.getName().toUpperCase().contains(DEPRECATED)) { if (currPlatformIndex > fromIndex) { - ArduinoPlatformVersion latestPlatformVersion = curPlatform.getNewestVersion(); + IArduinoPlatformVersion latestPlatformVersion = curPlatform.getNewestVersion(); if (!latestPlatformVersion.getName().toUpperCase().contains(DEPRECATED)) { install(latestPlatformVersion, monitor); } else { @@ -204,11 +206,11 @@ public static void installLatestPlatform(String JasonName, String packageName, S return; } envVarsNeedUpdating = true; - ArduinoPackage curPackage = getPackage(JasonName, packageName); + IArduinoPackage curPackage = getPackage(JasonName, packageName); if (curPackage != null) { - ArduinoPlatform curPlatform = curPackage.getPlatform(architectureName); + IArduinoPlatform curPlatform = curPackage.getPlatform(architectureName); if (curPlatform != null) { - ArduinoPlatformVersion curPlatformVersion = curPlatform.getNewestVersion(); + IArduinoPlatformVersion curPlatformVersion = curPlatform.getNewestVersion(); if (curPlatformVersion != null) { NullProgressMonitor monitor = new NullProgressMonitor(); install(curPlatformVersion, monitor); @@ -220,7 +222,7 @@ public static void installLatestPlatform(String JasonName, String packageName, S "failed to find " + JasonName + " " + packageName + " " + architectureName)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } - private static IStatus install(ArduinoPlatformVersion platformVersion, IProgressMonitor monitor) { + private static IStatus install(IArduinoPlatformVersion platformVersion, IProgressMonitor monitor) { boolean forceDownload = false; // String name = platformVersion.getName(); // String architecture = platformVersion.getArchitecture(); @@ -250,7 +252,7 @@ private static IStatus install(ArduinoPlatformVersion platformVersion, IProgress e.printStackTrace(); } - ArduinoPackage referencingPkg = platformVersion.getParent().getParent(); + IArduinoPackage referencingPkg = platformVersion.getParent().getParent(); for (ArduinoPlatformTooldDependency toolDependency : platformVersion.getToolsDependencies()) { ArduinoPlatformToolVersion tool = referencingPkg.getTool(toolDependency.getName(), toolDependency.getVersion()); @@ -258,7 +260,7 @@ private static IStatus install(ArduinoPlatformVersion platformVersion, IProgress //this is a tool provided by another platform //and the referencing platform does not specify the installable info //This means the package file of the referencing platform needs to be provided - ArduinoPackage pkg = getPackageByProvider(toolDependency.getPackager()); + IArduinoPackage pkg = getPackageByProvider(toolDependency.getPackager()); if (pkg != null) { tool = pkg.getTool(toolDependency.getName(), toolDependency.getVersion()); } @@ -353,8 +355,8 @@ private static String[] getHardwarePaths() { + ConfigurationPreferences.getInstallationPathPackages()).split(File.pathSeparator); } - public static IStatus updatePlatforms(List platformsToInstall, - List platformsToRemove, IProgressMonitor monitor, MultiStatus status) { + public static IStatus updatePlatforms(List platformsToInstall, + List platformsToRemove, IProgressMonitor monitor, MultiStatus status) { if (!isReady()) { status.add(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, null)); return status; @@ -366,10 +368,10 @@ public static IStatus updatePlatforms(List platformsToIn envVarsNeedUpdating = true; try { myIsReady = false; - for (ArduinoPlatformVersion curPlatform : platformsToRemove) { + for (IArduinoPlatformVersion curPlatform : platformsToRemove) { status.add(uninstall(curPlatform, monitor)); } - for (ArduinoPlatformVersion curPlatform : platformsToInstall) { + for (IArduinoPlatformVersion curPlatform : platformsToInstall) { status.add(install(curPlatform, monitor)); } @@ -381,7 +383,7 @@ public static IStatus updatePlatforms(List platformsToIn return status; } - public static IStatus uninstall(ArduinoPlatformVersion curPlatform, IProgressMonitor monitor) { + public static IStatus uninstall(IArduinoPlatformVersion curPlatform, IProgressMonitor monitor) { if (!curPlatform.isInstalled()) { return Status.OK_STATUS; } @@ -585,9 +587,9 @@ public static Map getEnvironmentVariables() { return myWorkbenchEnvironmentVariables; } myWorkbenchEnvironmentVariables.clear(); - ArduinoPlatformVersion latestAvrPlatform = getNewestInstalledPlatform(VENDOR_ARDUINO, AVR); - ArduinoPlatformVersion latestSamdPlatform = getNewestInstalledPlatform(VENDOR_ARDUINO, SAMD); - ArduinoPlatformVersion latestSamPlatform = getNewestInstalledPlatform(VENDOR_ARDUINO, SAM); + IArduinoPlatformVersion latestAvrPlatform = getNewestInstalledPlatform(VENDOR_ARDUINO, AVR); + IArduinoPlatformVersion latestSamdPlatform = getNewestInstalledPlatform(VENDOR_ARDUINO, SAMD); + IArduinoPlatformVersion latestSamPlatform = getNewestInstalledPlatform(VENDOR_ARDUINO, SAM); if (latestSamdPlatform != null) { myWorkbenchEnvironmentVariables.putAll(getEnvVarPlatformFileTools(latestSamdPlatform)); @@ -602,9 +604,9 @@ public static Map getEnvironmentVariables() { return myWorkbenchEnvironmentVariables; } - private static Map getEnvVarPlatformFileTools(ArduinoPlatformVersion platformVersion) { + private static Map getEnvVarPlatformFileTools(IArduinoPlatformVersion platformVersion) { HashMap vars = new HashMap<>(); - ArduinoPackage pkg = platformVersion.getParent().getParent(); + IArduinoPackage pkg = platformVersion.getParent().getParent(); for (ArduinoPlatformTooldDependency tool : platformVersion.getToolsDependencies()) { ArduinoPlatformTool theTool = pkg.getTool(tool.getName()); if (theTool == null) { @@ -624,8 +626,8 @@ private static Map getEnvVarPlatformFileTools(ArduinoPlatformVer * @param architecture * @return the found platformVersion or null if none found */ - public static ArduinoPlatformVersion getNewestInstalledPlatform(String vendor, String architecture) { - ArduinoPlatform platform = getPlatform(vendor, architecture); + public static IArduinoPlatformVersion getNewestInstalledPlatform(String vendor, String architecture) { + IArduinoPlatform platform = getPlatform(vendor, architecture); if (platform == null) { return null; } @@ -663,7 +665,7 @@ public static synchronized void startup_Pluging(IProgressMonitor monitor) { IStatus status = null; // if successfully installed the examples: add the boards - ArduinoPlatform platform = getPlatform(Defaults.DEFAULT_INSTALL_MAINTAINER, + IArduinoPlatform platform = getPlatform(Defaults.DEFAULT_INSTALL_MAINTAINER, Defaults.DEFAULT_INSTALL_ARCHITECTURE); //we failed to find arduino avr platform. Take the fiorst one if (platform == null) { @@ -689,29 +691,29 @@ public static synchronized void startup_Pluging(IProgressMonitor monitor) { } - synchronized static public List getPackageIndices() { + synchronized static public List getPackageIndices() { if (packageIndices == null) { loadJsons(false); } - return packageIndices; + return new LinkedList<>(packageIndices); } - public static List getPlatforms() { - List platforms = new ArrayList<>(); - for (ArduinoPlatformPackageIndex index : getPackageIndices()) { - for (ArduinoPackage pkg : index.getPackages()) { + public static List getPlatforms() { + List platforms = new ArrayList<>(); + for (IArduinoPlatformPackageIndex index : getPackageIndices()) { + for (IArduinoPackage pkg : index.getPackages()) { platforms.addAll(pkg.getPlatforms()); } } return platforms; } - public static ArduinoPlatform getPlatform(String vendor, String architecture) { + public static IArduinoPlatform getPlatform(String vendor, String architecture) { - for (ArduinoPlatformPackageIndex index : getPackageIndices()) { - ArduinoPackage pkg = index.getPackage(vendor); + for (IArduinoPlatformPackageIndex index : getPackageIndices()) { + IArduinoPackage pkg = index.getPackage(vendor); if (pkg != null) { - ArduinoPlatform platform = pkg.getPlatform(architecture); + IArduinoPlatform platform = pkg.getPlatform(architecture); if (platform != null) { return platform; } @@ -726,14 +728,14 @@ public static ArduinoPlatform getPlatform(String vendor, String architecture) { * @param platformTxt * @return the found platform otherwise null */ - public static ArduinoPlatformVersion getPlatform(IPath platformPath) { - for (ArduinoPlatformPackageIndex index : getPackageIndices()) { - for (ArduinoPackage pkg : index.getPackages()) { - for (ArduinoPlatform curPlatform : pkg.getPlatforms()) { + public static IArduinoPlatformVersion getPlatform(IPath platformPath) { + for (IArduinoPlatformPackageIndex index : getPackageIndices()) { + for (IArduinoPackage pkg : index.getPackages()) { + for (IArduinoPlatform curPlatform : pkg.getPlatforms()) { if (platformPath .matchingFirstSegments(curPlatform.getInstallPath()) > (platformPath.segmentCount() - 2)) - for (ArduinoPlatformVersion curPlatformVersion : curPlatform.getVersions()) { + for (IArduinoPlatformVersion curPlatformVersion : curPlatform.getVersions()) { if (curPlatformVersion.getInstallPath().equals(platformPath)) { return curPlatformVersion; } @@ -744,10 +746,10 @@ public static ArduinoPlatformVersion getPlatform(IPath platformPath) { return null; } - static public List getInstalledPlatforms() { - List platforms = new ArrayList<>(); - for (ArduinoPlatformPackageIndex index : getPackageIndices()) { - for (ArduinoPackage pkg : index.getPackages()) { + static public List getInstalledPlatforms() { + List platforms = new ArrayList<>(); + for (IArduinoPlatformPackageIndex index : getPackageIndices()) { + for (IArduinoPackage pkg : index.getPackages()) { platforms.addAll(pkg.getInstalledPlatforms()); @@ -757,8 +759,8 @@ static public List getInstalledPlatforms() { } static public boolean areThereInstalledBoards() { - for (ArduinoPlatformPackageIndex index : getPackageIndices()) { - for (ArduinoPackage pkg : index.getPackages()) { + for (IArduinoPlatformPackageIndex index : getPackageIndices()) { + for (IArduinoPackage pkg : index.getPackages()) { if (pkg.isInstalled()) { return true; } @@ -767,17 +769,17 @@ static public boolean areThereInstalledBoards() { return false; } - static public List getPackages() { - List packages = new ArrayList<>(); - for (ArduinoPlatformPackageIndex index : getPackageIndices()) { + static public List getPackages() { + List packages = new ArrayList<>(); + for (IArduinoPlatformPackageIndex index : getPackageIndices()) { packages.addAll(index.getPackages()); } return packages; } - static public ArduinoPackage getPackage(String JasonName, String packageName) { - for (ArduinoPlatformPackageIndex index : getPackageIndices()) { + static public IArduinoPackage getPackage(String JasonName, String packageName) { + for (IArduinoPlatformPackageIndex index : getPackageIndices()) { if (index.getJsonFile().getName().equals(JasonName)) { return index.getPackage(packageName); } @@ -785,9 +787,9 @@ static public ArduinoPackage getPackage(String JasonName, String packageName) { return null; } - static public ArduinoPackage getPackage(String packageName) { - for (ArduinoPlatformPackageIndex index : getPackageIndices()) { - ArduinoPackage pkg = index.getPackage(packageName); + static public IArduinoPackage getPackage(String packageName) { + for (IArduinoPlatformPackageIndex index : getPackageIndices()) { + IArduinoPackage pkg = index.getPackage(packageName); if (pkg != null) { return pkg; } @@ -804,23 +806,23 @@ public static void onlyKeepLatestPlatforms() { Common.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); return; } - List allPackages = getPackages(); - for (ArduinoPackage curPackage : allPackages) { + List allPackages = getPackages(); + for (IArduinoPackage curPackage : allPackages) { curPackage.onlyKeepLatestPlatforms(); } } - public static ArduinoPlatformVersion getPlatform(String vendor, String architecture, VersionNumber refVersion) { - ArduinoPlatform platform = getPlatform(vendor, architecture); + public static IArduinoPlatformVersion getPlatform(String vendor, String architecture, VersionNumber refVersion) { + IArduinoPlatform platform = getPlatform(vendor, architecture); if (platform != null) { return platform.getVersion(refVersion); } return null; } - public static ArduinoPackage getPackageByProvider(String packager) { - for (ArduinoPlatformPackageIndex index : getPackageIndices()) { - for (ArduinoPackage pkg : index.getPackages()) { + public static IArduinoPackage getPackageByProvider(String packager) { + for (IArduinoPlatformPackageIndex index : getPackageIndices()) { + for (IArduinoPackage pkg : index.getPackages()) { if (packager.equals(pkg.getID())) { return pkg; } diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoLibrary.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoLibrary.java new file mode 100644 index 00000000..62857bca --- /dev/null +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoLibrary.java @@ -0,0 +1,60 @@ +package io.sloeber.arduinoFramework.api; + +import java.util.Collection; +import java.util.List; + +import org.eclipse.core.runtime.IPath; + +import io.sloeber.arduinoFramework.internal.Node; +import io.sloeber.core.api.VersionNumber; + +public interface IArduinoLibrary extends Comparable { + + Collection getVersions(); + + String getAuthor(); + + String getMaintainer(); + + String getWebsite(); + + String getCategory(); + + List getArchitectures(); + + List getTypes(); + + /** + * Get the newest version of this library + * + * @return the newest version of this library + */ + IArduinoLibraryVersion getNewestVersion(); + + /** + * Get the version that is installed + * If no version is installed return NULL + * + * @return + */ + IArduinoLibraryVersion getInstalledVersion(); + + /** + * checks if a version of this library is installed. + * + * @return true if a version is installed. false in case no version is installed + */ + boolean isInstalled(); + + //Below are the Node overrides + String getNodeName(); + + Node[] getChildren(); + + String getID(); + + IPath getInstallPath(); + + IArduinoLibraryVersion getVersion(VersionNumber versionNumber); + +} \ No newline at end of file diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoLibraryIndex.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoLibraryIndex.java new file mode 100644 index 00000000..fac54de7 --- /dev/null +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoLibraryIndex.java @@ -0,0 +1,31 @@ +package io.sloeber.arduinoFramework.api; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + + +public interface IArduinoLibraryIndex extends Comparable{ + + /** + * given a library name provide the library + * + * @param libraryName + * @return the library or null if not found + */ + IArduinoLibrary getLibrary(String libraryName); + + /** + * get all the latest versions of all the libraries provided that can be + * installed but are not yet installed To do so I find all latest libraries and + * I remove the once that are installed. + * + * @return + */ + Map getLatestInstallableLibraries(Set libNames); + + Collection getLibraries(); + + String getID(); + +} \ No newline at end of file diff --git a/io.sloeber.core/src/io/sloeber/core/api/IArduinoLibraryVersion.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoLibraryVersion.java similarity index 60% rename from io.sloeber.core/src/io/sloeber/core/api/IArduinoLibraryVersion.java rename to io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoLibraryVersion.java index c91d08d7..368e4df2 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/IArduinoLibraryVersion.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoLibraryVersion.java @@ -1,8 +1,12 @@ -package io.sloeber.core.api; +package io.sloeber.arduinoFramework.api; + +import java.util.List; import org.eclipse.core.runtime.IPath; -public interface IArduinoLibraryVersion { +import io.sloeber.core.api.VersionNumber; + +public interface IArduinoLibraryVersion extends Comparable{ String getName(); @@ -39,5 +43,21 @@ public interface IArduinoLibraryVersion { public boolean equals(IArduinoLibraryVersion other); + IArduinoLibrary getLibrary(); + + VersionNumber getVersion(); + + boolean isInstalled(); + + List getArchitectures(); + + String getParagraph(); + + String getSentence(); + + String getMaintainer(); + + String getAuthor(); + } diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoPackage.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoPackage.java new file mode 100644 index 00000000..245e4c0e --- /dev/null +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoPackage.java @@ -0,0 +1,69 @@ +package io.sloeber.arduinoFramework.api; + +import java.util.Collection; +import java.util.List; + +import org.eclipse.core.runtime.IPath; + +import io.sloeber.arduinoFramework.internal.ArduinoPlatformTool; +import io.sloeber.arduinoFramework.internal.ArduinoPlatformToolVersion; +import io.sloeber.core.api.VersionNumber; + +public interface IArduinoPackage extends Comparable{ + + IArduinoPlatformPackageIndex getPackageIndex(); + + String getMaintainer(); + + String getWebsiteURL(); + + String getEmail(); + + String getHelp(); + + Collection getPlatforms(); + + /** + * This method looks up the installed platforms So if you have 2 arduino avr + * platform versions installed you will get back 2. + * + * @return all the installed platforms + */ + List getInstalledPlatforms(); + + /** + * get tyhe platform based on the platform ID + * The platform ID is the architecture + * + * @param platformID + * @return return the platfiorm or null if not found + */ + IArduinoPlatform getPlatform(String platformID); + + Collection getTools(); + + ArduinoPlatformToolVersion getTool(String toolName, VersionNumber version); + + ArduinoPlatformTool getTool(String toolName); + + ArduinoPlatformToolVersion getNewestInstalled(String toolName); + + void onlyKeepLatestPlatforms(); + + boolean isInstalled(); + + /** + * Is any version of the platform installed + * + * @param platformName + * @return if a platform with this name is installed + */ + boolean isAVersionOfThisPlatformInstalled(String platformName); + + String getID(); + + IPath getInstallPath(); + + String getName(); + +} \ No newline at end of file diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoPlatform.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoPlatform.java new file mode 100644 index 00000000..069682e0 --- /dev/null +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoPlatform.java @@ -0,0 +1,42 @@ +package io.sloeber.arduinoFramework.api; + +import java.util.Collection; + +import org.eclipse.core.runtime.IPath; + +import io.sloeber.core.api.VersionNumber; + +public interface IArduinoPlatform extends Comparable{ + + boolean isInstalled(); + + IPath getInstallPath(); + + /** + * Get the newest version of this platform + * + * @return the newest version of this platform + */ + IArduinoPlatformVersion getNewestVersion(); + + Collection getVersions(); + + IArduinoPlatformVersion getVersion(VersionNumber refVersion); + + /** + * return the installed version with the newest version number + * Null if no version is installed + * + * @return + */ + IArduinoPlatformVersion getNewestInstalled(); + + String getName(); + + String getArchitecture(); + + IArduinoPackage getParent(); + + String getID(); + +} \ No newline at end of file diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoPlatformPackageIndex.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoPlatformPackageIndex.java new file mode 100644 index 00000000..79332f1e --- /dev/null +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoPlatformPackageIndex.java @@ -0,0 +1,21 @@ +package io.sloeber.arduinoFramework.api; + +import java.io.File; +import java.util.List; + + +public interface IArduinoPlatformPackageIndex { + + List getPackages(); + + IArduinoPackage getPackage(String packageName); + + File getJsonFile(); + + boolean isInstalled(); + + String getID(); + + String getName(); + +} \ No newline at end of file diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoPlatformVersion.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoPlatformVersion.java new file mode 100644 index 00000000..3b84efc1 --- /dev/null +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoPlatformVersion.java @@ -0,0 +1,34 @@ +package io.sloeber.arduinoFramework.api; + +import java.io.File; +import java.util.List; + +import io.sloeber.arduinoFramework.internal.ArduinoPlatformTooldDependency; +import io.sloeber.core.api.VersionNumber; + +public abstract class IArduinoPlatformVersion extends ArduinoInstallable implements Comparable{ + + public abstract IArduinoPlatform getParent(); + + public abstract String getArchitecture(); + + public abstract VersionNumber getVersion(); + + public abstract String getCategory(); + + public abstract List getToolsDependencies(); + + public abstract boolean isInstalled(); + + public abstract File getBoardsFile(); + + public abstract File getPlatformFile(); + + public abstract List getBoardNames(); + + public abstract String getID(); + + public abstract String getConcattenatedBoardNames(); + + +} \ No newline at end of file diff --git a/io.sloeber.core/src/io/sloeber/core/api/IExample.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IExample.java similarity index 88% rename from io.sloeber.core/src/io/sloeber/core/api/IExample.java rename to io.sloeber.core/src/io/sloeber/arduinoFramework/api/IExample.java index f38712c8..40725635 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/IExample.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IExample.java @@ -1,4 +1,4 @@ -package io.sloeber.core.api; +package io.sloeber.arduinoFramework.api; import java.util.Collection; diff --git a/io.sloeber.core/src/io/sloeber/core/api/LibraryManager.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java similarity index 89% rename from io.sloeber.core/src/io/sloeber/core/api/LibraryManager.java rename to io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java index 9933eba4..fff18cdf 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/LibraryManager.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java @@ -1,4 +1,4 @@ -package io.sloeber.core.api; +package io.sloeber.arduinoFramework.api; import static io.sloeber.core.Messages.*; import static io.sloeber.core.api.Common.*; @@ -29,10 +29,13 @@ import com.google.gson.Gson; +import io.sloeber.arduinoFramework.internal.ArduinoLibraryIndex; +import io.sloeber.arduinoFramework.internal.ArduinoLibraryVersion; import io.sloeber.core.Activator; -import io.sloeber.core.api.Json.ArduinoLibrary; -import io.sloeber.core.api.Json.ArduinoLibraryIndex; -import io.sloeber.core.api.Json.ArduinoLibraryVersion; +import io.sloeber.core.api.Common; +import io.sloeber.core.api.ConfigurationPreferences; +import io.sloeber.core.api.Defaults; +import io.sloeber.core.api.IInstallLibraryHandler; import io.sloeber.core.common.InstancePreferences; import io.sloeber.core.core.DefaultInstallHandler; import io.sloeber.core.internal.ArduinoHardwareLibrary; @@ -56,10 +59,10 @@ public class LibraryManager { private static final Set IGNORE_FILES = new HashSet<>(Arrays.asList(DOT, DOT + DOT)); private static final Set CODE_EXTENSIONS = new HashSet<>(Arrays.asList("h", "cpp")); //$NON-NLS-1$ //$NON-NLS-2$ - static private List libraryIndices; + static private List libraryIndices; private static IInstallLibraryHandler myInstallLibraryHandler = new DefaultInstallHandler(); - static public List getLibraryIndices() { + static public List getLibraryIndices() { if (libraryIndices == null) { BoardsManager.getPackageIndices(); } @@ -91,13 +94,13 @@ public static void setPrivateLibraryPaths(String[] libraryPaths) { } public static void InstallDefaultLibraries(IProgressMonitor monitor) { - for (ArduinoLibraryIndex libindex : libraryIndices) { + for (IArduinoLibraryIndex libindex : libraryIndices) { for (String libraryName : Defaults.DEFAULT_INSTALLED_LIBRARIES) { - ArduinoLibrary toInstalLib = libindex.getLibrary(libraryName); + IArduinoLibrary toInstalLib = libindex.getLibrary(libraryName); if (toInstalLib != null) { - ArduinoLibraryVersion toInstalLibVersion = toInstalLib.getNewestVersion(); - ArduinoLibraryVersion instaledLibVersion = toInstalLib.getInstalledVersion(); + IArduinoLibraryVersion toInstalLibVersion = toInstalLib.getNewestVersion(); + IArduinoLibraryVersion instaledLibVersion = toInstalLib.getInstalledVersion(); if (toInstalLibVersion != null) { if (toInstalLibVersion != instaledLibVersion) { if (instaledLibVersion != null) { @@ -113,8 +116,8 @@ public static void InstallDefaultLibraries(IProgressMonitor monitor) { static public void loadJson(File jsonFile) { try (Reader reader = new FileReader(jsonFile)) { - ArduinoLibraryIndex index = new Gson().fromJson(reader, ArduinoLibraryIndex.class); - index.setJsonFile(jsonFile); + IArduinoLibraryIndex index = new Gson().fromJson(reader, ArduinoLibraryIndex.class); + ((ArduinoLibraryIndex)index).setJsonFile(jsonFile); libraryIndices.add(index); } catch (Exception e) { Common.log(new Status(IStatus.ERROR, Activator.getId(), @@ -163,7 +166,7 @@ public static IStatus install(IArduinoLibraryVersion inLib, IProgressMonitor mon * @param monitor * @return Status.OK_STATUS if delete is successful otherwise IStatus.ERROR */ - public static IStatus unInstall(ArduinoLibraryVersion lib, IProgressMonitor monitor) { + public static IStatus unInstall(IArduinoLibraryVersion lib, IProgressMonitor monitor) { if (!lib.isInstalled()) { return Status.OK_STATUS; } @@ -187,16 +190,16 @@ public static IStatus unInstall(ArduinoLibraryVersion lib, IProgressMonitor moni * @param category */ public static void installAllLatestLibraries() { - List libraryIndices1 = getLibraryIndices(); - for (ArduinoLibraryIndex libraryIndex : libraryIndices1) { - for (ArduinoLibrary curLib : libraryIndex.getLibraries()) { + List libraryIndices1 = getLibraryIndices(); + for (IArduinoLibraryIndex libraryIndex : libraryIndices1) { + for (IArduinoLibrary curLib : libraryIndex.getLibraries()) { String curLibName = curLib.getNodeName(); String[] skipArray = { "Base64", "Add others if needed" }; //$NON-NLS-1$ //$NON-NLS-2$ List skipList = Arrays.asList(skipArray); if (!skipList.contains(curLibName)) { - ArduinoLibraryVersion latestLibVersion = curLib.getNewestVersion(); + IArduinoLibraryVersion latestLibVersion = curLib.getNewestVersion(); if (!latestLibVersion.isInstalled()) { - ArduinoLibraryVersion installedLibVersion = curLib.getInstalledVersion(); + IArduinoLibraryVersion installedLibVersion = curLib.getInstalledVersion(); if (installedLibVersion != null) { unInstall(installedLibVersion, null); } @@ -222,7 +225,7 @@ public static void unInstallAllLibs() { public static Map getLatestInstallableLibraries(Set libnames) { Set remainingLibNames = new TreeSet<>(libnames); Map ret = new HashMap<>(); - for (ArduinoLibraryIndex libraryIndex : libraryIndices) { + for (IArduinoLibraryIndex libraryIndex : libraryIndices) { ret.putAll(libraryIndex.getLatestInstallableLibraries(remainingLibNames)); remainingLibNames.removeAll(ret.keySet()); } @@ -321,12 +324,12 @@ public static TreeMap getExamplesFromIDE() { return examples; } - public static IStatus updateLibraries(Set toUnInstallLibs, - Set toInstallLibs, IProgressMonitor monitor, MultiStatus status) { - for (ArduinoLibraryVersion curLib : toUnInstallLibs) { + public static IStatus updateLibraries(Set toUnInstallLibs, + Set toInstallLibs, IProgressMonitor monitor, MultiStatus status) { + for (IArduinoLibraryVersion curLib : toUnInstallLibs) { status.add(unInstall(curLib, monitor)); } - for (ArduinoLibraryVersion curLib : toInstallLibs) { + for (IArduinoLibraryVersion curLib : toInstallLibs) { status.add(install(curLib, monitor)); } return status; @@ -353,8 +356,8 @@ public static TreeMap getLibrariesAll(BoardDescr private static Map getLibrariesdManaged() { Map ret = new HashMap<>(); - for (ArduinoLibraryIndex libindex : libraryIndices) { - for (ArduinoLibrary curLib : libindex.getLibraries()) { + for (IArduinoLibraryIndex libindex : libraryIndices) { + for (IArduinoLibrary curLib : libindex.getLibraries()) { IArduinoLibraryVersion instVersion = curLib.getInstalledVersion(); if (instVersion != null) { ret.put(instVersion.getFQN().toPortableString(), instVersion); diff --git a/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoLibrary.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoLibrary.java similarity index 75% rename from io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoLibrary.java rename to io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoLibrary.java index 8978a8f9..ed165309 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoLibrary.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoLibrary.java @@ -1,9 +1,10 @@ -package io.sloeber.core.api.Json; +package io.sloeber.arduinoFramework.internal; -import static io.sloeber.core.Gson.GsonConverter.*; +import static io.sloeber.arduinoFramework.internal.GsonConverter.*; import java.util.Collection; import java.util.Collections; +import java.util.LinkedList; import java.util.List; import java.util.TreeMap; @@ -13,6 +14,8 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; +import io.sloeber.arduinoFramework.api.IArduinoLibrary; +import io.sloeber.arduinoFramework.api.IArduinoLibraryVersion; import io.sloeber.core.api.ConfigurationPreferences; import io.sloeber.core.api.VersionNumber; @@ -23,7 +26,7 @@ * */ -public class ArduinoLibrary extends Node implements Comparable { +public class ArduinoLibrary extends Node implements IArduinoLibrary { private String name; private TreeMap versions = new TreeMap<>(Collections.reverseOrder()); @@ -50,15 +53,18 @@ protected void addVersion(JsonElement json) { versions.put(versionNumber, new ArduinoLibraryVersion(jsonObject, this)); } - public Collection getVersions() { - return versions.values(); + @Override + public Collection getVersions() { + return new LinkedList<>(versions.values()); } - public String getAuthor() { + @Override + public String getAuthor() { return getNewestVersion().getAuthor(); } - public String getMaintainer() { + @Override + public String getMaintainer() { return getNewestVersion().getMaintainer(); } @@ -70,19 +76,23 @@ public String getParagraph() { return getNewestVersion().getParagraph(); } - public String getWebsite() { + @Override + public String getWebsite() { return getNewestVersion().getWebsite(); } - public String getCategory() { + @Override + public String getCategory() { return getNewestVersion().getCategory(); } - public List getArchitectures() { + @Override + public List getArchitectures() { return getNewestVersion().getArchitectures(); } - public List getTypes() { + @Override + public List getTypes() { return getNewestVersion().getTypes(); } @@ -95,7 +105,8 @@ public String getUrl() { * * @return the newest version of this library */ - public ArduinoLibraryVersion getNewestVersion() { + @Override + public ArduinoLibraryVersion getNewestVersion() { return versions.firstEntry().getValue(); } @@ -105,7 +116,8 @@ public ArduinoLibraryVersion getNewestVersion() { * * @return */ - public ArduinoLibraryVersion getInstalledVersion() { + @Override + public ArduinoLibraryVersion getInstalledVersion() { for (ArduinoLibraryVersion curVersion : versions.values()) { if (curVersion.isInstalled()) { return curVersion; @@ -119,12 +131,13 @@ public ArduinoLibraryVersion getInstalledVersion() { * * @return true if a version is installed. false in case no version is installed */ - public boolean isInstalled() { + @Override + public boolean isInstalled() { return getInstalledVersion() != null; } @Override - public int compareTo(ArduinoLibrary other) { + public int compareTo(IArduinoLibrary other) { return getID().compareTo(other.getID()); } @@ -149,11 +162,13 @@ public String getID() { return name; } - public IPath getInstallPath() { + @Override + public IPath getInstallPath() { return ConfigurationPreferences.getInstallationPathLibraries().append(this.name.replace(' ', '_')); } - public ArduinoLibraryVersion getVersion(VersionNumber versionNumber) { + @Override + public ArduinoLibraryVersion getVersion(VersionNumber versionNumber) { return versions.get(versionNumber); } diff --git a/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoLibraryIndex.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoLibraryIndex.java similarity index 76% rename from io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoLibraryIndex.java rename to io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoLibraryIndex.java index c9540ccc..12ddfae5 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoLibraryIndex.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoLibraryIndex.java @@ -1,11 +1,12 @@ -package io.sloeber.core.api.Json; +package io.sloeber.arduinoFramework.internal; -import static io.sloeber.core.Gson.GsonConverter.*; +import static io.sloeber.arduinoFramework.internal.GsonConverter.*; import java.io.File; import java.lang.reflect.Type; import java.util.Collection; import java.util.HashMap; +import java.util.LinkedList; import java.util.Map; import java.util.Set; import java.util.TreeMap; @@ -17,6 +18,10 @@ import com.google.gson.JsonParseException; import com.google.gson.annotations.JsonAdapter; +import io.sloeber.arduinoFramework.api.IArduinoLibrary; +import io.sloeber.arduinoFramework.api.IArduinoLibraryIndex; +import io.sloeber.arduinoFramework.api.IArduinoLibraryVersion; + /** * This class represents a json file that references libraries * @@ -25,7 +30,7 @@ */ @JsonAdapter(ArduinoLibraryIndex.class) public class ArduinoLibraryIndex extends Node - implements Comparable, JsonDeserializer { + implements JsonDeserializer, IArduinoLibraryIndex { private TreeMap libraries = new TreeMap<>(); private transient File jsonFile; @@ -35,11 +40,12 @@ public void setJsonFile(File packageFile) { /** * given a library name provide the library - * + * * @param libraryName * @return the library or null if not found */ - public ArduinoLibrary getLibrary(String libraryName) { + @Override + public IArduinoLibrary getLibrary(String libraryName) { return libraries.get(libraryName); } @@ -50,12 +56,13 @@ public ArduinoLibrary getLibrary(String libraryName) { * * @return */ - public Map getLatestInstallableLibraries(Set libNames) { - Map ret = new HashMap<>(); + @Override + public Map getLatestInstallableLibraries(Set libNames) { + Map ret = new HashMap<>(); if (libNames.isEmpty()) { return ret; } - for (ArduinoLibrary curLibrary : libraries.values()) { + for (IArduinoLibrary curLibrary : libraries.values()) { if (libNames.contains(curLibrary.getNodeName())) { if (!curLibrary.isInstalled()) { ret.put(curLibrary.getNodeName(), curLibrary.getNewestVersion()); @@ -111,12 +118,13 @@ public String getID() { } @Override - public int compareTo(ArduinoLibraryIndex o) { + public int compareTo(IArduinoLibraryIndex o) { return getID().compareTo(o.getID()); } - public Collection getLibraries() { - return libraries.values(); + @Override + public Collection getLibraries() { + return new LinkedList<> (libraries.values()); } } diff --git a/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoLibraryVersion.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoLibraryVersion.java similarity index 91% rename from io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoLibraryVersion.java rename to io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoLibraryVersion.java index 82a176a1..571121fd 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoLibraryVersion.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoLibraryVersion.java @@ -1,6 +1,6 @@ -package io.sloeber.core.api.Json; +package io.sloeber.arduinoFramework.internal; -import static io.sloeber.core.Gson.GsonConverter.*; +import static io.sloeber.arduinoFramework.internal.GsonConverter.*; import static io.sloeber.core.api.Const.*; import java.util.ArrayList; @@ -13,7 +13,8 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; -import io.sloeber.core.api.IArduinoLibraryVersion; +import io.sloeber.arduinoFramework.api.IArduinoLibrary; +import io.sloeber.arduinoFramework.api.IArduinoLibraryVersion; import io.sloeber.core.api.VersionNumber; /** @@ -23,7 +24,7 @@ * */ -public class ArduinoLibraryVersion extends Node implements IArduinoLibraryVersion, Comparable { +public class ArduinoLibraryVersion extends Node implements IArduinoLibraryVersion { private String name; private VersionNumber version; @@ -72,22 +73,27 @@ public ArduinoLibraryVersion(JsonElement json, ArduinoLibrary arduinoLibrary) { } + @Override public VersionNumber getVersion() { return version; } + @Override public String getAuthor() { return author; } + @Override public String getMaintainer() { return maintainer; } + @Override public String getSentence() { return sentence; } + @Override public String getParagraph() { return paragraph; } @@ -100,6 +106,7 @@ public String getCategory() { return category; } + @Override public List getArchitectures() { return architectures; } @@ -124,12 +131,13 @@ public String getChecksum() { return checksum; } + @Override public boolean isInstalled() { return getInstallPath().toFile().exists(); } @Override - public int compareTo(ArduinoLibraryVersion other) { + public int compareTo(IArduinoLibraryVersion other) { if (other == null) { return 1; } @@ -142,7 +150,8 @@ public int compareTo(ArduinoLibraryVersion other) { return version.toString().compareTo(other.getVersion().toString()); } - public ArduinoLibrary getLibrary() { + @Override + public IArduinoLibrary getLibrary() { return myParent; } diff --git a/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoPackage.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPackage.java similarity index 75% rename from io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoPackage.java rename to io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPackage.java index 962ddece..0c943896 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoPackage.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPackage.java @@ -5,9 +5,9 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/ -package io.sloeber.core.api.Json; +package io.sloeber.arduinoFramework.internal; -import static io.sloeber.core.Gson.GsonConverter.*; +import static io.sloeber.arduinoFramework.internal.GsonConverter.*; import java.util.Collection; import java.util.LinkedList; @@ -20,11 +20,14 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; -import io.sloeber.core.api.BoardsManager; +import io.sloeber.arduinoFramework.api.BoardsManager; +import io.sloeber.arduinoFramework.api.IArduinoPackage; +import io.sloeber.arduinoFramework.api.IArduinoPlatform; +import io.sloeber.arduinoFramework.api.IArduinoPlatformVersion; import io.sloeber.core.api.ConfigurationPreferences; import io.sloeber.core.api.VersionNumber; -public class ArduinoPackage extends Node implements Comparable { +public class ArduinoPackage extends Node implements IArduinoPackage { private String name; private String maintainer; @@ -75,7 +78,8 @@ public ArduinoPackage(JsonElement json, ArduinoPlatformPackageIndex packageIndex } } - public ArduinoPlatformPackageIndex getPackageIndex() { + @Override + public ArduinoPlatformPackageIndex getPackageIndex() { return myParent; } @@ -84,24 +88,29 @@ public String getNodeName() { return name; } - public String getMaintainer() { + @Override + public String getMaintainer() { return maintainer; } - public String getWebsiteURL() { + @Override + public String getWebsiteURL() { return websiteURL; } - public String getEmail() { + @Override + public String getEmail() { return email; } - public String getHelp() { + @Override + public String getHelp() { return helpOnline; } - public Collection getPlatforms() { - return platforms.values(); + @Override + public Collection getPlatforms() { + return new LinkedList<>( platforms.values()); } /** @@ -110,10 +119,11 @@ public Collection getPlatforms() { * * @return all the installed platforms */ - public List getInstalledPlatforms() { - List platformMap = new LinkedList<>(); + @Override + public List getInstalledPlatforms() { + List platformMap = new LinkedList<>(); for (ArduinoPlatform platform : platforms.values()) { - for (ArduinoPlatformVersion platformVersion : platform.getVersions()) { + for (IArduinoPlatformVersion platformVersion : platform.getVersions()) { if (platformVersion.isInstalled()) { platformMap.add(platformVersion); } @@ -129,15 +139,18 @@ public List getInstalledPlatforms() { * @param platformID * @return return the platfiorm or null if not found */ - public ArduinoPlatform getPlatform(String platformID) { + @Override + public IArduinoPlatform getPlatform(String platformID) { return platforms.get(platformID); } - public Collection getTools() { + @Override + public Collection getTools() { return tools.values(); } - public ArduinoPlatformToolVersion getTool(String toolName, VersionNumber version) { + @Override + public ArduinoPlatformToolVersion getTool(String toolName, VersionNumber version) { ArduinoPlatformTool tool = tools.get(toolName); if (tool == null) { return null; @@ -145,11 +158,13 @@ public ArduinoPlatformToolVersion getTool(String toolName, VersionNumber version return tool.getVersion(version); } - public ArduinoPlatformTool getTool(String toolName) { + @Override + public ArduinoPlatformTool getTool(String toolName) { return tools.get(toolName); } - public ArduinoPlatformToolVersion getNewestInstalled(String toolName) { + @Override + public ArduinoPlatformToolVersion getNewestInstalled(String toolName) { ArduinoPlatformTool tool = tools.get(toolName); if (tool == null) { return null; @@ -171,14 +186,15 @@ public int hashCode() { } @Override - public int compareTo(ArduinoPackage other) { - return this.name.compareTo(other.name); + public int compareTo(IArduinoPackage other) { + return this.name.compareTo(other.getName()); } - public void onlyKeepLatestPlatforms() { - for (ArduinoPlatform curplatform : platforms.values()) { - ArduinoPlatformVersion newestVersion = null; - for (ArduinoPlatformVersion curVersion : curplatform.getVersions()) { + @Override + public void onlyKeepLatestPlatforms() { + for (IArduinoPlatform curplatform : platforms.values()) { + IArduinoPlatformVersion newestVersion = null; + for (IArduinoPlatformVersion curVersion : curplatform.getVersions()) { if (curVersion.isInstalled()) { if (newestVersion == null) { newestVersion = curVersion; @@ -194,8 +210,9 @@ public void onlyKeepLatestPlatforms() { } } - public boolean isInstalled() { - for (ArduinoPlatform platform : platforms.values()) { + @Override + public boolean isInstalled() { + for (IArduinoPlatform platform : platforms.values()) { if (platform.isInstalled()) { return true; } @@ -209,7 +226,8 @@ public boolean isInstalled() { * @param platformName * @return if a platform with this name is installed */ - public boolean isAVersionOfThisPlatformInstalled(String platformName) { + @Override + public boolean isAVersionOfThisPlatformInstalled(String platformName) { for (ArduinoPlatform platform : this.platforms.values()) { if (platform.getName().equals(platformName)) { if (platform.isInstalled()) { @@ -225,7 +243,8 @@ public String getID() { return name; } - public IPath getInstallPath() { + @Override + public IPath getInstallPath() { return ConfigurationPreferences.getInstallationPathPackages().append(getID()); } @@ -239,4 +258,9 @@ public Node getParent() { return myParent; } + @Override + public String getName() { + return name; + } + } diff --git a/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoPlatform.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatform.java similarity index 67% rename from io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoPlatform.java rename to io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatform.java index b97a3a21..867e6fad 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoPlatform.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatform.java @@ -5,13 +5,14 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/ -package io.sloeber.core.api.Json; +package io.sloeber.arduinoFramework.internal; -import static io.sloeber.core.Gson.GsonConverter.*; +import static io.sloeber.arduinoFramework.internal.GsonConverter.*; import static io.sloeber.core.api.Const.*; import java.util.Collection; import java.util.Collections; +import java.util.LinkedList; import java.util.TreeMap; import org.eclipse.core.runtime.IPath; @@ -20,14 +21,17 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; +import io.sloeber.arduinoFramework.api.IArduinoPackage; +import io.sloeber.arduinoFramework.api.IArduinoPlatform; +import io.sloeber.arduinoFramework.api.IArduinoPlatformVersion; import io.sloeber.core.api.Const; import io.sloeber.core.api.VersionNumber; -public class ArduinoPlatform implements Comparable { +public class ArduinoPlatform implements IArduinoPlatform { private String name; private String architecture; - private TreeMap myVersions = new TreeMap<>(Collections.reverseOrder()); + private TreeMap myVersions = new TreeMap<>(Collections.reverseOrder()); private ArduinoPackage myParent; @@ -50,20 +54,24 @@ protected void addVersion(JsonElement json) { myVersions.put(version.getVersion(), version); } - public ArduinoPackage getParent() { + @Override + public IArduinoPackage getParent() { return this.myParent; } - public String getName() { + @Override + public String getName() { return this.name; } - public String getArchitecture() { + @Override + public String getArchitecture() { return architecture; } - public boolean isInstalled() { - for (ArduinoPlatformVersion curPlatformVersion : myVersions.values()) { + @Override + public boolean isInstalled() { + for (IArduinoPlatformVersion curPlatformVersion : myVersions.values()) { if (curPlatformVersion.isInstalled()) { return true; } @@ -71,16 +79,18 @@ public boolean isInstalled() { return false; } - public IPath getInstallPath() { + @Override + public IPath getInstallPath() { return myParent.getInstallPath().append(Const.ARDUINO_HARDWARE_FOLDER_NAME).append(getID()); } - public String getID() { + @Override + public String getID() { return architecture; } @Override - public int compareTo(ArduinoPlatform o) { + public int compareTo(IArduinoPlatform o) { return name.compareTo(o.getName()); } @@ -89,15 +99,18 @@ public int compareTo(ArduinoPlatform o) { * * @return the newest version of this platform */ - public ArduinoPlatformVersion getNewestVersion() { + @Override + public IArduinoPlatformVersion getNewestVersion() { return myVersions.firstEntry().getValue(); } - public Collection getVersions() { - return myVersions.values(); + @Override + public Collection getVersions() { + return new LinkedList<>(myVersions.values()); } - public ArduinoPlatformVersion getVersion(VersionNumber refVersion) { + @Override + public IArduinoPlatformVersion getVersion(VersionNumber refVersion) { return myVersions.get(refVersion); } @@ -107,8 +120,9 @@ public ArduinoPlatformVersion getVersion(VersionNumber refVersion) { * * @return */ - public ArduinoPlatformVersion getNewestInstalled() { - for (ArduinoPlatformVersion curVersion : myVersions.values()) { + @Override + public IArduinoPlatformVersion getNewestInstalled() { + for (IArduinoPlatformVersion curVersion : myVersions.values()) { if (curVersion.isInstalled()) { return curVersion; } diff --git a/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoPlatformPackageIndex.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformPackageIndex.java similarity index 80% rename from io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoPlatformPackageIndex.java rename to io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformPackageIndex.java index df34bcda..b97a3dc2 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoPlatformPackageIndex.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformPackageIndex.java @@ -5,11 +5,12 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/ -package io.sloeber.core.api.Json; +package io.sloeber.arduinoFramework.internal; import java.io.File; import java.lang.reflect.Type; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; import com.google.gson.JsonDeserializationContext; @@ -19,19 +20,24 @@ import com.google.gson.JsonParseException; import com.google.gson.annotations.JsonAdapter; +import io.sloeber.arduinoFramework.api.IArduinoPackage; +import io.sloeber.arduinoFramework.api.IArduinoPlatformPackageIndex; + @JsonAdapter(ArduinoPlatformPackageIndex.class) public class ArduinoPlatformPackageIndex extends Node - implements Comparable, JsonDeserializer { + implements Comparable, JsonDeserializer, IArduinoPlatformPackageIndex { private List myPackages = new ArrayList<>(); private transient File myJsonFile; - public List getPackages() { - return myPackages; + @Override + public List getPackages() { + return new LinkedList<>(myPackages); } - public ArduinoPackage getPackage(String packageName) { + @Override + public IArduinoPackage getPackage(String packageName) { for (ArduinoPackage pkg : myPackages) { if (pkg.getNodeName().equals(packageName)) { return pkg; @@ -44,7 +50,8 @@ public void setPackageFile(File packageFile) { myJsonFile = packageFile; } - public File getJsonFile() { + @Override + public File getJsonFile() { return myJsonFile; } @@ -76,8 +83,9 @@ public ArduinoPlatformPackageIndex deserialize(JsonElement json, Type arg1, Json } - public boolean isInstalled() { - for (ArduinoPackage pkg : myPackages) { + @Override + public boolean isInstalled() { + for (IArduinoPackage pkg : myPackages) { if (pkg.isInstalled()) { return true; } @@ -105,4 +113,9 @@ public int compareTo(ArduinoPlatformPackageIndex o) { return getID().compareTo(o.getID()); } + @Override + public String getName() { + return getNodeName(); + } + } diff --git a/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoPlatformTool.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformTool.java similarity index 94% rename from io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoPlatformTool.java rename to io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformTool.java index c7bbfc24..b970b689 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoPlatformTool.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformTool.java @@ -5,9 +5,9 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/ -package io.sloeber.core.api.Json; +package io.sloeber.arduinoFramework.internal; -import static io.sloeber.core.Gson.GsonConverter.*; +import static io.sloeber.arduinoFramework.internal.GsonConverter.*; import static io.sloeber.core.api.Const.*; import java.util.Collection; @@ -21,6 +21,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; +import io.sloeber.arduinoFramework.api.IArduinoPackage; import io.sloeber.core.api.VersionNumber; public class ArduinoPlatformTool extends Node { @@ -48,7 +49,7 @@ protected void addVersion(JsonElement json) { myVersions.put(version.getVersion(), version); } - public ArduinoPackage getPackage() { + public IArduinoPackage getPackage() { return myParentPackage; } diff --git a/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoPlatformToolVersion.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformToolVersion.java similarity index 89% rename from io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoPlatformToolVersion.java rename to io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformToolVersion.java index 23467bb5..aacad409 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoPlatformToolVersion.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformToolVersion.java @@ -5,9 +5,9 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/ -package io.sloeber.core.api.Json; +package io.sloeber.arduinoFramework.internal; -import static io.sloeber.core.Gson.GsonConverter.*; +import static io.sloeber.arduinoFramework.internal.GsonConverter.*; import static io.sloeber.core.api.Const.*; import java.util.ArrayList; @@ -20,6 +20,10 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; +import io.sloeber.arduinoFramework.api.ArduinoInstallable; +import io.sloeber.arduinoFramework.api.IArduinoPackage; +import io.sloeber.arduinoFramework.api.IArduinoPlatform; +import io.sloeber.arduinoFramework.api.IArduinoPlatformVersion; import io.sloeber.core.api.VersionNumber; public class ArduinoPlatformToolVersion extends Node { @@ -82,9 +86,9 @@ public IPath getInstallPath() { if (myInstallPath != null) { return myInstallPath; } - ArduinoPackage pkg = myParentTool.getPackage(); - for (ArduinoPlatform curPlatform : pkg.getPlatforms()) { - for (ArduinoPlatformVersion curplatformVersion : curPlatform.getVersions()) { + IArduinoPackage pkg = myParentTool.getPackage(); + for (IArduinoPlatform curPlatform : pkg.getPlatforms()) { + for (IArduinoPlatformVersion curplatformVersion : curPlatform.getVersions()) { for (ArduinoPlatformTooldDependency curTooldependency : curplatformVersion.getToolsDependencies()) { if (curTooldependency.getName().equals(myParentTool.getNodeName()) && curTooldependency.getVersion().compareTo(myVersion) == 0) { diff --git a/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoPlatformTooldDependency.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformTooldDependency.java similarity index 78% rename from io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoPlatformTooldDependency.java rename to io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformTooldDependency.java index 9d69a4a2..e335eda8 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoPlatformTooldDependency.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformTooldDependency.java @@ -1,6 +1,6 @@ -package io.sloeber.core.api.Json; +package io.sloeber.arduinoFramework.internal; -import static io.sloeber.core.Gson.GsonConverter.*; +import static io.sloeber.arduinoFramework.internal.GsonConverter.*; import static io.sloeber.core.api.Const.*; import org.eclipse.core.runtime.IPath; @@ -9,6 +9,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; +import io.sloeber.arduinoFramework.api.IArduinoPlatformVersion; import io.sloeber.core.api.ConfigurationPreferences; import io.sloeber.core.api.VersionNumber; @@ -18,10 +19,10 @@ public class ArduinoPlatformTooldDependency { private String myPackager; private VersionNumber myVersion; - private transient ArduinoPlatformVersion myParentPlatform; + private transient IArduinoPlatformVersion myParentPlatform; @SuppressWarnings("nls") - public ArduinoPlatformTooldDependency(JsonElement json, ArduinoPlatformVersion arduinoPlatformVersion) { + public ArduinoPlatformTooldDependency(JsonElement json, IArduinoPlatformVersion arduinoPlatformVersion) { myParentPlatform = arduinoPlatformVersion; JsonObject jsonObject = json.getAsJsonObject(); try { diff --git a/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoPlatformVersion.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformVersion.java similarity index 83% rename from io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoPlatformVersion.java rename to io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformVersion.java index 7d632e7d..c69bbb8e 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinoPlatformVersion.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformVersion.java @@ -5,9 +5,9 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/ -package io.sloeber.core.api.Json; +package io.sloeber.arduinoFramework.internal; -import static io.sloeber.core.Gson.GsonConverter.*; +import static io.sloeber.arduinoFramework.internal.GsonConverter.*; import static io.sloeber.core.api.Const.*; import java.io.File; @@ -20,9 +20,10 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; +import io.sloeber.arduinoFramework.api.IArduinoPlatformVersion; import io.sloeber.core.api.VersionNumber; -public class ArduinoPlatformVersion extends ArduinoInstallable implements Comparable { +public class ArduinoPlatformVersion extends IArduinoPlatformVersion { private String myArchitecture; private VersionNumber myVersion; @@ -60,35 +61,43 @@ public ArduinoPlatformVersion(JsonElement json, ArduinoPlatform parent) { } } - public ArduinoPlatform getParent() { + @Override + public ArduinoPlatform getParent() { return myParent; } - public String getArchitecture() { + @Override + public String getArchitecture() { return myArchitecture; } - public VersionNumber getVersion() { + @Override + public VersionNumber getVersion() { return myVersion; } - public String getCategory() { + @Override + public String getCategory() { return myCategory; } - public List getToolsDependencies() { + @Override + public List getToolsDependencies() { return myToolDependencies; } - public boolean isInstalled() { + @Override + public boolean isInstalled() { return getBoardsFile().exists(); } - public File getBoardsFile() { + @Override + public File getBoardsFile() { return getInstallPath().append(BOARDS_FILE_NAME).toFile(); } - public File getPlatformFile() { + @Override + public File getPlatformFile() { return getInstallPath().append(PLATFORM_FILE_NAME).toFile(); } @@ -135,20 +144,23 @@ public boolean equals(Object obj) { return true; } - public List getBoardNames() { + @Override + public List getBoardNames() { return myBoards; } - public String getID() { + @Override + public String getID() { return myVersion.toString(); } - public String getConcattenatedBoardNames() { + @Override + public String getConcattenatedBoardNames() { return String.join("\n", getBoardNames()); //$NON-NLS-1$ } @Override - public int compareTo(ArduinoPlatformVersion o) { + public int compareTo(IArduinoPlatformVersion o) { return myName.compareTo(o.getName()); } diff --git a/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinpPlatformToolSystem.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinpPlatformToolSystem.java similarity index 95% rename from io.sloeber.core/src/io/sloeber/core/api/Json/ArduinpPlatformToolSystem.java rename to io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinpPlatformToolSystem.java index 3f561df5..92ac3fee 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Json/ArduinpPlatformToolSystem.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinpPlatformToolSystem.java @@ -5,9 +5,9 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/ -package io.sloeber.core.api.Json; +package io.sloeber.arduinoFramework.internal; -import static io.sloeber.core.Gson.GsonConverter.*; +import static io.sloeber.arduinoFramework.internal.GsonConverter.*; import org.eclipse.core.runtime.IPath; @@ -15,6 +15,8 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; +import io.sloeber.arduinoFramework.api.ArduinoInstallable; + public class ArduinpPlatformToolSystem extends ArduinoInstallable { private transient ArduinoPlatformToolVersion myParent; diff --git a/io.sloeber.core/src/io/sloeber/core/Gson/GsonConverter.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/GsonConverter.java similarity index 93% rename from io.sloeber.core/src/io/sloeber/core/Gson/GsonConverter.java rename to io.sloeber.core/src/io/sloeber/arduinoFramework/internal/GsonConverter.java index 7f797bf6..af022007 100644 --- a/io.sloeber.core/src/io/sloeber/core/Gson/GsonConverter.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/GsonConverter.java @@ -1,4 +1,4 @@ -package io.sloeber.core.Gson; +package io.sloeber.arduinoFramework.internal; import com.google.gson.JsonElement; import com.google.gson.JsonObject; diff --git a/io.sloeber.core/src/io/sloeber/core/api/Json/Node.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/Node.java similarity index 85% rename from io.sloeber.core/src/io/sloeber/core/api/Json/Node.java rename to io.sloeber.core/src/io/sloeber/arduinoFramework/internal/Node.java index cfa59b30..61dc0162 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Json/Node.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/Node.java @@ -1,4 +1,4 @@ -package io.sloeber.core.api.Json; +package io.sloeber.arduinoFramework.internal; public abstract class Node { public boolean hasChildren() { diff --git a/io.sloeber.core/src/io/sloeber/core/Activator.java b/io.sloeber.core/src/io/sloeber/core/Activator.java index 829e6bb5..86cc8e1e 100644 --- a/io.sloeber.core/src/io/sloeber/core/Activator.java +++ b/io.sloeber.core/src/io/sloeber/core/Activator.java @@ -34,7 +34,7 @@ import org.osgi.service.prefs.Preferences; import cc.arduino.packages.discoverers.SloeberNetworkDiscovery; -import io.sloeber.core.api.BoardsManager; +import io.sloeber.arduinoFramework.api.BoardsManager; import io.sloeber.core.api.Common; import io.sloeber.core.api.ConfigurationPreferences; import io.sloeber.core.common.InstancePreferences; diff --git a/io.sloeber.core/src/io/sloeber/core/api/CodeDescription.java b/io.sloeber.core/src/io/sloeber/core/api/CodeDescription.java index 95269e09..aa2e0fd2 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/CodeDescription.java +++ b/io.sloeber.core/src/io/sloeber/core/api/CodeDescription.java @@ -25,6 +25,10 @@ import org.eclipse.core.runtime.Status; import org.osgi.framework.Bundle; +import io.sloeber.arduinoFramework.api.BoardDescription; +import io.sloeber.arduinoFramework.api.IArduinoLibraryVersion; +import io.sloeber.arduinoFramework.api.IExample; +import io.sloeber.arduinoFramework.api.LibraryManager; import io.sloeber.autoBuild.api.ICodeProvider; import io.sloeber.core.Activator; import io.sloeber.core.common.InstancePreferences; diff --git a/io.sloeber.core/src/io/sloeber/core/api/Defaults.java b/io.sloeber.core/src/io/sloeber/core/api/Defaults.java index 82a383df..ad4acaa9 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Defaults.java +++ b/io.sloeber.core/src/io/sloeber/core/api/Defaults.java @@ -1,6 +1,5 @@ package io.sloeber.core.api; -import static io.sloeber.core.api.Common.*; import static io.sloeber.core.api.Const.*; import org.eclipse.core.runtime.IPath; diff --git a/io.sloeber.core/src/io/sloeber/core/api/IInstallLibraryHandler.java b/io.sloeber.core/src/io/sloeber/core/api/IInstallLibraryHandler.java index edca8e28..36496593 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/IInstallLibraryHandler.java +++ b/io.sloeber.core/src/io/sloeber/core/api/IInstallLibraryHandler.java @@ -2,6 +2,8 @@ import java.util.Map; +import io.sloeber.arduinoFramework.api.IArduinoLibraryVersion; + /** * this interface is to allow the ui to handle the automatic installation * of libraries. diff --git a/io.sloeber.core/src/io/sloeber/core/api/ISloeberConfiguration.java b/io.sloeber.core/src/io/sloeber/core/api/ISloeberConfiguration.java index d5a8f37c..e7da6d78 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/ISloeberConfiguration.java +++ b/io.sloeber.core/src/io/sloeber/core/api/ISloeberConfiguration.java @@ -15,6 +15,8 @@ import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; +import io.sloeber.arduinoFramework.api.BoardDescription; +import io.sloeber.arduinoFramework.api.IArduinoLibraryVersion; import io.sloeber.autoBuild.api.IAutoBuildConfigurationDescription; import io.sloeber.autoBuild.integration.AutoBuildConfigurationDescription; import io.sloeber.core.Activator; diff --git a/io.sloeber.core/src/io/sloeber/core/api/LaunchConfiguration.java b/io.sloeber.core/src/io/sloeber/core/api/LaunchConfiguration.java index f1b8db47..4f028788 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/LaunchConfiguration.java +++ b/io.sloeber.core/src/io/sloeber/core/api/LaunchConfiguration.java @@ -9,7 +9,6 @@ import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.model.ILaunchConfigurationDelegate; -@SuppressWarnings("unused") public class LaunchConfiguration implements ILaunchConfigurationDelegate { /** diff --git a/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java b/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java index dbafa93b..25d9d514 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java +++ b/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java @@ -2,16 +2,8 @@ import static io.sloeber.core.api.Const.*; -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.io.InputStreamReader; import java.net.URI; -import java.util.Map; -import java.util.Map.Entry; import java.util.Set; -import java.util.stream.Collectors; - import org.eclipse.cdt.core.CCProjectNature; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.settings.model.CSourceEntry; @@ -19,7 +11,6 @@ import org.eclipse.cdt.core.settings.model.ICProjectDescription; import org.eclipse.cdt.core.settings.model.ICSettingEntry; import org.eclipse.cdt.core.settings.model.ICSourceEntry; -import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspace; @@ -33,6 +24,8 @@ import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; +import io.sloeber.arduinoFramework.api.BoardDescription; +import io.sloeber.arduinoFramework.api.IArduinoLibraryVersion; import io.sloeber.autoBuild.api.AutoBuildProject; import io.sloeber.autoBuild.api.IAutoBuildConfigurationDescription; import io.sloeber.autoBuild.buildTools.api.IBuildTools; @@ -492,33 +485,33 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { // // } - private static void storeConfigurationFile(IFile file, Map vars) throws Exception { - String content = EMPTY; - for (Entry curLine : vars.entrySet()) { - content += curLine.getKey() + '=' + curLine.getValue() + '\n'; - } - - if (file.exists()) { - // if the filecontent hasn't changed=>do nothing - try (InputStream inputStream = file.getContents();) { - String fileContent = new BufferedReader(new InputStreamReader(inputStream)).lines() - .collect(Collectors.joining("\n")); - -// Path filePath = Path.of(file.getLocation().toOSString()); -// String fileContent = Files.readString(filePath); - if (content.equals(fileContent)) { - return; - } - } - file.delete(true, null); - } - - if (!file.exists() && (!content.isBlank())) { - ByteArrayInputStream stream = new ByteArrayInputStream(content.getBytes()); - file.create(stream, true, null); - } - - } +// private static void storeConfigurationFile(IFile file, Map vars) throws Exception { +// String content = EMPTY; +// for (Entry curLine : vars.entrySet()) { +// content += curLine.getKey() + '=' + curLine.getValue() + '\n'; +// } +// +// if (file.exists()) { +// // if the filecontent hasn't changed=>do nothing +// try (InputStream inputStream = file.getContents();) { +// String fileContent = new BufferedReader(new InputStreamReader(inputStream)).lines() +// .collect(Collectors.joining("\n")); //$NON-NLS-1$ +// +//// Path filePath = Path.of(file.getLocation().toOSString()); +//// String fileContent = Files.readString(filePath); +// if (content.equals(fileContent)) { +// return; +// } +// } +// file.delete(true, null); +// } +// +// if (!file.exists() && (!content.isBlank())) { +// ByteArrayInputStream stream = new ByteArrayInputStream(content.getBytes()); +// file.create(stream, true, null); +// } +// +// } // /** // * get the Arduino project description based on a project description diff --git a/io.sloeber.core/src/io/sloeber/core/builder/SloeberBuilderExtension.java b/io.sloeber.core/src/io/sloeber/core/builder/SloeberBuilderExtension.java index 5cf655f5..67710292 100644 --- a/io.sloeber.core/src/io/sloeber/core/builder/SloeberBuilderExtension.java +++ b/io.sloeber.core/src/io/sloeber/core/builder/SloeberBuilderExtension.java @@ -16,6 +16,7 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; +import io.sloeber.arduinoFramework.api.BoardDescription; import io.sloeber.autoBuild.api.AutoBuildBuilderExtension; import io.sloeber.autoBuild.api.AutoBuildProject; import io.sloeber.autoBuild.api.IAutoBuildConfigurationDescription; @@ -25,7 +26,6 @@ import io.sloeber.autoBuild.integration.AutoBuildConfigurationDescription; import io.sloeber.autoBuild.schema.api.IBuilder; import io.sloeber.core.Messages; -import io.sloeber.core.api.BoardDescription; import io.sloeber.core.api.Common; import io.sloeber.core.internal.SloeberConfiguration; import io.sloeber.core.tools.Helpers; diff --git a/io.sloeber.core/src/io/sloeber/core/communication/ArduinoSerial.java b/io.sloeber.core/src/io/sloeber/core/communication/ArduinoSerial.java index 6fc2b2e8..0d7088b1 100644 --- a/io.sloeber.core/src/io/sloeber/core/communication/ArduinoSerial.java +++ b/io.sloeber.core/src/io/sloeber/core/communication/ArduinoSerial.java @@ -11,7 +11,7 @@ import org.eclipse.core.runtime.Status; import org.eclipse.ui.console.MessageConsoleStream; -import io.sloeber.core.api.BoardDescription; +import io.sloeber.arduinoFramework.api.BoardDescription; import io.sloeber.core.api.ISloeberConfiguration; import io.sloeber.core.api.Serial; diff --git a/io.sloeber.core/src/io/sloeber/core/core/DefaultInstallHandler.java b/io.sloeber.core/src/io/sloeber/core/core/DefaultInstallHandler.java index e08b8a30..687b3edf 100644 --- a/io.sloeber.core/src/io/sloeber/core/core/DefaultInstallHandler.java +++ b/io.sloeber.core/src/io/sloeber/core/core/DefaultInstallHandler.java @@ -2,7 +2,7 @@ import java.util.Map; -import io.sloeber.core.api.IArduinoLibraryVersion; +import io.sloeber.arduinoFramework.api.IArduinoLibraryVersion; import io.sloeber.core.api.IInstallLibraryHandler; public class DefaultInstallHandler implements IInstallLibraryHandler { diff --git a/io.sloeber.core/src/io/sloeber/core/internal/ArduinoHardwareLibrary.java b/io.sloeber.core/src/io/sloeber/core/internal/ArduinoHardwareLibrary.java index ed8f2a21..4546147f 100644 --- a/io.sloeber.core/src/io/sloeber/core/internal/ArduinoHardwareLibrary.java +++ b/io.sloeber.core/src/io/sloeber/core/internal/ArduinoHardwareLibrary.java @@ -2,12 +2,16 @@ import static io.sloeber.core.api.Const.*; +import java.util.List; + import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; -import io.sloeber.core.api.BoardDescription; +import io.sloeber.arduinoFramework.api.BoardDescription; +import io.sloeber.arduinoFramework.api.IArduinoLibrary; +import io.sloeber.arduinoFramework.api.IArduinoLibraryVersion; import io.sloeber.core.api.Common; -import io.sloeber.core.api.IArduinoLibraryVersion; +import io.sloeber.core.api.VersionNumber; public class ArduinoHardwareLibrary implements IArduinoLibraryVersion { private IPath myInstallPath; @@ -80,4 +84,50 @@ public boolean equals(IArduinoLibraryVersion other) { return myFQN.equals(other.getFQN()); } + @Override + public int compareTo(IArduinoLibraryVersion o) { + return 0; + } + + @Override + public IArduinoLibrary getLibrary() { + return null; + } + + @Override + public VersionNumber getVersion() { + return null; + } + + @Override + public boolean isInstalled() { + return true; + } + + @Override + public List getArchitectures() { + return null; + } + + @Override + public String getParagraph() { + return EMPTY_STRING; + } + + @Override + public String getSentence() { + return EMPTY_STRING; + } + + @Override + public String getMaintainer() { + return "Maintainer is board provider"; //$NON-NLS-1$ + } + + @Override + public String getAuthor() { + // TODO Auto-generated method stub + return "Author is board provider"; //$NON-NLS-1$ + } + } diff --git a/io.sloeber.core/src/io/sloeber/core/internal/ArduinoPrivateLibraryVersion.java b/io.sloeber.core/src/io/sloeber/core/internal/ArduinoPrivateLibraryVersion.java index 6c4ff16f..36ae0279 100644 --- a/io.sloeber.core/src/io/sloeber/core/internal/ArduinoPrivateLibraryVersion.java +++ b/io.sloeber.core/src/io/sloeber/core/internal/ArduinoPrivateLibraryVersion.java @@ -2,10 +2,14 @@ import static io.sloeber.core.api.Const.*; +import java.util.List; + import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; -import io.sloeber.core.api.IArduinoLibraryVersion; +import io.sloeber.arduinoFramework.api.IArduinoLibrary; +import io.sloeber.arduinoFramework.api.IArduinoLibraryVersion; +import io.sloeber.core.api.VersionNumber; import io.sloeber.core.common.InstancePreferences; public class ArduinoPrivateLibraryVersion implements IArduinoLibraryVersion { @@ -83,4 +87,49 @@ public boolean equals(IArduinoLibraryVersion other) { return myFQN.equals(other.getFQN()); } + @Override + public int compareTo(IArduinoLibraryVersion o) { + return 0; + } + + @Override + public IArduinoLibrary getLibrary() { + return null; + } + + @Override + public VersionNumber getVersion() { + return null; + } + + @Override + public boolean isInstalled() { + return true; + } + + @Override + public List getArchitectures() { + return null; + } + + @Override + public String getParagraph() { + return EMPTY_STRING; + } + + @Override + public String getSentence() { + return EMPTY_STRING; + } + + @Override + public String getMaintainer() { + return "unknown maintainer (private lib)"; //$NON-NLS-1$ + } + + @Override + public String getAuthor() { + return "unknown author (private lib)"; //$NON-NLS-1$ + } + } diff --git a/io.sloeber.core/src/io/sloeber/core/internal/Example.java b/io.sloeber.core/src/io/sloeber/core/internal/Example.java index 45b95554..b1b3a002 100644 --- a/io.sloeber.core/src/io/sloeber/core/internal/Example.java +++ b/io.sloeber.core/src/io/sloeber/core/internal/Example.java @@ -9,9 +9,9 @@ import java.util.HashMap; import java.util.Map; +import io.sloeber.arduinoFramework.api.IArduinoLibraryVersion; +import io.sloeber.arduinoFramework.api.IExample; import io.sloeber.core.api.ConfigurationPreferences; -import io.sloeber.core.api.IArduinoLibraryVersion; -import io.sloeber.core.api.IExample; public class Example implements IExample { protected IPath myFQN; diff --git a/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java b/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java index 87d7d766..dfccfd7b 100644 --- a/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java +++ b/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java @@ -29,6 +29,9 @@ import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; +import io.sloeber.arduinoFramework.api.BoardDescription; +import io.sloeber.arduinoFramework.api.IArduinoLibraryVersion; +import io.sloeber.arduinoFramework.api.LibraryManager; import io.sloeber.autoBuild.api.AutoBuildConfigurationExtensionDescription; import io.sloeber.autoBuild.api.AutoBuildProject; import io.sloeber.autoBuild.api.IAutoBuildConfigurationDescription; @@ -36,13 +39,10 @@ import io.sloeber.autoBuild.integration.AutoBuildConfigurationDescription; import io.sloeber.core.Activator; import io.sloeber.core.Messages; -import io.sloeber.core.api.BoardDescription; import io.sloeber.core.api.Common; import io.sloeber.core.api.CompileDescription; import io.sloeber.core.api.ConfigurationPreferences; -import io.sloeber.core.api.IArduinoLibraryVersion; import io.sloeber.core.api.ISloeberConfiguration; -import io.sloeber.core.api.LibraryManager; import io.sloeber.core.api.OtherDescription; import io.sloeber.core.tools.Helpers; import io.sloeber.core.tools.uploaders.UploadSketchWrapper; diff --git a/io.sloeber.core/src/io/sloeber/core/listeners/IndexerListener.java b/io.sloeber.core/src/io/sloeber/core/listeners/IndexerListener.java index 0a97a89c..982a9909 100644 --- a/io.sloeber.core/src/io/sloeber/core/listeners/IndexerListener.java +++ b/io.sloeber.core/src/io/sloeber/core/listeners/IndexerListener.java @@ -31,14 +31,14 @@ import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; +import io.sloeber.arduinoFramework.api.IArduinoLibraryVersion; +import io.sloeber.arduinoFramework.api.LibraryManager; import io.sloeber.core.Activator; import io.sloeber.core.Messages; import io.sloeber.core.api.Common; import io.sloeber.core.api.Const; -import io.sloeber.core.api.IArduinoLibraryVersion; import io.sloeber.core.api.IInstallLibraryHandler; import io.sloeber.core.api.ISloeberConfiguration; -import io.sloeber.core.api.LibraryManager; import io.sloeber.core.common.InstancePreferences; public class IndexerListener implements IIndexChangeListener, IIndexerStateListener { diff --git a/io.sloeber.core/src/io/sloeber/core/toolchain/SloeberConfigurationVariableSupplier.java b/io.sloeber.core/src/io/sloeber/core/toolchain/SloeberConfigurationVariableSupplier.java index 7ce41148..b0fac2d3 100644 --- a/io.sloeber.core/src/io/sloeber/core/toolchain/SloeberConfigurationVariableSupplier.java +++ b/io.sloeber.core/src/io/sloeber/core/toolchain/SloeberConfigurationVariableSupplier.java @@ -7,8 +7,9 @@ import org.eclipse.cdt.core.envvar.EnvironmentVariable; import org.eclipse.cdt.core.envvar.IEnvironmentVariable; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; + +import io.sloeber.arduinoFramework.api.BoardsManager; import io.sloeber.autoBuild.api.IEnvironmentVariableProvider; -import io.sloeber.core.api.BoardsManager; import io.sloeber.core.api.ISloeberConfiguration; public class SloeberConfigurationVariableSupplier implements IEnvironmentVariableProvider { diff --git a/io.sloeber.core/src/io/sloeber/core/tools/FileModifiers.java b/io.sloeber.core/src/io/sloeber/core/tools/FileModifiers.java index 93ffa5ca..f5c5868e 100644 --- a/io.sloeber.core/src/io/sloeber/core/tools/FileModifiers.java +++ b/io.sloeber.core/src/io/sloeber/core/tools/FileModifiers.java @@ -37,12 +37,12 @@ public static void appendString(File input, String addString) throws IOException try { String fileString = Files.readString(pathFile,StandardCharsets.UTF_8) + addString; Files.write(pathFile, fileString.getBytes(), StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE); - }catch(IOException e) { - String fileString = Files.readString(pathFile,Charset.forName("Cp1252")) + addString; + }catch(@SuppressWarnings("unused") IOException e) { + String fileString = Files.readString(pathFile,Charset.forName("Cp1252")) + addString; //$NON-NLS-1$ Files.write(pathFile, fileString.getBytes(), StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE); - + } - + } /** diff --git a/io.sloeber.core/src/io/sloeber/core/tools/PackageManager.java b/io.sloeber.core/src/io/sloeber/core/tools/PackageManager.java index fcfb9e03..e76f22f0 100644 --- a/io.sloeber.core/src/io/sloeber/core/tools/PackageManager.java +++ b/io.sloeber.core/src/io/sloeber/core/tools/PackageManager.java @@ -1,7 +1,6 @@ package io.sloeber.core.tools; import static io.sloeber.core.api.Const.*; -import static io.sloeber.core.api.Common.*; import static java.nio.file.StandardCopyOption.*; import java.io.File; @@ -29,11 +28,11 @@ import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; +import io.sloeber.arduinoFramework.api.ArduinoInstallable; import io.sloeber.core.Activator; import io.sloeber.core.Messages; import io.sloeber.core.api.Common; import io.sloeber.core.api.ConfigurationPreferences; -import io.sloeber.core.api.Json.ArduinoInstallable; public class PackageManager { private static final String FILE = Messages.FILE_TAG; diff --git a/io.sloeber.core/src/io/sloeber/core/tools/uploaders/UploadSketchWrapper.java b/io.sloeber.core/src/io/sloeber/core/tools/uploaders/UploadSketchWrapper.java index 1b16f06c..11ba33ca 100644 --- a/io.sloeber.core/src/io/sloeber/core/tools/uploaders/UploadSketchWrapper.java +++ b/io.sloeber.core/src/io/sloeber/core/tools/uploaders/UploadSketchWrapper.java @@ -41,8 +41,8 @@ import cc.arduino.packages.ssh.SSHClientSetupChainRing; import cc.arduino.packages.ssh.SSHConfigFileSetup; import cc.arduino.packages.ssh.SSHPwdSetup; +import io.sloeber.arduinoFramework.api.BoardDescription; import io.sloeber.core.Messages; -import io.sloeber.core.api.BoardDescription; import io.sloeber.core.api.ISloeberConfiguration; import io.sloeber.core.api.PasswordManager; import io.sloeber.core.api.Serial; diff --git a/io.sloeber.core/src/io/sloeber/core/txt/Programmers.java b/io.sloeber.core/src/io/sloeber/core/txt/Programmers.java index e3e53714..6bc0adbf 100644 --- a/io.sloeber.core/src/io/sloeber/core/txt/Programmers.java +++ b/io.sloeber.core/src/io/sloeber/core/txt/Programmers.java @@ -7,7 +7,7 @@ import org.eclipse.core.runtime.IPath; -import io.sloeber.core.api.BoardDescription; +import io.sloeber.arduinoFramework.api.BoardDescription; public class Programmers extends BoardTxtFile { private static final String programmersFileName1 = "programmers.txt";//$NON-NLS-1$ diff --git a/io.sloeber.core/src/io/sloeber/core/txt/WorkAround.java b/io.sloeber.core/src/io/sloeber/core/txt/WorkAround.java index 5395d3e7..e171b10c 100644 --- a/io.sloeber.core/src/io/sloeber/core/txt/WorkAround.java +++ b/io.sloeber.core/src/io/sloeber/core/txt/WorkAround.java @@ -20,10 +20,10 @@ import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; +import io.sloeber.arduinoFramework.api.IArduinoPlatformVersion; import io.sloeber.core.Activator; import io.sloeber.core.api.Common; import io.sloeber.core.api.VersionNumber; -import io.sloeber.core.api.Json.ArduinoPlatformVersion; import io.sloeber.core.tools.FileModifiers; /** @@ -77,7 +77,7 @@ public class WorkAround { * * @param arduinoPlatformVersion */ - static public void applyKnownWorkArounds(ArduinoPlatformVersion arduinoPlatformVersion) { + static public void applyKnownWorkArounds(IArduinoPlatformVersion arduinoPlatformVersion) { /* * for STM32 V1.8 and later #include "SrcWrapper.h" to Arduino.h remove the diff --git a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java index 28facd44..547fd950 100644 --- a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java +++ b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java @@ -40,17 +40,17 @@ import org.junit.jupiter.params.provider.MethodSource; import static org.junit.jupiter.api.Assertions.*; +import io.sloeber.arduinoFramework.api.BoardDescription; +import io.sloeber.arduinoFramework.api.BoardsManager; +import io.sloeber.arduinoFramework.api.IArduinoLibraryVersion; +import io.sloeber.arduinoFramework.api.IExample; +import io.sloeber.arduinoFramework.api.LibraryManager; import io.sloeber.autoBuild.api.IAutoBuildConfigurationDescription; -import io.sloeber.core.api.BoardDescription; -import io.sloeber.core.api.BoardsManager; import io.sloeber.core.api.CodeDescription; import io.sloeber.core.api.CompileDescription; -import io.sloeber.core.api.IExample; import io.sloeber.core.api.CompileDescription.SizeCommands; import io.sloeber.core.api.CompileDescription.WarningLevels; -import io.sloeber.core.api.IArduinoLibraryVersion; import io.sloeber.core.api.ISloeberConfiguration; -import io.sloeber.core.api.LibraryManager; import io.sloeber.core.api.OtherDescription; import io.sloeber.core.api.Preferences; import io.sloeber.core.api.SloeberProject; diff --git a/io.sloeber.tests/src/io/sloeber/core/CompileAndUpload.java b/io.sloeber.tests/src/io/sloeber/core/CompileAndUpload.java index 0358d094..4e3b4e57 100644 --- a/io.sloeber.tests/src/io/sloeber/core/CompileAndUpload.java +++ b/io.sloeber.tests/src/io/sloeber/core/CompileAndUpload.java @@ -34,11 +34,11 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import io.sloeber.arduinoFramework.api.BoardsManager; import io.sloeber.core.api.CodeDescription; import io.sloeber.core.api.CompileDescription; import io.sloeber.core.api.ConfigurationPreferences; import io.sloeber.core.api.ISloeberConfiguration; -import io.sloeber.core.api.BoardsManager; import io.sloeber.core.api.Preferences; import io.sloeber.core.api.Sketch; import io.sloeber.core.api.SloeberProject; diff --git a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileArduinoIDEExamplesOnAVRHardwareTest.java b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileArduinoIDEExamplesOnAVRHardwareTest.java index c6bbaee3..748cc971 100644 --- a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileArduinoIDEExamplesOnAVRHardwareTest.java +++ b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileArduinoIDEExamplesOnAVRHardwareTest.java @@ -26,10 +26,11 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; + +import io.sloeber.arduinoFramework.api.IExample; +import io.sloeber.arduinoFramework.api.LibraryManager; import io.sloeber.core.api.CodeDescription; import io.sloeber.core.api.CompileDescription; -import io.sloeber.core.api.IExample; -import io.sloeber.core.api.LibraryManager; import io.sloeber.core.api.Preferences; import io.sloeber.providers.Arduino; import io.sloeber.providers.MCUBoard; @@ -82,7 +83,7 @@ public static Stream avrHardwareData() throws Exception { } - private static boolean skipExample(Example example) { + private static boolean skipExample(IExample example) { // skip Teensy stuff on Arduino hardware // Teensy is so mutch more advanced that most arduino avr hardware can not // handle it diff --git a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileArduinoIDEExamplesOnTeensyTest.java b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileArduinoIDEExamplesOnTeensyTest.java index d73c2790..c38e0ec5 100644 --- a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileArduinoIDEExamplesOnTeensyTest.java +++ b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileArduinoIDEExamplesOnTeensyTest.java @@ -30,11 +30,11 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import io.sloeber.core.api.BoardDescription; +import io.sloeber.arduinoFramework.api.BoardDescription; +import io.sloeber.arduinoFramework.api.IExample; +import io.sloeber.arduinoFramework.api.LibraryManager; import io.sloeber.core.api.CodeDescription; import io.sloeber.core.api.CompileDescription; -import io.sloeber.core.api.IExample; -import io.sloeber.core.api.LibraryManager; import io.sloeber.core.api.Preferences; import io.sloeber.providers.MCUBoard; import io.sloeber.providers.Teensy; diff --git a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileArduinoIDEExamplesonJantjesBoardsTest.java b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileArduinoIDEExamplesonJantjesBoardsTest.java index cc6e184a..702c2464 100644 --- a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileArduinoIDEExamplesonJantjesBoardsTest.java +++ b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileArduinoIDEExamplesonJantjesBoardsTest.java @@ -18,11 +18,11 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import io.sloeber.core.api.BoardDescription; -import io.sloeber.core.api.BoardsManager; +import io.sloeber.arduinoFramework.api.BoardDescription; +import io.sloeber.arduinoFramework.api.BoardsManager; +import io.sloeber.arduinoFramework.api.IExample; +import io.sloeber.arduinoFramework.api.LibraryManager; import io.sloeber.core.api.CodeDescription; -import io.sloeber.core.api.IExample; -import io.sloeber.core.api.LibraryManager; import io.sloeber.core.api.Preferences; import io.sloeber.providers.Jantje; import io.sloeber.providers.MCUBoard; diff --git a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java index 6876c867..8aa42eeb 100644 --- a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java +++ b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java @@ -20,10 +20,10 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import io.sloeber.core.api.BoardDescription; -import io.sloeber.core.api.BoardsManager; +import io.sloeber.arduinoFramework.api.BoardDescription; +import io.sloeber.arduinoFramework.api.BoardsManager; +import io.sloeber.arduinoFramework.api.LibraryManager; import io.sloeber.core.api.CodeDescription; -import io.sloeber.core.api.LibraryManager; import io.sloeber.core.api.Preferences; @SuppressWarnings({"nls","static-method"}) diff --git a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileExamplesTest.java b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileExamplesTest.java index 1014f82b..603d73d1 100644 --- a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileExamplesTest.java +++ b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileExamplesTest.java @@ -15,12 +15,13 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import io.sloeber.core.api.BoardDescription; + +import io.sloeber.arduinoFramework.api.BoardDescription; +import io.sloeber.arduinoFramework.api.BoardsManager; +import io.sloeber.arduinoFramework.api.IExample; +import io.sloeber.arduinoFramework.api.LibraryManager; import io.sloeber.core.api.CodeDescription; import io.sloeber.core.api.CompileDescription; -import io.sloeber.core.api.IExample; -import io.sloeber.core.api.LibraryManager; -import io.sloeber.core.api.BoardsManager; import io.sloeber.core.api.Preferences; import io.sloeber.providers.Adafruit; import io.sloeber.providers.Arduino; diff --git a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileLibraryExamplesTest.java b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileLibraryExamplesTest.java index 7589f96e..200c7fa6 100644 --- a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileLibraryExamplesTest.java +++ b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileLibraryExamplesTest.java @@ -16,11 +16,12 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import io.sloeber.core.api.BoardDescription; + +import io.sloeber.arduinoFramework.api.BoardDescription; +import io.sloeber.arduinoFramework.api.BoardsManager; +import io.sloeber.arduinoFramework.api.IExample; +import io.sloeber.arduinoFramework.api.LibraryManager; import io.sloeber.core.api.CodeDescription; -import io.sloeber.core.api.IExample; -import io.sloeber.core.api.LibraryManager; -import io.sloeber.core.api.BoardsManager; import io.sloeber.core.api.Preferences; import io.sloeber.providers.Adafruit; import io.sloeber.providers.Arduino; diff --git a/io.sloeber.tests/src/io/sloeber/core/Example.java b/io.sloeber.tests/src/io/sloeber/core/Example.java index 21ca55f1..8fcc66ac 100644 --- a/io.sloeber.tests/src/io/sloeber/core/Example.java +++ b/io.sloeber.tests/src/io/sloeber/core/Example.java @@ -11,7 +11,7 @@ import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; -import io.sloeber.core.api.LibraryManager; +import io.sloeber.arduinoFramework.api.LibraryManager; import io.sloeber.providers.ESP32; import io.sloeber.providers.MCUBoard; import io.sloeber.providers.Teensy; diff --git a/io.sloeber.tests/src/io/sloeber/core/Shared.java b/io.sloeber.tests/src/io/sloeber/core/Shared.java index 28b42075..48518405 100644 --- a/io.sloeber.tests/src/io/sloeber/core/Shared.java +++ b/io.sloeber.tests/src/io/sloeber/core/Shared.java @@ -40,15 +40,15 @@ import static io.sloeber.core.api.Const.*; import static org.junit.jupiter.api.Assertions.fail; +import io.sloeber.arduinoFramework.api.BoardDescription; +import io.sloeber.arduinoFramework.api.BoardsManager; +import io.sloeber.arduinoFramework.api.IArduinoLibraryVersion; +import io.sloeber.arduinoFramework.api.IExample; import io.sloeber.autoBuild.api.AutoBuildProject; import io.sloeber.autoBuild.api.IAutoBuildConfigurationDescription; -import io.sloeber.core.api.BoardDescription; -import io.sloeber.core.api.BoardsManager; import io.sloeber.core.api.CodeDescription; import io.sloeber.core.api.CompileDescription; import io.sloeber.core.api.ConfigurationPreferences; -import io.sloeber.core.api.IArduinoLibraryVersion; -import io.sloeber.core.api.IExample; import io.sloeber.core.api.SloeberProject; import io.sloeber.providers.MCUBoard; diff --git a/io.sloeber.tests/src/io/sloeber/providers/Adafruit.java b/io.sloeber.tests/src/io/sloeber/providers/Adafruit.java index 409bb63d..9584d63c 100644 --- a/io.sloeber.tests/src/io/sloeber/providers/Adafruit.java +++ b/io.sloeber.tests/src/io/sloeber/providers/Adafruit.java @@ -3,8 +3,8 @@ import static org.junit.jupiter.api.Assertions.fail; -import io.sloeber.core.api.BoardDescription; -import io.sloeber.core.api.BoardsManager; +import io.sloeber.arduinoFramework.api.BoardDescription; +import io.sloeber.arduinoFramework.api.BoardsManager; @SuppressWarnings("nls") public class Adafruit extends MCUBoard { diff --git a/io.sloeber.tests/src/io/sloeber/providers/Arduino.java b/io.sloeber.tests/src/io/sloeber/providers/Arduino.java index c1510534..de640889 100644 --- a/io.sloeber.tests/src/io/sloeber/providers/Arduino.java +++ b/io.sloeber.tests/src/io/sloeber/providers/Arduino.java @@ -8,11 +8,11 @@ import java.util.Map; import java.util.TreeMap; -import io.sloeber.core.api.BoardDescription; -import io.sloeber.core.api.BoardsManager; -import io.sloeber.core.api.Json.ArduinoPackage; -import io.sloeber.core.api.Json.ArduinoPlatform; -import io.sloeber.core.api.Json.ArduinoPlatformVersion; +import io.sloeber.arduinoFramework.api.BoardDescription; +import io.sloeber.arduinoFramework.api.BoardsManager; +import io.sloeber.arduinoFramework.api.IArduinoPackage; +import io.sloeber.arduinoFramework.api.IArduinoPlatform; +import io.sloeber.arduinoFramework.api.IArduinoPlatformVersion; @SuppressWarnings("nls") public class Arduino extends MCUBoard { @@ -289,10 +289,10 @@ public static List getAllAvrBoards() { private static List getAllmBedBoardNames() { List ret = new LinkedList<>(); - ArduinoPackage arduinoPkg = BoardsManager.getPackageByProvider(providerArduino); - for (ArduinoPlatform curPlatform : arduinoPkg.getPlatforms()) { + IArduinoPackage arduinoPkg = BoardsManager.getPackageByProvider(providerArduino); + for (IArduinoPlatform curPlatform : arduinoPkg.getPlatforms()) { if (curPlatform.getArchitecture().equals(MBEDArchitectureName)) { - ArduinoPlatformVersion curPlatformVersion = curPlatform.getNewestInstalled(); + IArduinoPlatformVersion curPlatformVersion = curPlatform.getNewestInstalled(); if (curPlatformVersion != null) { List boardDescriptions = BoardDescription .makeBoardDescriptors(curPlatformVersion.getBoardsFile()); diff --git a/io.sloeber.tests/src/io/sloeber/providers/ESP32.java b/io.sloeber.tests/src/io/sloeber/providers/ESP32.java index 5ce017e2..0160c596 100644 --- a/io.sloeber.tests/src/io/sloeber/providers/ESP32.java +++ b/io.sloeber.tests/src/io/sloeber/providers/ESP32.java @@ -6,8 +6,8 @@ import java.util.Map; import java.util.TreeMap; -import io.sloeber.core.api.BoardDescription; -import io.sloeber.core.api.BoardsManager; +import io.sloeber.arduinoFramework.api.BoardDescription; +import io.sloeber.arduinoFramework.api.BoardsManager; @SuppressWarnings("nls") public class ESP32 extends MCUBoard { diff --git a/io.sloeber.tests/src/io/sloeber/providers/ESP8266.java b/io.sloeber.tests/src/io/sloeber/providers/ESP8266.java index fdc42fb0..b82762ce 100644 --- a/io.sloeber.tests/src/io/sloeber/providers/ESP8266.java +++ b/io.sloeber.tests/src/io/sloeber/providers/ESP8266.java @@ -6,8 +6,8 @@ import java.util.Map; import java.util.TreeMap; -import io.sloeber.core.api.BoardDescription; -import io.sloeber.core.api.BoardsManager; +import io.sloeber.arduinoFramework.api.BoardDescription; +import io.sloeber.arduinoFramework.api.BoardsManager; @SuppressWarnings("nls") public class ESP8266 extends MCUBoard { diff --git a/io.sloeber.tests/src/io/sloeber/providers/Jantje.java b/io.sloeber.tests/src/io/sloeber/providers/Jantje.java index 53086e41..3f93dc35 100644 --- a/io.sloeber.tests/src/io/sloeber/providers/Jantje.java +++ b/io.sloeber.tests/src/io/sloeber/providers/Jantje.java @@ -7,8 +7,8 @@ import java.util.Map; import java.util.TreeMap; -import io.sloeber.core.api.BoardDescription; -import io.sloeber.core.api.BoardsManager; +import io.sloeber.arduinoFramework.api.BoardDescription; +import io.sloeber.arduinoFramework.api.BoardsManager; @SuppressWarnings("nls") public class Jantje extends MCUBoard { diff --git a/io.sloeber.tests/src/io/sloeber/providers/MCUBoard.java b/io.sloeber.tests/src/io/sloeber/providers/MCUBoard.java index 9f6c5d6c..d4bf1b0e 100644 --- a/io.sloeber.tests/src/io/sloeber/providers/MCUBoard.java +++ b/io.sloeber.tests/src/io/sloeber/providers/MCUBoard.java @@ -5,13 +5,13 @@ import java.util.Map; import java.util.TreeMap; +import io.sloeber.arduinoFramework.api.BoardDescription; +import io.sloeber.arduinoFramework.api.BoardsManager; +import io.sloeber.arduinoFramework.api.IArduinoPackage; +import io.sloeber.arduinoFramework.api.IArduinoPlatform; +import io.sloeber.arduinoFramework.api.IArduinoPlatformVersion; import io.sloeber.core.AttributesBoard; import io.sloeber.core.Example; -import io.sloeber.core.api.BoardDescription; -import io.sloeber.core.api.BoardsManager; -import io.sloeber.core.api.Json.ArduinoPackage; -import io.sloeber.core.api.Json.ArduinoPlatform; -import io.sloeber.core.api.Json.ArduinoPlatformVersion; @SuppressWarnings("nls") public abstract class MCUBoard { @@ -47,12 +47,12 @@ public static List getAllBoards(String provider, MCUBoard board) { public static List getAllBoards(String provider, String architecture, MCUBoard board) { List ret = new LinkedList<>(); ret.add(board);//make the board provided the first in the list - ArduinoPackage arduinoPkg = BoardsManager.getPackageByProvider(provider); - for (ArduinoPlatform curPlatform : arduinoPkg.getPlatforms()) { + IArduinoPackage arduinoPkg = BoardsManager.getPackageByProvider(provider); + for (IArduinoPlatform curPlatform : arduinoPkg.getPlatforms()) { if(architecture!=null && !architecture.equals( curPlatform.getArchitecture())) { continue; } - ArduinoPlatformVersion curPlatformVersion = curPlatform.getNewestInstalled(); + IArduinoPlatformVersion curPlatformVersion = curPlatform.getNewestInstalled(); if (curPlatformVersion != null) { List boardDescriptions = BoardDescription .makeBoardDescriptors(curPlatformVersion.getBoardsFile()); diff --git a/io.sloeber.tests/src/io/sloeber/providers/Teensy.java b/io.sloeber.tests/src/io/sloeber/providers/Teensy.java index 5d2c3c0d..be433c2b 100644 --- a/io.sloeber.tests/src/io/sloeber/providers/Teensy.java +++ b/io.sloeber.tests/src/io/sloeber/providers/Teensy.java @@ -4,10 +4,10 @@ import java.util.Map; import java.util.TreeMap; +import io.sloeber.arduinoFramework.api.BoardDescription; +import io.sloeber.arduinoFramework.api.BoardsManager; import io.sloeber.core.AttributesCode; import io.sloeber.core.Example; -import io.sloeber.core.api.BoardDescription; -import io.sloeber.core.api.BoardsManager; @SuppressWarnings("nls") public class Teensy extends MCUBoard { diff --git a/io.sloeber.ui/src/io/sloeber/ui/Activator.java b/io.sloeber.ui/src/io/sloeber/ui/Activator.java index 34c33118..646365b2 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/Activator.java +++ b/io.sloeber.ui/src/io/sloeber/ui/Activator.java @@ -9,7 +9,7 @@ import org.eclipse.ui.statushandlers.StatusManager; import org.osgi.framework.BundleContext; -import io.sloeber.core.api.LibraryManager; +import io.sloeber.arduinoFramework.api.LibraryManager; import io.sloeber.ui.helpers.MyPreferences; import io.sloeber.ui.listeners.MyLibraryInstallHandler; import io.sloeber.ui.listeners.ProjectExplorerListener; diff --git a/io.sloeber.ui/src/io/sloeber/ui/Import_Libraries_Page.java b/io.sloeber.ui/src/io/sloeber/ui/Import_Libraries_Page.java index d4eb75a9..07302d63 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/Import_Libraries_Page.java +++ b/io.sloeber.ui/src/io/sloeber/ui/Import_Libraries_Page.java @@ -21,9 +21,9 @@ import org.eclipse.swt.widgets.TreeItem; import org.eclipse.ui.dialogs.WizardResourceImportPage; -import io.sloeber.core.api.IArduinoLibraryVersion; +import io.sloeber.arduinoFramework.api.IArduinoLibraryVersion; +import io.sloeber.arduinoFramework.api.LibraryManager; import io.sloeber.core.api.ISloeberConfiguration; -import io.sloeber.core.api.LibraryManager; public class Import_Libraries_Page extends WizardResourceImportPage { diff --git a/io.sloeber.ui/src/io/sloeber/ui/actions/AddLibraryAction.java b/io.sloeber.ui/src/io/sloeber/ui/actions/AddLibraryAction.java index e6c93fb1..220151c7 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/actions/AddLibraryAction.java +++ b/io.sloeber.ui/src/io/sloeber/ui/actions/AddLibraryAction.java @@ -15,7 +15,7 @@ import org.eclipse.ui.console.ConsolePlugin; import org.eclipse.ui.wizards.IWizardDescriptor; -import io.sloeber.core.api.BoardsManager; +import io.sloeber.arduinoFramework.api.BoardsManager; import io.sloeber.ui.Messages; import io.sloeber.ui.listeners.ProjectExplorerListener; diff --git a/io.sloeber.ui/src/io/sloeber/ui/actions/AddSourceFolderAction.java b/io.sloeber.ui/src/io/sloeber/ui/actions/AddSourceFolderAction.java index ae5afdec..fa15d541 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/actions/AddSourceFolderAction.java +++ b/io.sloeber.ui/src/io/sloeber/ui/actions/AddSourceFolderAction.java @@ -15,7 +15,7 @@ import org.eclipse.ui.console.ConsolePlugin; import org.eclipse.ui.wizards.IWizardDescriptor; -import io.sloeber.core.api.BoardsManager; +import io.sloeber.arduinoFramework.api.BoardsManager; import io.sloeber.ui.Messages; import io.sloeber.ui.listeners.ProjectExplorerListener; diff --git a/io.sloeber.ui/src/io/sloeber/ui/actions/BurnBootloaderHandler.java b/io.sloeber.ui/src/io/sloeber/ui/actions/BurnBootloaderHandler.java index 3c437d6e..a85087db 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/actions/BurnBootloaderHandler.java +++ b/io.sloeber.ui/src/io/sloeber/ui/actions/BurnBootloaderHandler.java @@ -9,7 +9,7 @@ import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; -import io.sloeber.core.api.BoardsManager; +import io.sloeber.arduinoFramework.api.BoardsManager; import io.sloeber.core.api.ISloeberConfiguration; import io.sloeber.ui.Messages; import io.sloeber.ui.listeners.ProjectExplorerListener; diff --git a/io.sloeber.ui/src/io/sloeber/ui/actions/NewSketchHandler.java b/io.sloeber.ui/src/io/sloeber/ui/actions/NewSketchHandler.java index 6b750186..47a7c115 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/actions/NewSketchHandler.java +++ b/io.sloeber.ui/src/io/sloeber/ui/actions/NewSketchHandler.java @@ -11,7 +11,7 @@ import org.eclipse.jface.wizard.WizardDialog; import org.eclipse.ui.console.ConsolePlugin; -import io.sloeber.core.api.BoardsManager; +import io.sloeber.arduinoFramework.api.BoardsManager; import io.sloeber.ui.Messages; import io.sloeber.ui.wizard.newsketch.NewSketchWizard; diff --git a/io.sloeber.ui/src/io/sloeber/ui/actions/OpenSerialMonitorHandler.java b/io.sloeber.ui/src/io/sloeber/ui/actions/OpenSerialMonitorHandler.java index 42ca985d..f5454992 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/actions/OpenSerialMonitorHandler.java +++ b/io.sloeber.ui/src/io/sloeber/ui/actions/OpenSerialMonitorHandler.java @@ -9,7 +9,7 @@ import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; -import io.sloeber.core.api.BoardDescription; +import io.sloeber.arduinoFramework.api.BoardDescription; import io.sloeber.core.api.ISloeberConfiguration; import io.sloeber.core.api.Sketch; import io.sloeber.ui.helpers.MyPreferences; diff --git a/io.sloeber.ui/src/io/sloeber/ui/actions/ProgramProjectHandler.java b/io.sloeber.ui/src/io/sloeber/ui/actions/ProgramProjectHandler.java index 2021c2c5..067187cd 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/actions/ProgramProjectHandler.java +++ b/io.sloeber.ui/src/io/sloeber/ui/actions/ProgramProjectHandler.java @@ -11,7 +11,7 @@ import org.eclipse.core.runtime.jobs.Job; import org.eclipse.ui.PlatformUI; -import io.sloeber.core.api.BoardsManager; +import io.sloeber.arduinoFramework.api.BoardsManager; import io.sloeber.ui.Messages; import io.sloeber.ui.listeners.ProjectExplorerListener; diff --git a/io.sloeber.ui/src/io/sloeber/ui/actions/UploadProjectHandler.java b/io.sloeber.ui/src/io/sloeber/ui/actions/UploadProjectHandler.java index 4b3a01d0..9084fc53 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/actions/UploadProjectHandler.java +++ b/io.sloeber.ui/src/io/sloeber/ui/actions/UploadProjectHandler.java @@ -22,7 +22,7 @@ import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; -import io.sloeber.core.api.BoardsManager; +import io.sloeber.arduinoFramework.api.BoardsManager; import io.sloeber.core.api.ISloeberConfiguration; import io.sloeber.ui.Messages; import io.sloeber.ui.helpers.MyPreferences; diff --git a/io.sloeber.ui/src/io/sloeber/ui/listeners/MyLibraryInstallHandler.java b/io.sloeber.ui/src/io/sloeber/ui/listeners/MyLibraryInstallHandler.java index 0ae56556..4c8b8164 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/listeners/MyLibraryInstallHandler.java +++ b/io.sloeber.ui/src/io/sloeber/ui/listeners/MyLibraryInstallHandler.java @@ -2,7 +2,7 @@ import java.util.Map; -import io.sloeber.core.api.IArduinoLibraryVersion; +import io.sloeber.arduinoFramework.api.IArduinoLibraryVersion; import io.sloeber.core.api.IInstallLibraryHandler; import io.sloeber.ui.helpers.MyPreferences; diff --git a/io.sloeber.ui/src/io/sloeber/ui/preferences/LibrarySelectionPage.java b/io.sloeber.ui/src/io/sloeber/ui/preferences/LibrarySelectionPage.java index bc945afd..de8aa927 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/preferences/LibrarySelectionPage.java +++ b/io.sloeber.ui/src/io/sloeber/ui/preferences/LibrarySelectionPage.java @@ -41,12 +41,12 @@ import org.eclipse.ui.dialogs.FilteredTree; import org.eclipse.ui.dialogs.PatternFilter; -import io.sloeber.core.api.LibraryManager; +import io.sloeber.arduinoFramework.api.IArduinoLibrary; +import io.sloeber.arduinoFramework.api.IArduinoLibraryIndex; +import io.sloeber.arduinoFramework.api.IArduinoLibraryVersion; +import io.sloeber.arduinoFramework.api.LibraryManager; +import io.sloeber.arduinoFramework.internal.Node; import io.sloeber.core.api.VersionNumber; -import io.sloeber.core.api.Json.ArduinoLibrary; -import io.sloeber.core.api.Json.ArduinoLibraryIndex; -import io.sloeber.core.api.Json.ArduinoLibraryVersion; -import io.sloeber.core.api.Json.Node; import io.sloeber.ui.Messages; import io.sloeber.ui.preferences.LibrarySelectionPage.LibraryTree.Category; import io.sloeber.ui.preferences.LibrarySelectionPage.LibraryTree.Library; @@ -60,7 +60,7 @@ public class LibrarySelectionPage extends PreferencePage implements IWorkbenchPr protected LibraryTree libs = new LibraryTree(); final static String emptyString = ""; //$NON-NLS-1$ final static String blankLine = "\n\n";//$NON-NLS-1$ - final static String canUpdateLabel = " (can update)"; + final static String canUpdateLabel = " (can update)"; //$NON-NLS-1$ public static class LibraryTree extends Node { @@ -110,23 +110,23 @@ public String getID() { } public class Library extends Node implements Comparable { - private ArduinoLibrary myLib; + private IArduinoLibrary myLib; private Category myParent; - protected ArduinoLibraryVersion myToInstallVersion; - protected ArduinoLibraryVersion myInstalledVersion; + protected IArduinoLibraryVersion myToInstallVersion; + protected IArduinoLibraryVersion myInstalledVersion; - public Library(Category category, ArduinoLibrary lib) { + public Library(Category category, IArduinoLibrary lib) { myParent = category; myLib = lib; myInstalledVersion = myLib.getInstalledVersion(); myToInstallVersion = myInstalledVersion; } - public ArduinoLibraryVersion getInstalledVersion() { + public IArduinoLibraryVersion getInstalledVersion() { return myInstalledVersion; } - public Collection getVersions() { + public Collection getVersions() { return myLib.getVersions(); } @@ -146,24 +146,23 @@ public boolean canUpdate() { public String getTooltip() { if (myTooltip == null) { - ArduinoLibraryVersion libVers = getLatest(); + IArduinoLibraryVersion libVers = getLatest(); myTooltip = "Architectures:" + libVers.getArchitectures().toString() + blankLine + libVers.getSentence() + blankLine + libVers.getParagraph() + blankLine + "Author: " - + libVers.getAuthor() + blankLine + "Maintainer: " + libVers.getMaintainer() + blankLine - + "provided by: " + libVers.getParent().getParent().getNodeName(); + + libVers.getAuthor() + blankLine + "Maintainer: " + libVers.getMaintainer() ; } return myTooltip; } - public ArduinoLibraryVersion getLatest() { + public IArduinoLibraryVersion getLatest() { return myLib.getNewestVersion(); } - public ArduinoLibraryVersion getVersion() { + public IArduinoLibraryVersion getVersion() { return myToInstallVersion; } - public void setVersion(ArduinoLibraryVersion version) { + public void setVersion(IArduinoLibraryVersion version) { myToInstallVersion = version; } @@ -179,7 +178,7 @@ public int compareTo(Library other) { return myLib.compareTo(other.getArduinoLibrary()); } - private ArduinoLibrary getArduinoLibrary() { + private IArduinoLibrary getArduinoLibrary() { return myLib; } @@ -213,8 +212,8 @@ public String getID() { } public LibraryTree() { - for (ArduinoLibraryIndex libraryIndex : LibraryManager.getLibraryIndices()) { - for (ArduinoLibrary arduinoLibrary : libraryIndex.getLibraries()) { + for (IArduinoLibraryIndex libraryIndex : LibraryManager.getLibraryIndices()) { + for (IArduinoLibrary arduinoLibrary : libraryIndex.getLibraries()) { String categoryName = arduinoLibrary.getCategory(); Category category = this.categories.get(categoryName); if (category == null) { @@ -288,12 +287,12 @@ public boolean performOk() { @Override protected IStatus run(IProgressMonitor monitor) { MultiStatus status = new MultiStatus(PLUGIN_ID, 0, Messages.ui_installing_arduino_libraries, null); - Set toRemoveLibs = new HashSet<>(); - Set toInstallLibs = new HashSet<>(); + Set toRemoveLibs = new HashSet<>(); + Set toInstallLibs = new HashSet<>(); for (Category category : libs.categories.values()) { for (Library library : category.libraries.values()) { - ArduinoLibraryVersion installedVersion = library.getInstalledVersion(); - ArduinoLibraryVersion toInstalVersion = library.getVersion(); + IArduinoLibraryVersion installedVersion = library.getInstalledVersion(); + IArduinoLibraryVersion toInstalVersion = library.getVersion(); if ((installedVersion != null) && (installedVersion.compareTo(toInstalVersion) != 0)) { toRemoveLibs.add(installedVersion); } @@ -383,7 +382,7 @@ public void widgetSelected(SelectionEvent event) { if (item.getChecked()) { child.setVersion(child.getLatest()); } else { - child.setVersion((ArduinoLibraryVersion) null); + child.setVersion((IArduinoLibraryVersion) null); } } for (TreeItem child : item.getItems()) { @@ -396,7 +395,7 @@ public void widgetSelected(SelectionEvent event) { if (item.getChecked()) { lib.setVersion(lib.getLatest()); } else { - lib.setVersion((ArduinoLibraryVersion) null); + lib.setVersion((IArduinoLibraryVersion) null); } item.setText(0, lib.getNodeName()); @@ -409,11 +408,11 @@ public void widgetSelected(SelectionEvent event) { // Create the dropdown and add data to it final CCombo combo = new CCombo(LibrarySelectionPage.this.viewer.getTree(), SWT.READ_ONLY); Library selectedLib = ((LibraryTree.Library) item.getData()); - for (ArduinoLibraryVersion version1 : selectedLib.getVersions()) { + for (IArduinoLibraryVersion version1 : selectedLib.getVersions()) { combo.add(version1.getVersion().toString()); } - ArduinoLibraryVersion displayVersion = selectedLib.getVersion(); + IArduinoLibraryVersion displayVersion = selectedLib.getVersion(); if (displayVersion == null) { displayVersion = selectedLib.getLatest(); selectedLib.setVersion(displayVersion); diff --git a/io.sloeber.ui/src/io/sloeber/ui/preferences/PlatformSelectionPage.java b/io.sloeber.ui/src/io/sloeber/ui/preferences/PlatformSelectionPage.java index 879ba7b4..cd8b3b59 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/preferences/PlatformSelectionPage.java +++ b/io.sloeber.ui/src/io/sloeber/ui/preferences/PlatformSelectionPage.java @@ -39,12 +39,12 @@ import org.eclipse.ui.dialogs.FilteredTree; import org.eclipse.ui.dialogs.PatternFilter; -import io.sloeber.core.api.BoardsManager; +import io.sloeber.arduinoFramework.api.BoardsManager; +import io.sloeber.arduinoFramework.api.IArduinoPackage; +import io.sloeber.arduinoFramework.api.IArduinoPlatform; +import io.sloeber.arduinoFramework.api.IArduinoPlatformPackageIndex; +import io.sloeber.arduinoFramework.api.IArduinoPlatformVersion; import io.sloeber.core.api.VersionNumber; -import io.sloeber.core.api.Json.ArduinoPackage; -import io.sloeber.core.api.Json.ArduinoPlatform; -import io.sloeber.core.api.Json.ArduinoPlatformPackageIndex; -import io.sloeber.core.api.Json.ArduinoPlatformVersion; import io.sloeber.ui.Messages; import io.sloeber.ui.helpers.MyPreferences; @@ -53,9 +53,9 @@ public class PlatformSelectionPage extends PreferencePage implements IWorkbenchP // platform index json, package,platform,versions structure private TreeMap>> myShownPlatforms = new TreeMap<>(); - private boolean mustBeInstalled(ArduinoPlatform platform) { - ArduinoPackage parentPkg = platform.getParent(); - ArduinoPlatformPackageIndex parentIndex = parentPkg.getPackageIndex(); + private boolean mustBeInstalled(IArduinoPlatform platform) { + IArduinoPackage parentPkg = platform.getParent(); + IArduinoPlatformPackageIndex parentIndex = parentPkg.getPackageIndex(); InstallableVersion[] inScopeVersions = myShownPlatforms.get(parentIndex.getID()).get(parentPkg.getID()) .get(platform.getID()); for (InstallableVersion version : inScopeVersions) { @@ -66,7 +66,7 @@ private boolean mustBeInstalled(ArduinoPlatform platform) { return false; } - private boolean mustBeInstalled(ArduinoPlatformPackageIndex packageIndex) { + private boolean mustBeInstalled(IArduinoPlatformPackageIndex packageIndex) { TreeMap> inScopeVersions = myShownPlatforms .get(packageIndex.getID()); for (TreeMap platform : inScopeVersions.values()) { @@ -81,8 +81,8 @@ private boolean mustBeInstalled(ArduinoPlatformPackageIndex packageIndex) { return false; } - private boolean mustBeInstalled(ArduinoPackage pkg) { - ArduinoPlatformPackageIndex parentIndex = pkg.getPackageIndex(); + private boolean mustBeInstalled(IArduinoPackage pkg) { + IArduinoPlatformPackageIndex parentIndex = pkg.getPackageIndex(); TreeMap inScopeVersions = myShownPlatforms.get(parentIndex.getID()) .get(pkg.getID()); for (InstallableVersion[] versions : inScopeVersions.values()) { @@ -96,10 +96,10 @@ private boolean mustBeInstalled(ArduinoPackage pkg) { } public static class InstallableVersion implements Comparable { - private ArduinoPlatformVersion myPlatformm; + private IArduinoPlatformVersion myPlatformm; private boolean myMustBeInstalled; - public InstallableVersion(ArduinoPlatformVersion platformm) { + public InstallableVersion(IArduinoPlatformVersion platformm) { myPlatformm = platformm; myMustBeInstalled = myPlatformm.isInstalled(); } @@ -125,25 +125,25 @@ public int compareTo(InstallableVersion o) { return getVersion().compareTo(o.getVersion()); } - public ArduinoPlatformVersion getPlatform() { + public IArduinoPlatformVersion getPlatform() { return myPlatformm; } } public PlatformSelectionPage() { - for (ArduinoPlatformPackageIndex curPackageIndex : BoardsManager.getPackageIndices()) { + for (IArduinoPlatformPackageIndex curPackageIndex : BoardsManager.getPackageIndices()) { String pkgIndexID = curPackageIndex.getID(); TreeMap> packageMap = new TreeMap<>(); - for (ArduinoPackage curPackage : curPackageIndex.getPackages()) { + for (IArduinoPackage curPackage : curPackageIndex.getPackages()) { TreeMap platformMap = new TreeMap<>(); String pkgID = curPackage.getID(); - for (ArduinoPlatform curPlatform : curPackage.getPlatforms()) { + for (IArduinoPlatform curPlatform : curPackage.getPlatforms()) { String platformID = curPlatform.getID(); - Collection platformVersions = curPlatform.getVersions(); + Collection platformVersions = curPlatform.getVersions(); InstallableVersion versions[] = new InstallableVersion[platformVersions.size()]; int index = 0; - for (ArduinoPlatformVersion curPlatformversion : platformVersions) { + for (IArduinoPlatformVersion curPlatformversion : platformVersions) { versions[index++] = new InstallableVersion(curPlatformversion); } // InstallableVersion arrayVersions[] = versions.toArray(new InstallableVersion[versions.size()]); @@ -201,23 +201,23 @@ protected boolean isLeafMatch(final Viewer viewer1, final Object element) { isMatch |= myWordMatches(ver.getPlatform()); } - if (element instanceof ArduinoPlatformPackageIndex) { - ArduinoPlatformPackageIndex indexFile = (ArduinoPlatformPackageIndex) element; + if (element instanceof IArduinoPlatformPackageIndex) { + IArduinoPlatformPackageIndex indexFile = (IArduinoPlatformPackageIndex) element; isMatch |= myWordMatches(indexFile); } - if (element instanceof ArduinoPackage) { - ArduinoPackage pac = (ArduinoPackage) element; + if (element instanceof IArduinoPackage) { + IArduinoPackage pac = (IArduinoPackage) element; isMatch |= myWordMatches(pac); } - if (element instanceof ArduinoPlatformVersion) { - ArduinoPlatformVersion platform = (ArduinoPlatformVersion) element; + if (element instanceof IArduinoPlatformVersion) { + IArduinoPlatformVersion platform = (IArduinoPlatformVersion) element; isMatch |= myWordMatches(platform); } return isMatch; } - private boolean myWordMatches(ArduinoPlatformVersion arduinoPlatformVersion) { + private boolean myWordMatches(IArduinoPlatformVersion arduinoPlatformVersion) { boolean ret = wordMatches(arduinoPlatformVersion.getName()); ret |= wordMatches(arduinoPlatformVersion.getArchitecture()); ret |= wordMatches(arduinoPlatformVersion.getConcattenatedBoardNames()); @@ -225,16 +225,17 @@ private boolean myWordMatches(ArduinoPlatformVersion arduinoPlatformVersion) { return ret; } - private boolean myWordMatches(ArduinoPackage pac) { + private boolean myWordMatches(IArduinoPackage pac) { boolean ret = wordMatches(pac.getEmail()); ret |= wordMatches(pac.getMaintainer()); - ret |= wordMatches(pac.getNodeName()); + //ret |= wordMatches(pac.getNodeName()); + ret |= wordMatches(pac.getName()); ret |= wordMatches(pac.getWebsiteURL().toString()); ret |= wordMatches(pac.getPackageIndex().getJsonFile().toString()); return ret; } - private boolean myWordMatches(ArduinoPlatformPackageIndex indexFile) { + private boolean myWordMatches(IArduinoPlatformPackageIndex indexFile) { return wordMatches(indexFile.getID()); } @@ -253,14 +254,14 @@ public boolean isChecked(Object element) { if (element instanceof InstallableVersion) { return ((InstallableVersion) element).mustBeInstalled(); } - if (element instanceof ArduinoPlatformPackageIndex) { - return mustBeInstalled((ArduinoPlatformPackageIndex) element); + if (element instanceof IArduinoPlatformPackageIndex) { + return mustBeInstalled((IArduinoPlatformPackageIndex) element); } - if (element instanceof ArduinoPackage) { - return mustBeInstalled((ArduinoPackage) element); + if (element instanceof IArduinoPackage) { + return mustBeInstalled((IArduinoPackage) element); } - if (element instanceof ArduinoPlatform) { - return mustBeInstalled((ArduinoPlatform) element); + if (element instanceof IArduinoPlatform) { + return mustBeInstalled((IArduinoPlatform) element); } return false; } @@ -270,14 +271,14 @@ public boolean isGrayed(Object element) { if (element instanceof InstallableVersion) { return false; } - if (element instanceof ArduinoPlatformPackageIndex) { - return mustBeInstalled((ArduinoPlatformPackageIndex) element); + if (element instanceof IArduinoPlatformPackageIndex) { + return mustBeInstalled((IArduinoPlatformPackageIndex) element); } - if (element instanceof ArduinoPackage) { - return mustBeInstalled((ArduinoPackage) element); + if (element instanceof IArduinoPackage) { + return mustBeInstalled((IArduinoPackage) element); } - if (element instanceof ArduinoPlatform) { - return mustBeInstalled((ArduinoPlatform) element); + if (element instanceof IArduinoPlatform) { + return mustBeInstalled((IArduinoPlatform) element); } return false; } @@ -302,11 +303,11 @@ public void checkStateChanged(CheckStateChangedEvent event) { @Override public Object[] getElements(Object inputElement) { if (PlatformSelectionPage.this.myHideJson) { - List packages = BoardsManager.getPackages(); + List packages = BoardsManager.getPackages(); Collections.sort(packages); return packages.toArray(new Object[packages.size()]); } - List indexFiles = BoardsManager.getPackageIndices(); + List indexFiles = BoardsManager.getPackageIndices(); return indexFiles.toArray(new Object[indexFiles.size()]); } @@ -322,21 +323,21 @@ public void inputChanged(Viewer viewer11, Object oldInput, Object newInput) { @Override public Object[] getChildren(Object parentElement) { - if (parentElement instanceof ArduinoPlatformPackageIndex) { - Collection packages = ((ArduinoPlatformPackageIndex) parentElement) + if (parentElement instanceof IArduinoPlatformPackageIndex) { + Collection packages = ((IArduinoPlatformPackageIndex) parentElement) .getPackages(); return packages.toArray(new Object[packages.size()]); } - if (parentElement instanceof ArduinoPackage) { - Collection platforms = ((ArduinoPackage) parentElement).getPlatforms(); - ArduinoPlatform platformArray[] = platforms.toArray(new ArduinoPlatform[platforms.size()]); + if (parentElement instanceof IArduinoPackage) { + Collection platforms = ((IArduinoPackage) parentElement).getPlatforms(); + IArduinoPlatform platformArray[] = platforms.toArray(new IArduinoPlatform[platforms.size()]); Arrays.sort(platformArray); return platformArray; } - if (parentElement instanceof ArduinoPlatform) { - ArduinoPlatform platform = (ArduinoPlatform) parentElement; - ArduinoPackage parentPackage = platform.getParent(); - ArduinoPlatformPackageIndex parentIndex = parentPackage.getPackageIndex(); + if (parentElement instanceof IArduinoPlatform) { + IArduinoPlatform platform = (IArduinoPlatform) parentElement; + IArduinoPackage parentPackage = platform.getParent(); + IArduinoPlatformPackageIndex parentIndex = parentPackage.getPackageIndex(); return myShownPlatforms.get(parentIndex.getID()).get(parentPackage.getID()) .get(platform.getID()); } @@ -358,13 +359,13 @@ public boolean hasChildren(Object element) { viewer1.setLabelProvider(new CellLabelProvider() { @Override public String getToolTipText(Object element) { - if (element instanceof ArduinoPlatformPackageIndex) { - return ((ArduinoPlatformPackageIndex) element).getID(); + if (element instanceof IArduinoPlatformPackageIndex) { + return ((IArduinoPlatformPackageIndex) element).getID(); } - if (element instanceof ArduinoPackage) { + if (element instanceof IArduinoPackage) { String NULL = "NULL"; //$NON-NLS-1$ - ArduinoPackage pkg = (ArduinoPackage) element; + IArduinoPackage pkg = (IArduinoPackage) element; String maintainer = pkg.getMaintainer(); String email = pkg.getEmail(); String weburlString = pkg.getWebsiteURL(); @@ -379,8 +380,8 @@ public String getToolTipText(Object element) { .replace(Messages.EMAIL, email).replace(Messages.URL, weburlString); } - if (element instanceof ArduinoPlatformVersion) { - return ((ArduinoPlatformVersion) element).getConcattenatedBoardNames(); + if (element instanceof IArduinoPlatformVersion) { + return ((IArduinoPlatformVersion) element).getConcattenatedBoardNames(); } if (element instanceof InstallableVersion) { @@ -407,16 +408,24 @@ public int getToolTipTimeDisplayed(Object object) { @Override public void update(ViewerCell cell) { - if (cell.getElement() instanceof ArduinoPlatformPackageIndex) { - cell.setText(((ArduinoPlatformPackageIndex) cell.getElement()).getNodeName()); +// if (cell.getElement() instanceof IArduinoPlatformPackageIndex) { +// cell.setText(((IArduinoPlatformPackageIndex) cell.getElement()).getNodeName()); +// +// } + if (cell.getElement() instanceof IArduinoPlatformPackageIndex) { + cell.setText(((IArduinoPlatformPackageIndex) cell.getElement()).getName()); } - if (cell.getElement() instanceof ArduinoPackage) { - cell.setText(((ArduinoPackage) cell.getElement()).getNodeName()); +// if (cell.getElement() instanceof IArduinoPackage) { +// cell.setText(((IArduinoPackage) cell.getElement()).getNodeName()); +// +// } + if (cell.getElement() instanceof IArduinoPackage) { + cell.setText(((IArduinoPackage) cell.getElement()).getName()); } - if (cell.getElement() instanceof ArduinoPlatform) { - cell.setText(((ArduinoPlatform) cell.getElement()).getName()); + if (cell.getElement() instanceof IArduinoPlatform) { + cell.setText(((IArduinoPlatform) cell.getElement()).getName()); } if (cell.getElement() instanceof InstallableVersion) { @@ -440,8 +449,8 @@ public void update(ViewerCell cell) { } protected IStatus updateInstallation(IProgressMonitor monitor) { - List platformsToInstall = new LinkedList<>(); - List platformsToRemove = new LinkedList<>(); + List platformsToInstall = new LinkedList<>(); + List platformsToRemove = new LinkedList<>(); for (TreeMap> packageIndex : myShownPlatforms.values()) { for (TreeMap arduinoPackage : packageIndex.values()) { for (InstallableVersion[] versions : arduinoPackage.values()) { diff --git a/io.sloeber.ui/src/io/sloeber/ui/preferences/PreferencePage.java b/io.sloeber.ui/src/io/sloeber/ui/preferences/PreferencePage.java index e2e2da65..6b4bddae 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/preferences/PreferencePage.java +++ b/io.sloeber.ui/src/io/sloeber/ui/preferences/PreferencePage.java @@ -23,9 +23,9 @@ import org.eclipse.ui.IWorkbenchPreferencePage; import org.eclipse.ui.preferences.ScopedPreferenceStore; +import io.sloeber.arduinoFramework.api.BoardsManager; +import io.sloeber.arduinoFramework.api.LibraryManager; import io.sloeber.core.api.Defaults; -import io.sloeber.core.api.LibraryManager; -import io.sloeber.core.api.BoardsManager; import io.sloeber.core.api.Preferences; import io.sloeber.ui.Messages; import io.sloeber.ui.helpers.MyPreferences; diff --git a/io.sloeber.ui/src/io/sloeber/ui/preferences/ThirdPartyHardwareSelectionPage.java b/io.sloeber.ui/src/io/sloeber/ui/preferences/ThirdPartyHardwareSelectionPage.java index 0e48add7..b40c7979 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/preferences/ThirdPartyHardwareSelectionPage.java +++ b/io.sloeber.ui/src/io/sloeber/ui/preferences/ThirdPartyHardwareSelectionPage.java @@ -25,7 +25,7 @@ import org.eclipse.ui.forms.widgets.Hyperlink; import org.eclipse.ui.preferences.ScopedPreferenceStore; -import io.sloeber.core.api.BoardsManager; +import io.sloeber.arduinoFramework.api.BoardsManager; import io.sloeber.core.api.Preferences; import io.sloeber.ui.Messages; import io.sloeber.ui.helpers.MyPreferences; diff --git a/io.sloeber.ui/src/io/sloeber/ui/project/properties/BoardSelectionPage.java b/io.sloeber.ui/src/io/sloeber/ui/project/properties/BoardSelectionPage.java index 6eb5c70a..5b1f6446 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/project/properties/BoardSelectionPage.java +++ b/io.sloeber.ui/src/io/sloeber/ui/project/properties/BoardSelectionPage.java @@ -25,8 +25,8 @@ import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; -import io.sloeber.core.api.BoardDescription; -import io.sloeber.core.api.BoardsManager; +import io.sloeber.arduinoFramework.api.BoardDescription; +import io.sloeber.arduinoFramework.api.BoardsManager; import io.sloeber.core.api.PasswordManager; import io.sloeber.core.api.SerialManager; import io.sloeber.ui.LabelCombo; diff --git a/io.sloeber.ui/src/io/sloeber/ui/wizard/newsketch/NewSketchWizard.java b/io.sloeber.ui/src/io/sloeber/ui/wizard/newsketch/NewSketchWizard.java index 0da63f98..7ca02d92 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/wizard/newsketch/NewSketchWizard.java +++ b/io.sloeber.ui/src/io/sloeber/ui/wizard/newsketch/NewSketchWizard.java @@ -21,7 +21,7 @@ import org.eclipse.ui.wizards.newresource.BasicNewProjectResourceWizard; import org.eclipse.ui.wizards.newresource.BasicNewResourceWizard; -import io.sloeber.core.api.BoardDescription; +import io.sloeber.arduinoFramework.api.BoardDescription; import io.sloeber.core.api.CodeDescription; import io.sloeber.core.api.CompileDescription; import io.sloeber.core.api.SloeberProject; diff --git a/io.sloeber.ui/src/io/sloeber/ui/wizard/newsketch/NewSketchWizardBoardPage.java b/io.sloeber.ui/src/io/sloeber/ui/wizard/newsketch/NewSketchWizardBoardPage.java index 5cc371d6..f2d1569a 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/wizard/newsketch/NewSketchWizardBoardPage.java +++ b/io.sloeber.ui/src/io/sloeber/ui/wizard/newsketch/NewSketchWizardBoardPage.java @@ -8,7 +8,7 @@ import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Shell; -import io.sloeber.core.api.BoardDescription; +import io.sloeber.arduinoFramework.api.BoardDescription; import io.sloeber.ui.project.properties.BoardSelectionPage; /** diff --git a/io.sloeber.ui/src/io/sloeber/ui/wizard/newsketch/NewSketchWizardCodeSelectionPage.java b/io.sloeber.ui/src/io/sloeber/ui/wizard/newsketch/NewSketchWizardCodeSelectionPage.java index 46388e44..f9bb89f7 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/wizard/newsketch/NewSketchWizardCodeSelectionPage.java +++ b/io.sloeber.ui/src/io/sloeber/ui/wizard/newsketch/NewSketchWizardCodeSelectionPage.java @@ -15,10 +15,10 @@ import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; -import io.sloeber.core.api.BoardDescription; +import io.sloeber.arduinoFramework.api.BoardDescription; +import io.sloeber.arduinoFramework.api.IExample; import io.sloeber.core.api.CodeDescription; import io.sloeber.core.api.CodeDescription.CodeTypes; -import io.sloeber.core.api.IExample; import io.sloeber.ui.LabelCombo; import io.sloeber.ui.Messages; diff --git a/io.sloeber.ui/src/io/sloeber/ui/wizard/newsketch/SampleSelector.java b/io.sloeber.ui/src/io/sloeber/ui/wizard/newsketch/SampleSelector.java index b918b528..07d89de7 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/wizard/newsketch/SampleSelector.java +++ b/io.sloeber.ui/src/io/sloeber/ui/wizard/newsketch/SampleSelector.java @@ -16,9 +16,9 @@ import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; -import io.sloeber.core.api.BoardDescription; -import io.sloeber.core.api.IExample; -import io.sloeber.core.api.LibraryManager; +import io.sloeber.arduinoFramework.api.BoardDescription; +import io.sloeber.arduinoFramework.api.IExample; +import io.sloeber.arduinoFramework.api.LibraryManager; import io.sloeber.ui.Messages; public class SampleSelector { From 7c85f270f925fdfd165baf034e0a15f9901be0cf Mon Sep 17 00:00:00 2001 From: jan Date: Mon, 26 Aug 2024 22:39:10 +0200 Subject: [PATCH 015/115] fixing the build --- ...ConfigurationDescriptionInvestigation.java | 54 ------------------- .../arduinoFramework/api/IArduinoLibrary.java | 33 +++++------- .../{internal => api}/Node.java | 2 +- .../internal/ArduinoLibrary.java | 3 +- .../internal/ArduinoLibraryIndex.java | 1 + .../internal/ArduinoLibraryVersion.java | 1 + .../internal/ArduinoPackage.java | 1 + .../internal/ArduinoPlatformPackageIndex.java | 1 + .../internal/ArduinoPlatformTool.java | 1 + .../internal/ArduinoPlatformToolVersion.java | 1 + .../ui/preferences/LibrarySelectionPage.java | 2 +- 11 files changed, 23 insertions(+), 77 deletions(-) delete mode 100644 io.sloeber.autoBuild.test/src/io/sloeber/autoBuilld/investigate/CConfigurationDescriptionInvestigation.java rename io.sloeber.core/src/io/sloeber/arduinoFramework/{internal => api}/Node.java (85%) diff --git a/io.sloeber.autoBuild.test/src/io/sloeber/autoBuilld/investigate/CConfigurationDescriptionInvestigation.java b/io.sloeber.autoBuild.test/src/io/sloeber/autoBuilld/investigate/CConfigurationDescriptionInvestigation.java deleted file mode 100644 index 4895e148..00000000 --- a/io.sloeber.autoBuild.test/src/io/sloeber/autoBuilld/investigate/CConfigurationDescriptionInvestigation.java +++ /dev/null @@ -1,54 +0,0 @@ -package io.sloeber.autoBuilld.investigate; - -import static io.sloeber.autoBuild.helpers.Defaults.*; -import static org.junit.jupiter.api.Assertions.*; - -import org.eclipse.cdt.core.model.CoreModel; -import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; -import org.eclipse.cdt.core.settings.model.ICFolderDescription; -import org.eclipse.cdt.core.settings.model.ICProjectDescription; -import org.eclipse.cdt.internal.core.settings.model.CConfigurationDescription; -import org.eclipse.cdt.internal.core.settings.model.CConfigurationDescriptionCache; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IWorkspace; -import org.eclipse.core.resources.IWorkspaceDescription; -import org.eclipse.core.resources.ResourcesPlugin; - -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import io.sloeber.autoBuild.api.AutoBuildProject; -import io.sloeber.autoBuild.buildTools.api.IBuildTools; -import io.sloeber.autoBuild.buildTools.api.IBuildToolsManager; -import io.sloeber.autoBuild.helpers.Shared; - -@SuppressWarnings({ "restriction", "nls", "static-method" }) -public class CConfigurationDescriptionInvestigation { - static int testCounter = 1; - - @BeforeAll - static void beforeAll() { - Shared.setDeleteProjects(false); - Shared.setCloseProjects(false); - // turn off auto building to make sure autobuild does not start a build behind our backs - final IWorkspace workspace = ResourcesPlugin.getWorkspace(); - IWorkspaceDescription workspaceDesc = workspace.getDescription(); - workspaceDesc.setAutoBuilding(false); - } - - @Test - public void testConfigDescription() { - String projectName = "testConfigDescription"; - IBuildTools buildTools = IBuildToolsManager.getDefault().getAnyInstalledBuildTools(defaultProjectType); - IProject testProject = AutoBuildProject.createProject(projectName, defaultProjectType, defaultNatureID, cpp_exeCodeProvider, buildTools, false, null); - ICProjectDescription projectDesc = CoreModel.getDefault().getProjectDescription(testProject, true); - for (ICConfigurationDescription curConf : projectDesc.getConfigurations()) { - assertFalse( curConf instanceof CConfigurationDescriptionCache,"conf is readOnly class instance"); - assertTrue( curConf instanceof CConfigurationDescription,"conf is of a unknown class instance"); - ICFolderDescription orgDescription = curConf.getRootFolderDescription(); - curConf.setDescription("A nice description"); - ICFolderDescription newDescription = curConf.getRootFolderDescription(); - } - } - -} diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoLibrary.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoLibrary.java index 62857bca..c2f7c703 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoLibrary.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoLibrary.java @@ -5,31 +5,30 @@ import org.eclipse.core.runtime.IPath; -import io.sloeber.arduinoFramework.internal.Node; import io.sloeber.core.api.VersionNumber; -public interface IArduinoLibrary extends Comparable { +public abstract class IArduinoLibrary extends Node implements Comparable{ - Collection getVersions(); + public abstract Collection getVersions(); - String getAuthor(); + public abstract String getAuthor(); - String getMaintainer(); + public abstract String getMaintainer(); - String getWebsite(); + public abstract String getWebsite(); - String getCategory(); + public abstract String getCategory(); - List getArchitectures(); + public abstract List getArchitectures(); - List getTypes(); + public abstract List getTypes(); /** * Get the newest version of this library * * @return the newest version of this library */ - IArduinoLibraryVersion getNewestVersion(); + public abstract IArduinoLibraryVersion getNewestVersion(); /** * Get the version that is installed @@ -37,24 +36,18 @@ public interface IArduinoLibrary extends Comparable { * * @return */ - IArduinoLibraryVersion getInstalledVersion(); + public abstract IArduinoLibraryVersion getInstalledVersion(); /** * checks if a version of this library is installed. * * @return true if a version is installed. false in case no version is installed */ - boolean isInstalled(); + public abstract boolean isInstalled(); - //Below are the Node overrides - String getNodeName(); - Node[] getChildren(); + public abstract IArduinoLibraryVersion getVersion(VersionNumber versionNumber); - String getID(); - - IPath getInstallPath(); - - IArduinoLibraryVersion getVersion(VersionNumber versionNumber); + public abstract IPath getInstallPath(); } \ No newline at end of file diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/Node.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/Node.java similarity index 85% rename from io.sloeber.core/src/io/sloeber/arduinoFramework/internal/Node.java rename to io.sloeber.core/src/io/sloeber/arduinoFramework/api/Node.java index 61dc0162..0f751d80 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/Node.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/Node.java @@ -1,4 +1,4 @@ -package io.sloeber.arduinoFramework.internal; +package io.sloeber.arduinoFramework.api; public abstract class Node { public boolean hasChildren() { diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoLibrary.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoLibrary.java index ed165309..cadd3198 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoLibrary.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoLibrary.java @@ -16,6 +16,7 @@ import io.sloeber.arduinoFramework.api.IArduinoLibrary; import io.sloeber.arduinoFramework.api.IArduinoLibraryVersion; +import io.sloeber.arduinoFramework.api.Node; import io.sloeber.core.api.ConfigurationPreferences; import io.sloeber.core.api.VersionNumber; @@ -26,7 +27,7 @@ * */ -public class ArduinoLibrary extends Node implements IArduinoLibrary { +public class ArduinoLibrary extends IArduinoLibrary { private String name; private TreeMap versions = new TreeMap<>(Collections.reverseOrder()); diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoLibraryIndex.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoLibraryIndex.java index 12ddfae5..66a12379 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoLibraryIndex.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoLibraryIndex.java @@ -21,6 +21,7 @@ import io.sloeber.arduinoFramework.api.IArduinoLibrary; import io.sloeber.arduinoFramework.api.IArduinoLibraryIndex; import io.sloeber.arduinoFramework.api.IArduinoLibraryVersion; +import io.sloeber.arduinoFramework.api.Node; /** * This class represents a json file that references libraries diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoLibraryVersion.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoLibraryVersion.java index 571121fd..0e4654c4 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoLibraryVersion.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoLibraryVersion.java @@ -15,6 +15,7 @@ import io.sloeber.arduinoFramework.api.IArduinoLibrary; import io.sloeber.arduinoFramework.api.IArduinoLibraryVersion; +import io.sloeber.arduinoFramework.api.Node; import io.sloeber.core.api.VersionNumber; /** diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPackage.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPackage.java index 0c943896..4e8d876c 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPackage.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPackage.java @@ -24,6 +24,7 @@ import io.sloeber.arduinoFramework.api.IArduinoPackage; import io.sloeber.arduinoFramework.api.IArduinoPlatform; import io.sloeber.arduinoFramework.api.IArduinoPlatformVersion; +import io.sloeber.arduinoFramework.api.Node; import io.sloeber.core.api.ConfigurationPreferences; import io.sloeber.core.api.VersionNumber; diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformPackageIndex.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformPackageIndex.java index b97a3dc2..01c22a5c 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformPackageIndex.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformPackageIndex.java @@ -22,6 +22,7 @@ import io.sloeber.arduinoFramework.api.IArduinoPackage; import io.sloeber.arduinoFramework.api.IArduinoPlatformPackageIndex; +import io.sloeber.arduinoFramework.api.Node; @JsonAdapter(ArduinoPlatformPackageIndex.class) public class ArduinoPlatformPackageIndex extends Node diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformTool.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformTool.java index b970b689..7faae3d1 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformTool.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformTool.java @@ -22,6 +22,7 @@ import com.google.gson.JsonParseException; import io.sloeber.arduinoFramework.api.IArduinoPackage; +import io.sloeber.arduinoFramework.api.Node; import io.sloeber.core.api.VersionNumber; public class ArduinoPlatformTool extends Node { diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformToolVersion.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformToolVersion.java index aacad409..2bea7630 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformToolVersion.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformToolVersion.java @@ -24,6 +24,7 @@ import io.sloeber.arduinoFramework.api.IArduinoPackage; import io.sloeber.arduinoFramework.api.IArduinoPlatform; import io.sloeber.arduinoFramework.api.IArduinoPlatformVersion; +import io.sloeber.arduinoFramework.api.Node; import io.sloeber.core.api.VersionNumber; public class ArduinoPlatformToolVersion extends Node { diff --git a/io.sloeber.ui/src/io/sloeber/ui/preferences/LibrarySelectionPage.java b/io.sloeber.ui/src/io/sloeber/ui/preferences/LibrarySelectionPage.java index de8aa927..89842d96 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/preferences/LibrarySelectionPage.java +++ b/io.sloeber.ui/src/io/sloeber/ui/preferences/LibrarySelectionPage.java @@ -45,7 +45,7 @@ import io.sloeber.arduinoFramework.api.IArduinoLibraryIndex; import io.sloeber.arduinoFramework.api.IArduinoLibraryVersion; import io.sloeber.arduinoFramework.api.LibraryManager; -import io.sloeber.arduinoFramework.internal.Node; +import io.sloeber.arduinoFramework.api.Node; import io.sloeber.core.api.VersionNumber; import io.sloeber.ui.Messages; import io.sloeber.ui.preferences.LibrarySelectionPage.LibraryTree.Category; From 4c3b6b0dd3d9e770526897b0bc571ab7620cfc4c Mon Sep 17 00:00:00 2001 From: jan Date: Wed, 28 Aug 2024 02:37:25 +0200 Subject: [PATCH 016/115] move log method to activator --- .../src/cc/arduino/packages/ssh/SSH.java | 3 +- .../cc/arduino/packages/ssh/SSHPwdSetup.java | 4 +- .../arduinoFramework/api/BoardsManager.java | 31 ++++++++-------- .../arduinoFramework/api/LibraryManager.java | 3 +- .../src/io/sloeber/core/Activator.java | 37 ++++++++++++++++--- .../io/sloeber/core/api/CodeDescription.java | 11 +++--- .../src/io/sloeber/core/api/Common.java | 28 +------------- .../sloeber/core/api/CompileDescription.java | 2 +- .../core/api/ISloeberConfiguration.java | 2 +- .../io/sloeber/core/api/PasswordManager.java | 5 ++- .../src/io/sloeber/core/api/Serial.java | 13 ++++--- .../io/sloeber/core/api/SloeberProject.java | 12 +++--- .../core/common/InstancePreferences.java | 6 +-- .../core/communication/ArduinoSerial.java | 3 +- .../core/internal/SloeberConfiguration.java | 3 +- .../core/listeners/IndexerListener.java | 11 +++--- .../core/tools/ExternalCommandLauncher.java | 3 +- .../io/sloeber/core/tools/FileModifiers.java | 3 +- .../src/io/sloeber/core/tools/Helpers.java | 10 ++--- .../src/io/sloeber/core/tools/Libraries.java | 8 ++-- .../io/sloeber/core/tools/PackageManager.java | 5 +-- .../src/io/sloeber/core/tools/Stream.java | 5 +-- .../tools/uploaders/UploadSketchWrapper.java | 11 +++--- .../src/io/sloeber/core/txt/TxtFile.java | 4 +- .../src/io/sloeber/core/txt/WorkAround.java | 17 ++++----- 25 files changed, 119 insertions(+), 121 deletions(-) diff --git a/io.sloeber.core/src/cc/arduino/packages/ssh/SSH.java b/io.sloeber.core/src/cc/arduino/packages/ssh/SSH.java index a4027232..f6d71d51 100644 --- a/io.sloeber.core/src/cc/arduino/packages/ssh/SSH.java +++ b/io.sloeber.core/src/cc/arduino/packages/ssh/SSH.java @@ -43,6 +43,7 @@ import com.jcraft.jsch.JSchException; import com.jcraft.jsch.Session; +import io.sloeber.core.Activator; import io.sloeber.core.Messages; import io.sloeber.core.api.Common; import io.sloeber.core.api.Const; @@ -159,7 +160,7 @@ public void run() { } catch (IOException e) { // This is unlikely to happen, but log it nevertheless IStatus status = new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID, Messages.command_io, e); - Common.log(status); + Activator.log(status); } finally { try { this.fReader.close(); diff --git a/io.sloeber.core/src/cc/arduino/packages/ssh/SSHPwdSetup.java b/io.sloeber.core/src/cc/arduino/packages/ssh/SSHPwdSetup.java index b76c126a..b6f4251d 100644 --- a/io.sloeber.core/src/cc/arduino/packages/ssh/SSHPwdSetup.java +++ b/io.sloeber.core/src/cc/arduino/packages/ssh/SSHPwdSetup.java @@ -37,8 +37,8 @@ import com.jcraft.jsch.Session; import cc.arduino.packages.BoardPort; +import io.sloeber.core.Activator; import io.sloeber.core.Messages; -import io.sloeber.core.api.Common; import io.sloeber.core.api.Const; import io.sloeber.core.api.PasswordManager; @@ -58,7 +58,7 @@ public Session setup(BoardPort port, JSch jSch) throws JSchException { } else { // The user should set the password in the project // properties->arduino - Common.log( + Activator.log( new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID, Messages.Upload_login_credentials_missing.replace(Messages.HOST_TAG, host))); return null; diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java index b15bb7f2..3d5d559a 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java @@ -44,7 +44,6 @@ import io.sloeber.arduinoFramework.internal.ArduinoPlatformTooldDependency; import io.sloeber.core.Activator; import io.sloeber.core.Messages; -import io.sloeber.core.api.Common; import io.sloeber.core.api.ConfigurationPreferences; import io.sloeber.core.api.Defaults; import io.sloeber.core.api.SloeberProject; @@ -132,7 +131,7 @@ static private BoardDescription getNewestBoardIDFromBoardsManager(String jsonFil public static void addPackageURLs(Collection packageUrlsToAdd, boolean forceDownload) { if (!isReady()) { - Common.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); return; } HashSet originalJsonUrls = new HashSet<>(Arrays.asList(getJsonURLList())); @@ -144,7 +143,7 @@ public static void addPackageURLs(Collection packageUrlsToAdd, boolean f public static void setPackageURLs(Collection packageUrls, boolean forceDownload) { if (!isReady()) { - Common.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); return; } setJsonURLs(packageUrls); @@ -164,7 +163,7 @@ public static void setPackageURLs(Collection packageUrls, boolean forceD public static void installsubsetOfLatestPlatforms(int fromIndex, int toIndex) { String DEPRECATED = "DEPRECATED"; //$NON-NLS-1$ if (!isReady()) { - Common.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); return; } envVarsNeedUpdating = true; @@ -202,7 +201,7 @@ public static void installAllLatestPlatforms() { public static void installLatestPlatform(String JasonName, String packageName, String architectureName) { if (!isReady()) { - Common.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); return; } envVarsNeedUpdating = true; @@ -218,7 +217,7 @@ public static void installLatestPlatform(String JasonName, String packageName, S } } } - Common.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, "failed to find " + JasonName + " " + packageName + " " + architectureName)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } @@ -314,7 +313,7 @@ public static File[] getAllBoardsFiles() { searchFiles(new File(CurFolder), boardFiles, BOARDS_FILE_NAME, 6); } if (boardFiles.size() == 0) { - Common.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, Helpers_No_boards_txt_found.replace(FILE_TAG, String.join("\n", hardwareFolders)), null)); //$NON-NLS-1$ return null; } @@ -326,7 +325,7 @@ private static void searchFiles(File folder, TreeSet Hardwarelists, String File[] a = folder.listFiles(); if (a == null) { if (!myHasbeenLogged) { - Common.log(new Status(IStatus.INFO, CORE_PLUGIN_ID, + Activator.log(new Status(IStatus.INFO, CORE_PLUGIN_ID, Helpers_Error_The_folder_is_empty.replace(FOLDER_TAG, folder.toString()), null)); myHasbeenLogged = true; } @@ -411,7 +410,7 @@ public static TreeMap getAllmenus() { public static void setPrivateHardwarePaths(String[] hardWarePaths) { if (!isReady()) { - Common.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); return; } InstancePreferences.setPrivateHardwarePaths(hardWarePaths); @@ -454,7 +453,7 @@ static private void loadJson(String url, boolean forceDownload) { try { PackageManager.mySafeCopy(new URL(url.trim()), jsonFile, false); } catch (IOException e) { - Common.log(new Status(IStatus.ERROR, Activator.getId(), "Unable to download " + url, e)); //$NON-NLS-1$ + Activator.log(new Status(IStatus.ERROR, Activator.getId(), "Unable to download " + url, e)); //$NON-NLS-1$ } } if (jsonFile.exists()) { @@ -463,7 +462,7 @@ static private void loadJson(String url, boolean forceDownload) { } else if (jsonFile.getName().toLowerCase().startsWith("library_")) { //$NON-NLS-1$ LibraryManager.loadJson(jsonFile); } else { - Common.log(new Status(IStatus.ERROR, Activator.getId(), + Activator.log(new Status(IStatus.ERROR, Activator.getId(), "json files should start with \"package_\" or \"library_\" " + url + " is ignored")); //$NON-NLS-1$ //$NON-NLS-2$ } } @@ -475,7 +474,7 @@ static private void loadPackage(File jsonFile) { index.setPackageFile(jsonFile); packageIndices.add(index); } catch (Exception e) { - Common.log(new Status(IStatus.ERROR, Activator.getId(), + Activator.log(new Status(IStatus.ERROR, Activator.getId(), Manager_Failed_to_parse.replace(FILE_TAG, jsonFile.getAbsolutePath()), e)); jsonFile.delete();// Delete the file so it stops damaging } @@ -497,7 +496,7 @@ protected static File getLocalFileName(String url, boolean show_error) { packageUrl = new URL(url.trim()); } catch (MalformedURLException e) { if (show_error) { - Common.log(new Status(IStatus.ERROR, Activator.getId(), "Malformed url " + url, e)); //$NON-NLS-1$ + Activator.log(new Status(IStatus.ERROR, Activator.getId(), "Malformed url " + url, e)); //$NON-NLS-1$ } return null; } @@ -563,7 +562,7 @@ private static IPath getThirdPartyURLStoragePath() { public static void removeAllInstalledPlatforms() { if (!isReady()) { - Common.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); return; } try { @@ -657,7 +656,7 @@ public static synchronized void startup_Pluging(IProgressMonitor monitor) { IPath examplesPath = ConfigurationPreferences.getInstallationPathExamples(); if (!examplesPath.toFile().exists()) {// examples are not installed // Download arduino IDE example programs - Common.log(PackageManager.downloadAndInstall(Defaults.EXAMPLES_URL, Defaults.EXAMPLE_PACKAGE, examplesPath, + Activator.log(PackageManager.downloadAndInstall(Defaults.EXAMPLES_URL, Defaults.EXAMPLE_PACKAGE, examplesPath, false, monitor)); } if (!areThereInstalledBoards()) { @@ -803,7 +802,7 @@ static public IArduinoPackage getPackage(String packageName) { */ public static void onlyKeepLatestPlatforms() { if (!isReady()) { - Common.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); return; } List allPackages = getPackages(); diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java index fff18cdf..ef797ca0 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java @@ -32,7 +32,6 @@ import io.sloeber.arduinoFramework.internal.ArduinoLibraryIndex; import io.sloeber.arduinoFramework.internal.ArduinoLibraryVersion; import io.sloeber.core.Activator; -import io.sloeber.core.api.Common; import io.sloeber.core.api.ConfigurationPreferences; import io.sloeber.core.api.Defaults; import io.sloeber.core.api.IInstallLibraryHandler; @@ -120,7 +119,7 @@ static public void loadJson(File jsonFile) { ((ArduinoLibraryIndex)index).setJsonFile(jsonFile); libraryIndices.add(index); } catch (Exception e) { - Common.log(new Status(IStatus.ERROR, Activator.getId(), + Activator.log(new Status(IStatus.ERROR, Activator.getId(), Manager_Failed_to_parse.replace(FILE_TAG, jsonFile.getAbsolutePath()), e)); jsonFile.delete();// Delete the file so it stops damaging } diff --git a/io.sloeber.core/src/io/sloeber/core/Activator.java b/io.sloeber.core/src/io/sloeber/core/Activator.java index 86cc8e1e..2949051f 100644 --- a/io.sloeber.core/src/io/sloeber/core/Activator.java +++ b/io.sloeber.core/src/io/sloeber/core/Activator.java @@ -29,13 +29,13 @@ 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.statushandlers.StatusManager; import org.osgi.framework.BundleContext; import org.osgi.service.prefs.BackingStoreException; import org.osgi.service.prefs.Preferences; import cc.arduino.packages.discoverers.SloeberNetworkDiscovery; import io.sloeber.arduinoFramework.api.BoardsManager; -import io.sloeber.core.api.Common; import io.sloeber.core.api.ConfigurationPreferences; import io.sloeber.core.common.InstancePreferences; import io.sloeber.core.listeners.ConfigurationChangeListener; @@ -168,7 +168,7 @@ private static void testKnownIssues() { if (!errorString.isEmpty()) { errorString += "\n\nSloeber might still function but if you get strange results you know where to look.\n"; errorString += "Do not create an issue if you see this!!!"; - Common.log(new Status(IStatus.ERROR, PLUGIN_ID, errorString)); + log(new Status(IStatus.ERROR, PLUGIN_ID, errorString)); } } @@ -215,7 +215,7 @@ private static void initializeImportantVariables() { try { workspace.setDescription(workspaceDesc); } catch (CoreException e) { - Common.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, e.getMessage(), e)); + log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, e.getMessage(), e)); } // Make sure some important variables are being initialized InstancePreferences.setPrivateLibraryPaths(InstancePreferences.getPrivateLibraryPaths()); @@ -381,7 +381,7 @@ private static void addFileAssociations() { 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(), + log(new Status(IStatus.WARNING, Activator.getId(), "Failed to add *.ino and *.pde as file extensions.", e)); } @@ -413,16 +413,41 @@ private static void installOtherStuff() { } if (!localMakePath.append(MAKE_EXE).toFile().exists()) { IProgressMonitor monitor = new NullProgressMonitor(); - Common.log(PackageManager.downloadAndInstall(MAKE_URL, MAKE_ZIP, localMakePath, false, monitor)); + log(PackageManager.downloadAndInstall(MAKE_URL, MAKE_ZIP, localMakePath, false, monitor)); } // Install awk if needed IPath localAwkPath = ConfigurationPreferences.getAwkPath(); if (!localAwkPath.append(AWK_EXE).toFile().exists()) { IProgressMonitor monitor = new NullProgressMonitor(); - Common.log(PackageManager.downloadAndInstall(AWK_URL, AWK_ZIP, localAwkPath, false, monitor)); + log(PackageManager.downloadAndInstall(AWK_URL, AWK_ZIP, localAwkPath, false, monitor)); } } } + + /** + * Logs the status information if status is OK then nothing happens + * + * @param status + * the status information to log + */ + public static void log(IStatus status) { + switch (status.getSeverity()) { + case IStatus.OK: { + break; + } + case IStatus.ERROR: { + int style = StatusManager.LOG | StatusManager.SHOW | StatusManager.BLOCK; + StatusManager stMan = StatusManager.getManager(); + stMan.handle(status, style); + break; + } + case SLOEBER_STATUS_DEBUG: + // break;//remove break to add debugging + default: + Activator.getDefault().getLog().log(status); + } + } + } diff --git a/io.sloeber.core/src/io/sloeber/core/api/CodeDescription.java b/io.sloeber.core/src/io/sloeber/core/api/CodeDescription.java index aa2e0fd2..9e464e6c 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/CodeDescription.java +++ b/io.sloeber.core/src/io/sloeber/core/api/CodeDescription.java @@ -1,6 +1,5 @@ package io.sloeber.core.api; -import static io.sloeber.core.api.Common.*; import static io.sloeber.core.api.Const.*; import java.io.File; @@ -52,8 +51,8 @@ public enum CodeTypes { static private final String DEFAULT_SKETCH_BASE = "sketch"; //$NON-NLS-1$ public static final String INO= "ino"; //$NON-NLS-1$ public static final String CPP= "cpp"; //$NON-NLS-1$ - public static final String DEFAULT_SKETCH_INO = DEFAULT_SKETCH_BASE + DOT+INO; - public static final String DEFAULT_SKETCH_CPP = DEFAULT_SKETCH_BASE + DOT+CPP; + public static final String DEFAULT_SKETCH_INO = DEFAULT_SKETCH_BASE + DOT+INO; + public static final String DEFAULT_SKETCH_CPP = DEFAULT_SKETCH_BASE + DOT+CPP; public static final String DEFAULT_SKETCH_H = DEFAULT_SKETCH_BASE + DOT+'h'; // // template Sketch information @@ -332,7 +331,7 @@ public boolean createFiles(IContainer scrContainer, IProgressMonitor monitor) { IPath folderName = myTemPlateFoldername; String files[] = folderName.toFile().list(); if (files == null) { - log(new Status(IStatus.WARNING, CORE_PLUGIN_ID, "No files found in template folder :" + folderName, + Activator.log(new Status(IStatus.WARNING, CORE_PLUGIN_ID, "No files found in template folder :" + folderName, null)); } else { for (String file : files) { @@ -354,7 +353,7 @@ public boolean createFiles(IContainer scrContainer, IProgressMonitor monitor) { replacers);) { Helpers.addFileToProject(scrContainer.getFile(IPath.fromOSString(renamedFile)), theFileStream, monitor, false); } catch (IOException e) { - log(new Status(IStatus.WARNING, CORE_PLUGIN_ID, + Activator.log(new Status(IStatus.WARNING, CORE_PLUGIN_ID, "Failed to add template file :" + sourceFile.toString(), e)); } @@ -373,7 +372,7 @@ public boolean createFiles(IContainer scrContainer, IProgressMonitor monitor) { Helpers.linkDirectory( curPath, folder); } else { - log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, "Can not create links to project root")); } } else { diff --git a/io.sloeber.core/src/io/sloeber/core/api/Common.java b/io.sloeber.core/src/io/sloeber/core/api/Common.java index dc92f61f..165b1e2e 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Common.java +++ b/io.sloeber.core/src/io/sloeber/core/api/Common.java @@ -22,8 +22,6 @@ import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; -import org.eclipse.ui.statushandlers.StatusManager; - import io.sloeber.autoBuild.api.AutoBuildCommon; import io.sloeber.autoBuild.api.IAutoBuildConfigurationDescription; import io.sloeber.core.Activator; @@ -51,7 +49,7 @@ private static String getSloeberHome() { } catch (URISyntaxException e) { // this should not happen // but it seems a space in the path makes it happen - Common.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, "Eclipse fails to provide its own installation folder :-(. \nThis is known to happen when you have a space ! # or other wierd characters in your eclipse installation path", //$NON-NLS-1$ e)); } @@ -124,29 +122,7 @@ public static String makeNameCompileSafe(String name) { return ret; } - /** - * Logs the status information if status is OK then nothing happens - * - * @param status - * the status information to log - */ - public static void log(IStatus status) { - switch (status.getSeverity()) { - case IStatus.OK: { - break; - } - case IStatus.ERROR: { - int style = StatusManager.LOG | StatusManager.SHOW | StatusManager.BLOCK; - StatusManager stMan = StatusManager.getManager(); - stMan.handle(status, style); - break; - } - case SLOEBER_STATUS_DEBUG: - // break;//remove break to add debugging - default: - Activator.getDefault().getLog().log(status); - } - } + /** * diff --git a/io.sloeber.core/src/io/sloeber/core/api/CompileDescription.java b/io.sloeber.core/src/io/sloeber/core/api/CompileDescription.java index d4d1cb66..01d976c4 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/CompileDescription.java +++ b/io.sloeber.core/src/io/sloeber/core/api/CompileDescription.java @@ -503,7 +503,7 @@ public CompileDescription(Map envVars) { mySizeCommand = SizeCommands.valueOf(sizeCommand); mySizeCommand.setCustomSizeCommand(customSizeCommand, true); } catch (Exception e) { - Common.log(new Status(IStatus.WARNING, Activator.getId(), "Deserialisation error", e)); //$NON-NLS-1$ + Activator.log(new Status(IStatus.WARNING, Activator.getId(), "Deserialisation error", e)); //$NON-NLS-1$ } } diff --git a/io.sloeber.core/src/io/sloeber/core/api/ISloeberConfiguration.java b/io.sloeber.core/src/io/sloeber/core/api/ISloeberConfiguration.java index e7da6d78..502495ff 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/ISloeberConfiguration.java +++ b/io.sloeber.core/src/io/sloeber/core/api/ISloeberConfiguration.java @@ -48,7 +48,7 @@ public static ISloeberConfiguration getConfig(ICConfigurationDescription config) CConfigurationData buildSettings = config.getConfigurationData(); if (!(buildSettings instanceof AutoBuildConfigurationDescription)) { //this should not happen as we just created a autoBuild project - Common.log(new Status(SLOEBER_STATUS_DEBUG, Activator.getId(), + Activator.log(new Status(SLOEBER_STATUS_DEBUG, Activator.getId(), "\"Auto build created a project that does not seem to be a autobuild project :-s : " //$NON-NLS-1$ + config.getProjectDescription().getName())); return null; diff --git a/io.sloeber.core/src/io/sloeber/core/api/PasswordManager.java b/io.sloeber.core/src/io/sloeber/core/api/PasswordManager.java index 07ae5dc3..6af882a0 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/PasswordManager.java +++ b/io.sloeber.core/src/io/sloeber/core/api/PasswordManager.java @@ -6,6 +6,7 @@ import org.eclipse.equinox.security.storage.SecurePreferencesFactory; import org.eclipse.equinox.security.storage.StorageException; +import io.sloeber.core.Activator; import io.sloeber.core.Messages; public class PasswordManager { @@ -56,7 +57,7 @@ public boolean setHost(String host) { } } catch (StorageException e) { - Common.log(new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID, + Activator.log(new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID, "Set a password on the project properties->Sloeber page", e)); //$NON-NLS-1$ } @@ -94,7 +95,7 @@ static public void setPwd(String host, String login, String pwd) { node.put(Messages.security_password, pwd, false); } catch (StorageException e) { - Common.log(new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID, "failed to set login info", e)); //$NON-NLS-1$ + Activator.log(new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID, "failed to set login info", e)); //$NON-NLS-1$ } } diff --git a/io.sloeber.core/src/io/sloeber/core/api/Serial.java b/io.sloeber.core/src/io/sloeber/core/api/Serial.java index 3be832ae..daf38e77 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Serial.java +++ b/io.sloeber.core/src/io/sloeber/core/api/Serial.java @@ -43,6 +43,7 @@ import org.osgi.framework.FrameworkUtil; import org.osgi.framework.ServiceRegistration; +import io.sloeber.core.Activator; import io.sloeber.core.common.InstancePreferences; import jssc.SerialPort; import jssc.SerialPortEvent; @@ -117,7 +118,7 @@ public Serial(String iname, int irate, char iparity, int idatabits, float istopb * something slightly more intelligent to do. */ public static void errorMessage(String where, Throwable e) { - Common.log(new Status(IStatus.WARNING, Const.CORE_PLUGIN_ID, "Error inside Serial. " + where, e)); //$NON-NLS-1$ + Activator.log(new Status(IStatus.WARNING, Const.CORE_PLUGIN_ID, "Error inside Serial. " + where, e)); //$NON-NLS-1$ } @@ -151,7 +152,7 @@ public static List list() { } return new ArrayList<>(Arrays.asList(portNames)); } catch (Exception e) { - Common.log(new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID, + Activator.log(new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID, "There is a config problem on your system.\nFor more detail see https://github.com/jantje/arduino-eclipse-plugin/issues/252", //$NON-NLS-1$ e)); List ret = new ArrayList<>(); @@ -194,16 +195,16 @@ public void connect(int maxTries) { // handle exception if (++count == maxTries) { if (SerialPortException.TYPE_PORT_BUSY.equals(e.getExceptionType())) { - Common.log(new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID, "Serial port " + this.portName //$NON-NLS-1$ + Activator.log(new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID, "Serial port " + this.portName //$NON-NLS-1$ + " already in use. Try quiting any programs that may be using it", //$NON-NLS-1$ e)); } else if (SerialPortException.TYPE_PORT_NOT_FOUND.equals(e.getExceptionType())) { - Common.log(new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID, "Serial port " //$NON-NLS-1$ + Activator.log(new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID, "Serial port " //$NON-NLS-1$ + this.portName + " not found. Did you select the right one from the project properties -> Sloeber?", //$NON-NLS-1$ e)); } else { - Common.log(new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID, + Activator.log(new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID, "Error opening serial port " + this.portName, e)); //$NON-NLS-1$ } return; @@ -211,7 +212,7 @@ public void connect(int maxTries) { try { Thread.sleep(200); } catch (InterruptedException e1) { - Common.log(new Status(IStatus.WARNING, Const.CORE_PLUGIN_ID, "Sleep failed", e1)); //$NON-NLS-1$ + Activator.log(new Status(IStatus.WARNING, Const.CORE_PLUGIN_ID, "Sleep failed", e1)); //$NON-NLS-1$ } } // If an exception was thrown, delete port variable diff --git a/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java b/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java index 25d9d514..04df0bde 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java +++ b/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java @@ -145,7 +145,7 @@ public static void convertToArduinoProject(IProject project, IProgressMonitor mo // cCorePlugin.setProjectDescription(project, prjCDesc, true, null); // // } catch (Exception e) { - // Common.log(new Status(IStatus.INFO, io.sloeber.core.Activator.getId(), + // Activator.log(new Status(IStatus.INFO, io.sloeber.core.Activator.getId(), // "Project conversion failed: ", e)); //$NON-NLS-1$ // } // @@ -157,7 +157,7 @@ public static void convertToArduinoProject(IProject project, IProgressMonitor mo // try { // workspace.run(runnable, root, IWorkspace.AVOID_UPDATE, monitor); // } catch (Exception e) { - // Common.log(new Status(IStatus.INFO, io.sloeber.core.Activator.getId(), + // Activator.log(new Status(IStatus.INFO, io.sloeber.core.Activator.getId(), // "Project conversion failed: ", e)); //$NON-NLS-1$ // } @@ -267,7 +267,7 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { .getConfig(curConfig); if (!(iAutoBuildConfig instanceof AutoBuildConfigurationDescription)) { // this should not happen as we just created a autoBuild project - Common.log(new Status(SLOEBER_STATUS_DEBUG, Activator.getId(), + Activator.log(new Status(SLOEBER_STATUS_DEBUG, Activator.getId(), "\"Auto build created a project that does not seem to be a autobuild project :-s : " //$NON-NLS-1$ + realProjectName)); continue; @@ -287,7 +287,7 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { cCorePlugin.setProjectDescription(newProjectHandle, prjCDesc, true, SubMonitor.convert(internalMonitor, 1)); - Common.log(new Status(SLOEBER_STATUS_DEBUG, Activator.getId(), + Activator.log(new Status(SLOEBER_STATUS_DEBUG, Activator.getId(), "internal creation of project is done: " + realProjectName)); //$NON-NLS-1$ // IndexerController.index(newProjectHandle); } @@ -298,7 +298,7 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { { workspace.run(runnable, root, IWorkspace.AVOID_UPDATE, monitor); } catch (Exception e) { - Common.log(new Status(IStatus.INFO, io.sloeber.core.Activator.getId(), + Activator.log(new Status(IStatus.INFO, io.sloeber.core.Activator.getId(), "Project creation failed: " + realProjectName, e)); //$NON-NLS-1$ } monitor.done(); @@ -479,7 +479,7 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { // storeConfigurationFile(getConfigVersionFile(), versionVars); // storeConfigurationFile(getConfigLocalFile(), configVars); // } catch (Exception e) { - // Common.log(new Status(IStatus.ERROR, io.sloeber.core.Activator.getId(), + // Activator.log(new Status(IStatus.ERROR, io.sloeber.core.Activator.getId(), // "failed to save the sloeber config files", e)); //$NON-NLS-1$ // } // diff --git a/io.sloeber.core/src/io/sloeber/core/common/InstancePreferences.java b/io.sloeber.core/src/io/sloeber/core/common/InstancePreferences.java index 55b59a48..3cd4b3f0 100644 --- a/io.sloeber.core/src/io/sloeber/core/common/InstancePreferences.java +++ b/io.sloeber.core/src/io/sloeber/core/common/InstancePreferences.java @@ -10,7 +10,7 @@ import org.eclipse.core.runtime.preferences.InstanceScope; import org.osgi.service.prefs.BackingStoreException; -import io.sloeber.core.api.Common; +import io.sloeber.core.Activator; import io.sloeber.core.api.Defaults; /** @@ -60,7 +60,7 @@ public static void setGlobalValue(String key, String value) { try { myScope.flush(); } catch (BackingStoreException e) { - Common.log(new Status(IStatus.WARNING, CORE_PLUGIN_ID, + Activator.log(new Status(IStatus.WARNING, CORE_PLUGIN_ID, "failed to set global variable of type string " + key,e)); //$NON-NLS-1$ } } @@ -73,7 +73,7 @@ private static void setValue(String key, boolean value) { try { myScope.flush(); } catch (BackingStoreException e) { - Common.log(new Status(IStatus.WARNING, CORE_PLUGIN_ID, + Activator.log(new Status(IStatus.WARNING, CORE_PLUGIN_ID, "failed to set global variable of type boolean " + key,e)); //$NON-NLS-1$ } } diff --git a/io.sloeber.core/src/io/sloeber/core/communication/ArduinoSerial.java b/io.sloeber.core/src/io/sloeber/core/communication/ArduinoSerial.java index 0d7088b1..a111d46a 100644 --- a/io.sloeber.core/src/io/sloeber/core/communication/ArduinoSerial.java +++ b/io.sloeber.core/src/io/sloeber/core/communication/ArduinoSerial.java @@ -12,6 +12,7 @@ import org.eclipse.ui.console.MessageConsoleStream; import io.sloeber.arduinoFramework.api.BoardDescription; +import io.sloeber.core.Activator; import io.sloeber.core.api.ISloeberConfiguration; import io.sloeber.core.api.Serial; @@ -43,7 +44,7 @@ public static boolean reset_Arduino_by_baud_rate(String comPort, int baudRate, l serialPort.dispose(); Thread.sleep(openTime); } catch (Exception e) { - log(new Status(IStatus.WARNING, CORE_PLUGIN_ID, + Activator.log(new Status(IStatus.WARNING, CORE_PLUGIN_ID, ArduinoSerial_unable_to_open_serial_port.replace(PORT_TAG, comPort), e)); return false; } diff --git a/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java b/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java index dfccfd7b..4b7541b9 100644 --- a/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java +++ b/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java @@ -39,7 +39,6 @@ import io.sloeber.autoBuild.integration.AutoBuildConfigurationDescription; import io.sloeber.core.Activator; import io.sloeber.core.Messages; -import io.sloeber.core.api.Common; import io.sloeber.core.api.CompileDescription; import io.sloeber.core.api.ConfigurationPreferences; import io.sloeber.core.api.ISloeberConfiguration; @@ -769,7 +768,7 @@ private boolean updateSourceEntries() { getAutoBuildDesc().getCdtConfigurationDescription().setSourceEntries(newSourceEntries); } catch (Exception e) { - Common.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, "Failed to modify configuration for rename", e)); + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, "Failed to modify configuration for rename", e)); } return true; } diff --git a/io.sloeber.core/src/io/sloeber/core/listeners/IndexerListener.java b/io.sloeber.core/src/io/sloeber/core/listeners/IndexerListener.java index 982a9909..d061f0f8 100644 --- a/io.sloeber.core/src/io/sloeber/core/listeners/IndexerListener.java +++ b/io.sloeber.core/src/io/sloeber/core/listeners/IndexerListener.java @@ -35,7 +35,6 @@ import io.sloeber.arduinoFramework.api.LibraryManager; import io.sloeber.core.Activator; import io.sloeber.core.Messages; -import io.sloeber.core.api.Common; import io.sloeber.core.api.Const; import io.sloeber.core.api.IInstallLibraryHandler; import io.sloeber.core.api.ISloeberConfiguration; @@ -62,7 +61,7 @@ public void indexChanged(IIndexChangeEvent event) { return; } if (!newChangedProjects.contains(sloeberConfDesc)) { - Common.log(new Status(Const.SLOEBER_STATUS_DEBUG, Activator.getId(), + Activator.log(new Status(Const.SLOEBER_STATUS_DEBUG, Activator.getId(), "Index of project changed :" + project.getName())); //$NON-NLS-1$ newChangedProjects.add(sloeberConfDesc); } @@ -78,13 +77,13 @@ public void indexChanged(IIndexerStateEvent event) { for (ISloeberConfiguration sloeberConfDesc : curChangedProjects) { String projectName = sloeberConfDesc.getProject().getName(); try { - Common.log(new Status(Const.SLOEBER_STATUS_DEBUG, Activator.getId(), + Activator.log(new Status(Const.SLOEBER_STATUS_DEBUG, Activator.getId(), "Looking for libraries for project :" + projectName)); //$NON-NLS-1$ checkLibraries(sloeberConfDesc); } catch (Exception e) { - Common.log(new Status(IStatus.WARNING, Activator.getId(), Messages.Failed_To_Add_Libraries, e)); + Activator.log(new Status(IStatus.WARNING, Activator.getId(), Messages.Failed_To_Add_Libraries, e)); } - Common.log(new Status(Const.SLOEBER_STATUS_DEBUG, Activator.getId(), + Activator.log(new Status(Const.SLOEBER_STATUS_DEBUG, Activator.getId(), "libraries added for project " + projectName)); //$NON-NLS-1$ } } @@ -162,7 +161,7 @@ private static void checkLibraries(ISloeberConfiguration SloeberCfg) { } if (!toInstallLibs.isEmpty()) { // there are possible libraries to add - Common.log(new Status(IStatus.INFO, CORE_PLUGIN_ID, "list of libraries to add to project " //$NON-NLS-1$ + Activator.log(new Status(IStatus.INFO, CORE_PLUGIN_ID, "list of libraries to add to project " //$NON-NLS-1$ + SloeberCfg.getProject().getName() + COLON + SPACE + toInstallLibString)); SloeberCfg.addLibraries(toInstallLibs); diff --git a/io.sloeber.core/src/io/sloeber/core/tools/ExternalCommandLauncher.java b/io.sloeber.core/src/io/sloeber/core/tools/ExternalCommandLauncher.java index 40235f44..db056eac 100644 --- a/io.sloeber.core/src/io/sloeber/core/tools/ExternalCommandLauncher.java +++ b/io.sloeber.core/src/io/sloeber/core/tools/ExternalCommandLauncher.java @@ -29,6 +29,7 @@ import org.eclipse.core.runtime.Status; import org.eclipse.ui.console.MessageConsoleStream; +import io.sloeber.core.Activator; import io.sloeber.core.Messages; import io.sloeber.core.api.Common; import io.sloeber.core.api.Const; @@ -120,7 +121,7 @@ public void run() { } catch (IOException e) { // This is unlikely to happen, but log it nevertheless IStatus status = new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID, Messages.command_io, e); - Common.log(status); + Activator.log(status); } finally { try { this.fReader.close(); diff --git a/io.sloeber.core/src/io/sloeber/core/tools/FileModifiers.java b/io.sloeber.core/src/io/sloeber/core/tools/FileModifiers.java index f5c5868e..54d3ac86 100644 --- a/io.sloeber.core/src/io/sloeber/core/tools/FileModifiers.java +++ b/io.sloeber.core/src/io/sloeber/core/tools/FileModifiers.java @@ -17,7 +17,6 @@ import org.eclipse.core.runtime.Status; import io.sloeber.core.Activator; -import io.sloeber.core.api.Common; import io.sloeber.core.common.InstancePreferences; public class FileModifiers { @@ -118,7 +117,7 @@ public static void replaceInFile(File file, boolean regex, String find, String r Files.write(pathFile, textFromFile.getBytes(), StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE); } catch (IOException e) { - Common.log(new Status(IStatus.WARNING, Activator.getId(), + Activator.log(new Status(IStatus.WARNING, Activator.getId(), "Failed to replace " + find + " with " + replace + " in file " + file.toString(), e)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } } diff --git a/io.sloeber.core/src/io/sloeber/core/tools/Helpers.java b/io.sloeber.core/src/io/sloeber/core/tools/Helpers.java index 2f2240e5..32199eac 100644 --- a/io.sloeber.core/src/io/sloeber/core/tools/Helpers.java +++ b/io.sloeber.core/src/io/sloeber/core/tools/Helpers.java @@ -1,6 +1,5 @@ package io.sloeber.core.tools; -import static io.sloeber.core.api.Common.*; import static io.sloeber.core.api.Const.*; import java.io.File; @@ -31,6 +30,7 @@ import org.eclipse.ui.console.MessageConsole; import io.sloeber.autoBuild.api.IAutoBuildConfigurationDescription; +import io.sloeber.core.Activator; import io.sloeber.core.Messages; /** @@ -133,7 +133,7 @@ public static boolean removeInvalidIncludeFolders(ICConfigurationDescription con || (((CIncludePathEntry) OrgIncludeEntries[curEntry]).isBuiltIn())) { OrgIncludeEntries[copiedEntry++] = OrgIncludeEntries[curEntry]; } else { - log(new Status(IStatus.WARNING, CORE_PLUGIN_ID, "Removed invalid include path" + cusPath, //$NON-NLS-1$ + Activator.log(new Status(IStatus.WARNING, CORE_PLUGIN_ID, "Removed invalid include path" + cusPath, //$NON-NLS-1$ null)); } } @@ -183,7 +183,7 @@ public static void LinkFolderToFolder(IPath source, IFolder projectFolder) { try { createNewFolder(projectFolder, source); } catch (CoreException e) { - log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, Messages.Helpers_Create_folder_failed.replace(FOLDER, projectFolder.toString()), e)); } } @@ -290,7 +290,7 @@ public static void deleteBuildFolder(IProject project, String cfgName) { try { buildFolder.delete(true, null); } catch (CoreException e) { - log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, Messages.Helpers_delete_folder_failed.replace(FOLDER, cfgName), e)); } } @@ -309,7 +309,7 @@ public static void linkDirectory(IPath source, IFolder target) { File[] sourceFiles = source.toFile().listFiles(); if (sourceFiles == null) { if (!myHasBeenLogged) { - log(new Status(IStatus.INFO, CORE_PLUGIN_ID, + Activator.log(new Status(IStatus.INFO, CORE_PLUGIN_ID, Messages.Helpers_error_link_folder_is_empty.replace(FILE, source.toOSString()), null)); myHasBeenLogged = true; } diff --git a/io.sloeber.core/src/io/sloeber/core/tools/Libraries.java b/io.sloeber.core/src/io/sloeber/core/tools/Libraries.java index 3caad74d..307e3ffe 100644 --- a/io.sloeber.core/src/io/sloeber/core/tools/Libraries.java +++ b/io.sloeber.core/src/io/sloeber/core/tools/Libraries.java @@ -14,7 +14,7 @@ import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; -import io.sloeber.core.api.Common; +import io.sloeber.core.Activator; import io.sloeber.core.api.ConfigurationPreferences; import io.sloeber.core.api.VersionNumber; @@ -45,7 +45,7 @@ public static Map findAllArduinoManagerLibraries() { if (versions != null) { switch (versions.length) { case 0:// A empty lib folder is hanging around - Common.log(new Status(IStatus.WARNING, CORE_PLUGIN_ID, + Activator.log(new Status(IStatus.WARNING, CORE_PLUGIN_ID, EmptyLibFolder.replace(LIB_TAG, curLib))); Lib_root.toFile().delete(); break; @@ -58,7 +58,7 @@ public static Map findAllArduinoManagerLibraries() { // latest int highestVersion = getHighestVersion(versions); ret.put(curLib, Lib_root.append(versions[highestVersion])); - Common.log(new Status(IStatus.WARNING, CORE_PLUGIN_ID, + Activator.log(new Status(IStatus.WARNING, CORE_PLUGIN_ID, MultipleVersionsOfLib.replace(LIB_TAG, curLib))); } @@ -109,7 +109,7 @@ public static boolean removeLibrariesFromProject(IProject project, ICConfigurati final IFolder folderHandle = project.getFolder(WORKSPACE_LIB_FOLDER + CurItem); folderHandle.delete(true, null); } catch (CoreException e) { - Common.log( + Activator.log( new Status(IStatus.ERROR, CORE_PLUGIN_ID, failed_to_remove_lib.replace(LIB_TAG, CurItem), e)); } } diff --git a/io.sloeber.core/src/io/sloeber/core/tools/PackageManager.java b/io.sloeber.core/src/io/sloeber/core/tools/PackageManager.java index e76f22f0..bbd3823b 100644 --- a/io.sloeber.core/src/io/sloeber/core/tools/PackageManager.java +++ b/io.sloeber.core/src/io/sloeber/core/tools/PackageManager.java @@ -31,7 +31,6 @@ import io.sloeber.arduinoFramework.api.ArduinoInstallable; import io.sloeber.core.Activator; import io.sloeber.core.Messages; -import io.sloeber.core.api.Common; import io.sloeber.core.api.ConfigurationPreferences; public class PackageManager { @@ -419,14 +418,14 @@ private static void myCopy(URL url, File localFile, boolean report_error, int re return; } if (report_error) { - Common.log(new Status(IStatus.WARNING, Activator.getId(), + Activator.log(new Status(IStatus.WARNING, Activator.getId(), "Failed to download url " + url + " error code is: " + status, null)); } throw new IOException("Failed to download url " + url + " error code is: " + status); } catch (Exception e) { if (report_error) { - Common.log(new Status(IStatus.WARNING, Activator.getId(), "Failed to download url " + url, e)); + Activator.log(new Status(IStatus.WARNING, Activator.getId(), "Failed to download url " + url, e)); } throw e; diff --git a/io.sloeber.core/src/io/sloeber/core/tools/Stream.java b/io.sloeber.core/src/io/sloeber/core/tools/Stream.java index c6ea4025..6089feb1 100644 --- a/io.sloeber.core/src/io/sloeber/core/tools/Stream.java +++ b/io.sloeber.core/src/io/sloeber/core/tools/Stream.java @@ -14,7 +14,6 @@ import org.eclipse.core.runtime.Status; import io.sloeber.core.Activator; -import io.sloeber.core.api.Common; /** * the Stream class is used to read the board.txt file @@ -43,7 +42,7 @@ public static InputStream openContentStream(String Resource, boolean isFile,Map< input = Stream.class.getResourceAsStream(Resource); } if (input == null) { - Common.log(new Status(IStatus.ERROR, Activator.getId(), + Activator.log(new Status(IStatus.ERROR, Activator.getId(), "openContentStream: resource " + Resource + " not found.\nThe file will not be processed!")); //$NON-NLS-1$ //$NON-NLS-2$ return null; } @@ -71,7 +70,7 @@ public static InputStream openContentStream(String Resource, boolean isFile,Map< } input = null; IStatus status = new Status(IStatus.ERROR, "NewFileWizard", IStatus.OK, ioe.getLocalizedMessage(), ioe); //$NON-NLS-1$ - Common.log(status); + Activator.log(status); throw new CoreException(status); } finally { if (input != null) { diff --git a/io.sloeber.core/src/io/sloeber/core/tools/uploaders/UploadSketchWrapper.java b/io.sloeber.core/src/io/sloeber/core/tools/uploaders/UploadSketchWrapper.java index 11ba33ca..9a3698cd 100644 --- a/io.sloeber.core/src/io/sloeber/core/tools/uploaders/UploadSketchWrapper.java +++ b/io.sloeber.core/src/io/sloeber/core/tools/uploaders/UploadSketchWrapper.java @@ -42,6 +42,7 @@ import cc.arduino.packages.ssh.SSHConfigFileSetup; import cc.arduino.packages.ssh.SSHPwdSetup; import io.sloeber.arduinoFramework.api.BoardDescription; +import io.sloeber.core.Activator; import io.sloeber.core.Messages; import io.sloeber.core.api.ISloeberConfiguration; import io.sloeber.core.api.PasswordManager; @@ -169,7 +170,7 @@ public void run() { theComPortIsPaused = SerialManager.pauseSerialMonitor(myProvidedUploadPort); } catch (Exception e) { ret = new Status(IStatus.WARNING, CORE_PLUGIN_ID, Upload_Error_com_port, e); - log(ret); + Activator.log(ret); } if (!actualUpload(monitor, highLevelStream, outStream, errStream)) { @@ -180,7 +181,7 @@ public void run() { } catch (Exception e) { String error = Upload_failed_upload_file.replace(FILE_TAG, projectName); - log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, error, e)); + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, error, e)); } finally { try { if (theComPortIsPaused) { @@ -202,7 +203,7 @@ public void run() { } } catch (Exception e) { ret = new Status(IStatus.WARNING, CORE_PLUGIN_ID, Messages.Upload_Error_serial_monitor_restart, e); - log(ret); + Activator.log(ret); } } monitor.done(); @@ -233,7 +234,7 @@ private boolean actualUpload(IProgressMonitor monitor, MessageConsoleStream high String command = getBuildEnvironmentVariable(mySloeberConf, uploadRecipoeKey, EMPTY); if (command.isEmpty()) { - log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, uploadRecipoeKey + " : not found in the platform.txt file")); //$NON-NLS-1$ highStream.println(uploader_Failed_to_get_upload_recipe); return false; @@ -252,7 +253,7 @@ private boolean actualUpload(IProgressMonitor monitor, MessageConsoleStream high if (cmdLauncher.launch(monitor, highStream, outStream, errStream) < 0) return false; } catch (IOException e) { - log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, Upload_failed, e)); + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, Upload_failed, e)); return false; } } diff --git a/io.sloeber.core/src/io/sloeber/core/txt/TxtFile.java b/io.sloeber.core/src/io/sloeber/core/txt/TxtFile.java index 13955895..e83fcb5e 100644 --- a/io.sloeber.core/src/io/sloeber/core/txt/TxtFile.java +++ b/io.sloeber.core/src/io/sloeber/core/txt/TxtFile.java @@ -18,7 +18,7 @@ import org.eclipse.core.runtime.Status; import io.sloeber.autoBuild.helpers.api.KeyValueTree; -import io.sloeber.core.api.Common; +import io.sloeber.core.Activator; import io.sloeber.core.api.Const; /** @@ -72,7 +72,7 @@ public void mergeFile(File boardsFileName) { } } } catch (Exception e) { - Common.log(new Status(IStatus.WARNING, Const.CORE_PLUGIN_ID, + Activator.log(new Status(IStatus.WARNING, Const.CORE_PLUGIN_ID, Boards_Failed_to_read_boards.replace(FILE_TAG, boardsFileName.getName()), e)); } } diff --git a/io.sloeber.core/src/io/sloeber/core/txt/WorkAround.java b/io.sloeber.core/src/io/sloeber/core/txt/WorkAround.java index e171b10c..cbf01656 100644 --- a/io.sloeber.core/src/io/sloeber/core/txt/WorkAround.java +++ b/io.sloeber.core/src/io/sloeber/core/txt/WorkAround.java @@ -22,7 +22,6 @@ import io.sloeber.arduinoFramework.api.IArduinoPlatformVersion; import io.sloeber.core.Activator; -import io.sloeber.core.api.Common; import io.sloeber.core.api.VersionNumber; import io.sloeber.core.tools.FileModifiers; @@ -122,7 +121,7 @@ static File MakeBoardsSloeberTxt(File requestedFileToWorkAround) { String inFile = requestedFileToWorkAround.toString(); String actualFileToLoad = inFile.replace(BOARDS_FILE_NAME, "boards.sloeber.txt"); if (inFile.equals(actualFileToLoad)) { - Common.log(new Status(IStatus.ERROR, Activator.getId(), + Activator.log(new Status(IStatus.ERROR, Activator.getId(), "Boards.txt file is not recognized " + requestedFileToWorkAround.toString())); return requestedFileToWorkAround; } @@ -144,7 +143,7 @@ static File MakeBoardsSloeberTxt(File requestedFileToWorkAround) { Files.write(boardsSloeberTXT.toPath(), boardsTXT.getBytes(), StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE); } catch (IOException e) { - Common.log(new Status(IStatus.WARNING, Activator.getId(), + Activator.log(new Status(IStatus.WARNING, Activator.getId(), "Failed to apply work arounds to " + requestedFileToWorkAround.toString(), e)); return requestedFileToWorkAround; } @@ -196,7 +195,7 @@ static File MakePlatformSloeberTXT(File requestedFileToWorkAround) { String inFile = requestedFileToWorkAround.toString(); String actualFileToLoad = inFile.replace(PLATFORM_FILE_NAME, "platform.sloeber.txt"); if (inFile.equals(actualFileToLoad)) { - Common.log(new Status(IStatus.ERROR, Activator.getId(), + Activator.log(new Status(IStatus.ERROR, Activator.getId(), "platform.txt file is not recognized " + requestedFileToWorkAround.toString())); return requestedFileToWorkAround; } @@ -219,7 +218,7 @@ static File MakePlatformSloeberTXT(File requestedFileToWorkAround) { Files.write(platformSloeberTXT.toPath(), platformTXT.getBytes(), StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE); } catch (IOException e) { - Common.log(new Status(IStatus.WARNING, Activator.getId(), + Activator.log(new Status(IStatus.WARNING, Activator.getId(), "Failed to apply work arounds to " + requestedFileToWorkAround.toString(), e)); return requestedFileToWorkAround; } @@ -549,7 +548,7 @@ private static String solveOSStuff(String inpuText) { Otherosses.add(WINDOWSKEY); Otherosses.add(LINUXKEY); } else { - Common.log(new Status(IStatus.ERROR, Activator.getId(), "Failed to recognize the os you are using")); + Activator.log(new Status(IStatus.ERROR, Activator.getId(), "Failed to recognize the os you are using")); return inpuText; } @@ -566,7 +565,7 @@ private static String solveOSStuff(String inpuText) { while (null != neededOSLine) { int keyIndex = neededOSLine.indexOf(thisOSKey); if (keyIndex < 0) { - Common.log(new Status(IStatus.ERROR, Activator.getId(), "Error processing txt file: " + neededOSLine)); + Activator.log(new Status(IStatus.ERROR, Activator.getId(), "Error processing txt file: " + neededOSLine)); neededOSLine = null; } else { String genericKey = neededOSLine.substring(0, keyIndex) + EQUAL; @@ -604,7 +603,7 @@ public static File MakeProgrammersSloeberTXT(File requestedFileToWorkAround) { String inFile = requestedFileToWorkAround.toString(); String actualFileToLoad = inFile.replace("programmers.txt", "programmers.sloeber.txt"); if (inFile.equals(actualFileToLoad)) { - Common.log(new Status(IStatus.ERROR, Activator.getId(), + Activator.log(new Status(IStatus.ERROR, Activator.getId(), "programmers.txt file is not recognized " + requestedFileToWorkAround.toString())); return requestedFileToWorkAround; } @@ -626,7 +625,7 @@ public static File MakeProgrammersSloeberTXT(File requestedFileToWorkAround) { Files.write(actualProgrammersTXT.toPath(), programmersTXT.getBytes(), StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE); } catch (IOException e) { - Common.log(new Status(IStatus.WARNING, Activator.getId(), + Activator.log(new Status(IStatus.WARNING, Activator.getId(), "Failed to apply work arounds to " + requestedFileToWorkAround.toString(), e)); return requestedFileToWorkAround; } From 4c315c2fe84daa7490a3a34b489ac7abf89ce644 Mon Sep 17 00:00:00 2001 From: jan Date: Wed, 28 Aug 2024 02:38:52 +0200 Subject: [PATCH 017/115] create sloeber.txt file based on json if not present #1676 --- .../api/BoardDescription.java | 129 ++++++++++++------ .../src/io/sloeber/core/api/Const.java | 1 + 2 files changed, 86 insertions(+), 44 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java index 108e3da6..4aaf9683 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java @@ -5,8 +5,12 @@ import static io.sloeber.core.api.Const.*; import static io.sloeber.autoBuild.api.AutoBuildCommon.*; +import java.io.BufferedReader; import java.io.File; +import java.io.FileReader; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -29,7 +33,7 @@ import io.sloeber.arduinoFramework.internal.ArduinoPlatformTooldDependency; import io.sloeber.autoBuild.api.IAutoBuildConfigurationDescription; import io.sloeber.autoBuild.helpers.api.KeyValueTree; -import io.sloeber.core.api.Common; +import io.sloeber.core.Activator; import io.sloeber.core.api.ConfigurationPreferences; import io.sloeber.core.api.Const; import io.sloeber.core.api.Preferences; @@ -41,6 +45,7 @@ import io.sloeber.core.txt.TxtFile; public class BoardDescription { + private static final String FIRST_SLOEBER_LINE = "#Sloeber created file please do not modify V1.00.test 02 "; //$NON-NLS-1$ private static final IEclipsePreferences myStorageNode = InstanceScope.INSTANCE.getNode(NODE_ARDUINO); /* @@ -165,7 +170,7 @@ private void ParseSection() { myBoardsCore = valueSplit[1]; myReferencedPlatformCore = BoardsManager.getNewestInstalledPlatform(refVendor, architecture); if (myReferencedPlatformCore == null) { - Common.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, Helpers_tool_reference_missing.replace(TOOL_TAG, core) .replace(FILE_TAG, getReferencingBoardsFile().toString()) .replace(BOARD_TAG, getBoardID()))); @@ -178,7 +183,7 @@ private void ParseSection() { myBoardsCore = valueSplit[3]; myReferencedPlatformCore = BoardsManager.getPlatform(refVendor, refArchitecture, refVersion); if (myReferencedPlatformCore == null) { - Common.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, Helpers_tool_reference_missing.replace(TOOL_TAG, core) .replace(FILE_TAG, getReferencingBoardsFile().toString()) .replace(BOARD_TAG, getBoardID()))); @@ -195,7 +200,7 @@ private void ParseSection() { myBoardsVariant = valueSplit[1]; myReferencedPlatformVariant = BoardsManager.getNewestInstalledPlatform(refVendor, architecture); if (myReferencedPlatformVariant == null) { - Common.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, Helpers_tool_reference_missing.replace(TOOL_TAG, variant) .replace(FILE_TAG, getReferencingBoardsFile().toString()) .replace(BOARD_TAG, getBoardID()))); @@ -212,7 +217,7 @@ private void ParseSection() { myReferencedPlatformVariant = BoardsManager.getPlatform(refVendor, refArchitecture, refVersion); } if (myReferencedPlatformVariant == null) { - Common.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, Helpers_tool_reference_missing.replace(TOOL_TAG, variant) .replace(FILE_TAG, getReferencingBoardsFile().toString()) .replace(BOARD_TAG, getBoardID()))); @@ -229,7 +234,7 @@ private void ParseSection() { myUploadTool = valueSplit[1]; myReferencedPlatformUpload = BoardsManager.getNewestInstalledPlatform(refVendor, architecture); if (myReferencedPlatformUpload == null) { - Common.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, Helpers_tool_reference_missing.replace(TOOL_TAG, upload) .replace(FILE_TAG, getReferencingBoardsFile().toString()) .replace(BOARD_TAG, getBoardID()))); @@ -242,7 +247,7 @@ private void ParseSection() { myUploadTool = valueSplit[3]; myReferencedPlatformUpload = BoardsManager.getPlatform(refVendor, refArchitecture, refVersion); if (this.myReferencedPlatformUpload == null) { - Common.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, Helpers_tool_reference_missing.replace(TOOL_TAG, upload) .replace(FILE_TAG, getReferencingBoardsFile().toString()) .replace(BOARD_TAG, getBoardID()))); @@ -879,7 +884,12 @@ public Map getEnvVars() { } // put in the installed tools info - allVars.putAll(getEnVarPlatformInfo()); + try { + allVars.putAll(getEnVarPlatformInfo()); + } catch (IOException e) { + // TODO Auto-generated catch block + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID,"failed to get platform paths",e)); + } // boards settings not coming from menu selections allVars.putAll(mySloeberBoardTxtFile.getBoardEnvironVars(getBoardID())); @@ -926,37 +936,28 @@ private String getBoardFQBN() { return fqbn+options; } - private Map getEnVarPlatformInfo() { + private Map getEnVarPlatformInfo() throws IOException { Map ret = new HashMap<>(); - if (myReferencedPlatformUpload != null) { ret.putAll(getEnvVarPlatformFileTools(myReferencedPlatformUpload)); - } - if (myReferencedPlatformVariant != null) { ret.putAll(getEnvVarPlatformFileTools(myReferencedPlatformVariant)); - } - - if (myReferencedPlatformCore != null) { ret.putAll(getEnvVarPlatformFileTools(myReferencedPlatformCore)); - } + + IArduinoPlatformVersion latestArduinoPlatform = BoardsManager.getNewestInstalledPlatform(Const.VENDOR_ARDUINO, + getArchitecture()); + ret.putAll(getEnvVarPlatformFileTools(latestArduinoPlatform)); IPath referencingPlatformPath = getreferencingPlatformPath(); IArduinoPlatformVersion referencingPlatform = BoardsManager.getPlatform(referencingPlatformPath); - - if (referencingPlatform == null) { - // This is the case for private hardware - //there is no need to specify tool path as they do not use them - return ret; - } - IArduinoPlatformVersion latestArduinoPlatform = BoardsManager.getNewestInstalledPlatform(Const.VENDOR_ARDUINO, - referencingPlatform.getArchitecture()); - if (latestArduinoPlatform != null) { - ret.putAll(getEnvVarPlatformFileTools(latestArduinoPlatform)); + if(referencingPlatform==null) { + ret.putAll(getEnvVarPlatformFileTools(referencingPlatformPath.toFile())); + }else { + ret.putAll(getEnvVarPlatformFileTools(referencingPlatform)); } + if (myReferencedPlatformCore == null) { //there is no referenced core so no need to do smart stuff - ret.putAll(getEnvVarPlatformFileTools(referencingPlatform)); return ret; } @@ -967,7 +968,6 @@ private Map getEnVarPlatformInfo() { return ret; } // standard arduino IDE way - ret.putAll(getEnvVarPlatformFileTools(referencingPlatform)); ret.putAll(getEnvVarPlatformFileTools(myReferencedPlatformCore)); return ret; @@ -980,23 +980,64 @@ private Map getEnVarPlatformInfo() { * * @param platformVersion * @return environment variables pointing to the tools used by the platform + * @throws IOException */ - private static Map getEnvVarPlatformFileTools(IArduinoPlatformVersion platformVersion) { - HashMap vars = new HashMap<>(); - for (ArduinoPlatformTooldDependency tool : platformVersion.getToolsDependencies()) { - IPath installPath = tool.getInstallPath(); - if (installPath.toFile().exists()) { - String value = installPath.toOSString(); - String keyString = ENV_KEY_RUNTIME_TOOLS + tool.getName() + tool.getVersion() + DOT_PATH; - vars.put(keyString, value); - keyString = ENV_KEY_RUNTIME_TOOLS + tool.getName() + '-' + tool.getVersion() + DOT_PATH; - vars.put(keyString, value); - keyString = ENV_KEY_RUNTIME_TOOLS + tool.getName() + DOT_PATH; - vars.put(keyString, value); - } - } - return vars; - } + private static Map getEnvVarPlatformFileTools(IArduinoPlatformVersion platformVersion) throws IOException { + if(platformVersion==null) { + return new HashMap<>(); + } + File sloeberTxtFile = platformVersion.getInstallPath().append(SLOEBER_TXT_FILE_NAME).toFile(); + deleteIfOutdated (sloeberTxtFile); + + if (!sloeberTxtFile.exists()) { + + String vars = FIRST_SLOEBER_LINE+NEWLINE; + for (ArduinoPlatformTooldDependency tool : platformVersion.getToolsDependencies()) { + IPath installPath = tool.getInstallPath(); + if (installPath.toFile().exists()) { + String value = installPath.toOSString(); + String keyString = ENV_KEY_RUNTIME_TOOLS + tool.getName() + tool.getVersion() + DOT_PATH; + vars=vars+NEWLINE+keyString+EQUAL+ value; + keyString = ENV_KEY_RUNTIME_TOOLS + tool.getName() + '-' + tool.getVersion() + DOT_PATH; + vars=vars+NEWLINE+keyString+EQUAL+ value; + keyString = ENV_KEY_RUNTIME_TOOLS + tool.getName() + DOT_PATH; + vars=vars+NEWLINE+keyString+EQUAL+ value; + } + } + Files.write(sloeberTxtFile.toPath(), vars.getBytes(), StandardOpenOption.TRUNCATE_EXISTING, + StandardOpenOption.CREATE); + } + + return getEnvVarPlatformFileTools(sloeberTxtFile); + } + + private static Map getEnvVarPlatformFileTools(File sloeberTxtFile) { + if(sloeberTxtFile==null || (!sloeberTxtFile.exists())) { + return new HashMap<>(); + } + TxtFile sloeberTxt = new TxtFile(sloeberTxtFile); + return sloeberTxt.getAllEnvironVars(EMPTY); + } + + /** + * If the sloeber.txt variant exists delete it if it is outdated + * + * @param tmpFile + */ + private static void deleteIfOutdated(File tmpFile) { + if (tmpFile.exists()) { + // delete if outdated + String firstLine = null; + try (BufferedReader Buff = new BufferedReader(new FileReader(tmpFile));) { + firstLine = Buff.readLine().trim(); + } catch (@SuppressWarnings("unused") Exception e) { + // ignore and delete the file + } + if (!FIRST_SLOEBER_LINE.trim().equals(firstLine)) { + tmpFile.delete(); + } + } + } /** * Following post processing is done diff --git a/io.sloeber.core/src/io/sloeber/core/api/Const.java b/io.sloeber.core/src/io/sloeber/core/api/Const.java index d7ada4c5..7cd6b148 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Const.java +++ b/io.sloeber.core/src/io/sloeber/core/api/Const.java @@ -96,6 +96,7 @@ public class Const extends AutoBuildConstants { public static final String SLOEBER_PROJECT = ".sproject"; public static final String LIBRARY_PROPERTIES = "library.properties"; public static final String LIBRARY_DOT_A_LINKAGE = "dot_a_linkage"; + public static final String SLOEBER_TXT_FILE_NAME="sloeber.txt"; // Environment variable stuff public static final String ENV_KEY_SLOEBER_START = "sloeber" + DOT; From 1fb3cd704dab10deb8ac811292ea189e071c8371 Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 1 Sep 2024 01:21:01 +0200 Subject: [PATCH 018/115] move getArchitecture and getVendor to BoardDescription --- .../src/io/sloeber/core/txt/TxtFile.java | 38 ------------------- 1 file changed, 38 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/core/txt/TxtFile.java b/io.sloeber.core/src/io/sloeber/core/txt/TxtFile.java index e83fcb5e..8f0f7a0a 100644 --- a/io.sloeber.core/src/io/sloeber/core/txt/TxtFile.java +++ b/io.sloeber.core/src/io/sloeber/core/txt/TxtFile.java @@ -12,9 +12,7 @@ import java.util.List; import java.util.Map; -import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import io.sloeber.autoBuild.helpers.api.KeyValueTree; @@ -119,43 +117,7 @@ public String getNiceNameFromID(String myBoardID) { return myData.getValue(myBoardID + DOT + NAME); } - /* - * Returns the architecture based on the platform file name Caters for the - * packages (with version number and for the old way if the boards file does not - * exists returns avr - */ - public String getArchitecture() { - - IPath platformFile = new Path(this.mLoadedTxtFile.toString().trim()); - String architecture = platformFile.removeLastSegments(1).lastSegment(); - if (architecture == null) {// for error conditions - architecture = AVR; - } - if (architecture.contains(DOT)) { // This is a version number so - // package - architecture = platformFile.removeLastSegments(2).lastSegment(); - } - return architecture; - } - /* - * Returns the architecture based on the platform file name Caters for the - * packages (with version number and for the old way if the boards file does not - * exists returns avr - */ - public String getVendor() { - - IPath platformFile = new Path(this.mLoadedTxtFile.toString().trim()); - String vendor = platformFile.removeLastSegments(2).lastSegment(); - if (vendor == null) {// for error conditions - vendor = "NotFound"; //$NON-NLS-1$ - } - if (vendor.contains(DOT)) { // This is a version number so - // package - vendor = platformFile.removeLastSegments(3).lastSegment(); - } - return vendor; - } /** * Given a nice name look for the ID The assumption is that the txt file From 34c3798bc6ebf6a69fc8481d54a658ea34e0492c Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 1 Sep 2024 01:24:59 +0200 Subject: [PATCH 019/115] Use KeyValueTree for serialisation; add json info to serialisation --- .../api/BoardDescription.java | 238 ++++++++++++++---- .../arduinoFramework/api/BoardsManager.java | 5 +- .../api/IArduinoPlatformPackageIndex.java | 2 + .../internal/ArduinoPlatformPackageIndex.java | 12 + .../sloeber/core/api/CompileDescription.java | 108 +++----- .../core/api/ConfigurationPreferences.java | 2 - .../src/io/sloeber/core/api/Const.java | 18 +- .../io/sloeber/core/api/OtherDescription.java | 27 +- .../core/internal/SloeberConfiguration.java | 18 +- 9 files changed, 265 insertions(+), 165 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java index 4aaf9683..29a1e13e 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java @@ -4,6 +4,7 @@ import static io.sloeber.core.api.Common.*; import static io.sloeber.core.api.Const.*; import static io.sloeber.autoBuild.api.AutoBuildCommon.*; +import static io.sloeber.autoBuild.helpers.api.AutoBuildConstants.DOT; import java.io.BufferedReader; import java.io.File; @@ -45,7 +46,7 @@ import io.sloeber.core.txt.TxtFile; public class BoardDescription { - private static final String FIRST_SLOEBER_LINE = "#Sloeber created file please do not modify V1.00.test 02 "; //$NON-NLS-1$ + private static final String FIRST_SLOEBER_LINE = "#Sloeber created file please do not modify V1.00.test 05 "; //$NON-NLS-1$ private static final IEclipsePreferences myStorageNode = InstanceScope.INSTANCE.getNode(NODE_ARDUINO); /* @@ -65,8 +66,10 @@ public class BoardDescription { private IArduinoPlatformVersion myReferencedPlatformVariant = null; private IArduinoPlatformVersion myReferencedPlatformCore = null; private IArduinoPlatformVersion myReferencedPlatformUpload = null; + private String myJsonFileName=null; + private String myJsonURL=null; - private boolean isDirty = true; + private boolean myIsDirty = true; @Override public String toString() { @@ -290,16 +293,23 @@ public static List makeBoardDescriptors(File boardFile) { * if null default options are taken */ public BoardDescription(File boardsFile, String boardID, Map options) { + File expandedBoardsFile=resolvePathEnvironmentString(boardsFile); + if(!expandedBoardsFile.exists()) { + Activator.log(new Status(IStatus.ERROR,Activator.getId(),"BoardsFile " +boardsFile+" does not exist")); //$NON-NLS-1$//$NON-NLS-2$ + return; + } myBoardID = boardID; myUserSelectedBoardsTxtFile = boardsFile; - mySloeberBoardTxtFile = new BoardTxtFile(resolvePathEnvironmentString(myUserSelectedBoardsTxtFile)); + mySloeberBoardTxtFile = new BoardTxtFile(expandedBoardsFile); + getJSonInfo(); setDefaultOptions(); if (options != null) { myOptions.putAll(options); } } - public BoardDescription() { + + public BoardDescription() { myUserSelectedBoardsTxtFile = new File(myStorageNode.get(KEY_LAST_USED_BOARDS_FILE, EMPTY)); if (!myUserSelectedBoardsTxtFile.exists()) { @@ -311,25 +321,55 @@ public BoardDescription() { } myUserSelectedBoardsTxtFile = platform.getBoardsFile(); mySloeberBoardTxtFile = new BoardTxtFile(myUserSelectedBoardsTxtFile); + getJSonInfo(platform); if (mySloeberBoardTxtFile.getAllBoardIDs().contains(UNO)) { myBoardID = UNO; } else { myBoardID = mySloeberBoardTxtFile.getAllBoardIDs().get(0); } + setDefaultOptions(); } else { mySloeberBoardTxtFile = new BoardTxtFile(myUserSelectedBoardsTxtFile); myBoardID = myStorageNode.get(KEY_LAST_USED_BOARD, EMPTY); myUploadPort = myStorageNode.get(KEY_LAST_USED_UPLOAD_PORT, EMPTY); myProgrammer = myStorageNode.get(KEY_LAST_USED_UPLOAD_PROTOCOL, EMPTY); + myJsonFileName=myStorageNode.get(KEY_LAST_USED_JSON_FILENAME, EMPTY); + myJsonURL=myStorageNode.get(KEY_LAST_USED_JSON_URL, EMPTY); myOptions = KeyValue.makeMap(myStorageNode.get(KEY_LAST_USED_BOARD_MENU_OPTIONS, EMPTY)); } } - public BoardDescription(BoardDescription srcObject) { + private void getJSonInfo(IArduinoPlatformVersion platform) { + IArduinoPlatformPackageIndex packageIndex=platform.getParent().getParent().getPackageIndex(); + myJsonFileName=packageIndex.getJsonFile().getName(); + myJsonURL=packageIndex.getJsonURL(); + } + + + private void getJSonInfo() { + IArduinoPlatformVersion platform = BoardsManager.getNewestInstalledPlatform(getVendor(), getArchitecture()); + if (platform == null) { + platform = BoardsManager.getNewestInstalledPlatform(VENDOR_ARDUINO, AVR); + } + if (platform == null) { + List platforms = BoardsManager.getInstalledPlatforms(); + // If you crash on the next line no platform have been installed + platform = platforms.get(0); + } + if (platform != null) { + myUserSelectedBoardsTxtFile = platform.getBoardsFile(); + mySloeberBoardTxtFile = new BoardTxtFile(myUserSelectedBoardsTxtFile); + getJSonInfo(platform); + } + } + + public BoardDescription(BoardDescription srcObject) { myUserSelectedBoardsTxtFile = srcObject.myUserSelectedBoardsTxtFile; mySloeberBoardTxtFile = srcObject.mySloeberBoardTxtFile; + myJsonFileName=srcObject.myJsonFileName; + myJsonURL=srcObject.myJsonURL; myBoardID = srcObject.myBoardID; myUploadPort = srcObject.myUploadPort; myProgrammer = srcObject.myProgrammer; @@ -382,20 +422,64 @@ private void removeInvalidMenuIDs() { * project */ public void saveUserSelection() { - myStorageNode.put(KEY_LAST_USED_BOARDS_FILE, getReferencingBoardsFile().toString()); - myStorageNode.put(KEY_LAST_USED_BOARD, this.myBoardID); - myStorageNode.put(KEY_LAST_USED_UPLOAD_PORT, this.myUploadPort); - myStorageNode.put(KEY_LAST_USED_UPLOAD_PROTOCOL, this.myProgrammer); - myStorageNode.put(KEY_LAST_USED_BOARD_MENU_OPTIONS, KeyValue.makeString(this.myOptions)); + myStorageNode.put(KEY_LAST_USED_BOARDS_FILE, myUserSelectedBoardsTxtFile.toString()); + myStorageNode.put(KEY_LAST_USED_BOARD, myBoardID); + myStorageNode.put(KEY_LAST_USED_UPLOAD_PORT, myUploadPort); + myStorageNode.put(KEY_LAST_USED_UPLOAD_PROTOCOL, myProgrammer); + myStorageNode.put(KEY_LAST_USED_JSON_FILENAME, myJsonFileName); + myStorageNode.put(KEY_LAST_USED_JSON_URL, myJsonURL); + myStorageNode.put(KEY_LAST_USED_BOARD_MENU_OPTIONS, KeyValue.makeString(myOptions)); } - public String getArchitecture() { - return mySloeberBoardTxtFile.getArchitecture(); - } + /* + * Returns the architecture based on the myUserSelectedBoardsTxtFile file name + * Caters for the packages (with version number and for the old way if the boards + * file does not exists returns avr + */ + public String getArchitecture() { + if (myUserSelectedBoardsTxtFile == null) { + return AVR; + } + IPath platformFile = new Path(myUserSelectedBoardsTxtFile.toString().trim()); + int index=hardwareSegmentIndex(platformFile); + if(index<0){ + return AVR; + } + return platformFile.segment(index+3); + } - public String getVendor() { - return mySloeberBoardTxtFile.getVendor(); - } + /* + * Returns the vendor based on the myUserSelectedBoardsTxtFile file name + * Caters for the packages (with version number and for the old way if the boards + * file does not exists returns VENDOR_ARDUINO + */ + public String getVendor() { + if (myUserSelectedBoardsTxtFile == null) { + return VENDOR_ARDUINO; + } + IPath platformFile = new Path(myUserSelectedBoardsTxtFile.toString().trim()); + int index=hardwareSegmentIndex(platformFile); + if(index<1){ + return VENDOR_ARDUINO; + } + return platformFile.segment(index+1); + } + + /** + * return the segment that contains the name packages + * or -1 if no such segment is found + * @param platformFile + * @return + */ + private static int hardwareSegmentIndex(IPath platformFile) { + for(int i=0;i getOptions() { } private void updateWhenDirty() { - if (isDirty) { + if (myIsDirty) { calculateDerivedFields(); - isDirty = false; + myIsDirty = false; } } @@ -690,8 +774,8 @@ protected BoardDescription(File boardsFile, String boardID) { myBoardID = boardID; myUserSelectedBoardsTxtFile = boardsFile; mySloeberBoardTxtFile = new BoardTxtFile(myUserSelectedBoardsTxtFile); - setDefaultOptions(); calculateDerivedFields(); + getJSonInfo(); } BoardDescription(TxtFile configFile, String prefix) { @@ -705,6 +789,10 @@ protected BoardDescription(File boardsFile, String boardID) { KeyValueTree optionsTree = section.getChild(KEY_SLOEBER_MENU_SELECTION); Map options = optionsTree.toKeyValues(EMPTY); + KeyValueTree boardSection = tree.getChild(BOARD); + myJsonFileName = boardSection.getValue(JSON_NAME); + myJsonURL = boardSection.getValue(JSON_URL); + myUserSelectedBoardsTxtFile = resolvePathEnvironmentString(new File(board_txt)); mySloeberBoardTxtFile = new BoardTxtFile(myUserSelectedBoardsTxtFile); setDefaultOptions(); @@ -719,32 +807,52 @@ protected BoardDescription(File boardsFile, String boardID) { * * @param envVars */ - public BoardDescription(Map envVars) { - int menuKeyLength = KEY_SLOEBER_MENU_SELECTION.length() + DOT.length(); - for (Entry curEnvVar : envVars.entrySet()) { - String key = curEnvVar.getKey(); - String value = curEnvVar.getValue(); - switch (key) { - case KEY_SLOEBER_PROGRAMMER: - myProgrammer = value; - break; - case KEY_SLOEBER_BOARD_ID: - myBoardID = value; - break; - case KEY_SLOEBER_BOARD_TXT: - myUserSelectedBoardsTxtFile = resolvePathEnvironmentString(new File(value)); - mySloeberBoardTxtFile = new BoardTxtFile(myUserSelectedBoardsTxtFile); - break; - case KEY_SLOEBER_UPLOAD_PORT: - myUploadPort = value; - break; - default: - if (key.startsWith(KEY_SLOEBER_MENU_SELECTION + DOT)) { - String cleanKey = key.substring(menuKeyLength); - myOptions.put(cleanKey, value); - } - } + public BoardDescription(KeyValueTree keyValues) { + myProgrammer=keyValues.getValue(KEY_SLOEBER_PROGRAMMER); + myUploadPort=keyValues.getValue(KEY_SLOEBER_UPLOAD_PORT); + + KeyValueTree boardvalueTree=keyValues.getChild(KEY_BOARD); + myBoardID=boardvalueTree.getValue(KEY_SLOEBER_BOARD_ID); + String txtFile=boardvalueTree.getValue(KEY_SLOEBER_BOARD_TXT); + + KeyValueTree jSonvalueTree=keyValues.getChild(KEY_JSON); + myJsonFileName=jSonvalueTree.getValue(KEY_JSON_FILENAME); + myJsonURL=jSonvalueTree.getValue(KEY_JSON_URL); + + + myUserSelectedBoardsTxtFile = resolvePathEnvironmentString(new File(txtFile)); + mySloeberBoardTxtFile = new BoardTxtFile(myUserSelectedBoardsTxtFile); + KeyValueTree options=keyValues.getChild(KEY_SLOEBER_MENU_SELECTION); + for(KeyValueTree curOption: options.getChildren().values()) { + myOptions.put(curOption.getKey(), curOption.getValue()); } + + +// int menuKeyLength = KEY_SLOEBER_MENU_SELECTION.length() + DOT.length(); +// for (Entry curEnvVar : envVars.entrySet()) { +// String key = curEnvVar.getKey(); +// String value = curEnvVar.getValue(); +// switch (key) { +// case KEY_SLOEBER_PROGRAMMER: +// myProgrammer = value; +// break; +// case KEY_SLOEBER_BOARD_ID: +// myBoardID = value; +// break; +// case KEY_SLOEBER_BOARD_TXT: +// myUserSelectedBoardsTxtFile = resolvePathEnvironmentString(new File(value)); +// mySloeberBoardTxtFile = new BoardTxtFile(myUserSelectedBoardsTxtFile); +// break; +// case KEY_SLOEBER_UPLOAD_PORT: +// myUploadPort = value; +// break; +// default: +// if (key.startsWith(KEY_SLOEBER_MENU_SELECTION + DOT)) { +// String cleanKey = key.substring(menuKeyLength); +// myOptions.put(cleanKey, value); +// } +// } +// } } /** @@ -755,19 +863,37 @@ public BoardDescription(Map envVars) { * * @return the minimum list of environment variables to recreate the project */ - public Map getEnvVarsConfig() { - Map allVars = new TreeMap<>(); + public void serialize(KeyValueTree keyValueTree) { String board_txt = makePathVersionString(getReferencingBoardsFile()); + keyValueTree.addChild(KEY_SLOEBER_PROGRAMMER, myProgrammer); + keyValueTree.addChild(KEY_SLOEBER_UPLOAD_PORT, myUploadPort); - allVars.put(KEY_SLOEBER_PROGRAMMER, myProgrammer); - allVars.put(KEY_SLOEBER_BOARD_ID, myBoardID); - allVars.put(KEY_SLOEBER_BOARD_TXT, board_txt); - allVars.put(KEY_SLOEBER_UPLOAD_PORT, myUploadPort); + KeyValueTree boardvalueTree=keyValueTree.addChild(KEY_BOARD); + boardvalueTree.addChild(KEY_SLOEBER_BOARD_ID, myBoardID); + boardvalueTree.addChild(KEY_SLOEBER_BOARD_TXT, board_txt); + KeyValueTree jSonvalueTree=keyValueTree.addChild(KEY_JSON); + jSonvalueTree.addChild(KEY_JSON_FILENAME, myJsonFileName); + jSonvalueTree.addChild(KEY_JSON_URL, myJsonURL); + + + KeyValueTree menuvalueTree=keyValueTree.addChild(KEY_SLOEBER_MENU_SELECTION); for (Entry curOption : myOptions.entrySet()) { - allVars.put(KEY_SLOEBER_MENU_SELECTION + DOT + curOption.getKey(), curOption.getValue()); + menuvalueTree.addValue( curOption.getKey(), curOption.getValue()); } - return allVars; + +// allVars.put(KEY_SLOEBER_PROGRAMMER, myProgrammer); +// allVars.put(KEY_SLOEBER_BOARD_ID, myBoardID); +// allVars.put(KEY_SLOEBER_BOARD_TXT, board_txt); +// allVars.put(KEY_SLOEBER_UPLOAD_PORT, myUploadPort); +// +// allVars.put(KEY_JSON_FILENAME, myJsonFileName); +// allVars.put(KEY_JSON_URL, myJsonURL); +// +// for (Entry curOption : myOptions.entrySet()) { +// allVars.put(KEY_SLOEBER_MENU_SELECTION + DOT + curOption.getKey(), curOption.getValue()); +// } +// return allVars; } private Map onlyKeepValidOptions(Map options) { @@ -982,9 +1108,11 @@ private Map getEnVarPlatformInfo() throws IOException { * @return environment variables pointing to the tools used by the platform * @throws IOException */ - private static Map getEnvVarPlatformFileTools(IArduinoPlatformVersion platformVersion) throws IOException { + private Map getEnvVarPlatformFileTools(IArduinoPlatformVersion platformVersion) throws IOException { if(platformVersion==null) { - return new HashMap<>(); + Path path=new Path(myUserSelectedBoardsTxtFile.toString()); + File sloeberTxtFile= path.removeLastSegments(1).append(SLOEBER_TXT_FILE_NAME).toFile(); + return getEnvVarPlatformFileTools(sloeberTxtFile); } File sloeberTxtFile = platformVersion.getInstallPath().append(SLOEBER_TXT_FILE_NAME).toFile(); deleteIfOutdated (sloeberTxtFile); diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java index 3d5d559a..21dd2190 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java @@ -458,7 +458,7 @@ static private void loadJson(String url, boolean forceDownload) { } if (jsonFile.exists()) { if (jsonFile.getName().toLowerCase().startsWith("package_")) { //$NON-NLS-1$ - loadPackage(jsonFile); + loadPackage(url,jsonFile); } else if (jsonFile.getName().toLowerCase().startsWith("library_")) { //$NON-NLS-1$ LibraryManager.loadJson(jsonFile); } else { @@ -468,10 +468,11 @@ static private void loadJson(String url, boolean forceDownload) { } } - static private void loadPackage(File jsonFile) { + static private void loadPackage(String url, File jsonFile) { try (Reader reader = new FileReader(jsonFile)) { ArduinoPlatformPackageIndex index = new Gson().fromJson(reader, ArduinoPlatformPackageIndex.class); index.setPackageFile(jsonFile); + index.setURL(url); packageIndices.add(index); } catch (Exception e) { Activator.log(new Status(IStatus.ERROR, Activator.getId(), diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoPlatformPackageIndex.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoPlatformPackageIndex.java index 79332f1e..250c1726 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoPlatformPackageIndex.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoPlatformPackageIndex.java @@ -18,4 +18,6 @@ public interface IArduinoPlatformPackageIndex { String getName(); + String getJsonURL(); + } \ No newline at end of file diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformPackageIndex.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformPackageIndex.java index 01c22a5c..719d0f05 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformPackageIndex.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPlatformPackageIndex.java @@ -32,6 +32,8 @@ public class ArduinoPlatformPackageIndex extends Node private transient File myJsonFile; + private String myURL; + @Override public List getPackages() { return new LinkedList<>(myPackages); @@ -119,4 +121,14 @@ public String getName() { return getNodeName(); } + @Override + public String getJsonURL() { + return myURL; + } + + public void setURL(String url) { + myURL=url; + + } + } diff --git a/io.sloeber.core/src/io/sloeber/core/api/CompileDescription.java b/io.sloeber.core/src/io/sloeber/core/api/CompileDescription.java index 01d976c4..cd17a00d 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/CompileDescription.java +++ b/io.sloeber.core/src/io/sloeber/core/api/CompileDescription.java @@ -414,23 +414,11 @@ public boolean needsRebuild(CompileDescription otherOptions) { * * @return the minimum list of environment variables to recreate the project */ - public Map getEnvVarsConfig() { - Map ret = new HashMap<>(); - ret.put(SLOEBER_ADDITIONAL_COMPILE_OPTIONS, this.my_C_andCPP_CompileOptions); - ret.put(SLOEBER_ADDITIONAL_CPP_COMPILE_OPTIONS, this.my_CPP_CompileOptions); - ret.put(SLOEBER_ADDITIONAL_C_COMPILE_OPTIONS, this.my_C_CompileOptions); - ret.put(SLOEBER_ASSEMBLY_COMPILE_OPTIONS, this.my_Assembly_CompileOptions); - ret.put(SLOEBER_ARCHIVE_COMPILE_OPTIONS, this.my_Archive_CompileOptions); - ret.put(SLOEBER_LINK_COMPILE_OPTIONS, this.my_Link_CompileOptions); - ret.put(SLOEBER_ALL_COMPILE_OPTIONS, this.my_All_CompileOptions); - ret.put(SLOEBER_WARNING_LEVEL, myWarningLevel.name()); - ret.put(SLOEBER_WARNING_LEVEL_CUSTOM, myWarningLevel.myCustomWarningLevel); - ret.put(SLOEBER_DEBUG_LEVEL, myDebugLevel.name()); - ret.put(SLOEBER_DEBUG_LEVEL_CUSTOM, myDebugLevel.myCustomDebugLevel); - ret.put(SLOEBER_SIZE_TYPE, mySizeCommand.name()); - ret.put(SLOEBER_SIZE_CUSTOM, mySizeCommand.myCustomSizeCommand); - - return ret; + public void serialize(KeyValueTree keyValueTree) { + Map ret = getEnvVarsVersion(); + for(Entry curvalue:ret.entrySet()) { + keyValueTree.addChild(curvalue.getKey(), curvalue.getValue()); + } } /** @@ -439,60 +427,21 @@ public Map getEnvVarsConfig() { * * @param envVars */ - public CompileDescription(Map envVars) { - String warningLevel = WarningLevels.ALL.name(); - String customWarningLevel = EMPTY; - String debugLevel = DebugLevels.OPTIMIZED_FOR_RELEASE.name(); - String customDebugLevel = EMPTY; - String sizeCommand = SizeCommands.RAW_RESULT.toString(); - String customSizeCommand = EMPTY; - for (Entry curEnvVar : envVars.entrySet()) { - String key = curEnvVar.getKey(); - String value = curEnvVar.getValue(); - switch (key) { - case SLOEBER_ADDITIONAL_COMPILE_OPTIONS: - my_C_andCPP_CompileOptions = value; - break; - case SLOEBER_ADDITIONAL_CPP_COMPILE_OPTIONS: - my_CPP_CompileOptions = value; - break; - case SLOEBER_ADDITIONAL_C_COMPILE_OPTIONS: - my_C_CompileOptions = value; - break; - case SLOEBER_ASSEMBLY_COMPILE_OPTIONS: - my_Assembly_CompileOptions = value; - break; - case SLOEBER_ARCHIVE_COMPILE_OPTIONS: - my_Archive_CompileOptions = value; - break; - case SLOEBER_LINK_COMPILE_OPTIONS: - my_Link_CompileOptions = value; - break; - case SLOEBER_ALL_COMPILE_OPTIONS: - my_All_CompileOptions = value; - break; - case SLOEBER_WARNING_LEVEL: - warningLevel = value; - break; - case SLOEBER_WARNING_LEVEL_CUSTOM: - customWarningLevel = value; - break; - case SLOEBER_DEBUG_LEVEL: - debugLevel = value; - break; - case SLOEBER_DEBUG_LEVEL_CUSTOM: - customDebugLevel = value; - break; - case SLOEBER_SIZE_TYPE: - sizeCommand = value; - break; - case SLOEBER_SIZE_CUSTOM: - customSizeCommand = value; - break; - default: - break; - } - } + public CompileDescription(KeyValueTree keyValues) { + my_C_andCPP_CompileOptions=keyValues.getValue(SLOEBER_ADDITIONAL_COMPILE_OPTIONS); + my_CPP_CompileOptions=keyValues.getValue(SLOEBER_ADDITIONAL_CPP_COMPILE_OPTIONS); + my_C_CompileOptions=keyValues.getValue(SLOEBER_ADDITIONAL_C_COMPILE_OPTIONS); + my_Assembly_CompileOptions=keyValues.getValue(SLOEBER_ASSEMBLY_COMPILE_OPTIONS); + my_Archive_CompileOptions=keyValues.getValue(SLOEBER_ARCHIVE_COMPILE_OPTIONS); + my_Link_CompileOptions=keyValues.getValue(SLOEBER_LINK_COMPILE_OPTIONS); + my_All_CompileOptions=keyValues.getValue(SLOEBER_ALL_COMPILE_OPTIONS); + String warningLevel=keyValues.getValue(SLOEBER_WARNING_LEVEL); + String customWarningLevel=keyValues.getValue(SLOEBER_WARNING_LEVEL_CUSTOM); + String debugLevel=keyValues.getValue(SLOEBER_DEBUG_LEVEL); + String customDebugLevel=keyValues.getValue(SLOEBER_DEBUG_LEVEL_CUSTOM); + String sizeCommand=keyValues.getValue(SLOEBER_SIZE_TYPE); + String customSizeCommand=keyValues.getValue(SLOEBER_SIZE_CUSTOM); + try { myWarningLevel = WarningLevels.valueOf(warningLevel); myWarningLevel.setCustomWarningLevel(customWarningLevel, true); @@ -508,7 +457,22 @@ public CompileDescription(Map envVars) { } public Map getEnvVarsVersion() { - return getEnvVarsConfig(); + Map ret = new HashMap<>(); + ret.put(SLOEBER_ADDITIONAL_COMPILE_OPTIONS, this.my_C_andCPP_CompileOptions); + ret.put(SLOEBER_ADDITIONAL_CPP_COMPILE_OPTIONS, this.my_CPP_CompileOptions); + ret.put(SLOEBER_ADDITIONAL_C_COMPILE_OPTIONS, this.my_C_CompileOptions); + ret.put(SLOEBER_ASSEMBLY_COMPILE_OPTIONS, this.my_Assembly_CompileOptions); + ret.put(SLOEBER_ARCHIVE_COMPILE_OPTIONS, this.my_Archive_CompileOptions); + ret.put(SLOEBER_LINK_COMPILE_OPTIONS, this.my_Link_CompileOptions); + ret.put(SLOEBER_ALL_COMPILE_OPTIONS, this.my_All_CompileOptions); + ret.put(SLOEBER_WARNING_LEVEL, myWarningLevel.name()); + ret.put(SLOEBER_WARNING_LEVEL_CUSTOM, myWarningLevel.myCustomWarningLevel); + ret.put(SLOEBER_DEBUG_LEVEL, myDebugLevel.name()); + ret.put(SLOEBER_DEBUG_LEVEL_CUSTOM, myDebugLevel.myCustomDebugLevel); + ret.put(SLOEBER_SIZE_TYPE, mySizeCommand.name()); + ret.put(SLOEBER_SIZE_CUSTOM, mySizeCommand.myCustomSizeCommand); + + return ret; } public CompileDescription(TxtFile configFile, String prefix) { diff --git a/io.sloeber.core/src/io/sloeber/core/api/ConfigurationPreferences.java b/io.sloeber.core/src/io/sloeber/core/api/ConfigurationPreferences.java index dd2264b3..219decdc 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/ConfigurationPreferences.java +++ b/io.sloeber.core/src/io/sloeber/core/api/ConfigurationPreferences.java @@ -28,8 +28,6 @@ public class ConfigurationPreferences { private static final String KEY_UPDATE_JASONS = "Update jsons files"; //$NON-NLS-1$ - // preference nodes - private static final String PACKAGES_FOLDER_NAME = "packages"; //$NON-NLS-1$ public static void removeKey(String key) { IEclipsePreferences myScope = ConfigurationScope.INSTANCE.getNode(NODE_ARDUINO); diff --git a/io.sloeber.core/src/io/sloeber/core/api/Const.java b/io.sloeber.core/src/io/sloeber/core/api/Const.java index 7cd6b148..bf514e7e 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Const.java +++ b/io.sloeber.core/src/io/sloeber/core/api/Const.java @@ -46,6 +46,8 @@ public class Const extends AutoBuildConstants { public static final String PRIVATE = "private"; public static final String MANAGED = "Managed"; public static final String BOARD = "Board"; + public static final String JSON_NAME = "json"; + public static final String JSON_URL = "json_url"; public static final String NETWORK = "network"; @@ -64,6 +66,7 @@ public class Const extends AutoBuildConstants { public static final String PROTOCOL = "protocol"; public static final String VENDOR = "VENDOR"; public static final String VENDOR_ARDUINO = ARDUINO; + public static final String PACKAGES_FOLDER_NAME = "packages"; // arduino txt pre and suffix public static final String NETWORK_PREFIX = "network_"; @@ -160,6 +163,9 @@ public class Const extends AutoBuildConstants { public static final String KEY_LAST_USED_BOARD = "Last used Board"; //$NON-NLS-1$ public static final String KEY_LAST_USED_UPLOAD_PORT = "Last Used Upload port"; //$NON-NLS-1$ public static final String KEY_LAST_USED_UPLOAD_PROTOCOL = "last Used upload Protocol"; //$NON-NLS-1$ + public static final String KEY_LAST_USED_JSON_FILENAME = "last Used json filename"; //$NON-NLS-1$ + public static final String KEY_LAST_USED_JSON_URL = "last Used json URL"; //$NON-NLS-1$ + public static final String KEY_LAST_USED_BOARDS_FILE = "Last used Boards file"; //$NON-NLS-1$ public static final String KEY_LAST_USED_BOARD_MENU_OPTIONS = "last used Board custom option selections"; //$NON-NLS-1$ public static final String ENV_KEY_SERIAL_PORT = "serial_port"; //$NON-NLS-1$ @@ -183,10 +189,16 @@ public class Const extends AutoBuildConstants { // stuff to store last used board public static final String KEY_SLOEBER_PROGRAMMER = "PROGRAMMER.NAME"; //$NON-NLS-1$ - public static final String KEY_SLOEBER_BOARD_TXT = "BOARD.TXT"; //$NON-NLS-1$ - public static final String KEY_SLOEBER_BOARD_ID = "BOARD.ID"; //$NON-NLS-1$ public static final String KEY_SLOEBER_UPLOAD_PORT = "UPLOAD.PORT"; //$NON-NLS-1$ - public static final String KEY_SLOEBER_MENU_SELECTION = "BOARD.MENU"; //$NON-NLS-1$ + public static final String KEY_BOARD = "BOARD"; //$NON-NLS-1$ + + public static final String KEY_SLOEBER_BOARD_TXT = "TXT"; //$NON-NLS-1$ + public static final String KEY_SLOEBER_BOARD_ID = "ID"; //$NON-NLS-1$ + public static final String KEY_SLOEBER_MENU_SELECTION = "MENU"; //$NON-NLS-1$ + + public static final String KEY_JSON="JSON";//$NON-NLS-1$ + public static final String KEY_JSON_FILENAME="FILE";//$NON-NLS-1$ + public static final String KEY_JSON_URL="URL";//$NON-NLS-1$ public static final String RELEASE = "Release"; diff --git a/io.sloeber.core/src/io/sloeber/core/api/OtherDescription.java b/io.sloeber.core/src/io/sloeber/core/api/OtherDescription.java index acdbf4e4..69ab7ed4 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/OtherDescription.java +++ b/io.sloeber.core/src/io/sloeber/core/api/OtherDescription.java @@ -2,7 +2,6 @@ import java.util.Map; import java.util.TreeMap; -import java.util.Map.Entry; import io.sloeber.autoBuild.helpers.api.KeyValueTree; import io.sloeber.core.txt.TxtFile; @@ -34,12 +33,8 @@ public Map getEnvVars() { return allVars; } - public Map getEnvVarsConfig() { - Map allVars = new TreeMap<>(); - - allVars.put(KEY_SLOEBER_IS_VERSION_CONTROLLED, Boolean.valueOf(myIsVersionControlled).toString()); - - return allVars; + public void serialize(KeyValueTree keyValueTree) { + keyValueTree.addChild(KEY_SLOEBER_IS_VERSION_CONTROLLED, Boolean.valueOf(myIsVersionControlled).toString()); } /** @@ -47,22 +42,14 @@ public Map getEnvVarsConfig() { * * @param envVars */ - public OtherDescription(Map envVars) { - for (Entry curEnvVar : envVars.entrySet()) { - String key = curEnvVar.getKey(); - String value = curEnvVar.getValue(); - switch (key) { - case KEY_SLOEBER_IS_VERSION_CONTROLLED: - myIsVersionControlled = Boolean.parseBoolean(value); - break; - default: - break; - } - } + public OtherDescription(KeyValueTree keyValues) { + myIsVersionControlled = Boolean.parseBoolean(keyValues.getValue(KEY_SLOEBER_IS_VERSION_CONTROLLED)); } public Map getEnvVarsVersion() { - return getEnvVarsConfig(); + Map allVars = new TreeMap<>(); + allVars.put(KEY_SLOEBER_IS_VERSION_CONTROLLED, Boolean.valueOf(myIsVersionControlled).toString()); + return allVars; } public boolean IsVersionControlled() { diff --git a/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java b/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java index 4b7541b9..76db4f16 100644 --- a/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java +++ b/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java @@ -143,22 +143,18 @@ public void copyData(AutoBuildConfigurationExtensionDescription from) { } @Override - public void serialize(KeyValueTree keyValuePairs) { - Map envVars = myBoardDescription.getEnvVarsConfig(); - envVars.putAll(myOtherDesc.getEnvVarsConfig()); - envVars.putAll(myCompileDescription.getEnvVarsConfig()); - for (Entry curEnvVar : envVars.entrySet()) { - keyValuePairs.addValue( curEnvVar.getKey() , curEnvVar.getValue() ); - } + public void serialize(KeyValueTree serialize) { + myBoardDescription.serialize(serialize); + myOtherDesc.serialize(serialize); + myCompileDescription.serialize(serialize); configureWhenDirty(); } public SloeberConfiguration(IAutoBuildConfigurationDescription autoCfgDescription, KeyValueTree keyValues) { setAutoBuildDescription(autoCfgDescription); - Map envVars =keyValues.toKeyValues(false); - myBoardDescription = new BoardDescription(envVars); - myOtherDesc = new OtherDescription(envVars); - myCompileDescription = new CompileDescription(envVars); + myBoardDescription = new BoardDescription(keyValues); + myOtherDesc = new OtherDescription(keyValues); + myCompileDescription = new CompileDescription(keyValues); myMemoryIsDirty = true; // configure(); Seems I can not do the config here } From e4dc0064987fa01b6cd9be8280e6625ead22fcd2 Mon Sep 17 00:00:00 2001 From: jan Date: Mon, 2 Sep 2024 02:30:19 +0200 Subject: [PATCH 020/115] Put the arduino size command in a bat file and run it this way #1671 --- .../sloeber/core/api/CompileDescription.java | 2 +- .../core/builder/SloeberBuilderExtension.java | 42 ++++++++++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/core/api/CompileDescription.java b/io.sloeber.core/src/io/sloeber/core/api/CompileDescription.java index cd17a00d..6b033e5b 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/CompileDescription.java +++ b/io.sloeber.core/src/io/sloeber/core/api/CompileDescription.java @@ -222,7 +222,7 @@ public String getEnvValue() { switch (this) { case ARDUINO_WAY: if(isWindows) { - return "wsl -- ${sloeber.size_command.awk}"; //$NON-NLS-1$ + return "arduino-size.bat"; //$NON-NLS-1$ } return "${sloeber.size_command.awk}"; //$NON-NLS-1$ case AVR_ALTERNATIVE: diff --git a/io.sloeber.core/src/io/sloeber/core/builder/SloeberBuilderExtension.java b/io.sloeber.core/src/io/sloeber/core/builder/SloeberBuilderExtension.java index 67710292..2b39e3a5 100644 --- a/io.sloeber.core/src/io/sloeber/core/builder/SloeberBuilderExtension.java +++ b/io.sloeber.core/src/io/sloeber/core/builder/SloeberBuilderExtension.java @@ -69,9 +69,14 @@ public void beforeAddingSourceRules(IAutoBuildMakeRules makeRules, makeRules.getSourceFilesToBuild().add(sloeberInoCppFile); } generateAwkFile(autoBuildConfData); + generateArduinoSizeCommandFile(autoBuildConfData); + super.beforeAddingSourceRules(makeRules, autoBuildConfData); } + + + @Override public boolean invokeBuild(IBuilder builder, int kind, String targetName, IAutoBuildConfigurationDescription autoData, IMarkerGenerator markerGenerator, IConsole console, IProgressMonitor monitor) throws CoreException { @@ -122,6 +127,33 @@ public SloeberBuilderExtension() { // Nothing to do here } + + @SuppressWarnings("nls") + private static void generateArduinoSizeCommandFile(IAutoBuildConfigurationDescription autoBuildConfData) { + if(!isWindows) { + return; + } + IFile sizeCommandIFile = autoBuildConfData.getBuildFolder().getFile("arduino-size.bat"); + SloeberConfiguration confDesc = SloeberConfiguration.getFromAutoBuildConfDesc(autoBuildConfData); + + File sizeCommandFile = sizeCommandIFile.getLocation().toFile(); + String content = Common.getBuildEnvironmentVariable(confDesc, "sloeber.size_command.awk", EMPTY); + + try { + if (sizeCommandFile.exists()) { + String curContent = FileUtils.readFileToString(sizeCommandFile, Charset.defaultCharset()); + if (!curContent.equals(content)) { + sizeCommandFile.delete(); + } + } + if (!sizeCommandFile.exists()) { + FileUtils.write(sizeCommandFile, content, Charset.defaultCharset()); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + @SuppressWarnings("nls") private static void generateAwkFile(IAutoBuildConfigurationDescription autoBuildConfData) { IFile sizeAwkFile1 = autoBuildConfData.getBuildFolder().getFile("size.awk"); @@ -144,7 +176,15 @@ private static void generateAwkFile(IAutoBuildConfigurationDescription autoBuild awkContent += "\"}"; try { - FileUtils.write(sizeAwkFile, awkContent, Charset.defaultCharset()); + if (sizeAwkFile.exists()) { + String curContent = FileUtils.readFileToString(sizeAwkFile, Charset.defaultCharset()); + if (!curContent.equals(awkContent)) { + sizeAwkFile.delete(); + } + } + if (!sizeAwkFile.exists()) { + FileUtils.write(sizeAwkFile, awkContent, Charset.defaultCharset()); + } } catch (IOException e) { e.printStackTrace(); } From 11336cdef28a33ac9ff45971911230c9220241d6 Mon Sep 17 00:00:00 2001 From: jan Date: Mon, 2 Sep 2024 02:30:42 +0200 Subject: [PATCH 021/115] add a comment --- io.sloeber.core/src/io/sloeber/core/Activator.java | 1 + 1 file changed, 1 insertion(+) diff --git a/io.sloeber.core/src/io/sloeber/core/Activator.java b/io.sloeber.core/src/io/sloeber/core/Activator.java index 2949051f..d90edb43 100644 --- a/io.sloeber.core/src/io/sloeber/core/Activator.java +++ b/io.sloeber.core/src/io/sloeber/core/Activator.java @@ -404,6 +404,7 @@ private static void installOtherStuff() { if (localMakePath.append(MAKE_EXE).toFile().exists()) { if (!localMakePath.append(SH_EXE).toFile().exists()) { try { + //Sloeber needs the make that also contains sh.exe deleteDirectory(localMakePath); } catch (IOException e) { // should not happen From b5db0cdf1d616cdd014003e89a42d174b1524974 Mon Sep 17 00:00:00 2001 From: jan Date: Mon, 2 Sep 2024 02:31:07 +0200 Subject: [PATCH 022/115] use toString instead of toOSString for paths --- .../arduinoFramework/api/BoardDescription.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java index 29a1e13e..d3a56185 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java @@ -46,7 +46,7 @@ import io.sloeber.core.txt.TxtFile; public class BoardDescription { - private static final String FIRST_SLOEBER_LINE = "#Sloeber created file please do not modify V1.00.test 05 "; //$NON-NLS-1$ + private static final String FIRST_SLOEBER_LINE = "#Sloeber created file please do not modify V1.00.test 06 "; //$NON-NLS-1$ private static final IEclipsePreferences myStorageNode = InstanceScope.INSTANCE.getNode(NODE_ARDUINO); /* @@ -966,9 +966,9 @@ public Map getEnvVars() { String architecture = getArchitecture(); IPath coreHardwarePath = getreferencedCoreHardwarePath(); allVars.put(ENV_KEY_BUILD_ARCH, architecture.toUpperCase()); - allVars.put(ENV_KEY_RUNTIME_HARDWARE_PATH, getreferencingPlatformPath().removeLastSegments(1).toOSString()); - allVars.put(ENV_KEY_BUILD_SYSTEM_PATH, coreHardwarePath.append(SYSTEM).toOSString()); - allVars.put(ENV_KEY_RUNTIME_PLATFORM_PATH, getreferencingPlatformPath().toOSString()); + allVars.put(ENV_KEY_RUNTIME_HARDWARE_PATH, getreferencingPlatformPath().removeLastSegments(1).toString()); + allVars.put(ENV_KEY_BUILD_SYSTEM_PATH, coreHardwarePath.append(SYSTEM).toString()); + allVars.put(ENV_KEY_RUNTIME_PLATFORM_PATH, getreferencingPlatformPath().toString()); //ide_version is defined in pre_processing_platform_default.txt allVars.put(ENV_KEY_RUNTIME_IDE_VERSION, makeEnvironmentVar("ide_version")); //$NON-NLS-1$ allVars.put(ENV_KEY_RUNTIME_IDE_PATH, makeEnvironmentVar(SLOEBER_HOME)); @@ -993,7 +993,7 @@ public Map getEnvVars() { allVars.put(ENV_KEY_BUILD_ACTUAL_CORE_PATH, getActualCoreCodePath().toString()); IPath variantPath = getActualVariantPath(); if (variantPath != null) { - allVars.put(ENV_KEY_BUILD_VARIANT_PATH, variantPath.toOSString()); + allVars.put(ENV_KEY_BUILD_VARIANT_PATH, variantPath.toString()); } else {// teensy does not use variant allVars.put(ENV_KEY_BUILD_VARIANT_PATH, EMPTY); } @@ -1123,7 +1123,7 @@ private Map getEnvVarPlatformFileTools(IArduinoPlatformVersion p for (ArduinoPlatformTooldDependency tool : platformVersion.getToolsDependencies()) { IPath installPath = tool.getInstallPath(); if (installPath.toFile().exists()) { - String value = installPath.toOSString(); + String value = installPath.toString(); String keyString = ENV_KEY_RUNTIME_TOOLS + tool.getName() + tool.getVersion() + DOT_PATH; vars=vars+NEWLINE+keyString+EQUAL+ value; keyString = ENV_KEY_RUNTIME_TOOLS + tool.getName() + '-' + tool.getVersion() + DOT_PATH; From a4871da8f74c4e732a77446a4c6394f51bb79754 Mon Sep 17 00:00:00 2001 From: jan Date: Wed, 4 Sep 2024 02:09:40 +0200 Subject: [PATCH 023/115] First iteration of "convert to sloeber project" --- .../AutoBuildProjectGenerator.java | 14 +- .../arduinoFramework/api/BoardsManager.java | 2 - .../io/sloeber/core/api/SloeberProject.java | 813 ++++++------------ .../core/internal/SloeberConfiguration.java | 23 +- .../toolchain/ArduinoLanguageProvider.java | 6 + .../sloeber/ui/wizard/ConvertToSloeber.java | 3 +- 6 files changed, 302 insertions(+), 559 deletions(-) diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildProjectGenerator.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildProjectGenerator.java index 7ce673e2..6767a9a8 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildProjectGenerator.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildProjectGenerator.java @@ -29,8 +29,6 @@ import org.eclipse.core.runtime.Status; import org.eclipse.tools.templates.core.IGenerator; -import io.sloeber.autoBuild.api.AutoBuildCommon; -import io.sloeber.autoBuild.api.IAutoBuildConfigurationDescription; import io.sloeber.autoBuild.api.ICodeProvider; import io.sloeber.autoBuild.buildTools.api.IBuildTools; import io.sloeber.autoBuild.core.Activator; @@ -68,6 +66,9 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { description.setLocationURI(myLocationURI); } myProject = root.getProject(myProjectName); + if(myProject.exists()) { + myProject.delete(false, false, monitor); + } myProject.create(description, monitor); myProject.open(monitor); CProjectNature.addCNature(myProject, monitor); @@ -128,10 +129,11 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { des.setCdtProjectCreated(); } mngr.setProjectDescription(myProject, des); - for(ICConfigurationDescription curConfig:des.getConfigurations()) { - IAutoBuildConfigurationDescription autodesc=IAutoBuildConfigurationDescription.getConfig(curConfig); - AutoBuildCommon.createFolder( autodesc.getBuildFolder()); - } + +// for(ICConfigurationDescription curConfig:des.getConfigurations()) { +// IAutoBuildConfigurationDescription autodesc=IAutoBuildConfigurationDescription.getConfig(curConfig); +// AutoBuildCommon.createFolder( autodesc.getBuildFolder()); +// } } }; try { diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java index 21dd2190..d9573cdb 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java @@ -46,7 +46,6 @@ import io.sloeber.core.Messages; import io.sloeber.core.api.ConfigurationPreferences; import io.sloeber.core.api.Defaults; -import io.sloeber.core.api.SloeberProject; import io.sloeber.core.api.VersionNumber; import io.sloeber.core.common.InstancePreferences; import io.sloeber.core.managers.InstallProgress; @@ -378,7 +377,6 @@ public static IStatus updatePlatforms(List platformsToI // do nothing } myIsReady = true; - SloeberProject.reloadTxtFile(); return status; } diff --git a/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java b/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java index 04df0bde..01a8123d 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java +++ b/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java @@ -2,7 +2,12 @@ import static io.sloeber.core.api.Const.*; +import java.io.File; import java.net.URI; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; import java.util.Set; import org.eclipse.cdt.core.CCProjectNature; import org.eclipse.cdt.core.CCorePlugin; @@ -11,7 +16,10 @@ import org.eclipse.cdt.core.settings.model.ICProjectDescription; import org.eclipse.cdt.core.settings.model.ICSettingEntry; import org.eclipse.cdt.core.settings.model.ICSourceEntry; +import org.eclipse.cdt.core.settings.model.util.CDataUtil; +import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IProjectDescription; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRoot; @@ -30,137 +38,243 @@ import io.sloeber.autoBuild.api.IAutoBuildConfigurationDescription; import io.sloeber.autoBuild.buildTools.api.IBuildTools; import io.sloeber.autoBuild.buildTools.api.IBuildToolsManager; +import io.sloeber.autoBuild.helpers.api.KeyValueTree; import io.sloeber.autoBuild.integration.AutoBuildConfigurationDescription; import io.sloeber.autoBuild.integration.AutoBuildManager; import io.sloeber.autoBuild.schema.api.IProjectType; import io.sloeber.core.Activator; import io.sloeber.core.internal.SloeberConfiguration; import io.sloeber.core.natures.SloeberNature; +import io.sloeber.core.txt.TxtFile; public class SloeberProject extends Common { public static String LATEST_EXTENSION_POINT_ID = "io.sloeber.autoBuild.buildDefinitions"; //$NON-NLS-1$ public static String LATEST_EXTENSION_ID = "io.sloeber.builddef"; //$NON-NLS-1$ public static String PROJECT_ID = "io.sloeber.core.sketch"; //$NON-NLS-1$ private static String SLOEBER_BUILD_TOOL_PROVIDER_ID = "io.sloeber.core.arduino.ToolProvider"; //$NON-NLS-1$ - private static String SOURCE_ENTRY_FILTER_ALL="/**"; //$NON-NLS-1$ + private static String SOURCE_ENTRY_FILTER_ALL = "/**"; //$NON-NLS-1$ - /** - * convenient method to create project - * - * @param proj1Name - * @param object - * @param proj1BoardDesc - * @param codeDesc - * @param proj1CompileDesc - * @param otherDesc - * @param nullProgressMonitor - * @return - */ public static void convertToArduinoProject(IProject project, IProgressMonitor monitor) { - // if (project == null) { - // return; - // } - // final IWorkspace workspace = ResourcesPlugin.getWorkspace(); - // IWorkspaceRoot root = workspace.getRoot(); - // ICoreRunnable runnable = new ICoreRunnable() { - // @Override - // public void run(IProgressMonitor internalMonitor) throws CoreException { - // IndexerController.doNotIndex(project); - // - // try { - // - // // create a sloeber project - // SloeberProject sloeberProject = new SloeberProject(project); - // CCorePlugin cCorePlugin = CCorePlugin.getDefault(); - // ICProjectDescription prjCDesc = cCorePlugin.getProjectDescription(project, - // true); - // upgradeArduinoProject(sloeberProject, prjCDesc); - // if (!sloeberProject.readConfigFromFiles()) { - // sloeberProject.setBoardDescription(RELEASE, new BoardDescription(), false); - // sloeberProject.setCompileDescription(RELEASE, new CompileDescription()); - // sloeberProject.setOtherDescription(RELEASE, new OtherDescription()); - // // we failed to read from disk so we set up some values - // // faking the stuff is in memory - // - // } - // String configName = - // sloeberProject.myBoardDescriptions.keySet().iterator().next(); - // BoardDescription boardDescriptor = - // sloeberProject.getBoardDescription(configName, true); - // - // // Add the arduino code folders - // List addToIncludePath = Helpers.addArduinoCodeToProject(project, - // boardDescriptor); - // - // // make the eclipse project a cdt project - // CCorePlugin.getDefault().createCProject(null, project, new - // NullProgressMonitor(), - // ManagedBuilderCorePlugin.MANAGED_MAKE_PROJECT_ID); - // - // // add the required natures - // AutoBuildNature.addNature(project, internalMonitor); - // - // // make the cdt project a managed build project - // IProjectType sloeberProjType = - // AutoBuildManager.getProjectType("io.sloeber.core.sketch"); //$NON-NLS-1$ - // ManagedBuildManager.createBuildInfo(project); - // IManagedProject newProject = - // ManagedBuildManager.createManagedProject(project, sloeberProjType); - // ManagedBuildManager.setNewProjectVersion(project); - // // Copy over the Sloeber configs - // IConfiguration defaultConfig = null; - // IConfiguration[] configs = sloeberProjType.getConfigurations(); - // for (int i = 0; i < configs.length; ++i) { - // IConfiguration curConfig = newProject.createConfiguration(configs[i], - // sloeberProjType.getId() + "." + i); //$NON-NLS-1$ - // curConfig.setArtifactName(newProject.getDefaultArtifactName()); - // // Make the first configuration the default - // if (i == 0) { - // defaultConfig = curConfig; - // } - // } - // - // ManagedBuildManager.setDefaultConfiguration(project, defaultConfig); - // - // ICConfigurationDescription activeConfig = prjCDesc.getActiveConfiguration(); - // - // for (String curConfigName : sloeberProject.myBoardDescriptions.keySet()) { - // ICConfigurationDescription curConfigDesc = - // prjCDesc.getConfigurationByName(curConfigName); - // if (curConfigDesc == null) { - // String id = CDataUtil.genId(null); - // curConfigDesc = prjCDesc.createConfiguration(id, curConfigName, - // activeConfig); - // } - // Helpers.addIncludeFolder(curConfigDesc, addToIncludePath, true); - // - // String curConfigKey = getConfigKey(curConfigDesc); - // sloeberProject.myEnvironmentVariables.put(curConfigKey, - // sloeberProject.getEnvVars(curConfigKey)); - // - // } - // sloeberProject.createSloeberConfigFiles(); - // SubMonitor refreshMonitor = SubMonitor.convert(internalMonitor, 3); - // project.refreshLocal(IResource.DEPTH_INFINITE, refreshMonitor); - // cCorePlugin.setProjectDescription(project, prjCDesc, true, null); - // - // } catch (Exception e) { - // Activator.log(new Status(IStatus.INFO, io.sloeber.core.Activator.getId(), - // "Project conversion failed: ", e)); //$NON-NLS-1$ - // } - // - // IndexerController.index(project); - // } - // - // }; - // - // try { - // workspace.run(runnable, root, IWorkspace.AVOID_UPDATE, monitor); - // } catch (Exception e) { - // Activator.log(new Status(IStatus.INFO, io.sloeber.core.Activator.getId(), - // "Project conversion failed: ", e)); //$NON-NLS-1$ - // } + if (project == null) { + Activator.log(new Status(IStatus.ERROR, Activator.getId(), + "The provided project is null. Sloeber can not upgrade.")); + return; + } + final IWorkspace workspace = ResourcesPlugin.getWorkspace(); + IWorkspaceRoot root = workspace.getRoot(); + ICoreRunnable runnable = new ICoreRunnable() { + @Override + public void run(IProgressMonitor internalMonitor) throws CoreException { + //remove managed build nature if it exists + String managedBuildNature="org.eclipse.cdt.managedbuilder.core.managedBuildNature"; //$NON-NLS-1$ + String ScannerConfigNature="org.eclipse.cdt.managedbuilder.core.ScannerConfigNature"; //$NON-NLS-1$ + if(project.hasNature(managedBuildNature) || project.hasNature(ScannerConfigNature)) { + + IProjectDescription description = project.getDescription(); + Set curNatures = new HashSet<>(Arrays.asList(description.getNatureIds())); + curNatures.remove(managedBuildNature); + curNatures.remove(ScannerConfigNature); + description.setNatureIds(curNatures.toArray(new String[curNatures.size()])); + project.setDescription(description, monitor); + } + IFolder OldCoreFolder =project.getFolder("core"); //$NON-NLS-1$ + if(OldCoreFolder.exists()) { + OldCoreFolder.delete(true, monitor); + } + + File sloeberDotFile = project.getFile(".sproject").getLocation().toFile(); //$NON-NLS-1$ + File sloeberCfgFile = project.getFile(SLOEBER_CFG).getLocation().toFile(); + TxtFile oldSloeberInfo = null; + if (sloeberDotFile.exists() && sloeberCfgFile.exists()) { + oldSloeberInfo = new TxtFile(sloeberDotFile); + oldSloeberInfo.mergeFile(sloeberCfgFile); + } else { + if (sloeberDotFile.exists()) { + oldSloeberInfo = new TxtFile(sloeberDotFile); + } + if (sloeberCfgFile.exists()) { + oldSloeberInfo = new TxtFile(sloeberCfgFile); + } + } + + String builderName = AutoBuildProject.INTERNAL_BUILDER_ID; + IBuildTools buildTools = IBuildToolsManager.getDefault().getBuildTools(SLOEBER_BUILD_TOOL_PROVIDER_ID, + null); + IProjectType projectType = AutoBuildManager.getProjectType(LATEST_EXTENSION_POINT_ID, + LATEST_EXTENSION_ID, PROJECT_ID, true); + CodeDescription codeProvider = CodeDescription.createNone(); + AutoBuildProject.createProject(project.getName(), null, projectType, builderName, + CCProjectNature.CC_NATURE_ID, codeProvider, buildTools, true, internalMonitor); + + SloeberNature.addNature(project, internalMonitor); + + CCorePlugin cCorePlugin = CCorePlugin.getDefault(); + ICProjectDescription prjCDesc = cCorePlugin.getProjectDescription(project, true); + + Set cfgNames=new HashSet<>(); + for (ICConfigurationDescription curConfig : prjCDesc.getConfigurations()) { + cfgNames.add( curConfig.getName()); + } + KeyValueTree oldConfigs=null; + if (oldSloeberInfo != null) { + oldConfigs = oldSloeberInfo.getData().getChild("Config"); //$NON-NLS-1$ + for (String curTree : oldConfigs.getChildren().keySet()) { + cfgNames.add(curTree); + } + } + for(String cfgName:cfgNames) { + KeyValueTree oldConfig=null; + if (oldConfigs != null) { + oldConfig = oldConfigs.getChild(cfgName); + } + //get the CDT config and AutoBuildConfigurationDescription (if it does not exist create it) + ICConfigurationDescription curConfig = prjCDesc.getConfigurationByName(cfgName); + if(curConfig==null) { + //config does not exists so create it + String id = CDataUtil.genId(Activator.getId()); + curConfig = prjCDesc.createConfiguration(id,cfgName,prjCDesc.getActiveConfiguration()); + } + IAutoBuildConfigurationDescription autoConf = IAutoBuildConfigurationDescription + .getConfig(curConfig); + if (!(autoConf instanceof AutoBuildConfigurationDescription)) { + // this should not happen as we just created a autoBuild project + Activator.log(new Status(SLOEBER_STATUS_DEBUG, Activator.getId(), + "\"Auto build created a project that does not seem to be a autobuild project :-s : " //$NON-NLS-1$ + + project.getName())); + continue; + } + + + + + ICSourceEntry newSourceEntries[] = new ICSourceEntry[2]; + // old Sloeber project so the code is in the root of the project for sure + // as we are at the root + // exclude bin folder and arduino folder + IPath excludes[] = new IPath[2]; + excludes[0] = autoConf.getBuildFolder().getProjectRelativePath().removeLastSegments(1) + .append(SOURCE_ENTRY_FILTER_ALL); + excludes[1] = project.getFolder(SLOEBER_ARDUINO_FOLDER_NAME).getProjectRelativePath() + .append(SOURCE_ENTRY_FILTER_ALL); + newSourceEntries[0] = new CSourceEntry(project.getFullPath(), excludes, ICSettingEntry.RESOLVED); + IPath excludes2[] = new IPath[8]; + excludes2[0] = IPath.fromOSString("**/*.ino"); //$NON-NLS-1$ + excludes2[1] = IPath.fromOSString("libraries/?*/**/doc*/**"); //$NON-NLS-1$ + excludes2[2] = IPath.fromOSString("libraries/?*/**/?xamples/**"); //$NON-NLS-1$ + excludes2[3] = IPath.fromOSString("libraries/?*/**/?xtras/**"); //$NON-NLS-1$ + excludes2[4] = IPath.fromOSString("libraries/?*/**/test*/**"); //$NON-NLS-1$ + excludes2[5] = IPath.fromOSString("libraries/?*/**/third-party/**"); //$NON-NLS-1$ + excludes2[6] = IPath.fromOSString("libraries/**/._*"); //$NON-NLS-1$ + excludes2[7] = IPath.fromOSString("libraries/?*/utility/*/*"); //$NON-NLS-1$ + + /* + * CDT currently causes issues with ${ConfigName] + * https://github.com/eclipse-cdt/cdt/issues/870 IPath arduinoRoot = + * newProjectHandle.getFolder(SLOEBER_ARDUINO_FOLDER_NAME).getFullPath().append( + * CONFIG_NAME_VARIABLE); + */ + IPath arduinoRoot = project.getFolder(SLOEBER_ARDUINO_FOLDER_NAME).getFullPath() + .append(curConfig.getName()); + newSourceEntries[1] = new CSourceEntry(arduinoRoot, excludes2, ICSettingEntry.NONE); + curConfig.setSourceEntries(newSourceEntries); + + //Get the Sloeber information from the original project (and if nothing found use defaults) + BoardDescription boardDescriptor = getBoardDescription(oldConfig); + CompileDescription compileDescriptor = getCompileDescription(oldConfig); + OtherDescription otherDesc = getOtherDescription(oldConfig); + + //Set the Sloeber configuration based on to the old project + autoConf.setIsParallelBuild(compileDescriptor.isParallelBuildEnabled()); + SloeberConfiguration sloeberConfiguration = new SloeberConfiguration(boardDescriptor, otherDesc, + compileDescriptor); + //Save the sloeber configuration in the autoBuild configuration + autoConf.setAutoBuildConfigurationExtensionDescription(sloeberConfiguration); + } + + SubMonitor refreshMonitor = SubMonitor.convert(internalMonitor, 3); + project.open(refreshMonitor); + project.refreshLocal(IResource.DEPTH_INFINITE, refreshMonitor); + prjCDesc.setCdtProjectCreated(); + cCorePlugin.setProjectDescription(project, prjCDesc, true, SubMonitor.convert(internalMonitor, 1)); + project.close(monitor); + project.open(monitor); + + Activator.log(new Status(SLOEBER_STATUS_DEBUG, Activator.getId(), + "internal creation of project is done: " + project.getName())); //$NON-NLS-1$ + // IndexerController.index(newProjectHandle); + } + }; + try + + { + workspace.run(runnable, root, IWorkspace.AVOID_UPDATE, monitor); + } catch (Exception e) { + Activator.log(new Status(IStatus.INFO, io.sloeber.core.Activator.getId(), + "Project creation failed: " + project.getName(), e)); //$NON-NLS-1$ + } + monitor.done(); + } + + private static OtherDescription getOtherDescription(KeyValueTree config) { + OtherDescription ret =new OtherDescription(); + if (config == null) { + return ret; + } + ret.setVersionControlled(Boolean.valueOf(config.getValue("other.IS_VERSION_CONTROLLED")).booleanValue()); //$NON-NLS-1$ + return ret; + } + + private static CompileDescription getCompileDescription(KeyValueTree oldConfig) { + CompileDescription ret = new CompileDescription(); + KeyValueTree compileConfig=oldConfig.getChild("compile").getChild("sloeber"); //$NON-NLS-1$ //$NON-NLS-2$ + if (compileConfig == null) { + return ret; + } + KeyValueTree extraConfig=compileConfig.getChild("extra"); //$NON-NLS-1$ + ret.set_All_CompileOptions(extraConfig.getValue("all")); //$NON-NLS-1$ + ret.set_Archive_CompileOptions(extraConfig.getValue("archive")); //$NON-NLS-1$ + ret.set_Assembly_CompileOptions(extraConfig.getValue("assembly")); //$NON-NLS-1$ + ret.set_C_CompileOptions(extraConfig.getValue("c.compile")); //$NON-NLS-1$ + ret.set_CPP_CompileOptions(extraConfig.getValue("cpp.compile")); //$NON-NLS-1$ + ret.set_Link_CompileOptions(extraConfig.getValue("link")); //$NON-NLS-1$ + ret.set_C_andCPP_CompileOptions(extraConfig.getValue("compile")); //$NON-NLS-1$ + + +// ret.setSizeCommand(extraConfig.getValue("compile")); //$NON-NLS-1$ +// +// +// Config.Release.compile.sloeber.size.custom= +// Config.Release.compile.sloeber.size.type=RAW_RESULT +// Config.Release.compile.sloeber.warning_level=NONE +// Config.Release.compile.sloeber.warning_level.custom= + return ret; + } + + private static BoardDescription getBoardDescription(KeyValueTree oldConfig) { + if (oldConfig == null) { + return new BoardDescription(); + } + String boardsFileString=oldConfig.getValue("board.BOARD.TXT"); //$NON-NLS-1$ + String boardID=oldConfig.getValue("board.BOARD.ID"); //$NON-NLS-1$ + + if(boardsFileString.isBlank() || boardID.isBlank()) { + return new BoardDescription(); + } + + KeyValueTree optionsHolder=oldConfig.getChild("board.BOARD.MENU"); //$NON-NLS-1$ + + Map options=new HashMap<>(); + for(KeyValueTree curOption:optionsHolder.getChildren().values()) { + options.put(curOption.getKey(), curOption.getValue()); + } + File boardsFile=new File(boardsFileString); + + BoardDescription ret= new BoardDescription( boardsFile, boardID, options); + String uploadPort=oldConfig.getValue("board.UPLOAD.PORT"); //$NON-NLS-1$ + ret.setUploadPort(uploadPort); + + return ret; } /** @@ -177,31 +291,32 @@ public static void convertToArduinoProject(IProject project, IProgressMonitor mo */ public static IProject createArduinoProject(String projectName, URI projectURI, BoardDescription boardDescriptor, CodeDescription codeDesc, CompileDescription compileDescriptor, IProgressMonitor monitor) { - return createArduinoProject(projectName, projectURI, boardDescriptor, codeDesc, compileDescriptor, - null,null, monitor); + return createArduinoProject(projectName, projectURI, boardDescriptor, codeDesc, compileDescriptor, null, null, + monitor); } - public static IProject createArduinoProject(String projectName, URI projectURI, BoardDescription boardDescriptor, - CodeDescription codeDesc, CompileDescription compileDescriptor,String builderName, + public static IProject createArduinoProject(String projectName, URI projectURI, BoardDescription boardDescriptor, + CodeDescription codeDesc, CompileDescription compileDescriptor, String builderName, IProgressMonitor monitor) { - return createArduinoProject(projectName, projectURI, boardDescriptor, codeDesc, compileDescriptor, - null,builderName, monitor); + return createArduinoProject(projectName, projectURI, boardDescriptor, codeDesc, compileDescriptor, null, + builderName, monitor); } - public static IProject createArduinoProject(String projectName, URI projectURI, BoardDescription boardDescriptor, + + public static IProject createArduinoProject(String projectName, URI projectURI, BoardDescription boardDescriptor, CodeDescription codeDesc, CompileDescription compileDescriptor, OtherDescription otherDesc, IProgressMonitor monitor) { - return createArduinoProject(projectName, projectURI, boardDescriptor, codeDesc, compileDescriptor, - otherDesc,null, monitor); + return createArduinoProject(projectName, projectURI, boardDescriptor, codeDesc, compileDescriptor, otherDesc, + null, monitor); } /* * Method to create a project based on the board */ - public static IProject createArduinoProject(String projectName, URI projectURI, BoardDescription boardDescriptor, - CodeDescription codeDesc, CompileDescription compileDescriptor, OtherDescription inOtherDesc,String inBuilderName, - IProgressMonitor monitor) { - OtherDescription otherDesc=inOtherDesc==null?new OtherDescription():inOtherDesc; - String builderName=inBuilderName==null?AutoBuildProject.INTERNAL_BUILDER_ID:inBuilderName; + public static IProject createArduinoProject(String projectName, URI projectURI, BoardDescription boardDescriptor, + CodeDescription codeDesc, CompileDescription compileDescriptor, OtherDescription inOtherDesc, + String inBuilderName, IProgressMonitor monitor) { + OtherDescription otherDesc = inOtherDesc == null ? new OtherDescription() : inOtherDesc; + String builderName = inBuilderName == null ? AutoBuildProject.INTERNAL_BUILDER_ID : inBuilderName; String realProjectName = makeNameCompileSafe(projectName); @@ -214,11 +329,11 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { // IndexerController.doNotIndex(newProjectHandle); IBuildTools buildTools = IBuildToolsManager.getDefault().getBuildTools(SLOEBER_BUILD_TOOL_PROVIDER_ID, - realProjectName); + null); IProjectType projectType = AutoBuildManager.getProjectType(LATEST_EXTENSION_POINT_ID, LATEST_EXTENSION_ID, PROJECT_ID, true); - newProjectHandle = AutoBuildProject.createProject(realProjectName, projectURI, projectType, - builderName ,CCProjectNature.CC_NATURE_ID, codeDesc, buildTools, true, internalMonitor); + newProjectHandle = AutoBuildProject.createProject(realProjectName, projectURI, projectType, builderName, + CCProjectNature.CC_NATURE_ID, codeDesc, buildTools, true, internalMonitor); String rootCodeFolder = codeDesc.getCodeFolder(); // Add the sketch code @@ -231,18 +346,22 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { for (ICConfigurationDescription curConfig : prjCDesc.getConfigurations()) { - IAutoBuildConfigurationDescription autoConf = IAutoBuildConfigurationDescription.getConfig( curConfig); + IAutoBuildConfigurationDescription autoConf = IAutoBuildConfigurationDescription + .getConfig(curConfig); ICSourceEntry newSourceEntries[] = new ICSourceEntry[2]; if (rootCodeFolder == null || rootCodeFolder.isBlank()) { // as we are at the root // exclude bin folder and arduino folder IPath excludes[] = new IPath[2]; - excludes[0] = autoConf.getBuildFolder().getProjectRelativePath().removeLastSegments(1).append(SOURCE_ENTRY_FILTER_ALL); - excludes[1] = newProjectHandle.getFolder(SLOEBER_ARDUINO_FOLDER_NAME).getProjectRelativePath().append(SOURCE_ENTRY_FILTER_ALL); - newSourceEntries[0] = new CSourceEntry(newProjectHandle.getFullPath(), excludes, ICSettingEntry.RESOLVED); + excludes[0] = autoConf.getBuildFolder().getProjectRelativePath().removeLastSegments(1) + .append(SOURCE_ENTRY_FILTER_ALL); + excludes[1] = newProjectHandle.getFolder(SLOEBER_ARDUINO_FOLDER_NAME).getProjectRelativePath() + .append(SOURCE_ENTRY_FILTER_ALL); + newSourceEntries[0] = new CSourceEntry(newProjectHandle.getFullPath(), excludes, + ICSettingEntry.RESOLVED); } else { // no need to exclude any folder as we have a dedicated code folder - IPath path=newProjectHandle.getFolder(rootCodeFolder).getFullPath(); + IPath path = newProjectHandle.getFolder(rootCodeFolder).getFullPath(); newSourceEntries[0] = new CSourceEntry(path, null, ICSettingEntry.RESOLVED); } IPath excludes[] = new IPath[8]; @@ -257,10 +376,12 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { /* * CDT currently causes issues with ${ConfigName] - * https://github.com/eclipse-cdt/cdt/issues/870 - * IPath arduinoRoot = newProjectHandle.getFolder(SLOEBER_ARDUINO_FOLDER_NAME).getFullPath().append(CONFIG_NAME_VARIABLE); - */ - IPath arduinoRoot = newProjectHandle.getFolder(SLOEBER_ARDUINO_FOLDER_NAME).getFullPath().append(curConfig.getName()); + * https://github.com/eclipse-cdt/cdt/issues/870 IPath arduinoRoot = + * newProjectHandle.getFolder(SLOEBER_ARDUINO_FOLDER_NAME).getFullPath().append( + * CONFIG_NAME_VARIABLE); + */ + IPath arduinoRoot = newProjectHandle.getFolder(SLOEBER_ARDUINO_FOLDER_NAME).getFullPath() + .append(curConfig.getName()); newSourceEntries[1] = new CSourceEntry(arduinoRoot, excludes, ICSettingEntry.NONE); curConfig.setSourceEntries(newSourceEntries); IAutoBuildConfigurationDescription iAutoBuildConfig = IAutoBuildConfigurationDescription @@ -293,9 +414,7 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { } }; - try - - { + try { workspace.run(runnable, root, IWorkspace.AVOID_UPDATE, monitor); } catch (Exception e) { Activator.log(new Status(IStatus.INFO, io.sloeber.core.Activator.getId(), @@ -305,408 +424,4 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { return root.getProject(realProjectName); } - // private HashMap getEnvVars(String configKey) { - // BoardDescription boardDescription = myBoardDescriptions.get(configKey); - // CompileDescription compileOptions = myCompileDescriptions.get(configKey); - // OtherDescription otherOptions = myOtherDescriptions.get(configKey); - // - // HashMap allVars = new HashMap<>(); - // - // allVars.put(ENV_KEY_BUILD_SOURCE_PATH, myProject.getLocation().toOSString()); - // allVars.put(ENV_KEY_BUILD_PATH, - // myProject.getLocation().append(configKey).toOSString()); - // - // if (boardDescription != null) { - // allVars.putAll(boardDescription.getEnvVars()); - // } - // if (compileOptions != null) { - // allVars.putAll(compileOptions.getEnvVars()); - // } - // if (otherOptions != null) { - // allVars.putAll(otherOptions.getEnvVars()); - // } - // // set the paths - // String pathDelimiter = makeEnvironmentVar("PathDelimiter"); //$NON-NLS-1$ - // if (Common.isWindows) { - // allVars.put(SLOEBER_MAKE_LOCATION, - // ConfigurationPreferences.getMakePath().addTrailingSeparator().toOSString()); - // allVars.put(SLOEBER_AWK_LOCATION, - // ConfigurationPreferences.getAwkPath().addTrailingSeparator().toOSString()); - // - // String systemroot = makeEnvironmentVar("SystemRoot"); //$NON-NLS-1$ - // allVars.put("PATH", //$NON-NLS-1$ - // makeEnvironmentVar(ENV_KEY_COMPILER_PATH) + pathDelimiter - // + makeEnvironmentVar(ENV_KEY_BUILD_GENERIC_PATH) + pathDelimiter + systemroot - // + "\\system32" //$NON-NLS-1$ - // + pathDelimiter + systemroot + pathDelimiter + systemroot + - // "\\system32\\Wbem" //$NON-NLS-1$ - // + pathDelimiter + makeEnvironmentVar("sloeber_path_extension")); - // //$NON-NLS-1$ - // } else { - // allVars.put("PATH", makeEnvironmentVar(ENV_KEY_COMPILER_PATH) + pathDelimiter - // //$NON-NLS-1$ - // + makeEnvironmentVar(ENV_KEY_BUILD_GENERIC_PATH) + pathDelimiter + - // makeEnvironmentVar("PATH")); //$NON-NLS-1$ - // } - // - // return allVars; - // } - - /** - * Read the sloeber configuration file and setup the project - * - * @return true if the files exist - */ - // private boolean readConfigFromFiles() { - // IFile file = getConfigLocalFile(); - // IFile versionFile = getConfigVersionFile(); - // if (!(file.exists() || versionFile.exists())) { - // // no sloeber files found - // return false; - // } - // if (file.exists()) { - // myCfgFile = new TxtFile(file.getLocation().toFile()); - // if (versionFile.exists()) { - // myCfgFile.mergeFile(versionFile.getLocation().toFile()); - // } - // } else { - // myCfgFile = new TxtFile(versionFile.getLocation().toFile()); - // } - // - // KeyValueTree allFileConfigs = myCfgFile.getData().getChild(CONFIG); - // for (Entry curChild : - // allFileConfigs.getChildren().entrySet()) { - // String curConfName = curChild.getKey(); - // BoardDescription boardDesc = new BoardDescription(myCfgFile, - // getBoardPrefix(curConfName)); - // CompileDescription compileDescription = new CompileDescription(myCfgFile, - // getCompilePrefix(curConfName)); - // OtherDescription otherDesc = new OtherDescription(myCfgFile, - // getOtherPrefix(curConfName)); - // String curConfKey = curConfName; - // myBoardDescriptions.put(curConfKey, boardDesc); - // myCompileDescriptions.put(curConfKey, compileDescription); - // myOtherDescriptions.put(curConfKey, otherDesc); - // } - // return true; - // } - - /** - * return the list of configNames known by Sloeber not known by CDT - * - * @param prjCDesc - * @return a list of sloeber known configurationNames unknown to CDT - */ - // private List newConfigsNeededInCDT(ICProjectDescription prjCDesc) { - // List ret = new LinkedList<>(); - // for (String curConfName : myBoardDescriptions.keySet()) { - // ICConfigurationDescription curConfDesc = - // prjCDesc.getConfigurationByName(curConfName); - // if (curConfDesc == null) { - // ret.add(curConfName); - // } - // } - // return ret; - // - // } - -// /** -// * create the cdt configurations from the list -// * -// * @param configs -// * @param prjCDesc -// * @return true if at least one config was created (basically only false if -// * configs is empty oe error conditions) -// */ -// private static boolean createNeededCDTConfigs(List configs, ICProjectDescription prjCDesc) { -// boolean ret = false; -// for (String curConfName : configs) { -// try { -// String id = CDataUtil.genId(null); -// prjCDesc.createConfiguration(id, curConfName, prjCDesc.getActiveConfiguration()); -// ret = true; -// } catch (Exception e) { -// // ignore as we will try again later -// e.printStackTrace(); -// } -// } -// return ret; -// } - - /** - * This methods creates/updates 2 files in the workspace. Together these files - * contain the Sloeber project configuration info The info is split into 2 files - * because you probably do not want to add all the info to a version control - * tool. - * - * sloeber.cfg is the file you can add to a version control .sproject is the - * file with settings you do not want to add to version control - * - * @param project the project to store the data for - */ - // private void createSloeberConfigFiles() { - // - // final IWorkspace workspace = ResourcesPlugin.getWorkspace(); - // if (workspace.isTreeLocked()) { - // // we cant save now do it later - // return; - // } - // - // Map configVars = new TreeMap<>(); - // Map versionVars = new TreeMap<>(); - // - // for (String configKey : myBoardDescriptions.keySet()) { - // BoardDescription boardDescription = myBoardDescriptions.get(configKey); - // CompileDescription compileDescription = myCompileDescriptions.get(configKey); - // OtherDescription otherDescription = myOtherDescriptions.get(configKey); - // - // String boardPrefix = getBoardPrefix(configKey); - // String compPrefix = getCompilePrefix(configKey); - // String otherPrefix = getOtherPrefix(configKey); - // - // configVars.putAll(boardDescription.getEnvVarsConfig(boardPrefix)); - // configVars.putAll(compileDescription.getEnvVarsConfig(compPrefix)); - // configVars.putAll(otherDescription.getEnvVarsConfig(otherPrefix)); - // - // if (otherDescription.IsVersionControlled()) { - // versionVars.putAll(boardDescription.getEnvVarsVersion(boardPrefix)); - // versionVars.putAll(compileDescription.getEnvVarsVersion(compPrefix)); - // versionVars.putAll(otherDescription.getEnvVarsVersion(otherPrefix)); - // } - // } - // - // try { - // storeConfigurationFile(getConfigVersionFile(), versionVars); - // storeConfigurationFile(getConfigLocalFile(), configVars); - // } catch (Exception e) { - // Activator.log(new Status(IStatus.ERROR, io.sloeber.core.Activator.getId(), - // "failed to save the sloeber config files", e)); //$NON-NLS-1$ - // } - // - // } - -// private static void storeConfigurationFile(IFile file, Map vars) throws Exception { -// String content = EMPTY; -// for (Entry curLine : vars.entrySet()) { -// content += curLine.getKey() + '=' + curLine.getValue() + '\n'; -// } -// -// if (file.exists()) { -// // if the filecontent hasn't changed=>do nothing -// try (InputStream inputStream = file.getContents();) { -// String fileContent = new BufferedReader(new InputStreamReader(inputStream)).lines() -// .collect(Collectors.joining("\n")); //$NON-NLS-1$ -// -//// Path filePath = Path.of(file.getLocation().toOSString()); -//// String fileContent = Files.readString(filePath); -// if (content.equals(fileContent)) { -// return; -// } -// } -// file.delete(true, null); -// } -// -// if (!file.exists() && (!content.isBlank())) { -// ByteArrayInputStream stream = new ByteArrayInputStream(content.getBytes()); -// file.create(stream, true, null); -// } -// -// } - - // /** - // * get the Arduino project description based on a project description - // * - // * @param project - // * @return the sloeber project or null if this is not a sloeber project - // */ - // public static SloeberProject getSloeberProject(IProject project) { - // - // if (project.isOpen() && project.getLocation().toFile().exists()) { - // if (Sketch.isSketch(project)) { - // Object sessionProperty = null; - // try { - // sessionProperty = project.getSessionProperty(sloeberQualifiedName); - // if (null != sessionProperty) { - // SloeberProject sloeberProject = (SloeberProject) sessionProperty; - // if (sloeberProject.isInMemory()) { - // IndexerController.index(project); - // } - // return sloeberProject; - // } - // } catch (CoreException e) { - // e.printStackTrace(); - // } - // return new SloeberProject(project); - // } - // } - // return null; - // } - -// private static String getBoardPrefix(String confDescName) { -// return CONFIG_DOT + confDescName + DOT + "board."; //$NON-NLS-1$ -// } -// -// private static String getCompilePrefix(String confDescName) { -// return CONFIG_DOT + confDescName + DOT + "compile."; //$NON-NLS-1$ -// } -// -// private static String getOtherPrefix(String confDescName) { -// return CONFIG_DOT + confDescName + DOT + "other."; //$NON-NLS-1$ -// } -// -// /* -// * Get the file that Sloeber maintains and that is meant to be stored in version control -// */ -// private IFile getConfigVersionFile() { -// return Sketch.getConfigVersionFile(myProject); -// } -// -// /* -// * Get the sloeber configuration file -// */ -// private IFile getConfigLocalFile() { -// return Sketch.getConfigLocalFile(myProject); -// } - - public void configChangeAboutToApply(ICProjectDescription newProjDesc, ICProjectDescription oldProjDesc) { - // ICConfigurationDescription newActiveConfig = - // newProjDesc.getActiveConfiguration(); - // ICConfigurationDescription oldActiveConfig = - // oldProjDesc.getActiveConfiguration(); - // - // //handle configuration name changes and new configs - // for (ICConfigurationDescription curConfig : newProjDesc.getConfigurations()) - // { - // String curConfigKey = getConfigKey(curConfig); - // ICConfigurationDescription oldConfig = - // oldProjDesc.getConfigurationById(curConfig.getId()); - // if (oldConfig == null) { - // //this is a new config - // myIsDirty = true; - // - // BoardDescription brdDesc = myBoardDescriptions.get(curConfigKey); - // if (brdDesc == null) { - // // This new configuration has not been created in project properties - // // I don't know how to get the "copied from" configuration - // // trying to get something somewhere - // // read: only copy configurations in project properties - // String copyConfKey = getConfigKey(oldActiveConfig); - // brdDesc = myBoardDescriptions.get(copyConfKey); - // if (brdDesc == null) { - // copyConfKey = getConfigKey(oldActiveConfig); - // brdDesc = myBoardDescriptions.get(copyConfKey); - // } - // BoardDescription boardDescription = new - // BoardDescription(myBoardDescriptions.get(copyConfKey)); - // myBoardDescriptions.put(curConfigKey, boardDescription); - // - // CompileDescription compileDescription = new CompileDescription( - // myCompileDescriptions.get(copyConfKey)); - // myCompileDescriptions.put(curConfigKey, compileDescription); - // - // OtherDescription otherDescription = new - // OtherDescription(myOtherDescriptions.get(copyConfKey)); - // myOtherDescriptions.put(curConfigKey, otherDescription); - // } - // - // } else { - // - // String oldConfigKey = getConfigKey(oldConfig); - // if (!oldConfigKey.equals(curConfigKey)) { - // //this is a rename - // myIsDirty = true; - // Helpers.deleteBuildFolder(myProject, oldConfig.getName()); - // BoardDescription boardDesc = myBoardDescriptions.get(oldConfigKey); - // myBoardDescriptions.remove(oldConfigKey); - // myBoardDescriptions.put(curConfigKey, boardDesc); - // - // CompileDescription compDesc = myCompileDescriptions.get(oldConfigKey); - // myCompileDescriptions.remove(oldConfigKey); - // myCompileDescriptions.put(curConfigKey, compDesc); - // - // OtherDescription otherDesc = myOtherDescriptions.get(oldConfigKey); - // myOtherDescriptions.remove(oldConfigKey); - // myOtherDescriptions.put(curConfigKey, otherDesc); - // - // } - // - // } - // } - // - // //delete all the deleted configs - // for (ICConfigurationDescription curConfig : oldProjDesc.getConfigurations()) - // { - // String curConfigKey = getConfigKey(curConfig); - // ICConfigurationDescription newConfig = - // newProjDesc.getConfigurationById(curConfig.getId()); - // if (newConfig == null) { - // //this is a deleted config - // myIsDirty = true; - // myBoardDescriptions.remove(curConfigKey); - // myCompileDescriptions.remove(curConfigKey); - // myOtherDescriptions.remove(curConfigKey); - // } - // } - // - // configure(newProjDesc, true); - - } - - /** - * Call this method when the sloeber.cfg file changed - * - */ - public void sloeberCfgChanged() { - // //CCorePlugin cCorePlugin = CCorePlugin.getDefault(); - // //ICProjectDescription projDesc = - // cCorePlugin.getProjectDescription(myProject, true); - // ///ICConfigurationDescription activeConfig = - // projDesc.getActiveConfiguration(); - // configure(); - // //all configs may have changed so only deleting the active config does not - // make sense - // //Helpers.deleteBuildFolder(myProject, activeConfig.getName()); - // - // //This code is only triggered when sloeber.cfg changed so no need to set the - // active config - // //projDescNeedsSaving = projDescNeedsSaving || setActiveConfig(activeConfig); - // // if (projDescNeedsSaving) { - // // try { - // // cCorePlugin.setProjectDescription(myProject, projDesc); - // // } catch (CoreException e) { - // // e.printStackTrace(); - // // } - // // } - - } - - /** - * When a board has been installed it may be that a boardDescription needs to - * reload the txt file - */ - public static void reloadTxtFile() { - // final IWorkspaceRoot workspaceRoot = - // ResourcesPlugin.getWorkspace().getRoot(); - // for (IProject curProject : workspaceRoot.getProjects()) { - // if (curProject.isOpen()) { - // SloeberProject sloeberProject = new - // SloeberProject(curProject);//getSloeberProject(curProject); - // if (sloeberProject != null) { - // sloeberProject.internalReloadTxtFile(); - // } - // } - // } - } - - /** - * When a board has been installed it may be that a boardDescription needs to - * reload the txt file - */ - // private void internalReloadTxtFile() { - // for (BoardDescription curBoardDescription : myBoardDescriptions.values()) { - // curBoardDescription.reloadTxtFile(); - // } - // - // } - } diff --git a/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java b/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java index 76db4f16..e93f8d22 100644 --- a/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java +++ b/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java @@ -26,6 +26,7 @@ import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; @@ -305,11 +306,31 @@ private IPath getCodeLocation() { continue; } //Just pick the first none arduino one as there should only be one - return curEntry.getLocation(); + return getLocation(curEntry); } return project.getLocation(); } + /** + * workaround code because ICSourceEntry.getLocation returns null if the + * resolved flag is not set moreover the resolved flag is not set when adding a + * source location via the gui Therefore one can not convert from root to src + * based projects because the manually added src is not marked as resolved This + * code is the same as ICSourceEntry.getLocation except for it assumes the + * resolved flag is set + * + * @param entry + * @return + */ + private static IPath getLocation(ICSourceEntry entry) { + if (!entry.isValueWorkspacePath()) + return new Path(entry.getValue()); + IPath path = new Path(entry.getValue()); + IResource rc = ResourcesPlugin.getWorkspace().getRoot().findMember(path); + if (rc != null) + return rc.getLocation(); + return null; + } /** * get the text for the decorator * diff --git a/io.sloeber.core/src/io/sloeber/core/toolchain/ArduinoLanguageProvider.java b/io.sloeber.core/src/io/sloeber/core/toolchain/ArduinoLanguageProvider.java index 60bff88e..ec5c25be 100644 --- a/io.sloeber.core/src/io/sloeber/core/toolchain/ArduinoLanguageProvider.java +++ b/io.sloeber.core/src/io/sloeber/core/toolchain/ArduinoLanguageProvider.java @@ -38,7 +38,13 @@ public List getSettingEntries(ICConfigurationDescription } List ret = new LinkedList<>(); IAutoBuildConfigurationDescription autoBuildConfData=IAutoBuildConfigurationDescription.getConfig(cfgDescription); + if(autoBuildConfData==null) { + return null; + } ISloeberConfiguration autoConfDesc= ISloeberConfiguration.getConfig(autoBuildConfData); + if(autoConfDesc==null) { + return null; + } ICSourceEntry[] mySrcEntries = IAutoBuildConfigurationDescription.getResolvedSourceEntries(autoBuildConfData); Set includeFolders = autoConfDesc.getIncludeFolders(); int flags = ICSettingEntry.READONLY | ICSettingEntry.VALUE_WORKSPACE_PATH | ICSettingEntry.RESOLVED; diff --git a/io.sloeber.ui/src/io/sloeber/ui/wizard/ConvertToSloeber.java b/io.sloeber.ui/src/io/sloeber/ui/wizard/ConvertToSloeber.java index a825b4e3..4dd58560 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/wizard/ConvertToSloeber.java +++ b/io.sloeber.ui/src/io/sloeber/ui/wizard/ConvertToSloeber.java @@ -1,6 +1,7 @@ package io.sloeber.ui.wizard; import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.wizard.Wizard; import org.eclipse.ui.INewWizard; @@ -23,7 +24,7 @@ public void init(IWorkbench workbench, IStructuredSelection selection) { @Override public boolean performFinish() { - SloeberProject.convertToArduinoProject(myProject, null); + SloeberProject.convertToArduinoProject(myProject, new NullProgressMonitor( )); return true; } From 376ab997a370e50d1a3f427a7cf1d3c11c14b11c Mon Sep 17 00:00:00 2001 From: jan Date: Thu, 5 Sep 2024 22:47:29 +0200 Subject: [PATCH 024/115] Fix the build. --- .../autoBuild/integration/AutoBuildProjectGenerator.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildProjectGenerator.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildProjectGenerator.java index 6767a9a8..cf441a80 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildProjectGenerator.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildProjectGenerator.java @@ -29,6 +29,7 @@ import org.eclipse.core.runtime.Status; import org.eclipse.tools.templates.core.IGenerator; +import io.sloeber.autoBuild.api.AutoBuildCommon; import io.sloeber.autoBuild.api.ICodeProvider; import io.sloeber.autoBuild.buildTools.api.IBuildTools; import io.sloeber.autoBuild.core.Activator; @@ -124,6 +125,7 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { if (cdtCfgDes instanceof ILanguageSettingsProvidersKeeper) { languageKeeper.setLanguageSettingProviders(providers); } + AutoBuildCommon.createFolder( data.getBuildFolder()); } if (!myNeedsMoreWork) { des.setCdtProjectCreated(); From 6efd158b118000bce9dd028feb57ae45ab94a88c Mon Sep 17 00:00:00 2001 From: jan Date: Fri, 6 Sep 2024 00:53:36 +0200 Subject: [PATCH 025/115] Remove comments --- .../autoBuild/integration/AutoBuildProjectGenerator.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildProjectGenerator.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildProjectGenerator.java index cf441a80..e84e13af 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildProjectGenerator.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildProjectGenerator.java @@ -132,10 +132,6 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { } mngr.setProjectDescription(myProject, des); -// for(ICConfigurationDescription curConfig:des.getConfigurations()) { -// IAutoBuildConfigurationDescription autodesc=IAutoBuildConfigurationDescription.getConfig(curConfig); -// AutoBuildCommon.createFolder( autodesc.getBuildFolder()); -// } } }; try { From e824ba2d57866550913b2e3f4eb642e4ee3ef12b Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 8 Sep 2024 03:05:33 +0200 Subject: [PATCH 026/115] remove comment --- .../autoBuild/api/IAutoBuildConfigurationDescription.java | 1 - 1 file changed, 1 deletion(-) diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/api/IAutoBuildConfigurationDescription.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/api/IAutoBuildConfigurationDescription.java index 4531a5c5..f608a587 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/api/IAutoBuildConfigurationDescription.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/api/IAutoBuildConfigurationDescription.java @@ -49,7 +49,6 @@ public static IAutoBuildConfigurationDescription getConfig(ICConfigurationDescri return null; } AutoBuildConfigurationDescription ret = (AutoBuildConfigurationDescription) confDesc.getConfigurationData(); - // ret.setWritable(!confDesc.isReadOnly()); return ret; } From ecf9ed9a74299d84a97509cf4a90cb0434e0b5f6 Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 8 Sep 2024 03:06:50 +0200 Subject: [PATCH 027/115] Do not delete the .cproject when converting to a Sloeber project --- .../src/io/sloeber/core/api/SloeberProject.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java b/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java index 01a8123d..13611146 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java +++ b/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java @@ -17,6 +17,7 @@ import org.eclipse.cdt.core.settings.model.ICSettingEntry; import org.eclipse.cdt.core.settings.model.ICSourceEntry; import org.eclipse.cdt.core.settings.model.util.CDataUtil; +import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProjectDescription; @@ -82,6 +83,11 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { OldCoreFolder.delete(true, monitor); } +// IFile CdtDotFile = project.getFile(".cproject"); //$NON-NLS-1$ +// if(CdtDotFile.exists()) { +// CdtDotFile.delete(true, monitor); +// } + File sloeberDotFile = project.getFile(".sproject").getLocation().toFile(); //$NON-NLS-1$ File sloeberCfgFile = project.getFile(SLOEBER_CFG).getLocation().toFile(); TxtFile oldSloeberInfo = null; @@ -174,7 +180,7 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { * CONFIG_NAME_VARIABLE); */ IPath arduinoRoot = project.getFolder(SLOEBER_ARDUINO_FOLDER_NAME).getFullPath() - .append(curConfig.getName()); + .append(cfgName); newSourceEntries[1] = new CSourceEntry(arduinoRoot, excludes2, ICSettingEntry.NONE); curConfig.setSourceEntries(newSourceEntries); From 608b334ba0232e3f32107dc12b9a82173e44af13 Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 8 Sep 2024 03:07:36 +0200 Subject: [PATCH 028/115] When converting to a sloeber project do not delete the project --- .../autoBuild/integration/AutoBuildProjectGenerator.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildProjectGenerator.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildProjectGenerator.java index e84e13af..6243b205 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildProjectGenerator.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildProjectGenerator.java @@ -67,11 +67,12 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { description.setLocationURI(myLocationURI); } myProject = root.getProject(myProjectName); - if(myProject.exists()) { - myProject.delete(false, false, monitor); + if(!myProject.exists()) { + //myProject.delete(false, false, monitor); + myProject.create(description, monitor); + myProject.open(monitor); } - myProject.create(description, monitor); - myProject.open(monitor); + CProjectNature.addCNature(myProject, monitor); if (CCProjectNature.CC_NATURE_ID.equals(myNatureID)) { CCProjectNature.addCCNature(myProject, monitor); From a6d58d3de2c7382fda0904de060142431a191a5a Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 8 Sep 2024 18:58:00 +0200 Subject: [PATCH 029/115] exclude deprecated dl.espressif.com/dl/package_esp32_index.json --- .../core/CreateAndCompileDefaultInoOnAllBoardsTest.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java index 8aa42eeb..729b4d5d 100644 --- a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java +++ b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java @@ -108,7 +108,10 @@ static public void beforeAll() throws Exception { // uses busybox on windows so command line issues and on Linux the all in one // archive build fails - "https://github.com/tenbaht/sduino/raw/master/package_sduino_stm8_index.json", }; + "https://github.com/tenbaht/sduino/raw/master/package_sduino_stm8_index.json", + + //moved to diferent place + "https://dl.espressif.com/dl/package_esp32_index.json",}; private static final String[] packageUrlsToIgnoreonWindows = { // following packages did not work in the arduino ide on windows at last test // confirmed 220 03 09 was version 1.0 From 2c8952253c9917bdf532a7b8ce18ad70341e5de7 Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 8 Sep 2024 19:00:21 +0200 Subject: [PATCH 030/115] ESP32 needs toOSString for the hooks For one reason or another the cmd \c fails when using \ and / interchangebly in the ESP32 hooks in eclipse (even in the makefile) ev"en though the work on the command line --- .../sloeber/arduinoFramework/api/BoardDescription.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java index d3a56185..a5458779 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java @@ -966,9 +966,9 @@ public Map getEnvVars() { String architecture = getArchitecture(); IPath coreHardwarePath = getreferencedCoreHardwarePath(); allVars.put(ENV_KEY_BUILD_ARCH, architecture.toUpperCase()); - allVars.put(ENV_KEY_RUNTIME_HARDWARE_PATH, getreferencingPlatformPath().removeLastSegments(1).toString()); - allVars.put(ENV_KEY_BUILD_SYSTEM_PATH, coreHardwarePath.append(SYSTEM).toString()); - allVars.put(ENV_KEY_RUNTIME_PLATFORM_PATH, getreferencingPlatformPath().toString()); + allVars.put(ENV_KEY_RUNTIME_HARDWARE_PATH, getreferencingPlatformPath().removeLastSegments(1).toOSString()); + allVars.put(ENV_KEY_BUILD_SYSTEM_PATH, coreHardwarePath.append(SYSTEM).toOSString()); + allVars.put(ENV_KEY_RUNTIME_PLATFORM_PATH, getreferencingPlatformPath().toOSString()); //ide_version is defined in pre_processing_platform_default.txt allVars.put(ENV_KEY_RUNTIME_IDE_VERSION, makeEnvironmentVar("ide_version")); //$NON-NLS-1$ allVars.put(ENV_KEY_RUNTIME_IDE_PATH, makeEnvironmentVar(SLOEBER_HOME)); @@ -990,10 +990,10 @@ public Map getEnvVars() { allVars.put(ENV_KEY_SERIAL_PORT_FILE, getActualUploadPort().replace("/dev/", EMPTY)); //$NON-NLS-1$ // if actual core path is osstring regression test issue555 willl fail teensy // stuff - allVars.put(ENV_KEY_BUILD_ACTUAL_CORE_PATH, getActualCoreCodePath().toString()); + allVars.put(ENV_KEY_BUILD_ACTUAL_CORE_PATH, getActualCoreCodePath().toOSString()); IPath variantPath = getActualVariantPath(); if (variantPath != null) { - allVars.put(ENV_KEY_BUILD_VARIANT_PATH, variantPath.toString()); + allVars.put(ENV_KEY_BUILD_VARIANT_PATH, variantPath.toOSString()); } else {// teensy does not use variant allVars.put(ENV_KEY_BUILD_VARIANT_PATH, EMPTY); } From de79fcf3e0672464bc3499d085fd5bba86f44b50 Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 8 Sep 2024 19:01:23 +0200 Subject: [PATCH 031/115] opening the project properties deletes the build folder --- .../integration/AutoBuildConfigurationDescription.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildConfigurationDescription.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildConfigurationDescription.java index 70fa7140..9d4bcb2b 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildConfigurationDescription.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildConfigurationDescription.java @@ -532,6 +532,10 @@ public void setGenerateMakeFilesAUtomatically(boolean generateMakeFilesAUtomatic @Override public void setBuildFolderString(String buildFolder) { + //TODO is this the correct place to delete the old folder + if(buildFolder.equals(myBuildFolderString)) { + return; + } checkIfWeCanWrite(); IFolder oldBuildFolder = getBuildFolder(); if (oldBuildFolder != null && oldBuildFolder.exists()) { From 22d10669994fddf9d6e1e4c87b13a309f6b2ca5e Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 8 Sep 2024 23:28:15 +0200 Subject: [PATCH 032/115] When converting to sloeber project cope with "wrong" locations The SLOEBER_HOME folder may have moved. As Sloeber V4 does not do a great job at identifying SLOEBER_HOMEit is likely (and it was durting tests) that the boards.txt points to a valid boards.txt (valid in the sense of: valid path on the original computer and valid if it had been stored with ${SLOEBER_HOME} Therefore I added code searching for the ArduinoPlugin folder and go from there. --- .../io/sloeber/core/api/SloeberProject.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java b/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java index 13611146..53c8486a 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java +++ b/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java @@ -30,6 +30,7 @@ import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; @@ -267,6 +268,22 @@ private static BoardDescription getBoardDescription(KeyValueTree oldConfig) { if(boardsFileString.isBlank() || boardID.isBlank()) { return new BoardDescription(); } + Path readBoardsFilePath=new Path(boardsFileString); + IPath foundBoardsFilePath=null; + String[] segments =readBoardsFilePath.segments(); + for(String curSegment:segments) { + + if(foundBoardsFilePath!=null) { + foundBoardsFilePath=foundBoardsFilePath.append(curSegment); + }else { + if(SLOEBER_HOME_SUB_FOLDER.equals( curSegment)) { + foundBoardsFilePath=new Path(sloeberHome).append(curSegment); + } + } + } + if (foundBoardsFilePath == null) { + return new BoardDescription(); + } KeyValueTree optionsHolder=oldConfig.getChild("board.BOARD.MENU"); //$NON-NLS-1$ @@ -274,7 +291,7 @@ private static BoardDescription getBoardDescription(KeyValueTree oldConfig) { for(KeyValueTree curOption:optionsHolder.getChildren().values()) { options.put(curOption.getKey(), curOption.getValue()); } - File boardsFile=new File(boardsFileString); + File boardsFile=foundBoardsFilePath.toFile();// new File(boardsFileString); BoardDescription ret= new BoardDescription( boardsFile, boardID, options); String uploadPort=oldConfig.getValue("board.UPLOAD.PORT"); //$NON-NLS-1$ From 97292be36b8454b0d56c9b5743dc3230c9e35310 Mon Sep 17 00:00:00 2001 From: jan Date: Tue, 10 Sep 2024 01:38:09 +0200 Subject: [PATCH 033/115] Added test to avoid null pointers for malformed json files #1681 --- .../ui/preferences/PlatformSelectionPage.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/io.sloeber.ui/src/io/sloeber/ui/preferences/PlatformSelectionPage.java b/io.sloeber.ui/src/io/sloeber/ui/preferences/PlatformSelectionPage.java index cd8b3b59..465db957 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/preferences/PlatformSelectionPage.java +++ b/io.sloeber.ui/src/io/sloeber/ui/preferences/PlatformSelectionPage.java @@ -58,6 +58,10 @@ private boolean mustBeInstalled(IArduinoPlatform platform) { IArduinoPlatformPackageIndex parentIndex = parentPkg.getPackageIndex(); InstallableVersion[] inScopeVersions = myShownPlatforms.get(parentIndex.getID()).get(parentPkg.getID()) .get(platform.getID()); + if(inScopeVersions==null) { + //in case there are no versions + return false; + } for (InstallableVersion version : inScopeVersions) { if (version.mustBeInstalled()) { return true; @@ -69,6 +73,10 @@ private boolean mustBeInstalled(IArduinoPlatform platform) { private boolean mustBeInstalled(IArduinoPlatformPackageIndex packageIndex) { TreeMap> inScopeVersions = myShownPlatforms .get(packageIndex.getID()); + if(inScopeVersions==null) { + //in case there are no versions + return false; + } for (TreeMap platform : inScopeVersions.values()) { for (InstallableVersion[] versions : platform.values()) { for (InstallableVersion version : versions) { @@ -85,6 +93,10 @@ private boolean mustBeInstalled(IArduinoPackage pkg) { IArduinoPlatformPackageIndex parentIndex = pkg.getPackageIndex(); TreeMap inScopeVersions = myShownPlatforms.get(parentIndex.getID()) .get(pkg.getID()); + if(inScopeVersions==null) { + //in case there are no versions + return false; + } for (InstallableVersion[] versions : inScopeVersions.values()) { for (InstallableVersion version : versions) { if (version.mustBeInstalled()) { From b1b2e633bdcaf7a72f38ae257e0387a90efff930 Mon Sep 17 00:00:00 2001 From: jan Date: Tue, 10 Sep 2024 14:45:36 +0200 Subject: [PATCH 034/115] Use ...schema.api.IConfiguration instead of Configuration --- .../integration/AutoBuildConfigurationDescription.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildConfigurationDescription.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildConfigurationDescription.java index 9d4bcb2b..8d7fe3a2 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildConfigurationDescription.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildConfigurationDescription.java @@ -52,7 +52,6 @@ import io.sloeber.autoBuild.schema.api.IOption; import io.sloeber.autoBuild.schema.api.IProjectType; import io.sloeber.autoBuild.schema.api.ITool; -import io.sloeber.autoBuild.schema.internal.Configuration; public class AutoBuildConfigurationDescription extends AutoBuildResourceData implements IAutoBuildConfigurationDescription { @@ -108,7 +107,7 @@ public class AutoBuildConfigurationDescription extends AutoBuildResourceData private boolean myIsWritable = false; private boolean myForceCleanBeforeBuild = false; - public AutoBuildConfigurationDescription(Configuration config, IProject project, IBuildTools buildTools, + public AutoBuildConfigurationDescription(IConfiguration config, IProject project, IBuildTools buildTools, String rootCodeFolder) { initializeResourceData(project, rootCodeFolder, myBuildFolderString); myBuildTools = buildTools; From 207bd1bd795000c4ead9fae91614bcf20c78f884 Mon Sep 17 00:00:00 2001 From: jan Date: Mon, 23 Sep 2024 01:20:10 +0200 Subject: [PATCH 035/115] Test failed but manual build after test succeeded Implemented 3 tries and cleaned up a bit --- .../src/io/sloeber/core/BuildTests.java | 1453 ++++++++--------- 1 file changed, 726 insertions(+), 727 deletions(-) diff --git a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java index 547fd950..d5dd092a 100644 --- a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java +++ b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java @@ -62,732 +62,731 @@ @SuppressWarnings({ "nls", "static-method", "null", "restriction" }) public class BuildTests { - private static final boolean reinstall_boards_and_libraries = false; - private final static String AUTOBUILD_CFG = ".autoBuildProject"; - private static final String HIDlibName = "HID-Project"; - - /* - * In new new installations (of the Sloeber development environment) the - * installer job will trigger downloads These must have finished before we can - * start testing - */ - @BeforeAll - public static void beforeClass() throws Exception { - Shared.waitForBoardsManager(); - Shared.setDeleteProjects(false); - Preferences.setUseBonjour(false); - installAdditionalBoards(); - installAdditionalLibs(); - } - - @BeforeEach - public void beforeTest() { - //make sure the fail message is cleaned before the tests is started - Shared.getLastFailMessage(); - } - - private static void installAdditionalLibs() { - LibraryManager.installLibrary(HIDlibName); - - } - - public static void installAdditionalBoards() throws Exception { - - String[] packageUrlsToAdd = { ESP8266.packageURL, ESP32.packageURL, - "http://talk2arduino.wisen.com.au/master/package_talk2.wisen.com_index.json" }; - BoardsManager.addPackageURLs(new HashSet<>(Arrays.asList(packageUrlsToAdd)), false); - Shared.waitForBoardsManager(); - if (reinstall_boards_and_libraries) { - BoardsManager.removeAllInstalledPlatforms(); - } - // make sure the needed boards are available - - ESP8266.installLatest(); - ESP32.installLatest(); - Arduino.installLatestAVRBoards(); - Teensy.installLatest(); - Arduino.installLatestSamDBoards(); - LibraryManager.installLibrary("RTCZero"); - - } - - /** - * make sure when switching between a board with variant file and without the - * build still succeeds - * - * @throws CoreException - */ - @Test - public void issue555() throws Exception { - BoardDescription unoBoardid = Arduino.uno().getBoardDescriptor(); - BoardDescription teensyBoardid = Teensy.Teensy3_1().getBoardDescriptor(); - - IProject theTestProject = null; - CodeDescription codeDescriptor = CodeDescription.createDefaultIno(); - String projectName = "issue555"; - NullProgressMonitor monitor = new NullProgressMonitor(); - theTestProject = SloeberProject.createArduinoProject(projectName, null, unoBoardid, codeDescriptor, - new CompileDescription(), monitor); - Shared.waitForIndexer(theTestProject); - theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, monitor); - assertNull(Shared.hasBuildErrors(theTestProject)); - - CCorePlugin cCorePlugin = CCorePlugin.getDefault(); - ICProjectDescription cProjectDescription = cCorePlugin.getProjectDescription(theTestProject); - ICConfigurationDescription activeCfg = cProjectDescription.getActiveConfiguration(); - ISloeberConfiguration sloeberConf = ISloeberConfiguration.getConfig(activeCfg); - sloeberConf.setBoardDescription(teensyBoardid); - cCorePlugin.setProjectDescription(theTestProject, cProjectDescription); - - if (sloeberConf.getArduinoVariantFolder().exists()) { - fail("variant folder exists"); - } - - theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, monitor); - if (Shared.hasBuildErrors(theTestProject) != null) { - theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, monitor); - assertNull(Shared.hasBuildErrors(theTestProject)); - } - } - - /** - * support void loop{}; - * - * @throws Exception - */ - @Test - public void issue687() throws Exception { - BoardDescription unoBoardid = Arduino.uno().getBoardDescriptor(); - - IProject theTestProject = null; - String projectName = "issue687"; - IPath templateFolder = Shared.getTemplateFolder(projectName); - CodeDescription codeDescriptor = CodeDescription.createCustomTemplate(templateFolder); - theTestProject = SloeberProject.createArduinoProject(projectName, null, unoBoardid, codeDescriptor, - new CompileDescription(), new NullProgressMonitor()); - Shared.waitForIndexer(theTestProject); - theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, new NullProgressMonitor()); - assertNull(Shared.hasBuildErrors(theTestProject)); - } - - - - /** - * Test multiple ino's - */ - @Test - public void multipleINOs() throws Exception { - BoardDescription unoBoardid = Arduino.uno().getBoardDescriptor(); - - IProject theTestProject = null; - String projectName = "multipleInos"; - IPath templateFolder = Shared.getTemplateFolder(projectName); - CodeDescription codeDescriptor = CodeDescription.createCustomTemplate(templateFolder); - theTestProject = SloeberProject.createArduinoProject(projectName, null, unoBoardid, codeDescriptor, - new CompileDescription(), new NullProgressMonitor()); - Shared.waitForIndexer(theTestProject); - theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, new NullProgressMonitor()); - assertNull(Shared.hasBuildErrors(theTestProject)); - } - - - - - - @Test - public void create_CPP_based_Sloeber_Project() throws Exception { - BoardDescription unoBoardid = Arduino.uno().getBoardDescriptor(); - - IProject theTestProject = null; - String projectName = "createCPPProject"; - - CodeDescription codeDescriptor = CodeDescription.createDefaultCPP(); - theTestProject = SloeberProject.createArduinoProject(projectName, null, unoBoardid, codeDescriptor, - new CompileDescription(), new NullProgressMonitor()); - Shared.waitForIndexer(theTestProject); - theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, new NullProgressMonitor()); - assertNull(Shared.hasBuildErrors(theTestProject)); - - } - - @Test - public void createDefaultInoProject() throws Exception { - BoardDescription unoBoardid = Arduino.uno().getBoardDescriptor(); - - IProject theTestProject = null; - String projectName = "createDefaultInoProject"; - - CodeDescription codeDescriptor = CodeDescription.createDefaultIno(); - theTestProject = SloeberProject.createArduinoProject(projectName, null, unoBoardid, codeDescriptor, - new CompileDescription(), new NullProgressMonitor()); - Shared.waitForIndexer(theTestProject); - theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, new NullProgressMonitor()); - assertNull(Shared.hasBuildErrors(theTestProject)); - } - - /** - * support void loop{}; - * - * @throws Exception - */ - @Test - public void issue1047_Board_Names_Can_Be_used_as_Strings() throws Exception { - MCUBoard unoBoard = ESP8266.nodeMCU(); - - String projectName = "issue1047_Board_Names_Can_Be_used_as_Strings"; - IPath templateFolder = Shared.getTemplateFolder(projectName); - CodeDescription codeDescriptor = CodeDescription.createCustomTemplate(templateFolder); - IProject theTestProject = SloeberProject.createArduinoProject(projectName, null, unoBoard.getBoardDescriptor(), - codeDescriptor, new CompileDescription(), new NullProgressMonitor()); - theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, new NullProgressMonitor()); - assertNull(Shared.hasBuildErrors(theTestProject)); - - } - - /** - * This test will fail if the arduino compile option are not taken into account - * To do sa a bunch of defines are added to the command line and the code checks - * whether these defines are set properly - * - * @throws Exception - */ - @Test - public void are_jantjes_options_taken_into_account() throws Exception { - BoardDescription unoBoardid = Arduino.uno().getBoardDescriptor(); - - IProject theTestProject = null; - String projectName = "are_defines_found"; - IPath templateFolder = Shared.getTemplateFolder(projectName); - CodeDescription codeDescriptor = CodeDescription.createCustomTemplate(templateFolder); - - NullProgressMonitor monitor = new NullProgressMonitor(); - CompileDescription compileOptions = new CompileDescription(); - compileOptions.set_C_andCPP_CompileOptions("-DTEST_C_CPP"); - compileOptions.set_C_CompileOptions("-DTEST_C"); - compileOptions.set_CPP_CompileOptions("-DTEST_CPP"); - theTestProject = SloeberProject.createArduinoProject(projectName, null, unoBoardid, codeDescriptor, - compileOptions, new NullProgressMonitor()); - - Shared.waitForIndexer(theTestProject); - theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, monitor); - assertNull(Shared.hasBuildErrors(theTestProject)); - } - - /** - * If a .ino file is including a include using extern C is this handled properly - * by the ino to cpp parser - * - * @throws Exception - */ - @Test - public void is_extern_C_taken_into_account() throws Exception { - BoardDescription unoBoardid = Arduino.uno().getBoardDescriptor(); - - IProject theTestProject = null; - String projectName = "externc"; - IPath templateFolder = Shared.getTemplateFolder(projectName); - CodeDescription codeDescriptor = CodeDescription.createCustomTemplate(templateFolder); - - NullProgressMonitor monitor = new NullProgressMonitor(); - theTestProject = SloeberProject.createArduinoProject(projectName, null, unoBoardid, codeDescriptor, - new CompileDescription(), new NullProgressMonitor()); - - Shared.waitForIndexer(theTestProject); - theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, monitor); - assertNull(Shared.hasBuildErrors(theTestProject)); - } - - /** - * If a .ino file is defining defines before including a include this should be - * handled properly by the ino to cpp parser - * - * @throws Exception - */ - @Test - public void are_defines_before_includes_taken_into_account() throws Exception { - BoardDescription unoBoardid = Arduino.uno().getBoardDescriptor(); - - IProject theTestProject = null; - String projectName = "defines_and_includes"; - IPath templateFolder = Shared.getTemplateFolder(projectName); - CodeDescription codeDescriptor = CodeDescription.createCustomTemplate(templateFolder); - - NullProgressMonitor monitor = new NullProgressMonitor(); - theTestProject = SloeberProject.createArduinoProject(projectName, null, unoBoardid, codeDescriptor, - new CompileDescription(), new NullProgressMonitor()); - - Shared.waitForIndexer(theTestProject); - theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, monitor); - assertNull(Shared.hasBuildErrors(theTestProject)); - - } - - /** - * Does Sloeber still compile after a configuration renamen - * - * @throws Exception - */ - @Test - public void rename_Configuration() throws Exception { - BoardDescription unoBoardid = Arduino.uno().getBoardDescriptor(); - - IProject theTestProject = null; - String projectName = "rename_Configuration"; - - CodeDescription codeDescriptor = CodeDescription.createDefaultIno(); - - NullProgressMonitor monitor = new NullProgressMonitor(); - theTestProject = SloeberProject.createArduinoProject(projectName, null, unoBoardid, codeDescriptor, - new CompileDescription(), new NullProgressMonitor()); - - Shared.waitForIndexer(theTestProject); - theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, monitor); - assertNull( Shared.hasBuildErrors(theTestProject),"Failed to compile the project before config rename"); - - CCorePlugin cCorePlugin = CCorePlugin.getDefault(); - ICProjectDescription prjCDesc = cCorePlugin.getProjectDescription(theTestProject); - ICConfigurationDescription activeConfig = prjCDesc.getActiveConfiguration(); - activeConfig.setName("renamedConfig"); - cCorePlugin.setProjectDescription(theTestProject, prjCDesc); - - theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, monitor); - assertNull(Shared.hasBuildErrors(theTestProject),"Failed to compile the project after config rename" ); - } - - // /** - // * Does Sloeber still compile after a project rename - // * - // * @throws Exception - // */ - // @Test - // public void rename_Project() throws Exception { - // BoardDescription unoBoardid = Arduino.uno().getBoardDescriptor(); - // - // IProject theTestProject = null; - // String projectName = "rename_project"; - // String projectNameRenamed = "renamed_project"; - // - // CodeDescription codeDescriptor = CodeDescription.createDefaultIno(); - // - // NullProgressMonitor monitor = new NullProgressMonitor(); - // theTestProject = SloeberProject.createArduinoProject(projectName, null, - // unoBoardid, codeDescriptor, - // new CompileDescription(), new NullProgressMonitor()); - // - // Shared.waitForAllJobsToFinish(); // for the indexer - // theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, monitor); - // if (Shared.hasBuildErrors(theTestProject)) { - // fail("Failed to compile the project before project rename"); - // } - // theTestProject.build(IncrementalProjectBuilder.CLEAN_BUILD, monitor); - // - // CCorePlugin cCorePlugin = CCorePlugin.getDefault(); - // ICProjectDescription prjCDesc = - // cCorePlugin.getProjectDescription(theTestProject); - // - // IProjectDescription descr = theTestProject.getDescription(); - // descr.ren(projectNameRenamed); - // theTestProject.setDescription(descr, null); - // - // Shared.waitForAllJobsToFinish(); // for the indexer - // theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, monitor); - // if (Shared.hasBuildErrors(theTestProject)) { - // fail("Failed to compile the project after project rename"); - // } - // } - - /** - * open and close a project should keep the compileDescription and - * BoardDescriotion - * - * @throws Exception - */ - @ParameterizedTest - @MethodSource("openAndClosePreservesSettingsValueCmd") - public void openAndClosePreservesSettings(String projectName, CodeDescription codeDescriptor) throws Exception { - BoardDescription unoBoardid = Arduino.uno().getBoardDescriptor(); - - IProject theTestProject = null; - - CompileDescription inCompileDescription = getBunkersCompileDescription(); - - theTestProject = SloeberProject.createArduinoProject(projectName, null, unoBoardid, codeDescriptor, - inCompileDescription, new NullProgressMonitor()); - - Shared.waitForIndexer(theTestProject); - // also do a build - theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, null); - assertNull(Shared.hasBuildErrors(theTestProject),"Failed to compile the project before close: " + projectName); - - // also do a clean - theTestProject.build(IncrementalProjectBuilder.CLEAN_BUILD, null); - - // Read the data we want to test - ISloeberConfiguration sloeberConf = ISloeberConfiguration.getActiveConfig(theTestProject); - BoardDescription createdBoardDesc = sloeberConf.getBoardDescription(); - CompileDescription createdCompileDesc = sloeberConf.getCompileDescription(); - - // close and reopen the project - theTestProject.close(null); - // just wait a while - Thread.sleep(1000); - theTestProject.open(null); - Shared.waitForIndexer(theTestProject); - - // read the data we want to test - sloeberConf = ISloeberConfiguration.getActiveConfig(theTestProject); - if (sloeberConf == null) { - fail("failed to open and close project"); - } - BoardDescription reopenedBoardDesc = sloeberConf.getBoardDescription(); - CompileDescription reopenedCompileDesc = sloeberConf.getCompileDescription(); - - // check the data is equal - boolean createBoardsDiff = !unoBoardid.equals(createdBoardDesc); - boolean createCompileDiff = !inCompileDescription.equals(createdCompileDesc); - boolean openBoardsDiff = !reopenedBoardDesc.equals(createdBoardDesc); - boolean openCompileDiff = !reopenedCompileDesc.equals(createdCompileDesc); - - if (createBoardsDiff || createCompileDiff) { - fail("Created project does not match creation parameters."); - } - if (openBoardsDiff || openCompileDiff) { - fail("Opened project does not match closed project parameters."); - } - - // also do a build - theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, null); - assertNull(Shared.hasBuildErrors(theTestProject),"Failed to compile the project after open: " + projectName); - - } - - /** - * open and close a project should keep the compileDescription and - * BoardDescriotion - * - * @throws Exception - */ - - @Test - public void openModAndCloseUsesSavedSettings() throws Exception { - CodeDescription codeDesc = new CodeDescription(CodeDescription.CodeTypes.defaultCPP); - - String proj1Name = "openModAndClose1"; - BoardDescription proj1BoardDesc = Arduino.uno().getBoardDescriptor(); - OtherDescription otherDesc = new OtherDescription(); - otherDesc.setVersionControlled(true); - CompileDescription proj1CompileDesc = getBunkersCompileDescription(); - IProject proj1 = SloeberProject.createArduinoProject(proj1Name, null, proj1BoardDesc, codeDesc, - proj1CompileDesc, otherDesc, new NullProgressMonitor()); - - String proj2Name = "openModAndClose2"; - BoardDescription proj2BoardDesc = Arduino.mega2560Board().getBoardDescriptor(); - CompileDescription proj2CompileDesc = new CompileDescription(); - IProject proj2 = SloeberProject.createArduinoProject(proj2Name, null, proj2BoardDesc, codeDesc, - proj2CompileDesc, new NullProgressMonitor()); - - // Read the data we want to test - ISloeberConfiguration sloeberConf = ISloeberConfiguration.getActiveConfig(proj1); - BoardDescription proj1CreatedBoardDesc = sloeberConf.getBoardDescription(); - CompileDescription proj1CreatedCompileDesc = sloeberConf.getCompileDescription(); - - ISloeberConfiguration sloeberConf2 = ISloeberConfiguration.getActiveConfig(proj2); - BoardDescription proj2CreatedBoardDesc = sloeberConf2.getBoardDescription(); - CompileDescription proj2CreatedCompileDesc = sloeberConf2.getCompileDescription(); - - // get the filenames to copy - IFile file = proj1.getFile(AUTOBUILD_CFG); - File proj1SloeberFile = file.getLocation().toFile(); - - file = proj2.getFile(AUTOBUILD_CFG); - File proj2SloeberFile = file.getLocation().toFile(); - - // close and reopen the project - proj2.close(null); - // just wait a while - Thread.sleep(1000); - - // copy from proj1 to proj2 - FileUtils.copyFile(proj1SloeberFile, proj2SloeberFile); - - // reopen the project - proj2.open(null); - Shared.waitForIndexer(proj2); - - // reread project 2 - ISloeberConfiguration sloebercfg2 = ISloeberConfiguration.getActiveConfig(proj2); - if (sloebercfg2 == null) { - fail("failed to load the sloeber configuration"); - } - BoardDescription proj2OpenedBoardDesc = sloebercfg2.getBoardDescription(); - CompileDescription proj2OpenedCompileDesc = sloebercfg2.getCompileDescription(); - - // check the setup was done correctly - if (!proj1BoardDesc.equals(proj1CreatedBoardDesc)) { - fail("Project 1 not created properly."); - } - if (!proj2BoardDesc.equals(proj2CreatedBoardDesc)) { - fail("Project 2 not created properly."); - } - if (!proj1CompileDesc.equals(proj1CreatedCompileDesc)) { - fail("Project 1 not created properly."); - } - if (!proj2CompileDesc.equals(proj2CreatedCompileDesc)) { - fail("Project 2 not created properly."); - } - - // check wether the file modification was taken into account - if (!proj1BoardDesc.equals(proj2OpenedBoardDesc)) { - fail("Project 2 not created properly."); - } - if (!proj1CompileDesc.equals(proj2OpenedCompileDesc)) { - fail("Project 2 not created properly."); - } - - } - - @Test - public void createProjectWithURI() throws Exception { - CodeDescription codeDesc = new CodeDescription(CodeDescription.CodeTypes.defaultCPP); - - String proj1Name = "projectWithURI"; - String codeFolderName = "locationWithURI"; - BoardDescription proj1BoardDesc = Arduino.uno().getBoardDescriptor(); - OtherDescription otherDesc = new OtherDescription(); - CompileDescription proj1CompileDesc = getBunkersCompileDescription(); - final IWorkspace workspace = ResourcesPlugin.getWorkspace(); - IPath projectFolder = workspace.getRoot().getLocation().removeLastSegments(1).append(codeFolderName); - URI uri = projectFolder.toFile().toURI(); - // workspace.getRoot().getFolder(Path.fromOSString(codeFolderName)).getLocationURI(); - IProject theTestProject = SloeberProject.createArduinoProject(proj1Name, uri, proj1BoardDesc, codeDesc, - proj1CompileDesc, otherDesc, new NullProgressMonitor()); - - Shared.waitForIndexer(theTestProject); - theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, null); - assertNull(Shared.hasBuildErrors(theTestProject), - "Failed to compile the project: " + Shared.hasBuildErrors(theTestProject)); - String fileLocation = projectFolder.append("src").append(proj1Name + ".cpp").toString(); - - IFile cppFile = theTestProject.getFolder("src").getFile(proj1Name + ".cpp"); - assertTrue( cppFile.exists(),"File not in correct location"); - assertEquals( cppFile.getLocation().toString(), fileLocation,"File not in correct location"); - - } - - static CompileDescription getBunkersCompileDescription() { - CompileDescription inCompileDescription = new CompileDescription(); - - inCompileDescription.set_All_CompileOptions("-Deen=1"); - inCompileDescription.set_Archive_CompileOptions("-Dtwee=2"); - inCompileDescription.set_Assembly_CompileOptions("-Drie=3"); - inCompileDescription.set_C_andCPP_CompileOptions("-Dvier=4"); - inCompileDescription.set_C_CompileOptions("-Dvijf=5"); - inCompileDescription.set_Link_CompileOptions("-Dzes=6"); - inCompileDescription.set_CPP_CompileOptions("-Dzeven=7"); - inCompileDescription.setSizeCommand(SizeCommands.ARDUINO_WAY); - inCompileDescription.setEnableParallelBuild(true); - inCompileDescription.setWarningLevel(WarningLevels.NONE); - return inCompileDescription; - } - - /** - * check to see whether upload recipe key is correct for a couple of boards that - * have failed in the past - * - * @throws Exception - */ - @Test - public void uploadPattern() throws Exception { - BoardDescription boardDescriptor = Arduino.uno().getBoardDescriptor(); - String recipeKey = boardDescriptor.getUploadPatternKey(); - assertEquals( "tools.avrdude.upload.pattern", recipeKey,"uno upload recipe key is wrong"); - boardDescriptor = ESP32.esp32().getBoardDescriptor(); - boardDescriptor.setUploadPort("host 10.10.10.10"); - recipeKey = boardDescriptor.getUploadPatternKey(); - assertEquals( "tools.esptool_py.upload.network_pattern", recipeKey,"ESP OTA upload recipe key is wrong"); - - } - - public static Stream testDifferentSourceFoldersData() throws Exception { - List ret = new LinkedList<>(); - OtherDescription otherDesc = new OtherDescription(); - CompileDescription projCompileDesc = new CompileDescription(); - - MCUBoard unoboard = Arduino.uno(); - CodeDescription codeDescriptor1 = CodeDescription.createDefaultCPP(); - codeDescriptor1.setCodeFolder(null); - String projectName1 = Shared.getProjectName(codeDescriptor1, unoboard); - ret.add(Arguments.of(projectName1, codeDescriptor1, unoboard, otherDesc, projCompileDesc)); - - CodeDescription codeDescriptor2 = CodeDescription.createDefaultCPP(); - codeDescriptor2.setCodeFolder("src"); - String projectName2 = Shared.getProjectName(codeDescriptor2, unoboard); - ret.add(Arguments.of(projectName2, codeDescriptor2, unoboard, otherDesc, projCompileDesc)); - - CodeDescription codeDescriptor3 = CodeDescription.createDefaultCPP(); - codeDescriptor3.setCodeFolder("SRC"); - String projectName3 = Shared.getProjectName(codeDescriptor3, unoboard); - ret.add(Arguments.of(projectName3, codeDescriptor3, unoboard, otherDesc, projCompileDesc)); - - CodeDescription codeDescriptor4 = CodeDescription.createDefaultCPP(); - codeDescriptor4.setCodeFolder("blabla"); - String projectName4 = Shared.getProjectName(codeDescriptor4, unoboard); - ret.add(Arguments.of(projectName4, codeDescriptor4, unoboard, otherDesc, projCompileDesc)); - - return ret.stream(); - - } - - @ParameterizedTest - @MethodSource("testDifferentSourceFoldersData") - public void testDifferentSourceFolders(String projectName, CodeDescription codeDescriptor, MCUBoard board, - OtherDescription otherDesc, CompileDescription proj1CompileDesc) throws Exception { - - BoardDescription proj1BoardDesc = board.getBoardDescriptor(); - IProject theTestProject = SloeberProject.createArduinoProject(projectName, null, proj1BoardDesc, codeDescriptor, - proj1CompileDesc, otherDesc, new NullProgressMonitor()); - - Shared.waitForIndexer(theTestProject); - theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, null); - assertNull( Shared.hasBuildErrors(theTestProject),"Failed to compile " + projectName); - - String srcFolder = codeDescriptor.getCodeFolder(); - IFile cppFile = null; - if (srcFolder == null) { - cppFile = theTestProject.getFile(projectName + ".cpp"); - } else { - cppFile = theTestProject.getFolder(srcFolder).getFile(projectName + ".cpp"); - } - assertTrue( cppFile.exists(),"Source File not in right location " + projectName); - } - - /** - * Test wether a platform json redirect is handled properly - * https://github.com/jantje/arduino-eclipse-plugin/issues/393 - * - * @throws Exception - */ - @Test - public void redirectedJson() throws Exception { - // this board references to arduino avr so install that one to - Arduino.installLatestAVRBoards(); - BoardsManager.installLatestPlatform("package_talk2.wisen.com_index.json", "Talk2", "avr"); - Map options = new HashMap<>(); - options.put("mhz", "16MHz"); - BoardDescription boardid = BoardsManager.getBoardDescription("package_talk2.wisen.com_index.json", "Talk2", - "avr", "whispernode", options); - if (boardid == null) { - fail("redirect Json "); - return; - } - assertNull(Shared.buildAndVerify("redirect_json", boardid, CodeDescription.createDefaultIno(), - new CompileDescription())); - } - - @Test - public void issue1126LibArchiver() throws Exception { - - MCUBoard leonardoBoard = Arduino.leonardo(); - Map examples = LibraryManager.getExamplesAll(null); - CompileDescription compileDesc = new CompileDescription(); - compileDesc.setEnableParallelBuild(true); - IArduinoLibraryVersion lib = null; - IExample example = null; - for (IExample curExample : examples.values()) { - Collection curLibs = curExample.getArduinoLibraries(); - if (curLibs.size() == 0) { - continue; - } - for (IArduinoLibraryVersion curLib : curLibs) { - if (curLib.getName().equals(HIDlibName)) { - example = curExample; - lib = curLib; - break; - } - } - } - assertNotNull( lib,"HID Lib \"" + HIDlibName + "\" Not found"); - - Set testExamples = new HashSet<>(); - testExamples.add(example); - CodeDescription codeDescriptor = CodeDescription.createExample(false, testExamples); - NullProgressMonitor monitor = new NullProgressMonitor(); - - IProject theTestProject = SloeberProject.createArduinoProject("issue1126LibArchiver", null, - leonardoBoard.getBoardDescriptor(), codeDescriptor, compileDesc, null, null, monitor); - - IndexerPreferences.setScope(theTestProject, IndexerPreferences.SCOPE_PROJECT_PRIVATE); - IndexerPreferences.set(theTestProject, IndexerPreferences.KEY_INCLUDE_HEURISTICS, Boolean.FALSE.toString()); - ICProject icProject = CoreModel.getDefault().create(theTestProject); - CCorePlugin.getIndexManager().reindex(icProject); - - Shared.waitForIndexer(theTestProject); - Thread.sleep(1000);//just make sure the libs can get added - theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, monitor); - IAutoBuildConfigurationDescription autoDesc = IAutoBuildConfigurationDescription.getActiveConfig(theTestProject, - false); - IFile libArchive = autoDesc.getBuildFolder().getFile(HIDlibName + ".ar"); - if(!libArchive.exists()) { - Thread.sleep(1000);//just make sure the libs can get added - Shared.waitForIndexer(theTestProject); - theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, monitor); - } - assertTrue( libArchive.exists(),"Archive " + libArchive.toString() + " does not exists"); - - } - - static Stream openAndClosePreservesSettingsValueCmd() throws Exception { - CodeDescription codeDescriptordefaultCPPRoot = new CodeDescription(CodeDescription.CodeTypes.defaultCPP); - CodeDescription codeDescriptordefaultCPPSrc = new CodeDescription(CodeDescription.CodeTypes.defaultCPP); - CodeDescription codeDescriptordefaultCPXX = new CodeDescription(CodeDescription.CodeTypes.defaultCPP); - - codeDescriptordefaultCPPRoot.setCodeFolder(null); - codeDescriptordefaultCPPSrc.setCodeFolder("src"); - codeDescriptordefaultCPXX.setCodeFolder("XX"); - - List ret = new LinkedList<>(); - ret.add(Arguments.of(Shared.getCounterName("openAndCloseRoot"), codeDescriptordefaultCPPRoot)); - ret.add(Arguments.of(Shared.getCounterName("openAndCloseSrc"), codeDescriptordefaultCPPSrc)); - ret.add(Arguments.of(Shared.getCounterName("openAndCloseXX"), codeDescriptordefaultCPXX)); - - return ret.stream(); - } - - public static Stream NightlyBoardPatronTestData() throws Exception { - Preferences.setUseArduinoToolSelection(true); - CompileDescription compileOptions = new CompileDescription(); - MCUBoard zeroBoard = Arduino.zeroProgrammingPort(); - - List ret = new LinkedList<>(); - TreeMap examples = LibraryManager.getExamplesLibrary(null); - for (Map.Entry curexample : examples.entrySet()) { - String fqn = curexample.getKey().trim(); - IExample example = curexample.getValue(); - IPath examplePath = example.getCodeLocation(); - if (fqn.contains("RTCZero")) { - Example SloeberExample = new Example(fqn, examplePath); - - ret.add(Arguments.of(Shared.getCounterName(SloeberExample.calcLibName() + ":" + example.getName()), - zeroBoard, SloeberExample, compileOptions)); - } - } - return ret.stream(); - } - - @ParameterizedTest - @MethodSource("NightlyBoardPatronTestData") - public void NightlyBoardPatron(String name, MCUBoard boardID, Example example, CompileDescription compileOptions) - throws Exception { - Shared.getLastFailMessage(); - Set examples = new HashSet<>(); - examples.add(example); - CodeDescription codeDescriptor = CodeDescription.createExample(false, examples); - - BoardDescription boardDescriptor = boardID.getBoardDescriptor(); - boardDescriptor.setOptions(boardID.getBoardOptions(example)); - assertNull( - Shared.buildAndVerifyAllBuilders(name, boardID.getBoardDescriptor(), codeDescriptor, compileOptions)); - - } + private static final boolean reinstall_boards_and_libraries = false; + private final static String AUTOBUILD_CFG = ".autoBuildProject"; + private static final String HIDlibName = "HID-Project"; + + /* + * In new new installations (of the Sloeber development environment) the + * installer job will trigger downloads These must have finished before we can + * start testing + */ + @BeforeAll + public static void beforeClass() throws Exception { + Shared.waitForBoardsManager(); + Shared.setDeleteProjects(false); + Preferences.setUseBonjour(false); + installAdditionalBoards(); + installAdditionalLibs(); + } + + @BeforeEach + public void beforeTest() { + //make sure the fail message is cleaned before the tests is started + Shared.getLastFailMessage(); + } + + private static void installAdditionalLibs() { + LibraryManager.installLibrary(HIDlibName); + + } + + public static void installAdditionalBoards() throws Exception { + + String[] packageUrlsToAdd = { ESP8266.packageURL, ESP32.packageURL, + "http://talk2arduino.wisen.com.au/master/package_talk2.wisen.com_index.json" }; + BoardsManager.addPackageURLs(new HashSet<>(Arrays.asList(packageUrlsToAdd)), false); + Shared.waitForBoardsManager(); + if (reinstall_boards_and_libraries) { + BoardsManager.removeAllInstalledPlatforms(); + } + // make sure the needed boards are available + + ESP8266.installLatest(); + ESP32.installLatest(); + Arduino.installLatestAVRBoards(); + Teensy.installLatest(); + Arduino.installLatestSamDBoards(); + LibraryManager.installLibrary("RTCZero"); + + } + + /** + * make sure when switching between a board with variant file and without the + * build still succeeds + * + * @throws CoreException + */ + @Test + public void issue555() throws Exception { + BoardDescription unoBoardid = Arduino.uno().getBoardDescriptor(); + BoardDescription teensyBoardid = Teensy.Teensy3_1().getBoardDescriptor(); + + IProject theTestProject = null; + CodeDescription codeDescriptor = CodeDescription.createDefaultIno(); + String projectName = "issue555"; + NullProgressMonitor monitor = new NullProgressMonitor(); + theTestProject = SloeberProject.createArduinoProject(projectName, null, unoBoardid, codeDescriptor, + new CompileDescription(), monitor); + Shared.waitForIndexer(theTestProject); + theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, monitor); + assertNull(Shared.hasBuildErrors(theTestProject)); + + CCorePlugin cCorePlugin = CCorePlugin.getDefault(); + ICProjectDescription cProjectDescription = cCorePlugin.getProjectDescription(theTestProject); + ICConfigurationDescription activeCfg = cProjectDescription.getActiveConfiguration(); + ISloeberConfiguration sloeberConf = ISloeberConfiguration.getConfig(activeCfg); + sloeberConf.setBoardDescription(teensyBoardid); + cCorePlugin.setProjectDescription(theTestProject, cProjectDescription); + + if (sloeberConf.getArduinoVariantFolder().exists()) { + fail("variant folder exists"); + } + + theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, monitor); + if (Shared.hasBuildErrors(theTestProject) != null) { + theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, monitor); + assertNull(Shared.hasBuildErrors(theTestProject)); + } + } + + /** + * support void loop{}; + * + * @throws Exception + */ + @Test + public void issue687() throws Exception { + BoardDescription unoBoardid = Arduino.uno().getBoardDescriptor(); + + IProject theTestProject = null; + String projectName = "issue687"; + IPath templateFolder = Shared.getTemplateFolder(projectName); + CodeDescription codeDescriptor = CodeDescription.createCustomTemplate(templateFolder); + theTestProject = SloeberProject.createArduinoProject(projectName, null, unoBoardid, codeDescriptor, + new CompileDescription(), new NullProgressMonitor()); + Shared.waitForIndexer(theTestProject); + theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, new NullProgressMonitor()); + assertNull(Shared.hasBuildErrors(theTestProject)); + } + + + + /** + * Test multiple ino's + */ + @Test + public void multipleINOs() throws Exception { + BoardDescription unoBoardid = Arduino.uno().getBoardDescriptor(); + + IProject theTestProject = null; + String projectName = "multipleInos"; + IPath templateFolder = Shared.getTemplateFolder(projectName); + CodeDescription codeDescriptor = CodeDescription.createCustomTemplate(templateFolder); + theTestProject = SloeberProject.createArduinoProject(projectName, null, unoBoardid, codeDescriptor, + new CompileDescription(), new NullProgressMonitor()); + Shared.waitForIndexer(theTestProject); + theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, new NullProgressMonitor()); + assertNull(Shared.hasBuildErrors(theTestProject)); + } + + + + + + @Test + public void create_CPP_based_Sloeber_Project() throws Exception { + BoardDescription unoBoardid = Arduino.uno().getBoardDescriptor(); + + IProject theTestProject = null; + String projectName = "createCPPProject"; + + CodeDescription codeDescriptor = CodeDescription.createDefaultCPP(); + theTestProject = SloeberProject.createArduinoProject(projectName, null, unoBoardid, codeDescriptor, + new CompileDescription(), new NullProgressMonitor()); + Shared.waitForIndexer(theTestProject); + theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, new NullProgressMonitor()); + assertNull(Shared.hasBuildErrors(theTestProject)); + + } + + @Test + public void createDefaultInoProject() throws Exception { + BoardDescription unoBoardid = Arduino.uno().getBoardDescriptor(); + + IProject theTestProject = null; + String projectName = "createDefaultInoProject"; + + CodeDescription codeDescriptor = CodeDescription.createDefaultIno(); + theTestProject = SloeberProject.createArduinoProject(projectName, null, unoBoardid, codeDescriptor, + new CompileDescription(), new NullProgressMonitor()); + Shared.waitForIndexer(theTestProject); + theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, new NullProgressMonitor()); + assertNull(Shared.hasBuildErrors(theTestProject)); + } + + /** + * support void loop{}; + * + * @throws Exception + */ + @Test + public void issue1047_Board_Names_Can_Be_used_as_Strings() throws Exception { + MCUBoard unoBoard = ESP8266.nodeMCU(); + + String projectName = "issue1047_Board_Names_Can_Be_used_as_Strings"; + IPath templateFolder = Shared.getTemplateFolder(projectName); + CodeDescription codeDescriptor = CodeDescription.createCustomTemplate(templateFolder); + IProject theTestProject = SloeberProject.createArduinoProject(projectName, null, unoBoard.getBoardDescriptor(), + codeDescriptor, new CompileDescription(), new NullProgressMonitor()); + theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, new NullProgressMonitor()); + assertNull(Shared.hasBuildErrors(theTestProject)); + + } + + /** + * This test will fail if the arduino compile option are not taken into account + * To do sa a bunch of defines are added to the command line and the code checks + * whether these defines are set properly + * + * @throws Exception + */ + @Test + public void are_jantjes_options_taken_into_account() throws Exception { + BoardDescription unoBoardid = Arduino.uno().getBoardDescriptor(); + + IProject theTestProject = null; + String projectName = "are_defines_found"; + IPath templateFolder = Shared.getTemplateFolder(projectName); + CodeDescription codeDescriptor = CodeDescription.createCustomTemplate(templateFolder); + + NullProgressMonitor monitor = new NullProgressMonitor(); + CompileDescription compileOptions = new CompileDescription(); + compileOptions.set_C_andCPP_CompileOptions("-DTEST_C_CPP"); + compileOptions.set_C_CompileOptions("-DTEST_C"); + compileOptions.set_CPP_CompileOptions("-DTEST_CPP"); + theTestProject = SloeberProject.createArduinoProject(projectName, null, unoBoardid, codeDescriptor, + compileOptions, new NullProgressMonitor()); + + Shared.waitForIndexer(theTestProject); + theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, monitor); + assertNull(Shared.hasBuildErrors(theTestProject)); + } + + /** + * If a .ino file is including a include using extern C is this handled properly + * by the ino to cpp parser + * + * @throws Exception + */ + @Test + public void is_extern_C_taken_into_account() throws Exception { + BoardDescription unoBoardid = Arduino.uno().getBoardDescriptor(); + + IProject theTestProject = null; + String projectName = "externc"; + IPath templateFolder = Shared.getTemplateFolder(projectName); + CodeDescription codeDescriptor = CodeDescription.createCustomTemplate(templateFolder); + + NullProgressMonitor monitor = new NullProgressMonitor(); + theTestProject = SloeberProject.createArduinoProject(projectName, null, unoBoardid, codeDescriptor, + new CompileDescription(), new NullProgressMonitor()); + + Shared.waitForIndexer(theTestProject); + theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, monitor); + assertNull(Shared.hasBuildErrors(theTestProject)); + } + + /** + * If a .ino file is defining defines before including a include this should be + * handled properly by the ino to cpp parser + * + * @throws Exception + */ + @Test + public void are_defines_before_includes_taken_into_account() throws Exception { + BoardDescription unoBoardid = Arduino.uno().getBoardDescriptor(); + + IProject theTestProject = null; + String projectName = "defines_and_includes"; + IPath templateFolder = Shared.getTemplateFolder(projectName); + CodeDescription codeDescriptor = CodeDescription.createCustomTemplate(templateFolder); + + NullProgressMonitor monitor = new NullProgressMonitor(); + theTestProject = SloeberProject.createArduinoProject(projectName, null, unoBoardid, codeDescriptor, + new CompileDescription(), new NullProgressMonitor()); + + Shared.waitForIndexer(theTestProject); + theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, monitor); + assertNull(Shared.hasBuildErrors(theTestProject)); + + } + + /** + * Does Sloeber still compile after a configuration renamen + * + * @throws Exception + */ + @Test + public void rename_Configuration() throws Exception { + BoardDescription unoBoardid = Arduino.uno().getBoardDescriptor(); + + IProject theTestProject = null; + String projectName = "rename_Configuration"; + + CodeDescription codeDescriptor = CodeDescription.createDefaultIno(); + + NullProgressMonitor monitor = new NullProgressMonitor(); + theTestProject = SloeberProject.createArduinoProject(projectName, null, unoBoardid, codeDescriptor, + new CompileDescription(), new NullProgressMonitor()); + + Shared.waitForIndexer(theTestProject); + theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, monitor); + assertNull( Shared.hasBuildErrors(theTestProject),"Failed to compile the project before config rename"); + + CCorePlugin cCorePlugin = CCorePlugin.getDefault(); + ICProjectDescription prjCDesc = cCorePlugin.getProjectDescription(theTestProject); + ICConfigurationDescription activeConfig = prjCDesc.getActiveConfiguration(); + activeConfig.setName("renamedConfig"); + cCorePlugin.setProjectDescription(theTestProject, prjCDesc); + + theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, monitor); + assertNull(Shared.hasBuildErrors(theTestProject),"Failed to compile the project after config rename" ); + } + + // /** + // * Does Sloeber still compile after a project rename + // * + // * @throws Exception + // */ + // @Test + // public void rename_Project() throws Exception { + // BoardDescription unoBoardid = Arduino.uno().getBoardDescriptor(); + // + // IProject theTestProject = null; + // String projectName = "rename_project"; + // String projectNameRenamed = "renamed_project"; + // + // CodeDescription codeDescriptor = CodeDescription.createDefaultIno(); + // + // NullProgressMonitor monitor = new NullProgressMonitor(); + // theTestProject = SloeberProject.createArduinoProject(projectName, null, + // unoBoardid, codeDescriptor, + // new CompileDescription(), new NullProgressMonitor()); + // + // Shared.waitForAllJobsToFinish(); // for the indexer + // theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, monitor); + // if (Shared.hasBuildErrors(theTestProject)) { + // fail("Failed to compile the project before project rename"); + // } + // theTestProject.build(IncrementalProjectBuilder.CLEAN_BUILD, monitor); + // + // CCorePlugin cCorePlugin = CCorePlugin.getDefault(); + // ICProjectDescription prjCDesc = + // cCorePlugin.getProjectDescription(theTestProject); + // + // IProjectDescription descr = theTestProject.getDescription(); + // descr.ren(projectNameRenamed); + // theTestProject.setDescription(descr, null); + // + // Shared.waitForAllJobsToFinish(); // for the indexer + // theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, monitor); + // if (Shared.hasBuildErrors(theTestProject)) { + // fail("Failed to compile the project after project rename"); + // } + // } + + /** + * open and close a project should keep the compileDescription and + * BoardDescriotion + * + * @throws Exception + */ + @ParameterizedTest + @MethodSource("openAndClosePreservesSettingsValueCmd") + public void openAndClosePreservesSettings(String projectName, CodeDescription codeDescriptor) throws Exception { + BoardDescription unoBoardid = Arduino.uno().getBoardDescriptor(); + + IProject theTestProject = null; + + CompileDescription inCompileDescription = getBunkersCompileDescription(); + + theTestProject = SloeberProject.createArduinoProject(projectName, null, unoBoardid, codeDescriptor, + inCompileDescription, new NullProgressMonitor()); + + Shared.waitForIndexer(theTestProject); + // also do a build + theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, null); + assertNull(Shared.hasBuildErrors(theTestProject),"Failed to compile the project before close: " + projectName); + + // also do a clean + theTestProject.build(IncrementalProjectBuilder.CLEAN_BUILD, null); + + // Read the data we want to test + ISloeberConfiguration sloeberConf = ISloeberConfiguration.getActiveConfig(theTestProject); + BoardDescription createdBoardDesc = sloeberConf.getBoardDescription(); + CompileDescription createdCompileDesc = sloeberConf.getCompileDescription(); + + // close and reopen the project + theTestProject.close(null); + // just wait a while + Thread.sleep(1000); + theTestProject.open(null); + Shared.waitForIndexer(theTestProject); + + // read the data we want to test + sloeberConf = ISloeberConfiguration.getActiveConfig(theTestProject); + if (sloeberConf == null) { + fail("failed to open and close project"); + } + BoardDescription reopenedBoardDesc = sloeberConf.getBoardDescription(); + CompileDescription reopenedCompileDesc = sloeberConf.getCompileDescription(); + + // check the data is equal + boolean createBoardsDiff = !unoBoardid.equals(createdBoardDesc); + boolean createCompileDiff = !inCompileDescription.equals(createdCompileDesc); + boolean openBoardsDiff = !reopenedBoardDesc.equals(createdBoardDesc); + boolean openCompileDiff = !reopenedCompileDesc.equals(createdCompileDesc); + + if (createBoardsDiff || createCompileDiff) { + fail("Created project does not match creation parameters."); + } + if (openBoardsDiff || openCompileDiff) { + fail("Opened project does not match closed project parameters."); + } + + // also do a build + theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, null); + assertNull(Shared.hasBuildErrors(theTestProject),"Failed to compile the project after open: " + projectName); + + } + + /** + * open and close a project should keep the compileDescription and + * BoardDescriotion + * + * @throws Exception + */ + + @Test + public void openModAndCloseUsesSavedSettings() throws Exception { + CodeDescription codeDesc = new CodeDescription(CodeDescription.CodeTypes.defaultCPP); + + String proj1Name = "openModAndClose1"; + BoardDescription proj1BoardDesc = Arduino.uno().getBoardDescriptor(); + OtherDescription otherDesc = new OtherDescription(); + otherDesc.setVersionControlled(true); + CompileDescription proj1CompileDesc = getBunkersCompileDescription(); + IProject proj1 = SloeberProject.createArduinoProject(proj1Name, null, proj1BoardDesc, codeDesc, + proj1CompileDesc, otherDesc, new NullProgressMonitor()); + + String proj2Name = "openModAndClose2"; + BoardDescription proj2BoardDesc = Arduino.mega2560Board().getBoardDescriptor(); + CompileDescription proj2CompileDesc = new CompileDescription(); + IProject proj2 = SloeberProject.createArduinoProject(proj2Name, null, proj2BoardDesc, codeDesc, + proj2CompileDesc, new NullProgressMonitor()); + + // Read the data we want to test + ISloeberConfiguration sloeberConf = ISloeberConfiguration.getActiveConfig(proj1); + BoardDescription proj1CreatedBoardDesc = sloeberConf.getBoardDescription(); + CompileDescription proj1CreatedCompileDesc = sloeberConf.getCompileDescription(); + + ISloeberConfiguration sloeberConf2 = ISloeberConfiguration.getActiveConfig(proj2); + BoardDescription proj2CreatedBoardDesc = sloeberConf2.getBoardDescription(); + CompileDescription proj2CreatedCompileDesc = sloeberConf2.getCompileDescription(); + + // get the filenames to copy + IFile file = proj1.getFile(AUTOBUILD_CFG); + File proj1SloeberFile = file.getLocation().toFile(); + + file = proj2.getFile(AUTOBUILD_CFG); + File proj2SloeberFile = file.getLocation().toFile(); + + // close and reopen the project + proj2.close(null); + // just wait a while + Thread.sleep(1000); + + // copy from proj1 to proj2 + FileUtils.copyFile(proj1SloeberFile, proj2SloeberFile); + + // reopen the project + proj2.open(null); + Shared.waitForIndexer(proj2); + + // reread project 2 + ISloeberConfiguration sloebercfg2 = ISloeberConfiguration.getActiveConfig(proj2); + if (sloebercfg2 == null) { + fail("failed to load the sloeber configuration"); + } + BoardDescription proj2OpenedBoardDesc = sloebercfg2.getBoardDescription(); + CompileDescription proj2OpenedCompileDesc = sloebercfg2.getCompileDescription(); + + // check the setup was done correctly + if (!proj1BoardDesc.equals(proj1CreatedBoardDesc)) { + fail("Project 1 not created properly."); + } + if (!proj2BoardDesc.equals(proj2CreatedBoardDesc)) { + fail("Project 2 not created properly."); + } + if (!proj1CompileDesc.equals(proj1CreatedCompileDesc)) { + fail("Project 1 not created properly."); + } + if (!proj2CompileDesc.equals(proj2CreatedCompileDesc)) { + fail("Project 2 not created properly."); + } + + // check wether the file modification was taken into account + if (!proj1BoardDesc.equals(proj2OpenedBoardDesc)) { + fail("Project 2 not created properly."); + } + if (!proj1CompileDesc.equals(proj2OpenedCompileDesc)) { + fail("Project 2 not created properly."); + } + + } + + @Test + public void createProjectWithURI() throws Exception { + CodeDescription codeDesc = new CodeDescription(CodeDescription.CodeTypes.defaultCPP); + + String proj1Name = "projectWithURI"; + String codeFolderName = "locationWithURI"; + BoardDescription proj1BoardDesc = Arduino.uno().getBoardDescriptor(); + OtherDescription otherDesc = new OtherDescription(); + CompileDescription proj1CompileDesc = getBunkersCompileDescription(); + final IWorkspace workspace = ResourcesPlugin.getWorkspace(); + IPath projectFolder = workspace.getRoot().getLocation().removeLastSegments(1).append(codeFolderName); + URI uri = projectFolder.toFile().toURI(); + // workspace.getRoot().getFolder(Path.fromOSString(codeFolderName)).getLocationURI(); + IProject theTestProject = SloeberProject.createArduinoProject(proj1Name, uri, proj1BoardDesc, codeDesc, + proj1CompileDesc, otherDesc, new NullProgressMonitor()); + + Shared.waitForIndexer(theTestProject); + theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, null); + assertNull(Shared.hasBuildErrors(theTestProject), + "Failed to compile the project: " + Shared.hasBuildErrors(theTestProject)); + String fileLocation = projectFolder.append("src").append(proj1Name + ".cpp").toString(); + + IFile cppFile = theTestProject.getFolder("src").getFile(proj1Name + ".cpp"); + assertTrue( cppFile.exists(),"File not in correct location"); + assertEquals( cppFile.getLocation().toString(), fileLocation,"File not in correct location"); + + } + + static CompileDescription getBunkersCompileDescription() { + CompileDescription inCompileDescription = new CompileDescription(); + + inCompileDescription.set_All_CompileOptions("-Deen=1"); + inCompileDescription.set_Archive_CompileOptions("-Dtwee=2"); + inCompileDescription.set_Assembly_CompileOptions("-Drie=3"); + inCompileDescription.set_C_andCPP_CompileOptions("-Dvier=4"); + inCompileDescription.set_C_CompileOptions("-Dvijf=5"); + inCompileDescription.set_Link_CompileOptions("-Dzes=6"); + inCompileDescription.set_CPP_CompileOptions("-Dzeven=7"); + inCompileDescription.setSizeCommand(SizeCommands.ARDUINO_WAY); + inCompileDescription.setEnableParallelBuild(true); + inCompileDescription.setWarningLevel(WarningLevels.NONE); + return inCompileDescription; + } + + /** + * check to see whether upload recipe key is correct for a couple of boards that + * have failed in the past + * + * @throws Exception + */ + @Test + public void uploadPattern() throws Exception { + BoardDescription boardDescriptor = Arduino.uno().getBoardDescriptor(); + String recipeKey = boardDescriptor.getUploadPatternKey(); + assertEquals( "tools.avrdude.upload.pattern", recipeKey,"uno upload recipe key is wrong"); + boardDescriptor = ESP32.esp32().getBoardDescriptor(); + boardDescriptor.setUploadPort("host 10.10.10.10"); + recipeKey = boardDescriptor.getUploadPatternKey(); + assertEquals( "tools.esptool_py.upload.network_pattern", recipeKey,"ESP OTA upload recipe key is wrong"); + + } + + public static Stream testDifferentSourceFoldersData() throws Exception { + List ret = new LinkedList<>(); + OtherDescription otherDesc = new OtherDescription(); + CompileDescription projCompileDesc = new CompileDescription(); + + MCUBoard unoboard = Arduino.uno(); + CodeDescription codeDescriptor1 = CodeDescription.createDefaultCPP(); + codeDescriptor1.setCodeFolder(null); + String projectName1 = Shared.getProjectName(codeDescriptor1, unoboard); + ret.add(Arguments.of(projectName1, codeDescriptor1, unoboard, otherDesc, projCompileDesc)); + + CodeDescription codeDescriptor2 = CodeDescription.createDefaultCPP(); + codeDescriptor2.setCodeFolder("src"); + String projectName2 = Shared.getProjectName(codeDescriptor2, unoboard); + ret.add(Arguments.of(projectName2, codeDescriptor2, unoboard, otherDesc, projCompileDesc)); + + CodeDescription codeDescriptor3 = CodeDescription.createDefaultCPP(); + codeDescriptor3.setCodeFolder("SRC"); + String projectName3 = Shared.getProjectName(codeDescriptor3, unoboard); + ret.add(Arguments.of(projectName3, codeDescriptor3, unoboard, otherDesc, projCompileDesc)); + + CodeDescription codeDescriptor4 = CodeDescription.createDefaultCPP(); + codeDescriptor4.setCodeFolder("blabla"); + String projectName4 = Shared.getProjectName(codeDescriptor4, unoboard); + ret.add(Arguments.of(projectName4, codeDescriptor4, unoboard, otherDesc, projCompileDesc)); + + return ret.stream(); + + } + + @ParameterizedTest + @MethodSource("testDifferentSourceFoldersData") + public void testDifferentSourceFolders(String projectName, CodeDescription codeDescriptor, MCUBoard board, + OtherDescription otherDesc, CompileDescription proj1CompileDesc) throws Exception { + + BoardDescription proj1BoardDesc = board.getBoardDescriptor(); + IProject theTestProject = SloeberProject.createArduinoProject(projectName, null, proj1BoardDesc, codeDescriptor, + proj1CompileDesc, otherDesc, new NullProgressMonitor()); + + Shared.waitForIndexer(theTestProject); + theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, null); + assertNull( Shared.hasBuildErrors(theTestProject),"Failed to compile " + projectName); + + String srcFolder = codeDescriptor.getCodeFolder(); + IFile cppFile = null; + if (srcFolder == null) { + cppFile = theTestProject.getFile(projectName + ".cpp"); + } else { + cppFile = theTestProject.getFolder(srcFolder).getFile(projectName + ".cpp"); + } + assertTrue( cppFile.exists(),"Source File not in right location " + projectName); + } + + /** + * Test wether a platform json redirect is handled properly + * https://github.com/jantje/arduino-eclipse-plugin/issues/393 + * + * @throws Exception + */ + @Test + public void redirectedJson() throws Exception { + // this board references to arduino avr so install that one to + Arduino.installLatestAVRBoards(); + BoardsManager.installLatestPlatform("package_talk2.wisen.com_index.json", "Talk2", "avr"); + Map options = new HashMap<>(); + options.put("mhz", "16MHz"); + BoardDescription boardid = BoardsManager.getBoardDescription("package_talk2.wisen.com_index.json", "Talk2", + "avr", "whispernode", options); + if (boardid == null) { + fail("redirect Json "); + return; + } + assertNull(Shared.buildAndVerify("redirect_json", boardid, CodeDescription.createDefaultIno(), + new CompileDescription())); + } + + @Test + public void issue1126LibArchiver() throws Exception { + + MCUBoard leonardoBoard = Arduino.leonardo(); + Map examples = LibraryManager.getExamplesAll(null); + CompileDescription compileDesc = new CompileDescription(); + compileDesc.setEnableParallelBuild(true); + IArduinoLibraryVersion lib = null; + IExample example = null; + for (IExample curExample : examples.values()) { + Collection curLibs = curExample.getArduinoLibraries(); + if (curLibs.size() == 0) { + continue; + } + for (IArduinoLibraryVersion curLib : curLibs) { + if (curLib.getName().equals(HIDlibName)) { + example = curExample; + lib = curLib; + break; + } + } + } + assertNotNull( lib,"HID Lib \"" + HIDlibName + "\" Not found"); + + Set testExamples = new HashSet<>(); + testExamples.add(example); + CodeDescription codeDescriptor = CodeDescription.createExample(false, testExamples); + NullProgressMonitor monitor = new NullProgressMonitor(); + + IProject theTestProject = SloeberProject.createArduinoProject("issue1126LibArchiver", null, + leonardoBoard.getBoardDescriptor(), codeDescriptor, compileDesc, null, null, monitor); + + IndexerPreferences.setScope(theTestProject, IndexerPreferences.SCOPE_PROJECT_PRIVATE); + IndexerPreferences.set(theTestProject, IndexerPreferences.KEY_INCLUDE_HEURISTICS, Boolean.FALSE.toString()); + ICProject icProject = CoreModel.getDefault().create(theTestProject); + CCorePlugin.getIndexManager().reindex(icProject); + + IAutoBuildConfigurationDescription autoDesc = IAutoBuildConfigurationDescription.getActiveConfig(theTestProject, + false); + IFile libArchive = autoDesc.getBuildFolder().getFile(HIDlibName + ".ar"); + int tries=1; + while (!(libArchive.exists() || (tries++>4))) { + Thread.sleep(1000);//just make sure the libs can get added + Shared.waitForIndexer(theTestProject); + Thread.sleep(1000);//just make sure the libs can get added + theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, monitor); + } + assertTrue( libArchive.exists(),"Archive " + libArchive.toString() + " does not exists"); + + } + + static Stream openAndClosePreservesSettingsValueCmd() throws Exception { + CodeDescription codeDescriptordefaultCPPRoot = new CodeDescription(CodeDescription.CodeTypes.defaultCPP); + CodeDescription codeDescriptordefaultCPPSrc = new CodeDescription(CodeDescription.CodeTypes.defaultCPP); + CodeDescription codeDescriptordefaultCPXX = new CodeDescription(CodeDescription.CodeTypes.defaultCPP); + + codeDescriptordefaultCPPRoot.setCodeFolder(null); + codeDescriptordefaultCPPSrc.setCodeFolder("src"); + codeDescriptordefaultCPXX.setCodeFolder("XX"); + + List ret = new LinkedList<>(); + ret.add(Arguments.of(Shared.getCounterName("openAndCloseRoot"), codeDescriptordefaultCPPRoot)); + ret.add(Arguments.of(Shared.getCounterName("openAndCloseSrc"), codeDescriptordefaultCPPSrc)); + ret.add(Arguments.of(Shared.getCounterName("openAndCloseXX"), codeDescriptordefaultCPXX)); + + return ret.stream(); + } + + public static Stream NightlyBoardPatronTestData() throws Exception { + Preferences.setUseArduinoToolSelection(true); + CompileDescription compileOptions = new CompileDescription(); + MCUBoard zeroBoard = Arduino.zeroProgrammingPort(); + + List ret = new LinkedList<>(); + TreeMap examples = LibraryManager.getExamplesLibrary(null); + for (Map.Entry curexample : examples.entrySet()) { + String fqn = curexample.getKey().trim(); + IExample example = curexample.getValue(); + IPath examplePath = example.getCodeLocation(); + if (fqn.contains("RTCZero")) { + Example SloeberExample = new Example(fqn, examplePath); + + ret.add(Arguments.of(Shared.getCounterName(SloeberExample.calcLibName() + ":" + example.getName()), + zeroBoard, SloeberExample, compileOptions)); + } + } + return ret.stream(); + } + + @ParameterizedTest + @MethodSource("NightlyBoardPatronTestData") + public void NightlyBoardPatron(String name, MCUBoard boardID, Example example, CompileDescription compileOptions) + throws Exception { + Shared.getLastFailMessage(); + Set examples = new HashSet<>(); + examples.add(example); + CodeDescription codeDescriptor = CodeDescription.createExample(false, examples); + + BoardDescription boardDescriptor = boardID.getBoardDescriptor(); + boardDescriptor.setOptions(boardID.getBoardOptions(example)); + assertNull( + Shared.buildAndVerifyAllBuilders(name, boardID.getBoardDescriptor(), codeDescriptor, compileOptions)); + + } } From ac9a26c1a4cde7b542392f8885f6dfe0c30edae9 Mon Sep 17 00:00:00 2001 From: jan Date: Mon, 23 Sep 2024 01:22:20 +0200 Subject: [PATCH 036/115] Cater for null configuration --- .../autoBuild/extensionPoint/providers/CommonBuilder.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/CommonBuilder.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/CommonBuilder.java index fe65ccd5..803b63b3 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/CommonBuilder.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/CommonBuilder.java @@ -311,6 +311,8 @@ private static Set getConfigsToBuild(IProject cfgToBuild.add((AutoBuildConfigurationDescription) IAutoBuildConfigurationDescription .getActiveConfig(project, false)); } + //remove null configurations that may have been added + cfgToBuild.remove(null); return cfgToBuild; } From 23c723b3a482f1d695232318a239af27fef75af9 Mon Sep 17 00:00:00 2001 From: jan Date: Tue, 24 Sep 2024 03:26:59 +0200 Subject: [PATCH 037/115] Reattaching libs when lib folder does not exists throws exception As the lib folder is now per configuration there are use cases (like imported projects) where it is expected the folder does not yet exists. So just create the folder if it does not exists. --- .../src/io/sloeber/core/internal/SloeberConfiguration.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java b/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java index e93f8d22..7cb39cd5 100644 --- a/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java +++ b/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java @@ -586,6 +586,9 @@ public void reAttachLibraries() { // Remove all existing lib folders that are not known or are linking to the // wrong lib try { + if(!libFolder.exists()) { + libFolder.create(true, true, new NullProgressMonitor()); + } for (IResource curResource : libFolder.members()) { if (curResource instanceof IFolder) { IFolder curFolder = (IFolder) curResource; From 4a54e027248583c4dd186981a2be31235da6acf5 Mon Sep 17 00:00:00 2001 From: jan Date: Tue, 24 Sep 2024 03:32:20 +0200 Subject: [PATCH 038/115] Convert to sloeber project basic functionality seems to work Now I basically read everything in memory and build the project from there. That is for both autoBuildProject creation as sloeber project migration --- .../AutoBuildProjectGenerator.java | 88 +++++++++++++------ .../src/io/sloeber/core/Activator.java | 3 + .../io/sloeber/core/api/SloeberProject.java | 88 ++++++++++--------- .../core/listeners/IndexerController.java | 88 +++++++++++++++++++ 4 files changed, 201 insertions(+), 66 deletions(-) create mode 100644 io.sloeber.core/src/io/sloeber/core/listeners/IndexerController.java diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildProjectGenerator.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildProjectGenerator.java index 6243b205..7e857377 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildProjectGenerator.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildProjectGenerator.java @@ -2,6 +2,7 @@ package io.sloeber.autoBuild.integration; import java.net.URI; +import java.util.HashSet; import java.util.List; import org.eclipse.cdt.core.CCProjectNature; @@ -35,7 +36,6 @@ import io.sloeber.autoBuild.core.Activator; import io.sloeber.autoBuild.schema.api.IConfiguration; import io.sloeber.autoBuild.schema.api.IProjectType; -import io.sloeber.autoBuild.schema.internal.Configuration; public class AutoBuildProjectGenerator implements IGenerator { private URI myLocationURI = null; @@ -62,16 +62,56 @@ public void generate(IProgressMonitor monitor) throws CoreException { @Override public void run(IProgressMonitor internalMonitor) throws CoreException { + + ICProjectDescriptionManager mngr = CoreModel.getDefault().getProjectDescriptionManager(); + IProjectType autoBuildProjType = AutoBuildManager.getProjectType(myProjectType.getExtensionPointID(), + myProjectType.getExtensionID(), myProjectType.getId(), true); + if (autoBuildProjType == null) { + // project type not found can not continue + IStatus status = new Status(IStatus.ERROR, Activator.getId(), + "Did not find the projectType with " + myProjectType.getId() + " for extension ID " //$NON-NLS-1$ //$NON-NLS-2$ + + myProjectType.getExtensionID() + " in extensionpointID " //$NON-NLS-1$ + + myProjectType.getExtensionPointID()); + CoreException exception = new CoreException(status); + throw (exception); + } + HashSet configNames = new HashSet<>(); + myProject = root.getProject(myProjectName); + if (myProject.exists()) { + // Update the existing configurations + ICProjectDescription orgdesc = mngr.getProjectDescription(myProject, false); + for (ICConfigurationDescription curConfig : orgdesc.getConfigurations()) { + configNames.add(curConfig.getName()); + } + myLocationURI=myProject.getLocationURI(); + //CDT reads the .cproject file when setting the configuration for making the delta + //and old stuff stays in it causing problems + //ICProject cProject = CoreModel.getDefault().getCModel().getCProject(myProject.getName()); + IFile CdtDotFile = myProject.getFile(".cproject"); //$NON-NLS-1$ + if(CdtDotFile.exists()) { + CdtDotFile.delete(true, monitor); + } + myProject.close(monitor); + myProject.delete(false, true, monitor); + + } else { + // Create new configurations based on the model configurations from the + // plugin.xml + IConfiguration[] modelConfigs = autoBuildProjType.getConfigurations(); + for (IConfiguration iConfig : modelConfigs) { + configNames.add(iConfig.getName()); + } + } + + + IProjectDescription description = workspace.newProjectDescription(myProjectName); if (myLocationURI != null) { description.setLocationURI(myLocationURI); } - myProject = root.getProject(myProjectName); - if(!myProject.exists()) { - //myProject.delete(false, false, monitor); - myProject.create(description, monitor); - myProject.open(monitor); - } + + myProject.create(description, monitor); + myProject.open(monitor); CProjectNature.addCNature(myProject, monitor); if (CCProjectNature.CC_NATURE_ID.equals(myNatureID)) { @@ -80,7 +120,7 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { AutoBuildNature.addNature(myProject, monitor); IContainer srcFolder = myProject; - if (myCodeRootFolder!=null && !myCodeRootFolder.isBlank()) { + if (myCodeRootFolder != null && !myCodeRootFolder.isBlank()) { IFolder actualFolder = myProject.getFolder(myCodeRootFolder); srcFolder = actualFolder; if (!srcFolder.exists()) { @@ -93,44 +133,40 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { myProject = CCorePlugin.getDefault().createCDTProject(description, myProject, monitor); - ICProjectDescriptionManager mngr = CoreModel.getDefault().getProjectDescriptionManager(); + ICProjectDescription des = mngr.createProjectDescription(myProject, false, true); - IProjectType sloeberProjType = AutoBuildManager.getProjectType(myProjectType.getExtensionPointID(), - myProjectType.getExtensionID(), myProjectType.getId(), true); - if (sloeberProjType == null) { - // project type not found can not continue - IStatus status = new Status(IStatus.ERROR, Activator.getId(), - "Did not find the projectType with " + myProjectType.getId() + " for extension ID " //$NON-NLS-1$ //$NON-NLS-2$ - + myProjectType.getExtensionID() + " in extensionpointID " //$NON-NLS-1$ - + myProjectType.getExtensionPointID()); - CoreException exception = new CoreException(status); - throw (exception); - } - IConfiguration[] modelConfigs = sloeberProjType.getConfigurations(); - for (IConfiguration iConfig : modelConfigs) { - Configuration config = (Configuration) iConfig; + IConfiguration defaultConfig = autoBuildProjType.getConfigurations()[0]; + + for (String curConfigName : configNames) { + IConfiguration config = autoBuildProjType.getConfiguration(curConfigName); + if (config == null) { + config = defaultConfig; + } AutoBuildConfigurationDescription data = new AutoBuildConfigurationDescription(config, myProject, myBuldTools, myCodeRootFolder); assert (data != null); + data.setBuilder(data.getBuilder(myBuilderID)); ICConfigurationDescription cdtCfgDes = des .createConfiguration(AutoBuildConfigurationDescriptionProvider.CFG_DATA_PROVIDER_ID, data); + cdtCfgDes.setName(curConfigName); data.setCdtConfigurationDescription(cdtCfgDes); - data.setBuilder(data.getBuilder(myBuilderID)); // Set the language Settings - String[] defaultIds = iConfig.getDefaultLanguageSettingsProviderIds().toArray(new String[0]); + String[] defaultIds = config.getDefaultLanguageSettingsProviderIds().toArray(new String[0]); List providers = LanguageSettingsManager .createLanguageSettingsProviders(defaultIds); ILanguageSettingsProvidersKeeper languageKeeper = (ILanguageSettingsProvidersKeeper) cdtCfgDes; if (cdtCfgDes instanceof ILanguageSettingsProvidersKeeper) { languageKeeper.setLanguageSettingProviders(providers); } - AutoBuildCommon.createFolder( data.getBuildFolder()); + AutoBuildCommon.createFolder(data.getBuildFolder()); } if (!myNeedsMoreWork) { des.setCdtProjectCreated(); } + des.setActiveConfiguration(des.getConfigurations()[0]); + des.setDefaultSettingConfiguration(des.getConfigurations()[0]); mngr.setProjectDescription(myProject, des); } diff --git a/io.sloeber.core/src/io/sloeber/core/Activator.java b/io.sloeber.core/src/io/sloeber/core/Activator.java index d90edb43..bd25f063 100644 --- a/io.sloeber.core/src/io/sloeber/core/Activator.java +++ b/io.sloeber.core/src/io/sloeber/core/Activator.java @@ -39,6 +39,7 @@ import io.sloeber.core.api.ConfigurationPreferences; import io.sloeber.core.common.InstancePreferences; import io.sloeber.core.listeners.ConfigurationChangeListener; +import io.sloeber.core.listeners.IndexerController; import io.sloeber.core.listeners.IndexerListener; import io.sloeber.core.tools.PackageManager; @@ -190,6 +191,7 @@ private static void registerListeners() { CCorePlugin.getIndexManager().addIndexChangeListener(myindexerListener); CCorePlugin.getIndexManager().addIndexerStateListener(myindexerListener); CoreModel singCoreModel = CoreModel.getDefault(); + IndexerController.registerIndexerController(); singCoreModel.addCProjectDescriptionListener(myConfigurationChangeListener,CProjectDescriptionEvent.ABOUT_TO_APPLY|CProjectDescriptionEvent.APPLIED); @@ -202,6 +204,7 @@ private static void unRegisterListeners() { CCorePlugin.getIndexManager().removeIndexChangeListener(myindexerListener); CoreModel singCoreModel = CoreModel.getDefault(); singCoreModel.removeCProjectDescriptionListener(myConfigurationChangeListener); + IndexerController.unRegisterIndexerController(); diff --git a/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java b/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java index 53c8486a..72a79c43 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java +++ b/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java @@ -16,7 +16,6 @@ import org.eclipse.cdt.core.settings.model.ICProjectDescription; import org.eclipse.cdt.core.settings.model.ICSettingEntry; import org.eclipse.cdt.core.settings.model.ICSourceEntry; -import org.eclipse.cdt.core.settings.model.util.CDataUtil; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; @@ -43,9 +42,11 @@ import io.sloeber.autoBuild.helpers.api.KeyValueTree; import io.sloeber.autoBuild.integration.AutoBuildConfigurationDescription; import io.sloeber.autoBuild.integration.AutoBuildManager; +import io.sloeber.autoBuild.schema.api.IConfiguration; import io.sloeber.autoBuild.schema.api.IProjectType; import io.sloeber.core.Activator; import io.sloeber.core.internal.SloeberConfiguration; +import io.sloeber.core.listeners.IndexerController; import io.sloeber.core.natures.SloeberNature; import io.sloeber.core.txt.TxtFile; @@ -84,10 +85,6 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { OldCoreFolder.delete(true, monitor); } -// IFile CdtDotFile = project.getFile(".cproject"); //$NON-NLS-1$ -// if(CdtDotFile.exists()) { -// CdtDotFile.delete(true, monitor); -// } File sloeberDotFile = project.getFile(".sproject").getLocation().toFile(); //$NON-NLS-1$ File sloeberCfgFile = project.getFile(SLOEBER_CFG).getLocation().toFile(); @@ -104,6 +101,22 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { } } + CCorePlugin cCorePlugin = CCorePlugin.getDefault(); + ICProjectDescription oldPrjCDesc = cCorePlugin.getProjectDescription(project, false); + Set cfgNames=new HashSet<>(); + for (ICConfigurationDescription curConfig : oldPrjCDesc.getConfigurations()) { + cfgNames.add( curConfig.getName()); + } + + + KeyValueTree oldConfigs=null; + if (oldSloeberInfo != null) { + oldConfigs = oldSloeberInfo.getData().getChild("Config"); //$NON-NLS-1$ + for (String curTree : oldConfigs.getChildren().keySet()) { + cfgNames.add(curTree); + } + } + String builderName = AutoBuildProject.INTERNAL_BUILDER_ID; IBuildTools buildTools = IBuildToolsManager.getDefault().getBuildTools(SLOEBER_BUILD_TOOL_PROVIDER_ID, null); @@ -114,46 +127,33 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { CCProjectNature.CC_NATURE_ID, codeProvider, buildTools, true, internalMonitor); SloeberNature.addNature(project, internalMonitor); - - CCorePlugin cCorePlugin = CCorePlugin.getDefault(); ICProjectDescription prjCDesc = cCorePlugin.getProjectDescription(project, true); - Set cfgNames=new HashSet<>(); - for (ICConfigurationDescription curConfig : prjCDesc.getConfigurations()) { - cfgNames.add( curConfig.getName()); - } - KeyValueTree oldConfigs=null; - if (oldSloeberInfo != null) { - oldConfigs = oldSloeberInfo.getData().getChild("Config"); //$NON-NLS-1$ - for (String curTree : oldConfigs.getChildren().keySet()) { - cfgNames.add(curTree); - } - } + + IConfiguration defaultConfig = projectType.getConfigurations()[0]; for(String cfgName:cfgNames) { KeyValueTree oldConfig=null; if (oldConfigs != null) { oldConfig = oldConfigs.getChild(cfgName); } - //get the CDT config and AutoBuildConfigurationDescription (if it does not exist create it) - ICConfigurationDescription curConfig = prjCDesc.getConfigurationByName(cfgName); - if(curConfig==null) { - //config does not exists so create it - String id = CDataUtil.genId(Activator.getId()); - curConfig = prjCDesc.createConfiguration(id,cfgName,prjCDesc.getActiveConfiguration()); + + IConfiguration config = projectType.getConfiguration(cfgName); + if (config == null) { + config = defaultConfig; } - IAutoBuildConfigurationDescription autoConf = IAutoBuildConfigurationDescription - .getConfig(curConfig); - if (!(autoConf instanceof AutoBuildConfigurationDescription)) { - // this should not happen as we just created a autoBuild project - Activator.log(new Status(SLOEBER_STATUS_DEBUG, Activator.getId(), - "\"Auto build created a project that does not seem to be a autobuild project :-s : " //$NON-NLS-1$ - + project.getName())); - continue; + ICConfigurationDescription curConfig = prjCDesc.getConfigurationByName(cfgName); + AutoBuildConfigurationDescription autoConf=null; + if (curConfig == null) { + // config does not exists so create it + autoConf = new AutoBuildConfigurationDescription(config, + project, buildTools, null); + autoConf.setName(cfgName); + curConfig = prjCDesc.createConfiguration(AutoBuildProject.INTERNAL_BUILDER_ID, autoConf); + autoConf.setCdtConfigurationDescription(curConfig); + }else { + autoConf = (AutoBuildConfigurationDescription) IAutoBuildConfigurationDescription.getConfig(curConfig); } - - - ICSourceEntry newSourceEntries[] = new ICSourceEntry[2]; // old Sloeber project so the code is in the root of the project for sure // as we are at the root @@ -201,24 +201,27 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { SubMonitor refreshMonitor = SubMonitor.convert(internalMonitor, 3); project.open(refreshMonitor); project.refreshLocal(IResource.DEPTH_INFINITE, refreshMonitor); + prjCDesc.setActiveConfiguration(prjCDesc.getConfigurations()[0]); prjCDesc.setCdtProjectCreated(); + cCorePlugin.setProjectDescription(project, prjCDesc, true, SubMonitor.convert(internalMonitor, 1)); - project.close(monitor); - project.open(monitor); +// project.close(monitor); +// project.open(monitor); Activator.log(new Status(SLOEBER_STATUS_DEBUG, Activator.getId(), "internal creation of project is done: " + project.getName())); //$NON-NLS-1$ - // IndexerController.index(newProjectHandle); } }; try { + IndexerController.doNotIndex(project); workspace.run(runnable, root, IWorkspace.AVOID_UPDATE, monitor); } catch (Exception e) { - Activator.log(new Status(IStatus.INFO, io.sloeber.core.Activator.getId(), + Activator.log(new Status(IStatus.ERROR, io.sloeber.core.Activator.getId(), "Project creation failed: " + project.getName(), e)); //$NON-NLS-1$ } + IndexerController.index(project); monitor.done(); } @@ -233,10 +236,14 @@ private static OtherDescription getOtherDescription(KeyValueTree config) { private static CompileDescription getCompileDescription(KeyValueTree oldConfig) { CompileDescription ret = new CompileDescription(); + if (oldConfig == null) { + return ret; + } KeyValueTree compileConfig=oldConfig.getChild("compile").getChild("sloeber"); //$NON-NLS-1$ //$NON-NLS-2$ if (compileConfig == null) { return ret; } + KeyValueTree extraConfig=compileConfig.getChild("extra"); //$NON-NLS-1$ ret.set_All_CompileOptions(extraConfig.getValue("all")); //$NON-NLS-1$ ret.set_Archive_CompileOptions(extraConfig.getValue("archive")); //$NON-NLS-1$ @@ -281,10 +288,11 @@ private static BoardDescription getBoardDescription(KeyValueTree oldConfig) { } } } - if (foundBoardsFilePath == null) { + if (foundBoardsFilePath == null || (!foundBoardsFilePath.toFile().exists()) ) { return new BoardDescription(); } + KeyValueTree optionsHolder=oldConfig.getChild("board.BOARD.MENU"); //$NON-NLS-1$ Map options=new HashMap<>(); diff --git a/io.sloeber.core/src/io/sloeber/core/listeners/IndexerController.java b/io.sloeber.core/src/io/sloeber/core/listeners/IndexerController.java new file mode 100644 index 00000000..43b697b7 --- /dev/null +++ b/io.sloeber.core/src/io/sloeber/core/listeners/IndexerController.java @@ -0,0 +1,88 @@ +package io.sloeber.core.listeners; + +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.index.IndexerSetupParticipant; +import org.eclipse.cdt.core.model.CoreModel; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +import io.sloeber.core.Activator; +import io.sloeber.core.natures.SloeberNature; + +public class IndexerController extends IndexerSetupParticipant { + private static Set doNotIndexProjects = new HashSet<>(); + private static Set indexingPosponedProjects = new HashSet<>(); + private static IndexerController myIndexController = null; + private static final String managedBuildNature="org.eclipse.cdt.managedbuilder.core.managedBuildNature"; //$NON-NLS-1$ + + @Override + public boolean postponeIndexerSetup(ICProject cProject) { + IProject project = cProject.getProject(); + String projectName=project.getName(); + boolean ret=false; + if( doNotIndexProjects.contains(projectName)) { + ret=true; + }else { + try { + if(project.hasNature(SloeberNature.NATURE_ID) &&project.hasNature(managedBuildNature)) { + ret=true; + } + } catch (CoreException e) { + e.printStackTrace(); + ret = false; + } + } + if (ret) { + Activator.log(new Status(IStatus.INFO, Activator.getId(), "pospone index " + projectName)); //$NON-NLS-1$ + indexingPosponedProjects.add(projectName); + } /* else { + Common.log(new Status(Const.SLOEBER_STATUS_DEBUG, Activator.getId(), + "do not pospone index " + project.getName())); //$NON-NLS-1$ + }*/ + return ret; + } + + static public boolean isPosponed(String projectName) { + return doNotIndexProjects.contains(projectName); + } + + public static void doNotIndex(IProject project) { + String projectName=project.getName(); + Activator.log(new Status(IStatus.INFO, Activator.getId(), "Do not index " + projectName)); //$NON-NLS-1$ + doNotIndexProjects.add(projectName); + } + + public static void index(IProject project) { + String projectName=project.getName(); + if (doNotIndexProjects.remove(projectName)) { + Activator.log(new Status(IStatus.INFO, Activator.getId(), "index " + projectName)); //$NON-NLS-1$ + } + if (indexingPosponedProjects.contains(projectName)) { + indexingPosponedProjects.remove(projectName); + ICProject cProject = CoreModel.getDefault().getCModel().getCProject(projectName); + myIndexController.notifyIndexerSetup(cProject); + } + } + + public static void registerIndexerController() { + if (myIndexController == null) { + myIndexController = new IndexerController(); + CCorePlugin.getIndexManager().addIndexerSetupParticipant(myIndexController); + } + } + + public static void unRegisterIndexerController() { + if (myIndexController != null) { + CCorePlugin.getIndexManager().removeIndexerSetupParticipant(myIndexController); + myIndexController=null; + } + + } + +} From a3c21eba7e0ea3f7994c87df4d3927d98be69372 Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 13 Oct 2024 23:07:18 +0200 Subject: [PATCH 039/115] Remove Sloeber setup and add getAnyInstalledPlatform to boardsmanager --- .../IAutoBuildConfigurationDescription.java | 4 +- .../api/BoardDescription.java | 2053 ++++++++--------- .../arduinoFramework/api/BoardsManager.java | 1602 +++++++------ .../arduinoFramework/api/IArduinoPackage.java | 2 - .../internal/ArduinoPackage.java | 21 +- .../src/io/sloeber/core/Activator.java | 27 +- .../src/io/sloeber/core/api/Defaults.java | 16 +- .../src/io/sloeber/core/api/Preferences.java | 14 +- .../ThirdPartyHardwareSelectionPage.java | 26 +- 9 files changed, 1923 insertions(+), 1842 deletions(-) diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/api/IAutoBuildConfigurationDescription.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/api/IAutoBuildConfigurationDescription.java index f608a587..1208307b 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/api/IAutoBuildConfigurationDescription.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/api/IAutoBuildConfigurationDescription.java @@ -13,6 +13,7 @@ import org.eclipse.cdt.core.settings.model.CSourceEntry; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.core.settings.model.ICProjectDescription; +import org.eclipse.cdt.core.settings.model.ICProjectDescriptionManager; import org.eclipse.cdt.core.settings.model.ICSettingEntry; import org.eclipse.cdt.core.settings.model.ICSourceEntry; import org.eclipse.core.resources.IFolder; @@ -31,7 +32,8 @@ public interface IAutoBuildConfigurationDescription { public static IAutoBuildConfigurationDescription getActiveConfig(IProject project, boolean write) { CoreModel coreModel = CoreModel.getDefault(); - ICProjectDescription projectDescription = coreModel.getProjectDescription(project, write); + ICProjectDescription projectDescription =coreModel.getProjectDescriptionManager().getProjectDescription(project, ICProjectDescriptionManager.GET_IF_LOADDED); + //ICProjectDescription projectDescription = coreModel.getProjectDescription(project, write); return getActiveConfig(projectDescription); } diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java index a5458779..24093393 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java @@ -47,306 +47,302 @@ public class BoardDescription { private static final String FIRST_SLOEBER_LINE = "#Sloeber created file please do not modify V1.00.test 06 "; //$NON-NLS-1$ - private static final IEclipsePreferences myStorageNode = InstanceScope.INSTANCE.getNode(NODE_ARDUINO); - - /* - * This is the basic info contained in the descriptor - */ - private String myUploadPort = EMPTY; - private String myProgrammer = EMPTY; - private String myBoardID = EMPTY; - private Map myOptions = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - private File myUserSelectedBoardsTxtFile; //this is the boards.txt file selected in the gui - private BoardTxtFile mySloeberBoardTxtFile; // this is the actual used and loaded sloeber.boards.txt file - - private String myBoardsCore = null; - private String myBoardsVariant = null; - private String myUploadTool = null; - - private IArduinoPlatformVersion myReferencedPlatformVariant = null; - private IArduinoPlatformVersion myReferencedPlatformCore = null; - private IArduinoPlatformVersion myReferencedPlatformUpload = null; - private String myJsonFileName=null; - private String myJsonURL=null; - - private boolean myIsDirty = true; - - @Override - public String toString() { - return getReferencingBoardsFile() + " \"" + getBoardName() + "\" " + getUploadPort(); //$NON-NLS-1$//$NON-NLS-2$ - } - - /** - * Compare 2 descriptors and return true is they are equal. This method detects - * - OS changes - project name changes - moves of workspace - changed runtine - * eclipse install - * - * @param obj - * @return true if equal otherwise false - */ - public boolean equals(BoardDescription otherBoardDescriptor) { - if (otherBoardDescriptor == null) { - return false; - } - if (!this.getUploadPort().equals(otherBoardDescriptor.getUploadPort())) { - return false; - } - if (!this.getProgrammer().equals(otherBoardDescriptor.getProgrammer())) { - return false; - } - return !needsRebuild(otherBoardDescriptor); - } - - /** - * compare 2 board descriptors and return true if replacing one board descriptor - * with the other implies that a rebuild is needed - * - * @param otherBoardDescriptor - * @return - */ - public boolean needsRebuild(BoardDescription otherBoardDescriptor) { - if (otherBoardDescriptor == null) { - return true; - } - if (!this.getBoardID().equals(otherBoardDescriptor.getBoardID())) { - return true; - } - String moddedReferencingBoardsFile = makePathVersionString(getReferencingBoardsFile()); - String moddedOtherReferencingBoardsFile = makePathVersionString( - otherBoardDescriptor.getReferencingBoardsFile()); - if (!moddedReferencingBoardsFile.equals(moddedOtherReferencingBoardsFile)) { - return true; - } - if (!this.getOptions().equals(otherBoardDescriptor.getOptions())) { - return true; - } - return false; - } - - /** - * after construction data needs to be derived from other data. This method - * derives all other data and puts it in fields - */ - private void calculateDerivedFields() { - - myReferencedPlatformCore = null; - myBoardsCore = null; - myReferencedPlatformVariant = null; - myBoardsVariant = null; - myReferencedPlatformUpload = null; - myUploadTool = null; - setDefaultOptions(); - // search in the board info - ParseSection(); - - } - - private void ParseSection() { - KeyValueTree rootData = mySloeberBoardTxtFile.getData(); - String boardID = getBoardID(); - KeyValueTree boardData = rootData.getChild(boardID); - - String core = boardData.getValue(BUILD + DOT + CORE); - String variant = boardData.getValue(BUILD + DOT + VARIANT); - String upload = boardData.getValue(UPLOAD + DOT + TOOL); - // also search the options - for (Entry curOption : this.myOptions.entrySet()) { - KeyValueTree curMenuData = boardData.getChild(MENU + DOT + curOption.getKey() + DOT + curOption.getValue()); - String coreOption = curMenuData.getValue(BUILD + DOT + CORE); - String variantOption = curMenuData.getValue(BUILD + DOT + VARIANT); - String uploadOption = curMenuData.getValue(UPLOAD + DOT + TOOL); - if (!coreOption.isEmpty()) { - core = coreOption; - } - if (!variantOption.isEmpty()) { - variant = variantOption; - } - if (!uploadOption.isEmpty()) { - upload = uploadOption; - } - } - String architecture = getArchitecture(); - if (core != null) { - String valueSplit[] = core.split(COLON); - if (valueSplit.length == 2) { - String refVendor = valueSplit[0]; - myBoardsCore = valueSplit[1]; - myReferencedPlatformCore = BoardsManager.getNewestInstalledPlatform(refVendor, architecture); - if (myReferencedPlatformCore == null) { - Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, - Helpers_tool_reference_missing.replace(TOOL_TAG, core) - .replace(FILE_TAG, getReferencingBoardsFile().toString()) - .replace(BOARD_TAG, getBoardID()))); - return; - } - } else if (valueSplit.length == 4) { - String refVendor = valueSplit[0]; - String refArchitecture = valueSplit[1]; - VersionNumber refVersion = new VersionNumber(valueSplit[2]); - myBoardsCore = valueSplit[3]; - myReferencedPlatformCore = BoardsManager.getPlatform(refVendor, refArchitecture, refVersion); - if (myReferencedPlatformCore == null) { - Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, - Helpers_tool_reference_missing.replace(TOOL_TAG, core) - .replace(FILE_TAG, getReferencingBoardsFile().toString()) - .replace(BOARD_TAG, getBoardID()))); - return; - } - } else { - this.myBoardsCore = core; - } - } - if (variant != null) { - String valueSplit[] = variant.split(COLON); - if (valueSplit.length == 2) { - String refVendor = valueSplit[0]; - myBoardsVariant = valueSplit[1]; - myReferencedPlatformVariant = BoardsManager.getNewestInstalledPlatform(refVendor, architecture); - if (myReferencedPlatformVariant == null) { - Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, - Helpers_tool_reference_missing.replace(TOOL_TAG, variant) - .replace(FILE_TAG, getReferencingBoardsFile().toString()) - .replace(BOARD_TAG, getBoardID()))); - return; - } - } else if (valueSplit.length == 4) { - String refVendor = valueSplit[0]; - String refArchitecture = valueSplit[1]; - VersionNumber refVersion = new VersionNumber(valueSplit[2]); - myBoardsVariant = valueSplit[3]; - if ("*".equals(valueSplit[2])) { //$NON-NLS-1$ - myReferencedPlatformVariant = BoardsManager.getNewestInstalledPlatform(refVendor, refArchitecture); - } else { - myReferencedPlatformVariant = BoardsManager.getPlatform(refVendor, refArchitecture, refVersion); - } - if (myReferencedPlatformVariant == null) { - Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, - Helpers_tool_reference_missing.replace(TOOL_TAG, variant) - .replace(FILE_TAG, getReferencingBoardsFile().toString()) - .replace(BOARD_TAG, getBoardID()))); - return; - } - } else { - myBoardsVariant = variant; - } - } - if (upload != null) { - String valueSplit[] = upload.split(COLON); - if (valueSplit.length == 2) { - String refVendor = valueSplit[0]; - myUploadTool = valueSplit[1]; - myReferencedPlatformUpload = BoardsManager.getNewestInstalledPlatform(refVendor, architecture); - if (myReferencedPlatformUpload == null) { - Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, - Helpers_tool_reference_missing.replace(TOOL_TAG, upload) - .replace(FILE_TAG, getReferencingBoardsFile().toString()) - .replace(BOARD_TAG, getBoardID()))); - return; - } - } else if (valueSplit.length == 4) { - String refVendor = valueSplit[0]; - String refArchitecture = valueSplit[1]; - VersionNumber refVersion = new VersionNumber(valueSplit[2]); - myUploadTool = valueSplit[3]; - myReferencedPlatformUpload = BoardsManager.getPlatform(refVendor, refArchitecture, refVersion); - if (this.myReferencedPlatformUpload == null) { - Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, - Helpers_tool_reference_missing.replace(TOOL_TAG, upload) - .replace(FILE_TAG, getReferencingBoardsFile().toString()) - .replace(BOARD_TAG, getBoardID()))); - return; - } - } else { - myUploadTool = upload; - } - } - } - - /** - * make a board descriptor for each board in the board.txt file with the default - * options - * - * @param boardFile - * @return a list of board descriptors - */ - public static List makeBoardDescriptors(File boardFile) { - BoardTxtFile txtFile = new BoardTxtFile(resolvePathEnvironmentString(boardFile)); - List boards = new ArrayList<>(); - List boardIDs = txtFile.getAllBoardIDs(); - for (String curboardID : boardIDs) { - Map boardSection = txtFile.getSection(curboardID); - if (boardSection != null) { - if (!"true".equalsIgnoreCase(boardSection.get("hide"))) { //$NON-NLS-1$ //$NON-NLS-2$ - boards.add(new BoardDescription(boardFile, curboardID, null)); - } - } - } - return boards; - } - - /** - * create a board descriptor - * - * @param boardsFile - * @param boardID + private static final IEclipsePreferences myStorageNode = InstanceScope.INSTANCE.getNode(NODE_ARDUINO); + + /* + * This is the basic info contained in the descriptor + */ + private String myUploadPort = EMPTY; + private String myProgrammer = EMPTY; + private String myBoardID = EMPTY; + private Map myOptions = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + private File myUserSelectedBoardsTxtFile; // this is the boards.txt file selected in the gui + private BoardTxtFile mySloeberBoardTxtFile; // this is the actual used and loaded sloeber.boards.txt file + + private String myBoardsCore = null; + private String myBoardsVariant = null; + private String myUploadTool = null; + + private IArduinoPlatformVersion myReferencedPlatformVariant = null; + private IArduinoPlatformVersion myReferencedPlatformCore = null; + private IArduinoPlatformVersion myReferencedPlatformUpload = null; + private String myJsonFileName = null; + private String myJsonURL = null; + + private boolean myIsDirty = true; + + @Override + public String toString() { + return getReferencingBoardsFile() + " \"" + getBoardName() + "\" " + getUploadPort(); //$NON-NLS-1$//$NON-NLS-2$ + } + + /** + * Compare 2 descriptors and return true is they are equal. This method detects + * - OS changes - project name changes - moves of workspace - changed runtine + * eclipse install + * + * @param obj + * @return true if equal otherwise false + */ + public boolean equals(BoardDescription otherBoardDescriptor) { + if (otherBoardDescriptor == null) { + return false; + } + if (!this.getUploadPort().equals(otherBoardDescriptor.getUploadPort())) { + return false; + } + if (!this.getProgrammer().equals(otherBoardDescriptor.getProgrammer())) { + return false; + } + return !needsRebuild(otherBoardDescriptor); + } + + /** + * compare 2 board descriptors and return true if replacing one board descriptor + * with the other implies that a rebuild is needed + * + * @param otherBoardDescriptor + * @return + */ + public boolean needsRebuild(BoardDescription otherBoardDescriptor) { + if (otherBoardDescriptor == null) { + return true; + } + if (!this.getBoardID().equals(otherBoardDescriptor.getBoardID())) { + return true; + } + String moddedReferencingBoardsFile = makePathVersionString(getReferencingBoardsFile()); + String moddedOtherReferencingBoardsFile = makePathVersionString( + otherBoardDescriptor.getReferencingBoardsFile()); + if (!moddedReferencingBoardsFile.equals(moddedOtherReferencingBoardsFile)) { + return true; + } + if (!this.getOptions().equals(otherBoardDescriptor.getOptions())) { + return true; + } + return false; + } + + /** + * after construction data needs to be derived from other data. This method + * derives all other data and puts it in fields + */ + private void calculateDerivedFields() { + + myReferencedPlatformCore = null; + myBoardsCore = null; + myReferencedPlatformVariant = null; + myBoardsVariant = null; + myReferencedPlatformUpload = null; + myUploadTool = null; + setDefaultOptions(); + // search in the board info + ParseSection(); + + } + + private void ParseSection() { + KeyValueTree rootData = mySloeberBoardTxtFile.getData(); + String boardID = getBoardID(); + KeyValueTree boardData = rootData.getChild(boardID); + + String core = boardData.getValue(BUILD + DOT + CORE); + String variant = boardData.getValue(BUILD + DOT + VARIANT); + String upload = boardData.getValue(UPLOAD + DOT + TOOL); + // also search the options + for (Entry curOption : this.myOptions.entrySet()) { + KeyValueTree curMenuData = boardData.getChild(MENU + DOT + curOption.getKey() + DOT + curOption.getValue()); + String coreOption = curMenuData.getValue(BUILD + DOT + CORE); + String variantOption = curMenuData.getValue(BUILD + DOT + VARIANT); + String uploadOption = curMenuData.getValue(UPLOAD + DOT + TOOL); + if (!coreOption.isEmpty()) { + core = coreOption; + } + if (!variantOption.isEmpty()) { + variant = variantOption; + } + if (!uploadOption.isEmpty()) { + upload = uploadOption; + } + } + String architecture = getArchitecture(); + if (core != null) { + String valueSplit[] = core.split(COLON); + if (valueSplit.length == 2) { + String refVendor = valueSplit[0]; + myBoardsCore = valueSplit[1]; + myReferencedPlatformCore = BoardsManager.getNewestInstalledPlatform(refVendor, architecture); + if (myReferencedPlatformCore == null) { + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, + Helpers_tool_reference_missing.replace(TOOL_TAG, core) + .replace(FILE_TAG, getReferencingBoardsFile().toString()) + .replace(BOARD_TAG, getBoardID()))); + return; + } + } else if (valueSplit.length == 4) { + String refVendor = valueSplit[0]; + String refArchitecture = valueSplit[1]; + VersionNumber refVersion = new VersionNumber(valueSplit[2]); + myBoardsCore = valueSplit[3]; + myReferencedPlatformCore = BoardsManager.getPlatform(refVendor, refArchitecture, refVersion); + if (myReferencedPlatformCore == null) { + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, + Helpers_tool_reference_missing.replace(TOOL_TAG, core) + .replace(FILE_TAG, getReferencingBoardsFile().toString()) + .replace(BOARD_TAG, getBoardID()))); + return; + } + } else { + this.myBoardsCore = core; + } + } + if (variant != null) { + String valueSplit[] = variant.split(COLON); + if (valueSplit.length == 2) { + String refVendor = valueSplit[0]; + myBoardsVariant = valueSplit[1]; + myReferencedPlatformVariant = BoardsManager.getNewestInstalledPlatform(refVendor, architecture); + if (myReferencedPlatformVariant == null) { + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, + Helpers_tool_reference_missing.replace(TOOL_TAG, variant) + .replace(FILE_TAG, getReferencingBoardsFile().toString()) + .replace(BOARD_TAG, getBoardID()))); + return; + } + } else if (valueSplit.length == 4) { + String refVendor = valueSplit[0]; + String refArchitecture = valueSplit[1]; + VersionNumber refVersion = new VersionNumber(valueSplit[2]); + myBoardsVariant = valueSplit[3]; + if ("*".equals(valueSplit[2])) { //$NON-NLS-1$ + myReferencedPlatformVariant = BoardsManager.getNewestInstalledPlatform(refVendor, refArchitecture); + } else { + myReferencedPlatformVariant = BoardsManager.getPlatform(refVendor, refArchitecture, refVersion); + } + if (myReferencedPlatformVariant == null) { + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, + Helpers_tool_reference_missing.replace(TOOL_TAG, variant) + .replace(FILE_TAG, getReferencingBoardsFile().toString()) + .replace(BOARD_TAG, getBoardID()))); + return; + } + } else { + myBoardsVariant = variant; + } + } + if (upload != null) { + String valueSplit[] = upload.split(COLON); + if (valueSplit.length == 2) { + String refVendor = valueSplit[0]; + myUploadTool = valueSplit[1]; + myReferencedPlatformUpload = BoardsManager.getNewestInstalledPlatform(refVendor, architecture); + if (myReferencedPlatformUpload == null) { + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, + Helpers_tool_reference_missing.replace(TOOL_TAG, upload) + .replace(FILE_TAG, getReferencingBoardsFile().toString()) + .replace(BOARD_TAG, getBoardID()))); + return; + } + } else if (valueSplit.length == 4) { + String refVendor = valueSplit[0]; + String refArchitecture = valueSplit[1]; + VersionNumber refVersion = new VersionNumber(valueSplit[2]); + myUploadTool = valueSplit[3]; + myReferencedPlatformUpload = BoardsManager.getPlatform(refVendor, refArchitecture, refVersion); + if (this.myReferencedPlatformUpload == null) { + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, + Helpers_tool_reference_missing.replace(TOOL_TAG, upload) + .replace(FILE_TAG, getReferencingBoardsFile().toString()) + .replace(BOARD_TAG, getBoardID()))); + return; + } + } else { + myUploadTool = upload; + } + } + } + + /** + * make a board descriptor for each board in the board.txt file with the default + * options + * + * @param boardFile + * @return a list of board descriptors + */ + public static List makeBoardDescriptors(File boardFile) { + BoardTxtFile txtFile = new BoardTxtFile(resolvePathEnvironmentString(boardFile)); + List boards = new ArrayList<>(); + List boardIDs = txtFile.getAllBoardIDs(); + for (String curboardID : boardIDs) { + Map boardSection = txtFile.getSection(curboardID); + if (boardSection != null) { + if (!"true".equalsIgnoreCase(boardSection.get("hide"))) { //$NON-NLS-1$ //$NON-NLS-2$ + boards.add(new BoardDescription(boardFile, curboardID, null)); + } + } + } + return boards; + } + + /** + * create a board descriptor + * + * @param boardsFile + * @param boardID * @param options * if null default options are taken - */ - public BoardDescription(File boardsFile, String boardID, Map options) { - File expandedBoardsFile=resolvePathEnvironmentString(boardsFile); - if(!expandedBoardsFile.exists()) { - Activator.log(new Status(IStatus.ERROR,Activator.getId(),"BoardsFile " +boardsFile+" does not exist")); //$NON-NLS-1$//$NON-NLS-2$ - return; - } - myBoardID = boardID; - myUserSelectedBoardsTxtFile = boardsFile; - mySloeberBoardTxtFile = new BoardTxtFile(expandedBoardsFile); - getJSonInfo(); - setDefaultOptions(); - if (options != null) { - myOptions.putAll(options); - } - } - + */ + public BoardDescription(File boardsFile, String boardID, Map options) { + File expandedBoardsFile = resolvePathEnvironmentString(boardsFile); + if (!expandedBoardsFile.exists()) { + Activator.log(new Status(IStatus.ERROR, Activator.getId(), "BoardsFile " + boardsFile + " does not exist")); //$NON-NLS-1$//$NON-NLS-2$ + return; + } + myBoardID = boardID; + myUserSelectedBoardsTxtFile = boardsFile; + mySloeberBoardTxtFile = new BoardTxtFile(expandedBoardsFile); + getJSonInfo(); + setDefaultOptions(); + if (options != null) { + myOptions.putAll(options); + } + } public BoardDescription() { - myUserSelectedBoardsTxtFile = new File(myStorageNode.get(KEY_LAST_USED_BOARDS_FILE, EMPTY)); - if (!myUserSelectedBoardsTxtFile.exists()) { - - IArduinoPlatformVersion platform = BoardsManager.getNewestInstalledPlatform(VENDOR_ARDUINO, AVR); - if (platform == null) { - List platforms = BoardsManager.getInstalledPlatforms(); - //If you crash on the next line no platform have been installed - platform = platforms.get(0); - } - myUserSelectedBoardsTxtFile = platform.getBoardsFile(); - mySloeberBoardTxtFile = new BoardTxtFile(myUserSelectedBoardsTxtFile); - getJSonInfo(platform); - - if (mySloeberBoardTxtFile.getAllBoardIDs().contains(UNO)) { - myBoardID = UNO; - } else { - myBoardID = mySloeberBoardTxtFile.getAllBoardIDs().get(0); - } - setDefaultOptions(); - } else { - mySloeberBoardTxtFile = new BoardTxtFile(myUserSelectedBoardsTxtFile); - myBoardID = myStorageNode.get(KEY_LAST_USED_BOARD, EMPTY); - myUploadPort = myStorageNode.get(KEY_LAST_USED_UPLOAD_PORT, EMPTY); - myProgrammer = myStorageNode.get(KEY_LAST_USED_UPLOAD_PROTOCOL, EMPTY); - myJsonFileName=myStorageNode.get(KEY_LAST_USED_JSON_FILENAME, EMPTY); - myJsonURL=myStorageNode.get(KEY_LAST_USED_JSON_URL, EMPTY); - myOptions = KeyValue.makeMap(myStorageNode.get(KEY_LAST_USED_BOARD_MENU_OPTIONS, EMPTY)); - } - - } - - private void getJSonInfo(IArduinoPlatformVersion platform) { - IArduinoPlatformPackageIndex packageIndex=platform.getParent().getParent().getPackageIndex(); - myJsonFileName=packageIndex.getJsonFile().getName(); - myJsonURL=packageIndex.getJsonURL(); + myUserSelectedBoardsTxtFile = new File(myStorageNode.get(KEY_LAST_USED_BOARDS_FILE, EMPTY)); + if (!myUserSelectedBoardsTxtFile.exists()) { + + IArduinoPlatformVersion platform = BoardsManager.getNewestInstalledPlatform(VENDOR_ARDUINO, AVR); + if (platform == null) { + platform = BoardsManager.getAnyInstalledPlatform(); + } + myUserSelectedBoardsTxtFile = platform.getBoardsFile(); + mySloeberBoardTxtFile = new BoardTxtFile(myUserSelectedBoardsTxtFile); + getJSonInfo(platform); + + if (mySloeberBoardTxtFile.getAllBoardIDs().contains(UNO)) { + myBoardID = UNO; + } else { + myBoardID = mySloeberBoardTxtFile.getAllBoardIDs().get(0); + } + setDefaultOptions(); + } else { + mySloeberBoardTxtFile = new BoardTxtFile(myUserSelectedBoardsTxtFile); + myBoardID = myStorageNode.get(KEY_LAST_USED_BOARD, EMPTY); + myUploadPort = myStorageNode.get(KEY_LAST_USED_UPLOAD_PORT, EMPTY); + myProgrammer = myStorageNode.get(KEY_LAST_USED_UPLOAD_PROTOCOL, EMPTY); + myJsonFileName = myStorageNode.get(KEY_LAST_USED_JSON_FILENAME, EMPTY); + myJsonURL = myStorageNode.get(KEY_LAST_USED_JSON_URL, EMPTY); + myOptions = KeyValue.makeMap(myStorageNode.get(KEY_LAST_USED_BOARD_MENU_OPTIONS, EMPTY)); + } + } + private void getJSonInfo(IArduinoPlatformVersion platform) { + IArduinoPlatformPackageIndex packageIndex = platform.getParent().getParent().getPackageIndex(); + myJsonFileName = packageIndex.getJsonFile().getName(); + myJsonURL = packageIndex.getJsonURL(); + } private void getJSonInfo() { IArduinoPlatformVersion platform = BoardsManager.getNewestInstalledPlatform(getVendor(), getArchitecture()); @@ -354,9 +350,7 @@ private void getJSonInfo() { platform = BoardsManager.getNewestInstalledPlatform(VENDOR_ARDUINO, AVR); } if (platform == null) { - List platforms = BoardsManager.getInstalledPlatforms(); - // If you crash on the next line no platform have been installed - platform = platforms.get(0); + platform = BoardsManager.getAnyInstalledPlatform(); } if (platform != null) { myUserSelectedBoardsTxtFile = platform.getBoardsFile(); @@ -366,103 +360,103 @@ private void getJSonInfo() { } public BoardDescription(BoardDescription srcObject) { - myUserSelectedBoardsTxtFile = srcObject.myUserSelectedBoardsTxtFile; - mySloeberBoardTxtFile = srcObject.mySloeberBoardTxtFile; - myJsonFileName=srcObject.myJsonFileName; - myJsonURL=srcObject.myJsonURL; - myBoardID = srcObject.myBoardID; - myUploadPort = srcObject.myUploadPort; - myProgrammer = srcObject.myProgrammer; - myUploadTool = srcObject.myUploadTool; - myOptions.putAll( new TreeMap<>(srcObject.myOptions)); - - } - - public String getuploadTool() { - return this.myUploadTool; - } - - /* - * Sets default options as follows If no option is specified take the first one - * if a option is specified but the value is invalid take the first one - * - * this is so because I want to provide a list of options but if the options are - * incomplete or invalid this method still returns a complete and valid set. - */ - private void setDefaultOptions() { - Map allMenuIDs = this.mySloeberBoardTxtFile.getMenus(); - for (Map.Entry curMenuID : allMenuIDs.entrySet()) { - String providedMenuValue = this.myOptions.get(curMenuID.getKey()); - ArrayList menuOptions = this.mySloeberBoardTxtFile.getMenuItemIDsFromMenuID(curMenuID.getKey(), - getBoardID()); - if (menuOptions.size() > 0) { - if (providedMenuValue == null) { - - this.myOptions.put(curMenuID.getKey(), menuOptions.get(0)); - } else if (!menuOptions.contains(providedMenuValue)) { - this.myOptions.put(curMenuID.getKey(), menuOptions.get(0)); - } - } - } - } - - private void removeInvalidMenuIDs() { - Set allMenuIDs = this.mySloeberBoardTxtFile.getMenus().keySet(); - Set optionKey=new HashSet<>(myOptions.keySet()); - - for ( String curOptionKey : optionKey) { - if(!allMenuIDs.contains(curOptionKey)) { - myOptions.remove(curOptionKey); - } - } - } - - /** - * Store the selections the user made so we can reuse them when creating a new - * project - */ - public void saveUserSelection() { - myStorageNode.put(KEY_LAST_USED_BOARDS_FILE, myUserSelectedBoardsTxtFile.toString()); - myStorageNode.put(KEY_LAST_USED_BOARD, myBoardID); - myStorageNode.put(KEY_LAST_USED_UPLOAD_PORT, myUploadPort); - myStorageNode.put(KEY_LAST_USED_UPLOAD_PROTOCOL, myProgrammer); - myStorageNode.put(KEY_LAST_USED_JSON_FILENAME, myJsonFileName); - myStorageNode.put(KEY_LAST_USED_JSON_URL, myJsonURL); - myStorageNode.put(KEY_LAST_USED_BOARD_MENU_OPTIONS, KeyValue.makeString(myOptions)); - } - - /* - * Returns the architecture based on the myUserSelectedBoardsTxtFile file name + myUserSelectedBoardsTxtFile = srcObject.myUserSelectedBoardsTxtFile; + mySloeberBoardTxtFile = srcObject.mySloeberBoardTxtFile; + myJsonFileName = srcObject.myJsonFileName; + myJsonURL = srcObject.myJsonURL; + myBoardID = srcObject.myBoardID; + myUploadPort = srcObject.myUploadPort; + myProgrammer = srcObject.myProgrammer; + myUploadTool = srcObject.myUploadTool; + myOptions.putAll(new TreeMap<>(srcObject.myOptions)); + + } + + public String getuploadTool() { + return this.myUploadTool; + } + + /* + * Sets default options as follows If no option is specified take the first one + * if a option is specified but the value is invalid take the first one + * + * this is so because I want to provide a list of options but if the options are + * incomplete or invalid this method still returns a complete and valid set. + */ + private void setDefaultOptions() { + Map allMenuIDs = this.mySloeberBoardTxtFile.getMenus(); + for (Map.Entry curMenuID : allMenuIDs.entrySet()) { + String providedMenuValue = this.myOptions.get(curMenuID.getKey()); + ArrayList menuOptions = this.mySloeberBoardTxtFile.getMenuItemIDsFromMenuID(curMenuID.getKey(), + getBoardID()); + if (menuOptions.size() > 0) { + if (providedMenuValue == null) { + + this.myOptions.put(curMenuID.getKey(), menuOptions.get(0)); + } else if (!menuOptions.contains(providedMenuValue)) { + this.myOptions.put(curMenuID.getKey(), menuOptions.get(0)); + } + } + } + } + + private void removeInvalidMenuIDs() { + Set allMenuIDs = this.mySloeberBoardTxtFile.getMenus().keySet(); + Set optionKey = new HashSet<>(myOptions.keySet()); + + for (String curOptionKey : optionKey) { + if (!allMenuIDs.contains(curOptionKey)) { + myOptions.remove(curOptionKey); + } + } + } + + /** + * Store the selections the user made so we can reuse them when creating a new + * project + */ + public void saveUserSelection() { + myStorageNode.put(KEY_LAST_USED_BOARDS_FILE, myUserSelectedBoardsTxtFile.toString()); + myStorageNode.put(KEY_LAST_USED_BOARD, myBoardID); + myStorageNode.put(KEY_LAST_USED_UPLOAD_PORT, myUploadPort); + myStorageNode.put(KEY_LAST_USED_UPLOAD_PROTOCOL, myProgrammer); + myStorageNode.put(KEY_LAST_USED_JSON_FILENAME, myJsonFileName); + myStorageNode.put(KEY_LAST_USED_JSON_URL, myJsonURL); + myStorageNode.put(KEY_LAST_USED_BOARD_MENU_OPTIONS, KeyValue.makeString(myOptions)); + } + + /* + * Returns the architecture based on the myUserSelectedBoardsTxtFile file name * Caters for the packages (with version number and for the old way if the boards * file does not exists returns avr - */ + */ public String getArchitecture() { if (myUserSelectedBoardsTxtFile == null) { return AVR; } IPath platformFile = new Path(myUserSelectedBoardsTxtFile.toString().trim()); - int index=hardwareSegmentIndex(platformFile); - if(index<0){ + int index = hardwareSegmentIndex(platformFile); + if (index < 0) { return AVR; } - return platformFile.segment(index+3); + return platformFile.segment(index + 3); } - /* + /* * Returns the vendor based on the myUserSelectedBoardsTxtFile file name * Caters for the packages (with version number and for the old way if the boards * file does not exists returns VENDOR_ARDUINO - */ + */ public String getVendor() { if (myUserSelectedBoardsTxtFile == null) { return VENDOR_ARDUINO; } IPath platformFile = new Path(myUserSelectedBoardsTxtFile.toString().trim()); - int index=hardwareSegmentIndex(platformFile); - if(index<1){ + int index = hardwareSegmentIndex(platformFile); + if (index < 1) { return VENDOR_ARDUINO; } - return platformFile.segment(index+1); + return platformFile.segment(index + 1); } /** @@ -472,8 +466,8 @@ public String getVendor() { * @return */ private static int hardwareSegmentIndex(IPath platformFile) { - for(int i=0;i options) { - if (options == null || options.size()==0) { - return; - } - myOptions.putAll(options); - removeInvalidMenuIDs(); - setDirty(); - } - - /** - * Returns the options for this board This reflects the options selected through - * the menu functionality in the boards.txt - * - * @return a map of case insensitive ordered key value pairs - */ - public Map getOptions() { - // no update is needed as this field is only modified at construction or set - return this.myOptions; - } - - private void updateWhenDirty() { - if (myIsDirty) { - calculateDerivedFields(); - myIsDirty = false; - } - } - - public String getBoardID() { - // no update is needed as this field is only modified at construction or set - return this.myBoardID; - } - - public String[] getCompatibleBoards() { - return this.mySloeberBoardTxtFile.getAllSectionNames(); - } - - public String[] getUploadProtocols() { - - return Programmers.getUploadProtocols(this); - - } - - public String[] getMenuItemNamesFromMenuID(String menuID) { - return this.mySloeberBoardTxtFile.getMenuItemNamesFromMenuID(menuID, this.myBoardID); - } - - - public String getMenuNameFromMenuID(String id) { - return this.mySloeberBoardTxtFile.getMenuNameFromID(id); - } - - public String getMenuItemNamedFromMenuItemID(String menuItemID, String menuID) { - return this.mySloeberBoardTxtFile.getMenuItemNameFromMenuItemID(this.myBoardID, menuID, menuItemID); - } - - public String getMenuItemIDFromMenuItemName(String menuItemName, String menuID) { - return this.mySloeberBoardTxtFile.getMenuItemIDFromMenuItemName(this.myBoardID, menuID, menuItemName); - } - - /** - * provide the actual path to the variant. Use this method if you want to know - * where the variant is - * - * @return the path to the variant; null if no variant is needed - */ - public IPath getActualVariantPath() { - updateWhenDirty(); - String boardVariant = getBoardVariant(); - if (boardVariant == null || boardVariant.isBlank()) { - return null; - } - if (myReferencedPlatformVariant == null) { - return new Path(myUserSelectedBoardsTxtFile.getParent().toString()).append(ARDUINO_VARIANTS_FOLDER_NAME) - .append(boardVariant); - } - return myReferencedPlatformVariant.getInstallPath().append(ARDUINO_VARIANTS_FOLDER_NAME).append(boardVariant); - } - - private String getBoardVariant() { - updateWhenDirty(); - return this.myBoardsVariant; - } - - public IPath getActualCoreCodePath() { - updateWhenDirty(); - if (myBoardsCore == null) { - return null; - } - IPath retPath = null; - if (myReferencedPlatformCore == null) { - retPath = getreferencingPlatformPath(); - } else { - retPath = myReferencedPlatformCore.getInstallPath(); - } - return retPath.append(CORES).append(myBoardsCore); - } - - public IPath getReferencedUploadPlatformPath() { - updateWhenDirty(); - if (myReferencedPlatformUpload != null) { - return myReferencedPlatformUpload.getInstallPath(); - } - return getreferencingPlatformPath(); - } - - public PlatformTxtFile getReferencingPlatformFile() { - updateWhenDirty(); - File platformFile = getreferencingPlatformPath().append(PLATFORM_FILE_NAME).toFile(); - if (platformFile != null && platformFile.exists()) { - return new PlatformTxtFile(platformFile); - } - return null; - } - - public Path getreferencingPlatformPath() { - try { - return new Path(myUserSelectedBoardsTxtFile.getParent()); - } catch (@SuppressWarnings("unused") Exception e) { - return new Path(EMPTY); - } - } - - public PlatformTxtFile getreferencedCorePlatformFile() { - updateWhenDirty(); - if (myReferencedPlatformCore == null) { - return null; - } - File platformFile = myReferencedPlatformCore.getInstallPath().append(PLATFORM_FILE_NAME).toFile(); - if (platformFile != null && platformFile.exists()) { - return new PlatformTxtFile(platformFile); - } - return null; - } - - public IPath getReferencedCoreLibraryPath() { - updateWhenDirty(); - if (myReferencedPlatformCore == null) { - return null; - } - return this.myReferencedPlatformCore.getInstallPath().append(ARDUINO_LIBRARY_FOLDER_NAME); - } - - public IPath getReferencingLibraryPath() { - updateWhenDirty(); - return this.getreferencingPlatformPath().append(ARDUINO_LIBRARY_FOLDER_NAME); - } - - public String getUploadPatternKey() { - updateWhenDirty(); - String upLoadTool = getuploadTool(); - String networkPrefix = EMPTY; - if (isNetworkUpload()) { - networkPrefix = NETWORK_PREFIX; - } - return TOOLS + DOT + upLoadTool + DOT + UPLOAD + DOT + networkPrefix + PATTERN; - } - - public IPath getreferencedCoreHardwarePath() { - updateWhenDirty(); - if (myReferencedPlatformCore == null) { - return getreferencingPlatformPath(); - } - return myReferencedPlatformCore.getInstallPath(); - } - - /* - * get the latest installed arduino platform with the same architecture. This is - * the platform to use the programmers.txt if no other programmers.txt are - * found. - */ - public IPath getArduinoPlatformPath() { - updateWhenDirty(); - IArduinoPlatform platform = BoardsManager.getPlatform(VENDOR_ARDUINO, getArchitecture()); - if (platform == null) { - return null; - } - IArduinoPlatformVersion platformVersion = platform.getNewestInstalled(); - if (platformVersion == null) { - return null; - } - return platformVersion.getInstallPath(); - } - - /** - * If the upload port contains a space everything before the first space is - * considered to be a dns name or ip adress. - * - * @param mComPort - * @return null if no space in uploadport return dns name op ipadress - */ - public String getHost() { - String host = myUploadPort.split(" ")[0]; //$NON-NLS-1$ - if (host.equals(myUploadPort)) - return null; - return host; - } - - /** - * true if this board needs a networkUpload else false - * - * @return - */ - public boolean isNetworkUpload() { - return getHost() != null; - } - - protected BoardDescription(File boardsFile, String boardID) { - myBoardID = boardID; - myUserSelectedBoardsTxtFile = boardsFile; - mySloeberBoardTxtFile = new BoardTxtFile(myUserSelectedBoardsTxtFile); - calculateDerivedFields(); - getJSonInfo(); - } - - BoardDescription(TxtFile configFile, String prefix) { - - KeyValueTree tree = configFile.getData(); - KeyValueTree section = tree.getChild(prefix); - myProgrammer = section.getValue(KEY_SLOEBER_PROGRAMMER); - myBoardID = section.getValue(KEY_SLOEBER_BOARD_ID); - String board_txt = section.getValue(KEY_SLOEBER_BOARD_TXT); - myUploadPort = section.getValue(KEY_SLOEBER_UPLOAD_PORT); - KeyValueTree optionsTree = section.getChild(KEY_SLOEBER_MENU_SELECTION); - Map options = optionsTree.toKeyValues(EMPTY); - - KeyValueTree boardSection = tree.getChild(BOARD); - myJsonFileName = boardSection.getValue(JSON_NAME); - myJsonURL = boardSection.getValue(JSON_URL); - - myUserSelectedBoardsTxtFile = resolvePathEnvironmentString(new File(board_txt)); - mySloeberBoardTxtFile = new BoardTxtFile(myUserSelectedBoardsTxtFile); - setDefaultOptions(); - if (options != null) { - // Only add the valid options for this board to our options - myOptions.putAll(onlyKeepValidOptions(options)); - } - } - - /** - * create a BoardDescription based on the environment variables given - * - * @param envVars - */ - public BoardDescription(KeyValueTree keyValues) { - myProgrammer=keyValues.getValue(KEY_SLOEBER_PROGRAMMER); - myUploadPort=keyValues.getValue(KEY_SLOEBER_UPLOAD_PORT); - - KeyValueTree boardvalueTree=keyValues.getChild(KEY_BOARD); - myBoardID=boardvalueTree.getValue(KEY_SLOEBER_BOARD_ID); - String txtFile=boardvalueTree.getValue(KEY_SLOEBER_BOARD_TXT); - - KeyValueTree jSonvalueTree=keyValues.getChild(KEY_JSON); - myJsonFileName=jSonvalueTree.getValue(KEY_JSON_FILENAME); - myJsonURL=jSonvalueTree.getValue(KEY_JSON_URL); - - - myUserSelectedBoardsTxtFile = resolvePathEnvironmentString(new File(txtFile)); - mySloeberBoardTxtFile = new BoardTxtFile(myUserSelectedBoardsTxtFile); - KeyValueTree options=keyValues.getChild(KEY_SLOEBER_MENU_SELECTION); - for(KeyValueTree curOption: options.getChildren().values()) { - myOptions.put(curOption.getKey(), curOption.getValue()); - } + * + */ + public void setOptions(Map options) { + if (options == null || options.size() == 0) { + return; + } + myOptions.putAll(options); + removeInvalidMenuIDs(); + setDirty(); + } + /** + * Returns the options for this board This reflects the options selected through + * the menu functionality in the boards.txt + * + * @return a map of case insensitive ordered key value pairs + */ + public Map getOptions() { + // no update is needed as this field is only modified at construction or set + return this.myOptions; + } + + private void updateWhenDirty() { + if (myIsDirty) { + calculateDerivedFields(); + myIsDirty = false; + } + } + + public String getBoardID() { + // no update is needed as this field is only modified at construction or set + return this.myBoardID; + } + + public String[] getCompatibleBoards() { + return this.mySloeberBoardTxtFile.getAllSectionNames(); + } + + public String[] getUploadProtocols() { + + return Programmers.getUploadProtocols(this); + + } + + public String[] getMenuItemNamesFromMenuID(String menuID) { + return this.mySloeberBoardTxtFile.getMenuItemNamesFromMenuID(menuID, this.myBoardID); + } + + public String getMenuNameFromMenuID(String id) { + return this.mySloeberBoardTxtFile.getMenuNameFromID(id); + } + + public String getMenuItemNamedFromMenuItemID(String menuItemID, String menuID) { + return this.mySloeberBoardTxtFile.getMenuItemNameFromMenuItemID(this.myBoardID, menuID, menuItemID); + } + + public String getMenuItemIDFromMenuItemName(String menuItemName, String menuID) { + return this.mySloeberBoardTxtFile.getMenuItemIDFromMenuItemName(this.myBoardID, menuID, menuItemName); + } + + /** + * provide the actual path to the variant. Use this method if you want to know + * where the variant is + * + * @return the path to the variant; null if no variant is needed + */ + public IPath getActualVariantPath() { + updateWhenDirty(); + String boardVariant = getBoardVariant(); + if (boardVariant == null || boardVariant.isBlank()) { + return null; + } + if (myReferencedPlatformVariant == null) { + return new Path(myUserSelectedBoardsTxtFile.getParent().toString()).append(ARDUINO_VARIANTS_FOLDER_NAME) + .append(boardVariant); + } + return myReferencedPlatformVariant.getInstallPath().append(ARDUINO_VARIANTS_FOLDER_NAME).append(boardVariant); + } + + private String getBoardVariant() { + updateWhenDirty(); + return this.myBoardsVariant; + } + + public IPath getActualCoreCodePath() { + updateWhenDirty(); + if (myBoardsCore == null) { + return null; + } + IPath retPath = null; + if (myReferencedPlatformCore == null) { + retPath = getreferencingPlatformPath(); + } else { + retPath = myReferencedPlatformCore.getInstallPath(); + } + return retPath.append(CORES).append(myBoardsCore); + } + + public IPath getReferencedUploadPlatformPath() { + updateWhenDirty(); + if (myReferencedPlatformUpload != null) { + return myReferencedPlatformUpload.getInstallPath(); + } + return getreferencingPlatformPath(); + } + + public PlatformTxtFile getReferencingPlatformFile() { + updateWhenDirty(); + File platformFile = getreferencingPlatformPath().append(PLATFORM_FILE_NAME).toFile(); + if (platformFile != null && platformFile.exists()) { + return new PlatformTxtFile(platformFile); + } + return null; + } + + public Path getreferencingPlatformPath() { + try { + return new Path(myUserSelectedBoardsTxtFile.getParent()); + } catch (@SuppressWarnings("unused") Exception e) { + return new Path(EMPTY); + } + } + + public PlatformTxtFile getreferencedCorePlatformFile() { + updateWhenDirty(); + if (myReferencedPlatformCore == null) { + return null; + } + File platformFile = myReferencedPlatformCore.getInstallPath().append(PLATFORM_FILE_NAME).toFile(); + if (platformFile != null && platformFile.exists()) { + return new PlatformTxtFile(platformFile); + } + return null; + } + + public IPath getReferencedCoreLibraryPath() { + updateWhenDirty(); + if (myReferencedPlatformCore == null) { + return null; + } + return this.myReferencedPlatformCore.getInstallPath().append(ARDUINO_LIBRARY_FOLDER_NAME); + } + + public IPath getReferencingLibraryPath() { + updateWhenDirty(); + return this.getreferencingPlatformPath().append(ARDUINO_LIBRARY_FOLDER_NAME); + } + + public String getUploadPatternKey() { + updateWhenDirty(); + String upLoadTool = getuploadTool(); + String networkPrefix = EMPTY; + if (isNetworkUpload()) { + networkPrefix = NETWORK_PREFIX; + } + return TOOLS + DOT + upLoadTool + DOT + UPLOAD + DOT + networkPrefix + PATTERN; + } + + public IPath getreferencedCoreHardwarePath() { + updateWhenDirty(); + if (myReferencedPlatformCore == null) { + return getreferencingPlatformPath(); + } + return myReferencedPlatformCore.getInstallPath(); + } + + /* + * get the latest installed arduino platform with the same architecture. This is + * the platform to use the programmers.txt if no other programmers.txt are + * found. + */ + public IPath getArduinoPlatformPath() { + updateWhenDirty(); + IArduinoPlatform platform = BoardsManager.getPlatform(VENDOR_ARDUINO, getArchitecture()); + if (platform == null) { + return null; + } + IArduinoPlatformVersion platformVersion = platform.getNewestInstalled(); + if (platformVersion == null) { + return null; + } + return platformVersion.getInstallPath(); + } + + /** + * If the upload port contains a space everything before the first space is + * considered to be a dns name or ip adress. + * + * @param mComPort + * @return null if no space in uploadport return dns name op ipadress + */ + public String getHost() { + String host = myUploadPort.split(" ")[0]; //$NON-NLS-1$ + if (host.equals(myUploadPort)) + return null; + return host; + } + + /** + * true if this board needs a networkUpload else false + * + * @return + */ + public boolean isNetworkUpload() { + return getHost() != null; + } + + protected BoardDescription(File boardsFile, String boardID) { + myBoardID = boardID; + myUserSelectedBoardsTxtFile = boardsFile; + mySloeberBoardTxtFile = new BoardTxtFile(myUserSelectedBoardsTxtFile); + calculateDerivedFields(); + getJSonInfo(); + } + + BoardDescription(TxtFile configFile, String prefix) { + + KeyValueTree tree = configFile.getData(); + KeyValueTree section = tree.getChild(prefix); + myProgrammer = section.getValue(KEY_SLOEBER_PROGRAMMER); + myBoardID = section.getValue(KEY_SLOEBER_BOARD_ID); + String board_txt = section.getValue(KEY_SLOEBER_BOARD_TXT); + myUploadPort = section.getValue(KEY_SLOEBER_UPLOAD_PORT); + KeyValueTree optionsTree = section.getChild(KEY_SLOEBER_MENU_SELECTION); + Map options = optionsTree.toKeyValues(EMPTY); + + KeyValueTree boardSection = tree.getChild(BOARD); + myJsonFileName = boardSection.getValue(JSON_NAME); + myJsonURL = boardSection.getValue(JSON_URL); + + myUserSelectedBoardsTxtFile = resolvePathEnvironmentString(new File(board_txt)); + mySloeberBoardTxtFile = new BoardTxtFile(myUserSelectedBoardsTxtFile); + setDefaultOptions(); + if (options != null) { + // Only add the valid options for this board to our options + myOptions.putAll(onlyKeepValidOptions(options)); + } + } + + /** + * create a BoardDescription based on the environment variables given + * + * @param envVars + */ + public BoardDescription(KeyValueTree keyValues) { + myProgrammer = keyValues.getValue(KEY_SLOEBER_PROGRAMMER); + myUploadPort = keyValues.getValue(KEY_SLOEBER_UPLOAD_PORT); + + KeyValueTree boardvalueTree = keyValues.getChild(KEY_BOARD); + myBoardID = boardvalueTree.getValue(KEY_SLOEBER_BOARD_ID); + String txtFile = boardvalueTree.getValue(KEY_SLOEBER_BOARD_TXT); + + KeyValueTree jSonvalueTree = keyValues.getChild(KEY_JSON); + myJsonFileName = jSonvalueTree.getValue(KEY_JSON_FILENAME); + myJsonURL = jSonvalueTree.getValue(KEY_JSON_URL); + + myUserSelectedBoardsTxtFile = resolvePathEnvironmentString(new File(txtFile)); + mySloeberBoardTxtFile = new BoardTxtFile(myUserSelectedBoardsTxtFile); + KeyValueTree options = keyValues.getChild(KEY_SLOEBER_MENU_SELECTION); + for (KeyValueTree curOption : options.getChildren().values()) { + myOptions.put(curOption.getKey(), curOption.getValue()); + } // int menuKeyLength = KEY_SLOEBER_MENU_SELECTION.length() + DOT.length(); // for (Entry curEnvVar : envVars.entrySet()) { @@ -853,34 +845,33 @@ public BoardDescription(KeyValueTree keyValues) { // } // } // } - } - - /** - * get the environment variables that need to be stored in the configuration - * files configuration files are files needed to setup the sloeber environment - * for instance when openiung a project or after import of a project in the - * workspace - * - * @return the minimum list of environment variables to recreate the project - */ - public void serialize(KeyValueTree keyValueTree) { - String board_txt = makePathVersionString(getReferencingBoardsFile()); - keyValueTree.addChild(KEY_SLOEBER_PROGRAMMER, myProgrammer); - keyValueTree.addChild(KEY_SLOEBER_UPLOAD_PORT, myUploadPort); - - KeyValueTree boardvalueTree=keyValueTree.addChild(KEY_BOARD); - boardvalueTree.addChild(KEY_SLOEBER_BOARD_ID, myBoardID); - boardvalueTree.addChild(KEY_SLOEBER_BOARD_TXT, board_txt); - - KeyValueTree jSonvalueTree=keyValueTree.addChild(KEY_JSON); - jSonvalueTree.addChild(KEY_JSON_FILENAME, myJsonFileName); - jSonvalueTree.addChild(KEY_JSON_URL, myJsonURL); - - - KeyValueTree menuvalueTree=keyValueTree.addChild(KEY_SLOEBER_MENU_SELECTION); - for (Entry curOption : myOptions.entrySet()) { - menuvalueTree.addValue( curOption.getKey(), curOption.getValue()); - } + } + + /** + * get the environment variables that need to be stored in the configuration + * files configuration files are files needed to setup the sloeber environment + * for instance when openiung a project or after import of a project in the + * workspace + * + * @return the minimum list of environment variables to recreate the project + */ + public void serialize(KeyValueTree keyValueTree) { + String board_txt = makePathVersionString(getReferencingBoardsFile()); + keyValueTree.addChild(KEY_SLOEBER_PROGRAMMER, myProgrammer); + keyValueTree.addChild(KEY_SLOEBER_UPLOAD_PORT, myUploadPort); + + KeyValueTree boardvalueTree = keyValueTree.addChild(KEY_BOARD); + boardvalueTree.addChild(KEY_SLOEBER_BOARD_ID, myBoardID); + boardvalueTree.addChild(KEY_SLOEBER_BOARD_TXT, board_txt); + + KeyValueTree jSonvalueTree = keyValueTree.addChild(KEY_JSON); + jSonvalueTree.addChild(KEY_JSON_FILENAME, myJsonFileName); + jSonvalueTree.addChild(KEY_JSON_URL, myJsonURL); + + KeyValueTree menuvalueTree = keyValueTree.addChild(KEY_SLOEBER_MENU_SELECTION); + for (Entry curOption : myOptions.entrySet()) { + menuvalueTree.addValue(curOption.getKey(), curOption.getValue()); + } // allVars.put(KEY_SLOEBER_PROGRAMMER, myProgrammer); // allVars.put(KEY_SLOEBER_BOARD_ID, myBoardID); @@ -894,255 +885,255 @@ public void serialize(KeyValueTree keyValueTree) { // allVars.put(KEY_SLOEBER_MENU_SELECTION + DOT + curOption.getKey(), curOption.getValue()); // } // return allVars; - } - - private Map onlyKeepValidOptions(Map options) { - Map ret = new HashMap<>(); - - KeyValueTree tree = mySloeberBoardTxtFile.getData(); - KeyValueTree boardMenuSection = tree.getChild(myBoardID + DOT + MENU); - if (boardMenuSection != null) { - for (Entry curoption : options.entrySet()) { - String key = curoption.getKey(); - if (boardMenuSection.getChild(key).getKey() != null) { - ret.put(key, curoption.getValue()); - } - } - } - return ret; - } - - /** - * get the environment variables that need to be stored in version control - * - * @return the minimum list of environment variables to recreate the project - * from version control - */ - public Map getEnvVarsVersion(String prefix) { - Map allVars = new TreeMap<>(); - String board_txt = makePathVersionString(getReferencingBoardsFile()); - - allVars.put(prefix + KEY_SLOEBER_BOARD_ID, myBoardID); - allVars.put(prefix + KEY_SLOEBER_BOARD_TXT, board_txt); - - for (Entry curOption : myOptions.entrySet()) { - allVars.put(prefix + KEY_SLOEBER_MENU_SELECTION + DOT + curOption.getKey(), curOption.getValue()); - } - return allVars; - } - - /** - * This method creates environment variables based on the platform.txt and - * boards.txt. platform.txt is processed first and then boards.txt. This way - * boards.txt settings can overwrite common settings in platform.txt The - * environment variables are only valid for the project given as parameter The - * project properties are used to identify the boards.txt and platform.txt as - * well as the board id to select the settings in the board.txt file At the end - * also the path variable is set - * - * - * To be able to quickly fix boards.txt and platform.txt problems I also added a - * pre and post platform and boards files that are processed before and after - * the arduino delivered boards.txt file. - * + } + + private Map onlyKeepValidOptions(Map options) { + Map ret = new HashMap<>(); + + KeyValueTree tree = mySloeberBoardTxtFile.getData(); + KeyValueTree boardMenuSection = tree.getChild(myBoardID + DOT + MENU); + if (boardMenuSection != null) { + for (Entry curoption : options.entrySet()) { + String key = curoption.getKey(); + if (boardMenuSection.getChild(key).getKey() != null) { + ret.put(key, curoption.getValue()); + } + } + } + return ret; + } + + /** + * get the environment variables that need to be stored in version control + * + * @return the minimum list of environment variables to recreate the project + * from version control + */ + public Map getEnvVarsVersion(String prefix) { + Map allVars = new TreeMap<>(); + String board_txt = makePathVersionString(getReferencingBoardsFile()); + + allVars.put(prefix + KEY_SLOEBER_BOARD_ID, myBoardID); + allVars.put(prefix + KEY_SLOEBER_BOARD_TXT, board_txt); + + for (Entry curOption : myOptions.entrySet()) { + allVars.put(prefix + KEY_SLOEBER_MENU_SELECTION + DOT + curOption.getKey(), curOption.getValue()); + } + return allVars; + } + + /** + * This method creates environment variables based on the platform.txt and + * boards.txt. platform.txt is processed first and then boards.txt. This way + * boards.txt settings can overwrite common settings in platform.txt The + * environment variables are only valid for the project given as parameter The + * project properties are used to identify the boards.txt and platform.txt as + * well as the board id to select the settings in the board.txt file At the end + * also the path variable is set + * + * + * To be able to quickly fix boards.txt and platform.txt problems I also added a + * pre and post platform and boards files that are processed before and after + * the arduino delivered boards.txt file. + * * @param project * the project for which the environment variables are set * @param arduinoProperties * the info of the selected board to set the variables for - */ - public Map getEnvVars() { - updateWhenDirty(); - - TxtFile pluginPreProcessingPlatformTxt = new TxtFile(ConfigurationPreferences.getPreProcessingPlatformFile()); - TxtFile pluginPostProcessingPlatformTxt = new TxtFile(ConfigurationPreferences.getPostProcessingPlatformFile()); - BoardTxtFile pluginPreProcessingBoardsTxt = new BoardTxtFile( - ConfigurationPreferences.getPreProcessingBoardsFile()); - BoardTxtFile pluginPostProcessingBoardsTxt = new BoardTxtFile( - ConfigurationPreferences.getPostProcessingBoardsFile()); - - Map allVars = pluginPreProcessingPlatformTxt.getAllEnvironVars(EMPTY); - allVars.putAll(pluginPreProcessingBoardsTxt.getBoardEnvironVars(getBoardID())); - - String architecture = getArchitecture(); - IPath coreHardwarePath = getreferencedCoreHardwarePath(); - allVars.put(ENV_KEY_BUILD_ARCH, architecture.toUpperCase()); - allVars.put(ENV_KEY_RUNTIME_HARDWARE_PATH, getreferencingPlatformPath().removeLastSegments(1).toOSString()); - allVars.put(ENV_KEY_BUILD_SYSTEM_PATH, coreHardwarePath.append(SYSTEM).toOSString()); - allVars.put(ENV_KEY_RUNTIME_PLATFORM_PATH, getreferencingPlatformPath().toOSString()); - //ide_version is defined in pre_processing_platform_default.txt - allVars.put(ENV_KEY_RUNTIME_IDE_VERSION, makeEnvironmentVar("ide_version")); //$NON-NLS-1$ - allVars.put(ENV_KEY_RUNTIME_IDE_PATH, makeEnvironmentVar(SLOEBER_HOME)); - if(isWindows) { - allVars.put(ENV_KEY_RUNTIME_OS, "windows"); //$NON-NLS-1$ - } - if(isLinux) { - allVars.put(ENV_KEY_RUNTIME_OS, "linux"); //$NON-NLS-1$ - } - if(isMac) { - allVars.put(ENV_KEY_RUNTIME_OS, "macosx"); //$NON-NLS-1$ - } - allVars.put(ENV_KEY_ID,getBoardID()); - allVars.put(ENV_KEY_BUILD_FQBN,getBoardFQBN()); - - allVars.put(ENV_KEY_SERIAL_PORT, getActualUploadPort()); - allVars.put(ENV_KEY_SERIAL_DOT_PORT, getActualUploadPort()); - - allVars.put(ENV_KEY_SERIAL_PORT_FILE, getActualUploadPort().replace("/dev/", EMPTY)); //$NON-NLS-1$ - // if actual core path is osstring regression test issue555 willl fail teensy - // stuff - allVars.put(ENV_KEY_BUILD_ACTUAL_CORE_PATH, getActualCoreCodePath().toOSString()); - IPath variantPath = getActualVariantPath(); - if (variantPath != null) { - allVars.put(ENV_KEY_BUILD_VARIANT_PATH, variantPath.toOSString()); - } else {// teensy does not use variant - allVars.put(ENV_KEY_BUILD_VARIANT_PATH, EMPTY); - } - - PlatformTxtFile referencedPlatfromFile = getreferencedCorePlatformFile(); - // process the platform file referenced by the boards.txt - if (referencedPlatfromFile != null) { - allVars.putAll(referencedPlatfromFile.getAllEnvironVars()); - } - PlatformTxtFile referencingPlatfromFile = getReferencingPlatformFile(); - // process the platform file next to the selected boards.txt - if (referencingPlatfromFile != null) { - allVars.putAll(referencingPlatfromFile.getAllEnvironVars()); - } - - // put in the installed tools info - try { + */ + public Map getEnvVars() { + updateWhenDirty(); + + TxtFile pluginPreProcessingPlatformTxt = new TxtFile(ConfigurationPreferences.getPreProcessingPlatformFile()); + TxtFile pluginPostProcessingPlatformTxt = new TxtFile(ConfigurationPreferences.getPostProcessingPlatformFile()); + BoardTxtFile pluginPreProcessingBoardsTxt = new BoardTxtFile( + ConfigurationPreferences.getPreProcessingBoardsFile()); + BoardTxtFile pluginPostProcessingBoardsTxt = new BoardTxtFile( + ConfigurationPreferences.getPostProcessingBoardsFile()); + + Map allVars = pluginPreProcessingPlatformTxt.getAllEnvironVars(EMPTY); + allVars.putAll(pluginPreProcessingBoardsTxt.getBoardEnvironVars(getBoardID())); + + String architecture = getArchitecture(); + IPath coreHardwarePath = getreferencedCoreHardwarePath(); + allVars.put(ENV_KEY_BUILD_ARCH, architecture.toUpperCase()); + allVars.put(ENV_KEY_RUNTIME_HARDWARE_PATH, getreferencingPlatformPath().removeLastSegments(1).toOSString()); + allVars.put(ENV_KEY_BUILD_SYSTEM_PATH, coreHardwarePath.append(SYSTEM).toOSString()); + allVars.put(ENV_KEY_RUNTIME_PLATFORM_PATH, getreferencingPlatformPath().toOSString()); + // ide_version is defined in pre_processing_platform_default.txt + allVars.put(ENV_KEY_RUNTIME_IDE_VERSION, makeEnvironmentVar("ide_version")); //$NON-NLS-1$ + allVars.put(ENV_KEY_RUNTIME_IDE_PATH, makeEnvironmentVar(SLOEBER_HOME)); + if (isWindows) { + allVars.put(ENV_KEY_RUNTIME_OS, "windows"); //$NON-NLS-1$ + } + if (isLinux) { + allVars.put(ENV_KEY_RUNTIME_OS, "linux"); //$NON-NLS-1$ + } + if (isMac) { + allVars.put(ENV_KEY_RUNTIME_OS, "macosx"); //$NON-NLS-1$ + } + allVars.put(ENV_KEY_ID, getBoardID()); + allVars.put(ENV_KEY_BUILD_FQBN, getBoardFQBN()); + + allVars.put(ENV_KEY_SERIAL_PORT, getActualUploadPort()); + allVars.put(ENV_KEY_SERIAL_DOT_PORT, getActualUploadPort()); + + allVars.put(ENV_KEY_SERIAL_PORT_FILE, getActualUploadPort().replace("/dev/", EMPTY)); //$NON-NLS-1$ + // if actual core path is osstring regression test issue555 willl fail teensy + // stuff + allVars.put(ENV_KEY_BUILD_ACTUAL_CORE_PATH, getActualCoreCodePath().toOSString()); + IPath variantPath = getActualVariantPath(); + if (variantPath != null) { + allVars.put(ENV_KEY_BUILD_VARIANT_PATH, variantPath.toOSString()); + } else {// teensy does not use variant + allVars.put(ENV_KEY_BUILD_VARIANT_PATH, EMPTY); + } + + PlatformTxtFile referencedPlatfromFile = getreferencedCorePlatformFile(); + // process the platform file referenced by the boards.txt + if (referencedPlatfromFile != null) { + allVars.putAll(referencedPlatfromFile.getAllEnvironVars()); + } + PlatformTxtFile referencingPlatfromFile = getReferencingPlatformFile(); + // process the platform file next to the selected boards.txt + if (referencingPlatfromFile != null) { + allVars.putAll(referencingPlatfromFile.getAllEnvironVars()); + } + + // put in the installed tools info + try { allVars.putAll(getEnVarPlatformInfo()); } catch (IOException e) { // TODO Auto-generated catch block - Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID,"failed to get platform paths",e)); - } - - // boards settings not coming from menu selections - allVars.putAll(mySloeberBoardTxtFile.getBoardEnvironVars(getBoardID())); - - // board settings from menu selections - Map options = getOptions(); - KeyValueTree rootData = mySloeberBoardTxtFile.getData(); - KeyValueTree menuData = rootData.getChild(getBoardID() + DOT + MENU); - for (Entry curOption : options.entrySet()) { - String menuID = curOption.getKey(); - String SelectedMenuItemID = curOption.getValue(); - KeyValueTree curSelectedMenuItem = menuData.getChild(menuID + DOT + SelectedMenuItemID); - allVars.putAll(curSelectedMenuItem.toKeyValues(EMPTY)); - } - - //This moved last. See github issue 1410 - Programmers localProgrammers[] = Programmers.fromBoards(this); - String programmer = getProgrammer(); - for (Programmers curProgrammer : localProgrammers) { - String programmerID = curProgrammer.getIDFromNiceName(programmer); - if (programmerID != null) { - allVars.putAll(curProgrammer.getAllEnvironVars(programmerID)); - } - } - - // add the stuff that comes with the plugin that is marked as post - allVars.putAll(pluginPostProcessingPlatformTxt.getAllEnvironVars(EMPTY)); - allVars.putAll(pluginPostProcessingBoardsTxt.getBoardEnvironVars(getBoardID())); - - // Do some coded post processing - allVars.putAll(getEnvVarsPostProcessing(allVars)); - return allVars; - - } - - private String getBoardFQBN() { - String fqbn=getVendor()+COLON+getArchitecture()+COLON+getBoardID(); - String options=EMPTY_STRING; - String prefix=COLON; - for(Entry curOption:myOptions.entrySet()){ - options=options+prefix+curOption.getKey()+EQUAL+curOption.getValue(); - prefix=COMMA; - } - return fqbn+options; + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, "failed to get platform paths", e)); + } + + // boards settings not coming from menu selections + allVars.putAll(mySloeberBoardTxtFile.getBoardEnvironVars(getBoardID())); + + // board settings from menu selections + Map options = getOptions(); + KeyValueTree rootData = mySloeberBoardTxtFile.getData(); + KeyValueTree menuData = rootData.getChild(getBoardID() + DOT + MENU); + for (Entry curOption : options.entrySet()) { + String menuID = curOption.getKey(); + String SelectedMenuItemID = curOption.getValue(); + KeyValueTree curSelectedMenuItem = menuData.getChild(menuID + DOT + SelectedMenuItemID); + allVars.putAll(curSelectedMenuItem.toKeyValues(EMPTY)); + } + + // This moved last. See github issue 1410 + Programmers localProgrammers[] = Programmers.fromBoards(this); + String programmer = getProgrammer(); + for (Programmers curProgrammer : localProgrammers) { + String programmerID = curProgrammer.getIDFromNiceName(programmer); + if (programmerID != null) { + allVars.putAll(curProgrammer.getAllEnvironVars(programmerID)); + } + } + + // add the stuff that comes with the plugin that is marked as post + allVars.putAll(pluginPostProcessingPlatformTxt.getAllEnvironVars(EMPTY)); + allVars.putAll(pluginPostProcessingBoardsTxt.getBoardEnvironVars(getBoardID())); + + // Do some coded post processing + allVars.putAll(getEnvVarsPostProcessing(allVars)); + return allVars; + + } + + private String getBoardFQBN() { + String fqbn = getVendor() + COLON + getArchitecture() + COLON + getBoardID(); + String options = EMPTY_STRING; + String prefix = COLON; + for (Entry curOption : myOptions.entrySet()) { + options = options + prefix + curOption.getKey() + EQUAL + curOption.getValue(); + prefix = COMMA; + } + return fqbn + options; } private Map getEnVarPlatformInfo() throws IOException { - Map ret = new HashMap<>(); - - ret.putAll(getEnvVarPlatformFileTools(myReferencedPlatformUpload)); - ret.putAll(getEnvVarPlatformFileTools(myReferencedPlatformVariant)); - ret.putAll(getEnvVarPlatformFileTools(myReferencedPlatformCore)); - - IArduinoPlatformVersion latestArduinoPlatform = BoardsManager.getNewestInstalledPlatform(Const.VENDOR_ARDUINO, - getArchitecture()); - ret.putAll(getEnvVarPlatformFileTools(latestArduinoPlatform)); - - IPath referencingPlatformPath = getreferencingPlatformPath(); - IArduinoPlatformVersion referencingPlatform = BoardsManager.getPlatform(referencingPlatformPath); - if(referencingPlatform==null) { - ret.putAll(getEnvVarPlatformFileTools(referencingPlatformPath.toFile())); - }else { - ret.putAll(getEnvVarPlatformFileTools(referencingPlatform)); - } - - - if (myReferencedPlatformCore == null) { - //there is no referenced core so no need to do smart stuff - return ret; - } - - boolean jsonBasedPlatformManagement = !Preferences.getUseArduinoToolSelection(); - if (jsonBasedPlatformManagement) { - // overrule the Arduino IDE way of working and use the json refereced tools - ret.putAll(getEnvVarPlatformFileTools(referencingPlatform)); - return ret; - } - // standard arduino IDE way - ret.putAll(getEnvVarPlatformFileTools(myReferencedPlatformCore)); - return ret; - - } - - /** - * This method only returns environment variables with and without version + Map ret = new HashMap<>(); + + ret.putAll(getEnvVarPlatformFileTools(myReferencedPlatformUpload)); + ret.putAll(getEnvVarPlatformFileTools(myReferencedPlatformVariant)); + ret.putAll(getEnvVarPlatformFileTools(myReferencedPlatformCore)); + + BoardsManager.update(false);// This way we know the boardsmanager is started or we wait for the lock + IArduinoPlatformVersion latestArduinoPlatform = BoardsManager.getNewestInstalledPlatform(Const.VENDOR_ARDUINO, + getArchitecture()); + ret.putAll(getEnvVarPlatformFileTools(latestArduinoPlatform)); + + IPath referencingPlatformPath = getreferencingPlatformPath(); + IArduinoPlatformVersion referencingPlatform = BoardsManager.getPlatform(referencingPlatformPath); + if (referencingPlatform == null) { + ret.putAll(getEnvVarPlatformFileTools(referencingPlatformPath.toFile())); + } else { + ret.putAll(getEnvVarPlatformFileTools(referencingPlatform)); + } + + if (myReferencedPlatformCore == null) { + // there is no referenced core so no need to do smart stuff + return ret; + } + + boolean jsonBasedPlatformManagement = !Preferences.getUseArduinoToolSelection(); + if (jsonBasedPlatformManagement) { + // overrule the Arduino IDE way of working and use the json refereced tools + ret.putAll(getEnvVarPlatformFileTools(referencingPlatform)); + return ret; + } + // standard arduino IDE way + ret.putAll(getEnvVarPlatformFileTools(myReferencedPlatformCore)); + return ret; + + } + + /** + * This method only returns environment variables with and without version * number * These are purely based on the tool dependencies - * - * @param platformVersion - * @return environment variables pointing to the tools used by the platform - * @throws IOException - */ - private Map getEnvVarPlatformFileTools(IArduinoPlatformVersion platformVersion) throws IOException { - if(platformVersion==null) { - Path path=new Path(myUserSelectedBoardsTxtFile.toString()); - File sloeberTxtFile= path.removeLastSegments(1).append(SLOEBER_TXT_FILE_NAME).toFile(); - return getEnvVarPlatformFileTools(sloeberTxtFile); - } + * + * @param platformVersion + * @return environment variables pointing to the tools used by the platform + * @throws IOException + */ + private Map getEnvVarPlatformFileTools(IArduinoPlatformVersion platformVersion) throws IOException { + if (platformVersion == null) { + Path path = new Path(myUserSelectedBoardsTxtFile.toString()); + File sloeberTxtFile = path.removeLastSegments(1).append(SLOEBER_TXT_FILE_NAME).toFile(); + return getEnvVarPlatformFileTools(sloeberTxtFile); + } File sloeberTxtFile = platformVersion.getInstallPath().append(SLOEBER_TXT_FILE_NAME).toFile(); - deleteIfOutdated (sloeberTxtFile); + deleteIfOutdated(sloeberTxtFile); if (!sloeberTxtFile.exists()) { - String vars = FIRST_SLOEBER_LINE+NEWLINE; + String vars = FIRST_SLOEBER_LINE + NEWLINE; for (ArduinoPlatformTooldDependency tool : platformVersion.getToolsDependencies()) { IPath installPath = tool.getInstallPath(); if (installPath.toFile().exists()) { String value = installPath.toString(); String keyString = ENV_KEY_RUNTIME_TOOLS + tool.getName() + tool.getVersion() + DOT_PATH; - vars=vars+NEWLINE+keyString+EQUAL+ value; + vars = vars + NEWLINE + keyString + EQUAL + value; keyString = ENV_KEY_RUNTIME_TOOLS + tool.getName() + '-' + tool.getVersion() + DOT_PATH; - vars=vars+NEWLINE+keyString+EQUAL+ value; + vars = vars + NEWLINE + keyString + EQUAL + value; keyString = ENV_KEY_RUNTIME_TOOLS + tool.getName() + DOT_PATH; - vars=vars+NEWLINE+keyString+EQUAL+ value; + vars = vars + NEWLINE + keyString + EQUAL + value; } } Files.write(sloeberTxtFile.toPath(), vars.getBytes(), StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE); } - return getEnvVarPlatformFileTools(sloeberTxtFile); + return getEnvVarPlatformFileTools(sloeberTxtFile); } - private static Map getEnvVarPlatformFileTools(File sloeberTxtFile) { - if(sloeberTxtFile==null || (!sloeberTxtFile.exists())) { - return new HashMap<>(); - } + private static Map getEnvVarPlatformFileTools(File sloeberTxtFile) { + if (sloeberTxtFile == null || (!sloeberTxtFile.exists())) { + return new HashMap<>(); + } TxtFile sloeberTxt = new TxtFile(sloeberTxtFile); return sloeberTxt.getAllEnvironVars(EMPTY); } @@ -1167,54 +1158,54 @@ private static void deleteIfOutdated(File tmpFile) { } } - /** - * Following post processing is done - * - * + /** + * Following post processing is done + * + * * The handling of the upload variables is done differently in arduino * than in Sloeber. This is taken care of here. for example the output of this * input - * tools.avrdude.upload.pattern="{cmd.path}" "-C{config.path}" {upload.verbose} - * is changed as if it were the output of this input - * tools.avrdude.upload.pattern="{tools.avrdude.cmd.path}" - * "-C{tools.avrdude.config.path}" {tools.avrdude.upload.verbose} - * + * tools.avrdude.upload.pattern="{cmd.path}" "-C{config.path}" {upload.verbose} + * is changed as if it were the output of this input + * tools.avrdude.upload.pattern="{tools.avrdude.cmd.path}" + * "-C{tools.avrdude.config.path}" {tools.avrdude.upload.verbose} + * * if a programmer is selected different from default some extra actions * are done here so no special code is needed to handle programmers - * + * * The build path for the core is {build.path}/core/core in sloeber * where it is {build.path}/core/ in arduino world and used to be {build.path}/ * This only gives problems in the link command as sometimes there are hardcoded * links to some sys files so ${build.path}/core/sys* ${build.path}/sys* is * replaced with ${build.path}/core/core/sys* - * - * @param contribEnv - * @param confDesc - * @param boardsDescriptor - */ - private static Map getEnvVarsPostProcessing(Map vars) { - - Map extraVars = new HashMap<>(); - - ArrayList objcopyCommand = new ArrayList<>(); - for (Entry curVariable : vars.entrySet()) { - String name = curVariable.getKey(); - String value = curVariable.getValue(); - if (name.startsWith(RECIPE_OBJCOPY) && name.endsWith(".pattern") && !value.isEmpty()) { //$NON-NLS-1$ - objcopyCommand.add(makeEnvironmentVar(name)); - } - } - Collections.sort(objcopyCommand); - extraVars.put(SLOEBER_OBJCOPY, StringUtil.join(objcopyCommand, NEWLINE)); - - // add -relax for mega boards; the arduino ide way - String buildMCU = vars.get(ENV_KEY_BUILD_MCU); - if ("atmega2560".equalsIgnoreCase(buildMCU)) { //$NON-NLS-1$ - String c_elf_flags = vars.get(ENV_KEY_BUILD_COMPILER_C_ELF_FLAGS); - extraVars.put(ENV_KEY_BUILD_COMPILER_C_ELF_FLAGS, c_elf_flags + ",--relax"); //$NON-NLS-1$ - } - return extraVars; - } + * + * @param contribEnv + * @param confDesc + * @param boardsDescriptor + */ + private static Map getEnvVarsPostProcessing(Map vars) { + + Map extraVars = new HashMap<>(); + + ArrayList objcopyCommand = new ArrayList<>(); + for (Entry curVariable : vars.entrySet()) { + String name = curVariable.getKey(); + String value = curVariable.getValue(); + if (name.startsWith(RECIPE_OBJCOPY) && name.endsWith(".pattern") && !value.isEmpty()) { //$NON-NLS-1$ + objcopyCommand.add(makeEnvironmentVar(name)); + } + } + Collections.sort(objcopyCommand); + extraVars.put(SLOEBER_OBJCOPY, StringUtil.join(objcopyCommand, NEWLINE)); + + // add -relax for mega boards; the arduino ide way + String buildMCU = vars.get(ENV_KEY_BUILD_MCU); + if ("atmega2560".equalsIgnoreCase(buildMCU)) { //$NON-NLS-1$ + String c_elf_flags = vars.get(ENV_KEY_BUILD_COMPILER_C_ELF_FLAGS); + extraVars.put(ENV_KEY_BUILD_COMPILER_C_ELF_FLAGS, c_elf_flags + ",--relax"); //$NON-NLS-1$ + } + return extraVars; + } public LinkedHashMap getHookSteps(LinkedHashSet hookNames,IAutoBuildConfigurationDescription autoData) { LinkedHashMap hookSteps = new LinkedHashMap<>(); @@ -1242,69 +1233,69 @@ public LinkedHashMap getHookSteps(LinkedHashSet hookName } KeyValueTree hooks = keyValueTree.getChild(RECIPE).getChild(HOOKS); - for(String hookName:hookNames) { - hooks=hooks.getChild(hookName); + for (String hookName : hookNames) { + hooks = hooks.getChild(hookName); } // Try to find the nums from 1 to 10 in order // that is 01 1 02 2 for (int hoookNum = 1; hoookNum < 10; hoookNum++) { - boolean exists=false; + boolean exists = false; for (int numDigits = 1; numDigits <= 2; numDigits++) { String formatter = "%0" + Integer.toString(numDigits) + "d"; //$NON-NLS-1$ //$NON-NLS-2$ String curKey = String.format(formatter, Integer.valueOf(hoookNum)); KeyValueTree curHook = hooks.getChild(curKey).getChild(PATTERN); if (curHook.getValue() != null) { - String cmd=resolve(curHook.getValue(), EMPTY_STRING, WHITESPACE, autoData); + String cmd = resolve(curHook.getValue(), EMPTY_STRING, WHITESPACE, autoData); hookSteps.put(curKey, cmd); - exists=true; + exists = true; } } - if(!exists) { + if (!exists) { break; } } return hookSteps; } - public boolean isValid() { - if (!myUserSelectedBoardsTxtFile.exists()) { - return false; - } - File boardsFile = mySloeberBoardTxtFile.getLoadedFile(); - if (boardsFile == null) { - return false; - } - return boardsFile.exists(); - } - - public void reloadTxtFile() { - mySloeberBoardTxtFile.reloadTxtFile(); + public boolean isValid() { + if (!myUserSelectedBoardsTxtFile.exists()) { + return false; + } + File boardsFile = mySloeberBoardTxtFile.getLoadedFile(); + if (boardsFile == null) { + return false; + } + return boardsFile.exists(); + } - } + public void reloadTxtFile() { + mySloeberBoardTxtFile.reloadTxtFile(); - public Map getAllMenus() { - return mySloeberBoardTxtFile.getMenus(); - } + } - public boolean isSSHUpload() { - if (!isNetworkUpload()) { - return false; - } - // This is a hardcoded fix. Not sure how to do better - return myBoardID.equals("yun"); //$NON-NLS-1$ - } + public Map getAllMenus() { + return mySloeberBoardTxtFile.getMenus(); + } + public boolean isSSHUpload() { + if (!isNetworkUpload()) { + return false; + } + // This is a hardcoded fix. Not sure how to do better + return myBoardID.equals("yun"); //$NON-NLS-1$ + } public String getDefaultValueIDFromMenu(String menuID) { - return this.mySloeberBoardTxtFile.getDefaultValueIDFromMenu(getBoardID(),menuID); + return this.mySloeberBoardTxtFile.getDefaultValueIDFromMenu(getBoardID(), menuID); } + public String getDefaultValueNameFromMenu(String menuID) { - return this.mySloeberBoardTxtFile.getDefaultValueNameFromMenu(getBoardID(),menuID); + return this.mySloeberBoardTxtFile.getDefaultValueNameFromMenu(getBoardID(), menuID); } public boolean isPrivate() { - return ! sloeberHomePath.isPrefixOf( new Path(myUserSelectedBoardsTxtFile.toString())); + return !sloeberHomePath.isPrefixOf(new Path(myUserSelectedBoardsTxtFile.toString())); } } diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java index d9573cdb..0ac79d9a 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java @@ -19,13 +19,12 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.TreeMap; +import java.util.Set; import java.util.TreeSet; import org.eclipse.core.runtime.IPath; @@ -34,8 +33,6 @@ import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Status; -import org.eclipse.ui.statushandlers.StatusManager; - import com.google.gson.Gson; import io.sloeber.arduinoFramework.internal.ArduinoPlatformPackageIndex; @@ -51,7 +48,6 @@ import io.sloeber.core.managers.InstallProgress; import io.sloeber.core.tools.MyMultiStatus; import io.sloeber.core.tools.PackageManager; -import io.sloeber.core.txt.BoardTxtFile; import io.sloeber.core.txt.WorkAround; /** @@ -62,771 +58,849 @@ * */ public class BoardsManager { - private static final String THIRD_PARTY_URL_FILE="sloeber_third_party_url.txt"; //$NON-NLS-1$ - private static final String[] DEFAULT_JSON_URLS = {"https://downloads.arduino.cc/packages/package_index.json", //$NON-NLS-1$ - "https://raw.githubusercontent.com/jantje/hardware/master/package_jantje_index.json", //$NON-NLS-1$ - "https://raw.githubusercontent.com/jantje/ArduinoLibraries/master/library_jantje_index.json", //$NON-NLS-1$ - "https://arduino.esp8266.com/stable/package_esp8266com_index.json", //$NON-NLS-1$ - "https://www.pjrc.com/teensy/package_teensy_index.json", //$NON-NLS-1$ - "https://downloads.arduino.cc/libraries/library_index.json"};//$NON-NLS-1$ - - protected static List packageIndices; - private static boolean myHasbeenLogged = false; - private static boolean envVarsNeedUpdating = true;// reset global variables at startup - - private static HashMap myWorkbenchEnvironmentVariables = new HashMap<>(); - - /** - * Gets the board description based on the information provided. If - * jsonFileName="local" the board is assumed not to be installed by the boards - * manager. Otherwise the boardsmanager is queried to find the board descriptor. - * In this case the latest installed board will be returned - * - * @param jsonFileName - * equals to "local" or the name of the json file used by the boards - * manager to install the boards - * @param packageName - * if jsonFileName equals "local" the filename of the boards.txt - * containing the boards. otherwise the name of the package - * containing the board - * @param architectureID - * ignored if jsonFileName equals "local" otherwise the architecture - * name of the platform containing the board (this assumes the - * architecture is the unique id for the platform) - * @param boardID - * the id of the board in the boards.txt file - * @param options - * the options to specify the board (the menu named on the boards.txt - * file) or null for defaults - * @return The class BoardDescriptor or null - */ - static public BoardDescription getBoardDescription(String jsonFileName, String packageName, String architectureID, - String boardID, Map options) { - if (LOCAL.equals(jsonFileName)) { - return new BoardDescription(new File(packageName), boardID, options); - } - return getNewestBoardIDFromBoardsManager(jsonFileName, packageName, architectureID, boardID, options); - } - - static private BoardDescription getNewestBoardIDFromBoardsManager(String jsonFileName, String packageName, - String architectureID, String boardID, Map options) { - - IArduinoPackage thePackage = getPackage(jsonFileName, packageName); - if (thePackage == null) { - System.err.println("failed to find package:" + packageName); //$NON-NLS-1$ - return null; - } - IArduinoPlatform platform = thePackage.getPlatform(architectureID); - if (platform == null) { - System.err.println("failed to find architecture ID " + architectureID + " in package:" + packageName); //$NON-NLS-1$ //$NON-NLS-2$ - return null; - } - IArduinoPlatformVersion platformVersion = platform.getNewestVersion(); - java.io.File boardsFile = platformVersion.getBoardsFile(); - BoardDescription boardid = new BoardDescription(boardsFile, boardID, options); - - return boardid; - } - - public static void addPackageURLs(Collection packageUrlsToAdd, boolean forceDownload) { - if (!isReady()) { - Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); - return; - } - HashSet originalJsonUrls = new HashSet<>(Arrays.asList(getJsonURLList())); - packageUrlsToAdd.addAll(originalJsonUrls); - - setJsonURLs(packageUrlsToAdd); - loadJsons(forceDownload); - } - - public static void setPackageURLs(Collection packageUrls, boolean forceDownload) { - if (!isReady()) { - Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); - return; - } - setJsonURLs(packageUrls); - loadJsons(forceDownload); - } - - /** - * installs a subset of the latest platforms It skips the first - * platforms And stops at platforms. To install the 5 first latest - * platforms installsubsetOfLatestPlatforms(0,5) - * - * @param fromIndex - * the platforms at the start to skip - * @param toIndex - * the platforms after this platform are skipped - */ - public static void installsubsetOfLatestPlatforms(int fromIndex, int toIndex) { - String DEPRECATED = "DEPRECATED"; //$NON-NLS-1$ - if (!isReady()) { - Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); - return; - } - envVarsNeedUpdating = true; - int currPlatformIndex = 1; - NullProgressMonitor monitor = new NullProgressMonitor(); - List allPackages = getPackages(); - for (IArduinoPackage curPackage : allPackages) { - Collection latestPlatforms = curPackage.getPlatforms(); - for (IArduinoPlatform curPlatform : latestPlatforms) { - if (!curPlatform.getName().toUpperCase().contains(DEPRECATED)) { - if (currPlatformIndex > fromIndex) { - IArduinoPlatformVersion latestPlatformVersion = curPlatform.getNewestVersion(); - if (!latestPlatformVersion.getName().toUpperCase().contains(DEPRECATED)) { - install(latestPlatformVersion, monitor); - } else { - System.out.println("skipping platform " + latestPlatformVersion.toString()); //$NON-NLS-1$ - } - } - if (currPlatformIndex++ > toIndex) { - return; - } - } else { - System.out.println("skipping platform " + curPlatform.toString()); //$NON-NLS-1$ - } - } - } - } - - /** - * Install all the latest platforms Assumes there are less than 100000 platforms - */ - public static void installAllLatestPlatforms() { - installsubsetOfLatestPlatforms(0, 100000); - } - - public static void installLatestPlatform(String JasonName, String packageName, String architectureName) { - if (!isReady()) { - Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); - return; - } - envVarsNeedUpdating = true; - IArduinoPackage curPackage = getPackage(JasonName, packageName); - if (curPackage != null) { - IArduinoPlatform curPlatform = curPackage.getPlatform(architectureName); - if (curPlatform != null) { - IArduinoPlatformVersion curPlatformVersion = curPlatform.getNewestVersion(); - if (curPlatformVersion != null) { - NullProgressMonitor monitor = new NullProgressMonitor(); - install(curPlatformVersion, monitor); - return; - } - } - } - Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, - "failed to find " + JasonName + " " + packageName + " " + architectureName)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - } - - private static IStatus install(IArduinoPlatformVersion platformVersion, IProgressMonitor monitor) { - boolean forceDownload = false; - // String name = platformVersion.getName(); - // String architecture = platformVersion.getArchitecture(); - // String version = platformVersion.getVersion().toString(); - // Check if we're installed already - if (platformVersion.isInstalled()) { - System.out.println("reusing platform " + platformVersion.toString()); //$NON-NLS-1$ - return Status.OK_STATUS; - } - - // Download platform archive - System.out.println("start installing platform " + platformVersion.toString()); //$NON-NLS-1$ - - MyMultiStatus mstatus = new MyMultiStatus("Failed to install " + platformVersion.getName()); //$NON-NLS-1$ - mstatus.addErrors(PackageManager.downloadAndInstall(platformVersion, forceDownload, monitor)); - if (!mstatus.isOK()) { - // no use installing tools when the boards failed installing - return mstatus; - } - - //keep a copy of the json file used at install - File packageFile = platformVersion.getParent().getParent().getPackageIndex().getJsonFile(); - File copyToFile = platformVersion.getInstallPath().append(packageFile.getName()).toFile(); - try { - Files.copy(packageFile.toPath(), copyToFile.toPath(), StandardCopyOption.REPLACE_EXISTING); - } catch (IOException e) { - e.printStackTrace(); - } - - IArduinoPackage referencingPkg = platformVersion.getParent().getParent(); - for (ArduinoPlatformTooldDependency toolDependency : platformVersion.getToolsDependencies()) { - ArduinoPlatformToolVersion tool = referencingPkg.getTool(toolDependency.getName(), - toolDependency.getVersion()); - if (tool == null) { - //this is a tool provided by another platform - //and the referencing platform does not specify the installable info - //This means the package file of the referencing platform needs to be provided - IArduinoPackage pkg = getPackageByProvider(toolDependency.getPackager()); - if (pkg != null) { - tool = pkg.getTool(toolDependency.getName(), toolDependency.getVersion()); - } - } - if (tool == null) { - mstatus.add(new Status(IStatus.ERROR, Activator.getId(), - Messages.Tool_no_valid_system.replace(Messages.KEY_TAG, toolDependency.getName()))); - } else if (!tool.isInstalled()) { - ArduinoInstallable installable = tool.getInstallable(); - if (installable != null) { - monitor.setTaskName(InstallProgress.getRandomMessage()); - mstatus.addErrors(PackageManager.downloadAndInstall(installable, forceDownload, monitor)); - } - } - } - - WorkAround.applyKnownWorkArounds(platformVersion); - - System.out.println("done installing platform " + platformVersion.toString()); //$NON-NLS-1$ - return mstatus; - } - - public static void addPrivateHardwarePath(String newHardwarePath) { - if (newHardwarePath == null) { - return; - } - String currentPaths[] = InstancePreferences.getPrivateHardwarePaths(); - String newPaths[] = new String[currentPaths.length + 1]; - for (int i = 0; i < currentPaths.length; i++) { - if (currentPaths[i].equals(newHardwarePath)) { - return; - } - newPaths[i] = currentPaths[i]; - } - newPaths[currentPaths.length] = newHardwarePath; - InstancePreferences.setPrivateHardwarePaths(newPaths); - } - - /** - * Searches for all boards.txt files from the hardware folders and the boards - * manager - * - * @return all the boards.txt files with full path and in a case insensitive - * order - */ - public static File[] getAllBoardsFiles() { - String hardwareFolders[] = getHardwarePaths(); - - TreeSet boardFiles = new TreeSet<>(); - for (String CurFolder : hardwareFolders) { - searchFiles(new File(CurFolder), boardFiles, BOARDS_FILE_NAME, 6); - } - if (boardFiles.size() == 0) { - Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, - Helpers_No_boards_txt_found.replace(FILE_TAG, String.join("\n", hardwareFolders)), null)); //$NON-NLS-1$ - return null; - } - return boardFiles.toArray(new File[boardFiles.size()]); - } - - private static void searchFiles(File folder, TreeSet Hardwarelists, String Filename, int depth) { - if (depth > 0) { - File[] a = folder.listFiles(); - if (a == null) { - if (!myHasbeenLogged) { - Activator.log(new Status(IStatus.INFO, CORE_PLUGIN_ID, - Helpers_Error_The_folder_is_empty.replace(FOLDER_TAG, folder.toString()), null)); - myHasbeenLogged = true; - } - return; - } - for (File f : a) { - if (f.isDirectory()) { - //ignore folders named tools - if(!f.getName().equals(TOOLS)) { - searchFiles(f, Hardwarelists, Filename, depth - 1); - } - } else if (f.getName().equals(Filename)) { - Hardwarelists.add(f); - } - } - } - } - - /** - * Gets all the folders that can contain hardware - * - * @return a list of all the folder locations that can contain hardware - */ - private static String[] getHardwarePaths() { - return (InstancePreferences.getPrivateHardwarePathsString() + File.pathSeparator - + ConfigurationPreferences.getInstallationPathPackages()).split(File.pathSeparator); - } - - public static IStatus updatePlatforms(List platformsToInstall, - List platformsToRemove, IProgressMonitor monitor, MultiStatus status) { - if (!isReady()) { - status.add(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, null)); - return status; - } - //TODO updating the jsons after selecting what to install seems dangerous to me; check to delete - if (!ConfigurationPreferences.getUpdateJasonFilesFlag()) { - loadJsons(true); - } - envVarsNeedUpdating = true; - try { - myIsReady = false; - for (IArduinoPlatformVersion curPlatform : platformsToRemove) { - status.add(uninstall(curPlatform, monitor)); - } - for (IArduinoPlatformVersion curPlatform : platformsToInstall) { - status.add(install(curPlatform, monitor)); - } - - } catch (@SuppressWarnings("unused") Exception e) { - // do nothing - } - myIsReady = true; - return status; - } - - public static IStatus uninstall(IArduinoPlatformVersion curPlatform, IProgressMonitor monitor) { - if (!curPlatform.isInstalled()) { - return Status.OK_STATUS; - } - - IPath installFolder = curPlatform.getInstallPath(); - try { - deleteDirectory(installFolder); - } catch (IOException e) { - return new Status(IStatus.ERROR, Activator.getId(), "Failed to remove folder" + installFolder.toString(), //$NON-NLS-1$ - e); - } - - return Status.OK_STATUS; - } - - public static TreeMap getAllmenus() { - TreeMap ret = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - File[] boardFiles = getAllBoardsFiles(); - for (File curBoardFile : boardFiles) { - BoardTxtFile txtFile = new BoardTxtFile(curBoardFile); - ret.putAll(txtFile.getMenus()); - } - return ret; - } - - public static void setPrivateHardwarePaths(String[] hardWarePaths) { - if (!isReady()) { - Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); - return; - } - InstancePreferences.setPrivateHardwarePaths(hardWarePaths); - } - - public static String getPrivateHardwarePathsString() { - return InstancePreferences.getPrivateHardwarePathsString(); - } - - protected static synchronized void loadJsons(boolean forceDownload) { - packageIndices = new ArrayList<>(); - LibraryManager.flushIndices(); - - String[] jsonUrls = getJsonURLList(); - for (String jsonUrl : jsonUrls) { - if (!jsonUrl.trim().isEmpty()) // skip empty lines - loadJson(jsonUrl, forceDownload); - } - //sorting here so things look good in the gui - Collections.sort(packageIndices); - } - - /** - * This method takes a json boards file url and downloads it and parses it for - * usage in the boards manager - * - * @param url - * the url of the file to download and load - * @param forceDownload - * set true if you want to download the file even if it is already - * available locally - */ - static private void loadJson(String url, boolean forceDownload) { - File jsonFile = getLocalFileName(url, true); - if (jsonFile == null) { - return; - } - if (!jsonFile.exists() || forceDownload) { - jsonFile.getParentFile().mkdirs(); - try { - PackageManager.mySafeCopy(new URL(url.trim()), jsonFile, false); - } catch (IOException e) { - Activator.log(new Status(IStatus.ERROR, Activator.getId(), "Unable to download " + url, e)); //$NON-NLS-1$ - } - } - if (jsonFile.exists()) { - if (jsonFile.getName().toLowerCase().startsWith("package_")) { //$NON-NLS-1$ - loadPackage(url,jsonFile); - } else if (jsonFile.getName().toLowerCase().startsWith("library_")) { //$NON-NLS-1$ - LibraryManager.loadJson(jsonFile); - } else { - Activator.log(new Status(IStatus.ERROR, Activator.getId(), - "json files should start with \"package_\" or \"library_\" " + url + " is ignored")); //$NON-NLS-1$ //$NON-NLS-2$ - } - } - } - - static private void loadPackage(String url, File jsonFile) { - try (Reader reader = new FileReader(jsonFile)) { - ArduinoPlatformPackageIndex index = new Gson().fromJson(reader, ArduinoPlatformPackageIndex.class); - index.setPackageFile(jsonFile); - index.setURL(url); - packageIndices.add(index); - } catch (Exception e) { - Activator.log(new Status(IStatus.ERROR, Activator.getId(), - Manager_Failed_to_parse.replace(FILE_TAG, jsonFile.getAbsolutePath()), e)); - jsonFile.delete();// Delete the file so it stops damaging - } - } - - /** - * convert a web url to a local file name. The local file name is the cache of - * the web - * - * @param url - * url of the file we want a local cache - * @return the file that represents the file that is the local cache. the file - * itself may not exists. If the url is malformed return null; - * @throws MalformedURLException - */ - protected static File getLocalFileName(String url, boolean show_error) { - URL packageUrl; - try { - packageUrl = new URL(url.trim()); - } catch (MalformedURLException e) { - if (show_error) { - Activator.log(new Status(IStatus.ERROR, Activator.getId(), "Malformed url " + url, e)); //$NON-NLS-1$ - } - return null; - } - if ("file".equals(packageUrl.getProtocol())) { //$NON-NLS-1$ - String tst = packageUrl.getFile(); - File file = new File(tst); - String localFileName = file.getName(); - java.nio.file.Path packagePath = Paths - .get(ConfigurationPreferences.getInstallationPath().append(localFileName).toString()); - return packagePath.toFile(); - } - String localFileName = Paths.get(packageUrl.getPath()).getFileName().toString(); - java.nio.file.Path packagePath = Paths - .get(ConfigurationPreferences.getInstallationPath().append(localFileName).toString()); - return packagePath.toFile(); - } - - public static String[] getDefaultJsonURLs() { - return DEFAULT_JSON_URLS; - } - - - private static void setJsonURLs(Collection urls) { - IPath myThirdPartyURLStoragePath=getThirdPartyURLStoragePath(); - try { - if(myThirdPartyURLStoragePath!=null ) { - Files.write(myThirdPartyURLStoragePath.toPath(),urls, Charset.forName(StandardCharsets.UTF_8.name())); - } + private static final String THIRD_PARTY_URL_FILE = "sloeber_third_party_url.txt"; //$NON-NLS-1$ + + private static Set packageIndices = new HashSet<>(); + private static Set myPackageURLs = new HashSet<>(); + private static boolean myHasbeenLogged = false; + private static boolean envVarsNeedUpdating = true;// reset global variables at startup + + private static HashMap myWorkbenchEnvironmentVariables = new HashMap<>(); + + private static boolean myIsDirty = true; + + static { + getPersistentPackageURLList(); + } + + public static boolean isReady() { + return !myIsDirty; + } + + /** + * Gets the board description based on the information provided. If + * jsonFileName="local" the board is assumed not to be installed by the boards + * manager. Otherwise the boardsmanager is queried to find the board descriptor. + * In this case the latest installed board will be returned + * + * @param jsonURL equals to "local" or the name of the json file used by + * the boards manager to install the boards + * @param packageName if jsonFileName equals "local" the filename of the + * boards.txt containing the boards. otherwise the name of + * the package containing the board + * @param architectureID ignored if jsonFileName equals "local" otherwise the + * architecture name of the platform containing the board + * (this assumes the architecture is the unique id for the + * platform) + * @param boardID the id of the board in the boards.txt file + * @param options the options to specify the board (the menu named on the + * boards.txt file) or null for defaults + * @return The class BoardDescriptor or null + */ + static public BoardDescription getBoardDescription(String jsonURL, String packageName, String architectureID, + String boardID, Map options) { + if (LOCAL.equals(jsonURL)) { + return new BoardDescription(new File(packageName), boardID, options); + } + update(false); + return getNewestBoardIDFromBoardsManager(jsonURL, packageName, architectureID, boardID, options); + } + + static private BoardDescription getNewestBoardIDFromBoardsManager(String jsonURL, String packageName, + String architectureID, String boardID, Map options) { + + IArduinoPackage thePackage = getPackage(jsonURL, packageName); + if (thePackage == null) { + System.err.println("failed to find package:" + packageName); //$NON-NLS-1$ + return null; + } + IArduinoPlatform platform = thePackage.getPlatform(architectureID); + if (platform == null) { + System.err.println("failed to find architecture ID " + architectureID + " in package:" + packageName); //$NON-NLS-1$ //$NON-NLS-2$ + return null; + } + IArduinoPlatformVersion platformVersion = platform.getNewestVersion(); + java.io.File boardsFile = platformVersion.getBoardsFile(); + BoardDescription boardid = new BoardDescription(boardsFile, boardID, options); + + return boardid; + } + + public static void addPackageURLs(Collection packageUrlsToAdd) { + Set newList = new HashSet<>(); + newList.addAll(myPackageURLs); + newList.addAll(packageUrlsToAdd); + setPackageURLs(newList); +// HashSet originalJsonUrls = new HashSet<>(Arrays.asList(getJsonURLList())); +// packageUrlsToAdd.addAll(originalJsonUrls); +// +// setJsonURLs(packageUrlsToAdd); +// downloadJsons( forceDownload); +// readJsons(); + } + + public static void setPackageURLs(Collection packageUrls) { + Set orgList = new HashSet<>(); + orgList.addAll(myPackageURLs); + myPackageURLs.clear(); + myPackageURLs.addAll(packageUrls); + if (!orgList.equals(myPackageURLs)) { + myIsDirty = true; + + IPath myThirdPartyURLStoragePath = getThirdPartyURLStoragePath(); + try { + if (myThirdPartyURLStoragePath != null) { + Files.write(myThirdPartyURLStoragePath.toPath(), myPackageURLs, + Charset.forName(StandardCharsets.UTF_8.name())); + } + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } +// +// if (!isReady()) { +// Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); +// return; +// } +// setJsonURLs(packageUrls); +// downloadJsons( forceDownload); +// readJsons(); + } + + /** + * installs a subset of the latest platforms It skips the first + * platforms And stops at platforms. To install the 5 first latest + * platforms installsubsetOfLatestPlatforms(0,5) + * + * @param fromIndex the platforms at the start to skip + * @param toIndex the platforms after this platform are skipped + */ + public static void installsubsetOfLatestPlatforms(int fromIndex, int toIndex) { + String DEPRECATED = "DEPRECATED"; //$NON-NLS-1$ + if (!isReady()) { + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); + return; + } + + synchronized (packageIndices) { + int currPlatformIndex = 1; + NullProgressMonitor monitor = new NullProgressMonitor(); + List allPackages = getPackages(); + for (IArduinoPackage curPackage : allPackages) { + Collection latestPlatforms = curPackage.getPlatforms(); + for (IArduinoPlatform curPlatform : latestPlatforms) { + if (!curPlatform.getName().toUpperCase().contains(DEPRECATED)) { + if (currPlatformIndex > fromIndex) { + IArduinoPlatformVersion latestPlatformVersion = curPlatform.getNewestVersion(); + if (!latestPlatformVersion.getName().toUpperCase().contains(DEPRECATED)) { + install(latestPlatformVersion, monitor); + } else { + System.out.println("skipping platform " + latestPlatformVersion.toString()); //$NON-NLS-1$ + } + } + if (currPlatformIndex++ > toIndex) { + return; + } + } else { + System.out.println("skipping platform " + curPlatform.toString()); //$NON-NLS-1$ + } + } + } + } + } + + /** + * Install all the latest platforms Assumes there are less than 100000 platforms + */ + public static void installAllLatestPlatforms() { + installsubsetOfLatestPlatforms(0, 100000); + } + + public static void installLatestPlatform(String JasonName, String packagerName, String architectureName) { + if (!isReady()) { + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); + return; + } + internalInstallLatestPlatform( JasonName, packagerName, architectureName); + } + + private static void internalInstallLatestPlatform(String JasonName, String packagerName, String architectureName) { + synchronized (packageIndices) { + IArduinoPackage curPackage = getPackage(JasonName, packagerName); + if (curPackage != null) { + IArduinoPlatform curPlatform = curPackage.getPlatform(architectureName); + if (curPlatform != null) { + IArduinoPlatformVersion curPlatformVersion = curPlatform.getNewestVersion(); + if (curPlatformVersion != null) { + NullProgressMonitor monitor = new NullProgressMonitor(); + install(curPlatformVersion, monitor); + return; + } + } + } + } + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, + "failed to find " + JasonName + " " + packagerName + " " + architectureName)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + private static IStatus install(IArduinoPlatformVersion platformVersion, IProgressMonitor monitor) { + boolean forceDownload = false; + // Check if we're installed already + if (platformVersion.isInstalled()) { + System.out.println("reusing platform " + platformVersion.toString()); //$NON-NLS-1$ + return Status.OK_STATUS; + } + + envVarsNeedUpdating = true; + + // Download platform archive + System.out.println("start installing platform " + platformVersion.toString()); //$NON-NLS-1$ + + MyMultiStatus mstatus = new MyMultiStatus("Failed to install " + platformVersion.getName()); //$NON-NLS-1$ + mstatus.addErrors(PackageManager.downloadAndInstall(platformVersion, forceDownload, monitor)); + if (!mstatus.isOK()) { + // no use installing tools when the boards failed installing + return mstatus; + } + + // keep a copy of the json file used at install + File packageFile = platformVersion.getParent().getParent().getPackageIndex().getJsonFile(); + File copyToFile = platformVersion.getInstallPath().append(packageFile.getName()).toFile(); + try { + Files.copy(packageFile.toPath(), copyToFile.toPath(), StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { - // TODO Auto-generated catch block e.printStackTrace(); } - } - - public static String[] getJsonURLList() { - IPath myThirdPartyURLStoragePath=getThirdPartyURLStoragePath(); - try { - if(myThirdPartyURLStoragePath!=null && myThirdPartyURLStoragePath.toFile().exists()) { - ListthirdPartyURLs = Files.readAllLines(myThirdPartyURLStoragePath.toPath(), Charset.forName(StandardCharsets.UTF_8.name())); - if(thirdPartyURLs.size()>0) { - return thirdPartyURLs.toArray(new String[thirdPartyURLs.size()]); - } - } + + IArduinoPackage referencingPkg = platformVersion.getParent().getParent(); + for (ArduinoPlatformTooldDependency toolDependency : platformVersion.getToolsDependencies()) { + ArduinoPlatformToolVersion tool = referencingPkg.getTool(toolDependency.getName(), + toolDependency.getVersion()); + if (tool == null) { + // this is a tool provided by another platform + // and the referencing platform does not specify the installable info + // This means the package file of the referencing platform needs to be provided + IArduinoPackage pkg = getPackageByProvider(toolDependency.getPackager()); + if (pkg != null) { + tool = pkg.getTool(toolDependency.getName(), toolDependency.getVersion()); + } + } + if (tool == null) { + mstatus.add(new Status(IStatus.ERROR, Activator.getId(), + Messages.Tool_no_valid_system.replace(Messages.KEY_TAG, toolDependency.getName()))); + } else if (!tool.isInstalled()) { + ArduinoInstallable installable = tool.getInstallable(); + if (installable != null) { + monitor.setTaskName(InstallProgress.getRandomMessage()); + mstatus.addErrors(PackageManager.downloadAndInstall(installable, forceDownload, monitor)); + } + } + } + + WorkAround.applyKnownWorkArounds(platformVersion); + + System.out.println("done installing platform " + platformVersion.toString()); //$NON-NLS-1$ + return mstatus; + } + + public static void addPrivateHardwarePath(String newHardwarePath) { + if (newHardwarePath == null) { + return; + } + String currentPaths[] = InstancePreferences.getPrivateHardwarePaths(); + String newPaths[] = new String[currentPaths.length + 1]; + for (int i = 0; i < currentPaths.length; i++) { + if (currentPaths[i].equals(newHardwarePath)) { + return; + } + newPaths[i] = currentPaths[i]; + } + newPaths[currentPaths.length] = newHardwarePath; + InstancePreferences.setPrivateHardwarePaths(newPaths); + } + + /** + * Searches for all boards.txt files from the hardware folders and the boards + * manager + * + * @return all the boards.txt files with full path and in a case insensitive + * order + */ + public static File[] getAllBoardsFiles() { + update(false); + String hardwareFolders[] = getHardwarePaths(); + + TreeSet boardFiles = new TreeSet<>(); + for (String CurFolder : hardwareFolders) { + searchFiles(new File(CurFolder), boardFiles, BOARDS_FILE_NAME, 6); + } + if (boardFiles.size() == 0) { + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, + Helpers_No_boards_txt_found.replace(FILE_TAG, String.join("\n", hardwareFolders)), null)); //$NON-NLS-1$ + return null; + } + return boardFiles.toArray(new File[boardFiles.size()]); + } + + private static void searchFiles(File folder, TreeSet Hardwarelists, String Filename, int depth) { + if (depth > 0) { + File[] a = folder.listFiles(); + if (a == null) { + if (!myHasbeenLogged) { + Activator.log(new Status(IStatus.INFO, CORE_PLUGIN_ID, + Helpers_Error_The_folder_is_empty.replace(FOLDER_TAG, folder.toString()), null)); + myHasbeenLogged = true; + } + return; + } + for (File f : a) { + if (f.isDirectory()) { + // ignore folders named tools + if (!f.getName().equals(TOOLS)) { + searchFiles(f, Hardwarelists, Filename, depth - 1); + } + } else if (f.getName().equals(Filename)) { + Hardwarelists.add(f); + } + } + } + } + + /** + * Gets all the folders that can contain hardware + * + * @return a list of all the folder locations that can contain hardware + */ + private static String[] getHardwarePaths() { + return (InstancePreferences.getPrivateHardwarePathsString() + File.pathSeparator + + ConfigurationPreferences.getInstallationPathPackages()).split(File.pathSeparator); + } + + public static IStatus updatePlatforms(List platformsToInstall, + List platformsToRemove, IProgressMonitor monitor, MultiStatus status) { + if (!isReady()) { + status.add(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, null)); + return status; + } +// //TODO updating the jsons after selecting what to install seems dangerous to me; check to delete +// if (!ConfigurationPreferences.getUpdateJasonFilesFlag()) { +// downloadJsons( true); +// readJsons(); +// } + try { + synchronized (packageIndices) { + for (IArduinoPlatformVersion curPlatform : platformsToRemove) { + status.add(uninstall(curPlatform)); + } + for (IArduinoPlatformVersion curPlatform : platformsToInstall) { + status.add(install(curPlatform, monitor)); + } + } + } catch (@SuppressWarnings("unused") Exception e) { + // do nothing + } + return status; + } + + private static IStatus uninstall(IArduinoPlatformVersion curPlatform) { + if (!curPlatform.isInstalled()) { + return Status.OK_STATUS; + } + + IPath installFolder = curPlatform.getInstallPath(); + try { + deleteDirectory(installFolder); + envVarsNeedUpdating = true; + } catch (IOException e) { + return new Status(IStatus.ERROR, Activator.getId(), "Failed to remove folder" + installFolder.toString(), //$NON-NLS-1$ + e); + } + + return Status.OK_STATUS; + } + +// public static TreeMap getAllmenus() { +// update(false); +// TreeMap ret = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); +// File[] boardFiles = getAllBoardsFiles(); +// for (File curBoardFile : boardFiles) { +// BoardTxtFile txtFile = new BoardTxtFile(curBoardFile); +// ret.putAll(txtFile.getMenus()); +// } +// return ret; +// } + + public static void setPrivateHardwarePaths(String[] hardWarePaths) { + InstancePreferences.setPrivateHardwarePaths(hardWarePaths); + } + + public static String getPrivateHardwarePathsString() { + return InstancePreferences.getPrivateHardwarePathsString(); + } + + /** + * Download the json files from the internet If the file has already been + * downloaded it will only be downloaded again when forceDownload is true + * + * If the file exists and the download fails the original file will not be + * deleted. + * + * @param forceDownload if true all the json files will be replaced with new + * versions + */ + private static void downloadJsons(boolean forceDownload) { + String[] jsonUrls = getJsonURLList(); + for (String jsonUrl : jsonUrls) { + if (!jsonUrl.trim().isEmpty()) // skip empty lines + downloadJson(jsonUrl, forceDownload); + } + } + + private static void downloadJson(String jsonUrl, boolean forceDownload) { + File jsonFile = getLocalFileName(jsonUrl, true); + if (jsonFile == null) { + return; + } + File jsonTmpFile = new File(jsonFile.toString() + ".new"); //$NON-NLS-1$ + File jsonOldFile = new File(jsonFile.toString() + ".prev"); //$NON-NLS-1$ + if (!jsonFile.exists() || forceDownload) { + jsonFile.getParentFile().mkdirs(); + try { + if (jsonTmpFile.exists()) { + jsonTmpFile.delete(); + } + PackageManager.mySafeCopy(new URL(jsonUrl.trim()), jsonTmpFile, false); + if (jsonOldFile.exists()) { + jsonOldFile.delete(); + } + jsonFile.renameTo(jsonOldFile); + jsonTmpFile.renameTo(jsonFile); + } catch (IOException e) { + Activator.log(new Status(IStatus.ERROR, Activator.getId(), "Unable to download " + jsonUrl, e)); //$NON-NLS-1$ + } + } + } + + /** + * This method takes all the json boards file urls and downloads them and parses + * them for usage in the boards manager + * + * @param url the url of the file to download and load + * @param forceDownload set true if you want to download the file even if it is + * already available locally + */ + private static void readJsons() { + LibraryManager.flushIndices(); + packageIndices.clear(); + + String[] jsonUrls = getJsonURLList(); + for (String jsonUrl : jsonUrls) { + if (!jsonUrl.isBlank()) {// skip empty lines + File jsonFile = getLocalFileName(jsonUrl, true); + if (jsonFile == null) { + return; + } + if (jsonFile.exists()) { + if (jsonFile.getName().toLowerCase().startsWith("package_")) { //$NON-NLS-1$ + loadPackageJson(jsonUrl, jsonFile); + } else if (jsonFile.getName().toLowerCase().startsWith("library_")) { //$NON-NLS-1$ + LibraryManager.loadJson(jsonFile); + } else { + Activator.log(new Status(IStatus.ERROR, Activator.getId(), + "json files should start with \"package_\" or \"library_\" " + jsonUrl //$NON-NLS-1$ + + " is ignored")); //$NON-NLS-1$ + } + } + } + } + } + + static private void loadPackageJson(String url, File jsonFile) { + try (Reader reader = new FileReader(jsonFile)) { + ArduinoPlatformPackageIndex index = new Gson().fromJson(reader, ArduinoPlatformPackageIndex.class); + index.setPackageFile(jsonFile); + index.setURL(url); + packageIndices.add(index); + } catch (Exception e) { + Activator.log(new Status(IStatus.ERROR, Activator.getId(), + Manager_Failed_to_parse.replace(FILE_TAG, jsonFile.getAbsolutePath()), e)); + jsonFile.delete();// Delete the file so it stops damaging + } + } + + /** + * convert a web url to a local file name. The local file name is the cache of + * the web + * + * @param url url of the file we want a local cache + * @return the file that represents the file that is the local cache. the file + * itself may not exists. If the url is malformed return null; + * @throws MalformedURLException + */ + private static File getLocalFileName(String url, boolean show_error) { + URL packageUrl; + try { + packageUrl = new URL(url.trim()); + } catch (MalformedURLException e) { + if (show_error) { + Activator.log(new Status(IStatus.ERROR, Activator.getId(), "Malformed url " + url, e)); //$NON-NLS-1$ + } + return null; + } + if ("file".equals(packageUrl.getProtocol())) { //$NON-NLS-1$ + String tst = packageUrl.getFile(); + File file = new File(tst); + String localFileName = file.getName(); + java.nio.file.Path packagePath = Paths + .get(ConfigurationPreferences.getInstallationPath().append(localFileName).toString()); + return packagePath.toFile(); + } + String localFileName = Paths.get(packageUrl.getPath()).getFileName().toString(); + java.nio.file.Path packagePath = Paths + .get(ConfigurationPreferences.getInstallationPath().append(localFileName).toString()); + return packagePath.toFile(); + } + + public static String[] getDefaultJsonURLs() { + return Defaults.DEFAULT_JSON_URLS; + } + + public static String[] getJsonURLList() { + return myPackageURLs.toArray(new String[myPackageURLs.size()]); + } + + private static void getPersistentPackageURLList() { + myPackageURLs.clear(); + IPath myThirdPartyURLStoragePath = getThirdPartyURLStoragePath(); + try { + if (myThirdPartyURLStoragePath != null && myThirdPartyURLStoragePath.toFile().exists()) { + List thirdPartyURLs = Files.readAllLines(myThirdPartyURLStoragePath.toPath(), + Charset.forName(StandardCharsets.UTF_8.name())); + if (thirdPartyURLs.size() > 0) { + myPackageURLs.addAll(thirdPartyURLs); + return; + } + } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } - //The new way of storing the thirdparty urls's failed - //try the Sloeber V3 way for downwards compatibility - String[] sloeberV4Storage= getString("Manager jsons", EMPTY_STRING).replace("\r", new String()).split("\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - if(sloeberV4Storage.length>3) { - return sloeberV4Storage; - } - //Everything failed; This is probably a new install; return the defaults; - return DEFAULT_JSON_URLS; - - } - - private static IPath getThirdPartyURLStoragePath() { - return sloeberHomePath.append(SLOEBER_HOME_SUB_FOLDER).append(THIRD_PARTY_URL_FILE); - } - - - public static void removeAllInstalledPlatforms() { - if (!isReady()) { - Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); - return; - } - try { - deleteDirectory(ConfigurationPreferences.getInstallationPathPackages()); - } catch (IOException e) { - e.printStackTrace(); - } - } - - public static IPath getInstallationPath() { - return ConfigurationPreferences.getInstallationPath(); - } - - /** - * If something has been installed or deinstalled update the global variables - * with references to the installed stuff this to support platforms that do not - * explicitly define tools or platform dependencies Like private hardware - */ - public static Map getEnvironmentVariables() { - if (!envVarsNeedUpdating) { - return myWorkbenchEnvironmentVariables; - } - myWorkbenchEnvironmentVariables.clear(); - IArduinoPlatformVersion latestAvrPlatform = getNewestInstalledPlatform(VENDOR_ARDUINO, AVR); - IArduinoPlatformVersion latestSamdPlatform = getNewestInstalledPlatform(VENDOR_ARDUINO, SAMD); - IArduinoPlatformVersion latestSamPlatform = getNewestInstalledPlatform(VENDOR_ARDUINO, SAM); - - if (latestSamdPlatform != null) { - myWorkbenchEnvironmentVariables.putAll(getEnvVarPlatformFileTools(latestSamdPlatform)); - } - if (latestSamPlatform != null) { - myWorkbenchEnvironmentVariables.putAll(getEnvVarPlatformFileTools(latestSamPlatform)); - } - if (latestAvrPlatform != null) { - myWorkbenchEnvironmentVariables.putAll(getEnvVarPlatformFileTools(latestAvrPlatform)); - } - envVarsNeedUpdating = false; - return myWorkbenchEnvironmentVariables; - } - - private static Map getEnvVarPlatformFileTools(IArduinoPlatformVersion platformVersion) { - HashMap vars = new HashMap<>(); - IArduinoPackage pkg = platformVersion.getParent().getParent(); - for (ArduinoPlatformTooldDependency tool : platformVersion.getToolsDependencies()) { - ArduinoPlatformTool theTool = pkg.getTool(tool.getName()); - if (theTool == null) { - System.err.println("Did not find " + tool.getName() + " in package with ID " + pkg.getID()); //$NON-NLS-1$ //$NON-NLS-2$ - } else { - vars.putAll(theTool.getEnvVars(null)); - } - } - return vars; - } - - /** - * given a vendor and a architecture provide the newest installed platform - * version - * - * @param vendor - * @param architecture - * @return the found platformVersion or null if none found - */ - public static IArduinoPlatformVersion getNewestInstalledPlatform(String vendor, String architecture) { - IArduinoPlatform platform = getPlatform(vendor, architecture); - if (platform == null) { - return null; - } - return platform.getNewestInstalled(); - } - - //Below is what used to be the internal package manager class - - private static boolean myIsReady = false; - - public static boolean isReady() { - return myIsReady; - } - - /** - * Loads all stuff needed and if this is the first time downloads the avr boards - * and needed tools - * - * @param monitor - */ - public static synchronized void startup_Pluging(IProgressMonitor monitor) { - loadJsons(ConfigurationPreferences.getUpdateJasonFilesFlag()); - - if (!LibraryManager.libsAreInstalled()) { - LibraryManager.InstallDefaultLibraries(monitor); - } - IPath examplesPath = ConfigurationPreferences.getInstallationPathExamples(); - if (!examplesPath.toFile().exists()) {// examples are not installed - // Download arduino IDE example programs - Activator.log(PackageManager.downloadAndInstall(Defaults.EXAMPLES_URL, Defaults.EXAMPLE_PACKAGE, examplesPath, - false, monitor)); - } - if (!areThereInstalledBoards()) { - - IStatus status = null; - - // if successfully installed the examples: add the boards - IArduinoPlatform platform = getPlatform(Defaults.DEFAULT_INSTALL_MAINTAINER, - Defaults.DEFAULT_INSTALL_ARCHITECTURE); - //we failed to find arduino avr platform. Take the fiorst one - if (platform == null) { - try { - platform = getPlatforms().get(0); - } catch (@SuppressWarnings("unused") Exception e) { - //no need to do anything - } - } - if (platform == null) { - status = new Status(IStatus.ERROR, Activator.getId(), Messages.No_Platform_available); - } else { - status = install(platform.getNewestVersion(), monitor); - } - - if (!status.isOK()) { - StatusManager stMan = StatusManager.getManager(); - stMan.handle(status, StatusManager.LOG | StatusManager.SHOW | StatusManager.BLOCK); - } - - } - myIsReady = true; - - } - - synchronized static public List getPackageIndices() { - if (packageIndices == null) { - loadJsons(false); - } - return new LinkedList<>(packageIndices); - } - - public static List getPlatforms() { - List platforms = new ArrayList<>(); - for (IArduinoPlatformPackageIndex index : getPackageIndices()) { - for (IArduinoPackage pkg : index.getPackages()) { - platforms.addAll(pkg.getPlatforms()); - } - } - return platforms; - } - - public static IArduinoPlatform getPlatform(String vendor, String architecture) { - - for (IArduinoPlatformPackageIndex index : getPackageIndices()) { - IArduinoPackage pkg = index.getPackage(vendor); - if (pkg != null) { - IArduinoPlatform platform = pkg.getPlatform(architecture); - if (platform != null) { - return platform; - } - } - } - return null; - } - - /** - * Given a platform.txt file find the platform in the platform manager - * - * @param platformTxt - * @return the found platform otherwise null - */ - public static IArduinoPlatformVersion getPlatform(IPath platformPath) { - for (IArduinoPlatformPackageIndex index : getPackageIndices()) { - for (IArduinoPackage pkg : index.getPackages()) { - for (IArduinoPlatform curPlatform : pkg.getPlatforms()) { - if (platformPath - .matchingFirstSegments(curPlatform.getInstallPath()) > (platformPath.segmentCount() - 2)) - - for (IArduinoPlatformVersion curPlatformVersion : curPlatform.getVersions()) { - if (curPlatformVersion.getInstallPath().equals(platformPath)) { - return curPlatformVersion; - } - } - } - } - } - return null; - } - - static public List getInstalledPlatforms() { - List platforms = new ArrayList<>(); - for (IArduinoPlatformPackageIndex index : getPackageIndices()) { - for (IArduinoPackage pkg : index.getPackages()) { - - platforms.addAll(pkg.getInstalledPlatforms()); - - } - } - return platforms; - } - - static public boolean areThereInstalledBoards() { - for (IArduinoPlatformPackageIndex index : getPackageIndices()) { - for (IArduinoPackage pkg : index.getPackages()) { - if (pkg.isInstalled()) { - return true; - } - } - } - return false; - } - - static public List getPackages() { - List packages = new ArrayList<>(); - for (IArduinoPlatformPackageIndex index : getPackageIndices()) { - packages.addAll(index.getPackages()); - } - - return packages; - } - - static public IArduinoPackage getPackage(String JasonName, String packageName) { - for (IArduinoPlatformPackageIndex index : getPackageIndices()) { - if (index.getJsonFile().getName().equals(JasonName)) { - return index.getPackage(packageName); - } - } - return null; - } - - static public IArduinoPackage getPackage(String packageName) { - for (IArduinoPlatformPackageIndex index : getPackageIndices()) { - IArduinoPackage pkg = index.getPackage(packageName); - if (pkg != null) { - return pkg; - } - } - return null; - } - - - /** - * Remove all packages that have a more recent version - */ - public static void onlyKeepLatestPlatforms() { - if (!isReady()) { - Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); - return; - } - List allPackages = getPackages(); - for (IArduinoPackage curPackage : allPackages) { - curPackage.onlyKeepLatestPlatforms(); - } - } - - public static IArduinoPlatformVersion getPlatform(String vendor, String architecture, VersionNumber refVersion) { - IArduinoPlatform platform = getPlatform(vendor, architecture); - if (platform != null) { - return platform.getVersion(refVersion); - } - return null; - } - - public static IArduinoPackage getPackageByProvider(String packager) { - for (IArduinoPlatformPackageIndex index : getPackageIndices()) { - for (IArduinoPackage pkg : index.getPackages()) { - if (packager.equals(pkg.getID())) { - return pkg; - } - } - } - return null; - } + // The new way of storing the thirdparty urls's failed + // try the Sloeber V3 way for downwards compatibility + String[] sloeberV4Storage = getString("Manager jsons", EMPTY_STRING).replace("\r", new String()).split("\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + if (sloeberV4Storage.length > 3) { + myPackageURLs.addAll(Arrays.asList(sloeberV4Storage)); + return; + } + // Everything failed; This is probably a new install; return the defaults; + myPackageURLs.addAll(Arrays.asList(Defaults.DEFAULT_JSON_URLS)); + + } + + private static IPath getThirdPartyURLStoragePath() { + return sloeberHomePath.append(SLOEBER_HOME_SUB_FOLDER).append(THIRD_PARTY_URL_FILE); + } + + public static void removeAllInstalledPlatforms() { + if (!isReady()) { + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); + return; + } + try { + synchronized (packageIndices) { + deleteDirectory(ConfigurationPreferences.getInstallationPathPackages()); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static IPath getInstallationPath() { + return ConfigurationPreferences.getInstallationPath(); + } + + /** + * If something has been installed or deinstalled update the global variables + * with references to the installed stuff this to support platforms that do not + * explicitly define tools or platform dependencies Like private hardware + */ + public static Map getEnvironmentVariables() { + update(false); + return myWorkbenchEnvironmentVariables; + } + + private static Map getEnvVarPlatformFileTools(IArduinoPlatformVersion platformVersion) { + HashMap vars = new HashMap<>(); + IArduinoPackage pkg = platformVersion.getParent().getParent(); + for (ArduinoPlatformTooldDependency tool : platformVersion.getToolsDependencies()) { + ArduinoPlatformTool theTool = pkg.getTool(tool.getName()); + if (theTool == null) { + System.err.println("Did not find " + tool.getName() + " in package with ID " + pkg.getID()); //$NON-NLS-1$ //$NON-NLS-2$ + } else { + vars.putAll(theTool.getEnvVars(null)); + } + } + return vars; + } + + /** + * given a vendor and a architecture provide the newest installed platform + * version + * + * @param vendor + * @param architecture + * @return the found platformVersion or null if none found + */ + public static IArduinoPlatformVersion getNewestInstalledPlatform(String vendor, String architecture) { + IArduinoPlatform platform = getPlatform(vendor, architecture); + if (platform == null) { + return null; + } + return platform.getNewestInstalled(); + } + + // Below is what used to be the internal package manager class + + public static List getPackageIndices() { + update(false); + return new LinkedList<>(packageIndices); + } + +// public static List getPlatforms() { +// List platforms = new ArrayList<>(); +// for (IArduinoPlatformPackageIndex index : getPackageIndices()) { +// for (IArduinoPackage pkg : index.getPackages()) { +// platforms.addAll(pkg.getPlatforms()); +// } +// } +// return platforms; +// } + + public static IArduinoPlatform getPlatform(String vendor, String architecture) { + if (!isReady()) { + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); + return null; + } + for (IArduinoPlatformPackageIndex index : packageIndices) { + IArduinoPackage pkg = index.getPackage(vendor); + if (pkg != null) { + IArduinoPlatform platform = pkg.getPlatform(architecture); + if (platform != null) { + return platform; + } + } + } + return null; + } + + /** + * Given a platform.txt file find the platform in the platform manager + * + * @param platformTxt + * @return the found platform otherwise null + */ + public static IArduinoPlatformVersion getPlatform(IPath platformPath) { + if (!isReady()) { + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); + return null; + } + for (IArduinoPlatformPackageIndex index : packageIndices) { + for (IArduinoPackage pkg : index.getPackages()) { + for (IArduinoPlatform curPlatform : pkg.getPlatforms()) { + if (platformPath + .matchingFirstSegments(curPlatform.getInstallPath()) > (platformPath.segmentCount() - 2)) + + for (IArduinoPlatformVersion curPlatformVersion : curPlatform.getVersions()) { + if (curPlatformVersion.getInstallPath().equals(platformPath)) { + return curPlatformVersion; + } + } + } + } + } + return null; + } + +// static public List getInstalledPlatforms() { +// List platforms = new ArrayList<>(); +// for (IArduinoPlatformPackageIndex index : getPackageIndices()) { +// for (IArduinoPackage pkg : index.getPackages()) { +// platforms.addAll(pkg.getInstalledPlatforms()); +// } +// } +// return platforms; +// } + + /** + * Get any installed platform version This can be used in case of error + * conditions where any platform is better than null + * + * @return a platform or null if no platforms are installed + */ + static public IArduinoPlatformVersion getAnyInstalledPlatform() { + if (!isReady()) { + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); + return null; + } + for (IArduinoPlatformPackageIndex index : packageIndices) { + for (IArduinoPackage pkg : index.getPackages()) { + for (IArduinoPlatformVersion platformVersion : pkg.getInstalledPlatforms()) { + return platformVersion; + } + } + } + return null; + } + + static private boolean areThereInstalledBoards() { + for (IArduinoPlatformPackageIndex index : packageIndices) { + for (IArduinoPackage pkg : index.getPackages()) { + if (pkg.isInstalled()) { + return true; + } + } + } + return false; + } + + static public List getPackages() { + List packages = new ArrayList<>(); + if (!isReady()) { + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); + return packages; + } + + for (IArduinoPlatformPackageIndex index : packageIndices) { + packages.addAll(index.getPackages()); + } + + return packages; + } + + static private IArduinoPackage getPackage(String jasonURL, String packageName) { + for (IArduinoPlatformPackageIndex index : packageIndices) { + if (index.getJsonURL().equals(jasonURL)) { + return index.getPackage(packageName); + } + } + return null; + } + +// static public IArduinoPackage getPackage(String packageName) { +// for (IArduinoPlatformPackageIndex index : getPackageIndices()) { +// IArduinoPackage pkg = index.getPackage(packageName); +// if (pkg != null) { +// return pkg; +// } +// } +// return null; +// } + + /** + * Remove all packages that have a more recent version + */ + public static void onlyKeepLatestPlatforms() { + if (!isReady()) { + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); + return; + } + synchronized (packageIndices) { + List allPackages = getPackages(); + for (IArduinoPackage curPackage : allPackages) { + onlyKeepLatestPlatforms(curPackage); + } + } + } + + private static void onlyKeepLatestPlatforms(IArduinoPackage curPackage) { + for (IArduinoPlatform curplatform : curPackage.getPlatforms()) { + IArduinoPlatformVersion newestVersion = null; + for (IArduinoPlatformVersion curVersion : curplatform.getVersions()) { + if (curVersion.isInstalled()) { + if (newestVersion == null) { + newestVersion = curVersion; + } else { + if (newestVersion.getVersion().compareTo(curVersion.getVersion()) > 0) { + uninstall(curVersion); + } else { + uninstall(newestVersion); + } + } + } + } + } + } + + public static IArduinoPlatformVersion getPlatform(String vendor, String architecture, VersionNumber refVersion) { + IArduinoPlatform platform = getPlatform(vendor, architecture); + if (platform != null) { + return platform.getVersion(refVersion); + } + return null; + } + + public static IArduinoPackage getPackageByProvider(String packager) { + if (!isReady()) { + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); + return null; + } + for (IArduinoPlatformPackageIndex index : packageIndices) { + for (IArduinoPackage pkg : index.getPackages()) { + if (packager.equals(pkg.getID())) { + return pkg; + } + } + } + return null; + } + + /** + * If there are changes to the package url's this method will process these + * changes + * + * If there are no boards installed this methoid will install the latest arduino + * avr boards + * + * @param reloadFromInternet if true the downloaded the package files will be + * replaced with a newer download (if download + * succeeded) + */ + public static void update(boolean reloadFromInternet) { + synchronized (packageIndices) { + if (myIsDirty) { + downloadJsons(reloadFromInternet); + readJsons(); + + } + + if (!areThereInstalledBoards()) { + internalInstallLatestPlatform(Defaults.DEFAULT_INSTALL_JSON, Defaults.DEFAULT_INSTALL_MAINTAINER, + Defaults.DEFAULT_INSTALL_ARCHITECTURE); + } + + myIsDirty = false; + + if (envVarsNeedUpdating) { + myWorkbenchEnvironmentVariables.clear(); + IArduinoPlatformVersion latestAvrPlatform = getNewestInstalledPlatform(VENDOR_ARDUINO, AVR); + IArduinoPlatformVersion latestSamdPlatform = getNewestInstalledPlatform(VENDOR_ARDUINO, SAMD); + IArduinoPlatformVersion latestSamPlatform = getNewestInstalledPlatform(VENDOR_ARDUINO, SAM); + + if (latestSamdPlatform != null) { + myWorkbenchEnvironmentVariables.putAll(getEnvVarPlatformFileTools(latestSamdPlatform)); + } + if (latestSamPlatform != null) { + myWorkbenchEnvironmentVariables.putAll(getEnvVarPlatformFileTools(latestSamPlatform)); + } + if (latestAvrPlatform != null) { + myWorkbenchEnvironmentVariables.putAll(getEnvVarPlatformFileTools(latestAvrPlatform)); + } + envVarsNeedUpdating = false; + } + + } + + } } diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoPackage.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoPackage.java index 245e4c0e..47dc56f5 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoPackage.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/IArduinoPackage.java @@ -48,8 +48,6 @@ public interface IArduinoPackage extends Comparable{ ArduinoPlatformToolVersion getNewestInstalled(String toolName); - void onlyKeepLatestPlatforms(); - boolean isInstalled(); /** diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPackage.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPackage.java index 4e8d876c..c2d97d65 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPackage.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoPackage.java @@ -20,7 +20,6 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; -import io.sloeber.arduinoFramework.api.BoardsManager; import io.sloeber.arduinoFramework.api.IArduinoPackage; import io.sloeber.arduinoFramework.api.IArduinoPlatform; import io.sloeber.arduinoFramework.api.IArduinoPlatformVersion; @@ -191,25 +190,7 @@ public int compareTo(IArduinoPackage other) { return this.name.compareTo(other.getName()); } - @Override - public void onlyKeepLatestPlatforms() { - for (IArduinoPlatform curplatform : platforms.values()) { - IArduinoPlatformVersion newestVersion = null; - for (IArduinoPlatformVersion curVersion : curplatform.getVersions()) { - if (curVersion.isInstalled()) { - if (newestVersion == null) { - newestVersion = curVersion; - } else { - if (newestVersion.getVersion().compareTo(curVersion.getVersion()) > 0) { - BoardsManager.uninstall(curVersion, null); - } else { - BoardsManager.uninstall(newestVersion, null); - } - } - } - } - } - } + @Override public boolean isInstalled() { diff --git a/io.sloeber.core/src/io/sloeber/core/Activator.java b/io.sloeber.core/src/io/sloeber/core/Activator.java index bd25f063..d930a9f8 100644 --- a/io.sloeber.core/src/io/sloeber/core/Activator.java +++ b/io.sloeber.core/src/io/sloeber/core/Activator.java @@ -36,7 +36,9 @@ import cc.arduino.packages.discoverers.SloeberNetworkDiscovery; import io.sloeber.arduinoFramework.api.BoardsManager; +import io.sloeber.arduinoFramework.api.LibraryManager; import io.sloeber.core.api.ConfigurationPreferences; +import io.sloeber.core.api.Defaults; import io.sloeber.core.common.InstancePreferences; import io.sloeber.core.listeners.ConfigurationChangeListener; import io.sloeber.core.listeners.IndexerController; @@ -261,7 +263,7 @@ protected IStatus run(IProgressMonitor monitor) { installOtherStuff(); - BoardsManager.startup_Pluging(monitor); + startup_BoardsManager(monitor); monitor.setTaskName("Done!"); if (InstancePreferences.useBonjour()) { @@ -454,4 +456,27 @@ public static void log(IStatus status) { } } + /** + * Loads all stuff needed and if this is the first time downloads the avr boards + * and needed tools + * + * @param monitor + */ + private static synchronized void startup_BoardsManager(IProgressMonitor monitor) { + BoardsManager.update( ConfigurationPreferences.getUpdateJasonFilesFlag()); + + if (!LibraryManager.libsAreInstalled()) { + LibraryManager.InstallDefaultLibraries(monitor); + } + IPath examplesPath = ConfigurationPreferences.getInstallationPathExamples(); + if (!examplesPath.toFile().exists()) {// examples are not installed + // Download arduino IDE example programs + Activator.log(PackageManager.downloadAndInstall(Defaults.EXAMPLES_URL, Defaults.EXAMPLE_PACKAGE, examplesPath, + false, monitor)); + } + + + } + + } diff --git a/io.sloeber.core/src/io/sloeber/core/api/Defaults.java b/io.sloeber.core/src/io/sloeber/core/api/Defaults.java index ad4acaa9..42f94165 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Defaults.java +++ b/io.sloeber.core/src/io/sloeber/core/api/Defaults.java @@ -7,12 +7,24 @@ @SuppressWarnings("nls") public class Defaults { + public static final String DEFAULT_INSTALL_JSON = "https://downloads.arduino.cc/packages/package_index.json"; + public static final String JANTJE_BOARD_JSON_URL = "https://raw.githubusercontent.com/jantje/hardware/master/package_jantje_index.json"; + public static final String DEFAULT_INSTALL_ARCHITECTURE = "avr"; + public static final String DEFAULT_INSTALL_MAINTAINER = "arduino"; + public static final String[] DEFAULT_JSON_URLS = {DEFAULT_INSTALL_JSON, + JANTJE_BOARD_JSON_URL, + "https://raw.githubusercontent.com/jantje/ArduinoLibraries/master/library_jantje_index.json", + "https://arduino.esp8266.com/stable/package_esp8266com_index.json", + "https://www.pjrc.com/teensy/package_teensy_index.json", + "https://downloads.arduino.cc/libraries/library_index.json"}; + + + public static final String EXAMPLE_PACKAGE = "examples_Arduino_1_8_10.zip"; public static final String EXAMPLES_URL = "https://github.com/Sloeber/arduino-eclipse-plugin/releases/download/V4_3_2/" + EXAMPLE_PACKAGE; - public static final String DEFAULT_INSTALL_ARCHITECTURE = "avr"; - public static final String DEFAULT_INSTALL_MAINTAINER = "arduino"; + public static final String[] DEFAULT_INSTALLED_LIBRARIES = new String[] { "Ethernet", "Firmata", "GSM", "Keyboard", "LiquidCrystal", "Mouse", "SD", "Servo", "Stepper", "TFT", "WiFi", "CapacitiveSensor" }; public static final String DEFAULT = "Default"; diff --git a/io.sloeber.core/src/io/sloeber/core/api/Preferences.java b/io.sloeber.core/src/io/sloeber/core/api/Preferences.java index 6603fcd6..fb92a06d 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Preferences.java +++ b/io.sloeber.core/src/io/sloeber/core/api/Preferences.java @@ -48,13 +48,13 @@ public static boolean getUseArduinoToolSelection() { return InstancePreferences.getUseArduinoToolSelection(); } - public static void setUpdateJsonFiles(boolean flag) { - ConfigurationPreferences.setUpdateJasonFilesFlag(flag); - } - public static boolean getUpdateJsonFiles() { - return ConfigurationPreferences.getUpdateJasonFilesFlag(); - } - +// public static void setUpdateJsonFiles(boolean flag) { +// ConfigurationPreferences.setUpdateJasonFilesFlag(flag); +// } +// public static boolean getUpdateJsonFiles() { +// return ConfigurationPreferences.getUpdateJasonFilesFlag(); +// } + /** *wrapper for ConfigurationPreferences.useBonjour(); */ diff --git a/io.sloeber.ui/src/io/sloeber/ui/preferences/ThirdPartyHardwareSelectionPage.java b/io.sloeber.ui/src/io/sloeber/ui/preferences/ThirdPartyHardwareSelectionPage.java index b40c7979..a4ee2c5d 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/preferences/ThirdPartyHardwareSelectionPage.java +++ b/io.sloeber.ui/src/io/sloeber/ui/preferences/ThirdPartyHardwareSelectionPage.java @@ -10,9 +10,7 @@ import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.preferences.ConfigurationScope; -import org.eclipse.jface.preference.BooleanFieldEditor; import org.eclipse.jface.preference.FieldEditorPreferencePage; -import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; @@ -26,14 +24,13 @@ import org.eclipse.ui.preferences.ScopedPreferenceStore; import io.sloeber.arduinoFramework.api.BoardsManager; -import io.sloeber.core.api.Preferences; import io.sloeber.ui.Messages; import io.sloeber.ui.helpers.MyPreferences; public class ThirdPartyHardwareSelectionPage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { - private static final String KEY_UPDATE_JASONS = "Update jsons files"; //$NON-NLS-1$ + //private static final String KEY_UPDATE_JASONS = "Update jsons files"; //$NON-NLS-1$ private Text urlsText; - BooleanFieldEditor upDateJsons; + //BooleanFieldEditor upDateJsons; public ThirdPartyHardwareSelectionPage() { super(org.eclipse.jface.preference.FieldEditorPreferencePage.GRID); @@ -43,10 +40,11 @@ public ThirdPartyHardwareSelectionPage() { @Override public boolean performOk() { - boolean updateFiles=this.upDateJsons.getBooleanValue(); - Preferences.setUpdateJsonFiles(updateFiles); +// boolean updateFiles=this.upDateJsons.getBooleanValue(); +// Preferences.setUpdateJsonFiles(updateFiles); HashSet toSetList = new HashSet<>(Arrays.asList(urlsText.getText().split(System.lineSeparator()))); - BoardsManager.setPackageURLs(toSetList,updateFiles); + BoardsManager.setPackageURLs(toSetList); + BoardsManager.update(false); return super.performOk(); } @@ -73,12 +71,12 @@ protected void createFieldEditors() { this.urlsText.setLayoutData(gd); this.urlsText.setText(StringUtil.join(selectedJsons, System.lineSeparator())); - this.upDateJsons = new BooleanFieldEditor(KEY_UPDATE_JASONS, Messages.json_update, BooleanFieldEditor.DEFAULT, - parent); - addField(this.upDateJsons); - IPreferenceStore prefstore = getPreferenceStore(); - prefstore.setValue(KEY_UPDATE_JASONS, Preferences.getUpdateJsonFiles()); - prefstore.setDefault(KEY_UPDATE_JASONS, true); +// this.upDateJsons = new BooleanFieldEditor(KEY_UPDATE_JASONS, Messages.json_update, BooleanFieldEditor.DEFAULT, +// parent); +// addField(this.upDateJsons); +// IPreferenceStore prefstore = getPreferenceStore(); +// prefstore.setValue(KEY_UPDATE_JASONS, Preferences.getUpdateJsonFiles()); +// prefstore.setDefault(KEY_UPDATE_JASONS, true); final Hyperlink link = new Hyperlink(parent, SWT.NONE); link.setText(Messages.json_find); From 3be09fc240336cbafc5028755fab9e0ba37d0569 Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 13 Oct 2024 23:07:48 +0200 Subject: [PATCH 040/115] test impact Remove Sloeber setup and ... to boardsmanager --- .../BuildTests Sloeber Load packages.launch | 314 ++++++++++++++++++ .../src/io/sloeber/core/BuildTests.java | 11 +- .../src/io/sloeber/core/CompileAndUpload.java | 2 +- ...ArduinoIDEExamplesonJantjesBoardsTest.java | 2 +- ...teAndCompileDefaultInoOnAllBoardsTest.java | 2 +- .../core/CreateAndCompileExamplesTest.java | 2 +- .../CreateAndCompileLibraryExamplesTest.java | 2 +- .../src/io/sloeber/core/Shared.java | 6 +- .../src/io/sloeber/providers/Adafruit.java | 2 +- .../src/io/sloeber/providers/Arduino.java | 13 +- .../src/io/sloeber/providers/ESP32.java | 5 +- .../src/io/sloeber/providers/ESP8266.java | 5 +- .../src/io/sloeber/providers/Jantje.java | 7 +- .../src/io/sloeber/providers/Teensy.java | 5 +- 14 files changed, 347 insertions(+), 31 deletions(-) create mode 100644 io.sloeber.tests/BuildTests Sloeber Load packages.launch diff --git a/io.sloeber.tests/BuildTests Sloeber Load packages.launch b/io.sloeber.tests/BuildTests Sloeber Load packages.launch new file mode 100644 index 00000000..b57e331f --- /dev/null +++ b/io.sloeber.tests/BuildTests Sloeber Load packages.launch @@ -0,0 +1,314 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java index d5dd092a..0d6782c7 100644 --- a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java +++ b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java @@ -65,6 +65,7 @@ public class BuildTests { private static final boolean reinstall_boards_and_libraries = false; private final static String AUTOBUILD_CFG = ".autoBuildProject"; private static final String HIDlibName = "HID-Project"; + private static final String packageURL= "http://talk2arduino.wisen.com.au/master/package_talk2.wisen.com_index.json"; /* * In new new installations (of the Sloeber development environment) the @@ -93,9 +94,9 @@ private static void installAdditionalLibs() { public static void installAdditionalBoards() throws Exception { - String[] packageUrlsToAdd = { ESP8266.packageURL, ESP32.packageURL, - "http://talk2arduino.wisen.com.au/master/package_talk2.wisen.com_index.json" }; - BoardsManager.addPackageURLs(new HashSet<>(Arrays.asList(packageUrlsToAdd)), false); + String[] packageUrlsToAdd = { ESP8266.packageURL, ESP32.packageURL,packageURL}; + BoardsManager.addPackageURLs(new HashSet<>(Arrays.asList(packageUrlsToAdd))); + BoardsManager.update(false); Shared.waitForBoardsManager(); if (reinstall_boards_and_libraries) { BoardsManager.removeAllInstalledPlatforms(); @@ -671,10 +672,10 @@ public void testDifferentSourceFolders(String projectName, CodeDescription codeD public void redirectedJson() throws Exception { // this board references to arduino avr so install that one to Arduino.installLatestAVRBoards(); - BoardsManager.installLatestPlatform("package_talk2.wisen.com_index.json", "Talk2", "avr"); + BoardsManager.installLatestPlatform(packageURL, "Talk2", "avr"); Map options = new HashMap<>(); options.put("mhz", "16MHz"); - BoardDescription boardid = BoardsManager.getBoardDescription("package_talk2.wisen.com_index.json", "Talk2", + BoardDescription boardid = BoardsManager.getBoardDescription(packageURL, "Talk2", "avr", "whispernode", options); if (boardid == null) { fail("redirect Json "); diff --git a/io.sloeber.tests/src/io/sloeber/core/CompileAndUpload.java b/io.sloeber.tests/src/io/sloeber/core/CompileAndUpload.java index 4e3b4e57..584245af 100644 --- a/io.sloeber.tests/src/io/sloeber/core/CompileAndUpload.java +++ b/io.sloeber.tests/src/io/sloeber/core/CompileAndUpload.java @@ -100,7 +100,7 @@ static Stream uploadBourds() throws Exception { public static void installAdditionalBoards() { Preferences.setUseBonjour(false); String[] packageUrlsToAdd = { ESP32.packageURL, ESP8266.packageURL }; - BoardsManager.addPackageURLs(new HashSet<>(Arrays.asList(packageUrlsToAdd)), true); + BoardsManager.addPackageURLs(new HashSet<>(Arrays.asList(packageUrlsToAdd))); if (reinstall_boards_and_libraries) { BoardsManager.removeAllInstalledPlatforms(); diff --git a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileArduinoIDEExamplesonJantjesBoardsTest.java b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileArduinoIDEExamplesonJantjesBoardsTest.java index 702c2464..cddf2047 100644 --- a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileArduinoIDEExamplesonJantjesBoardsTest.java +++ b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileArduinoIDEExamplesonJantjesBoardsTest.java @@ -38,7 +38,7 @@ public static void setup() throws Exception { Preferences.setUseBonjour(false); Shared.waitForAllJobsToFinish(); String[] packageUrlsToAdd = { Jantje.additionalJsonURL }; - BoardsManager.addPackageURLs(new HashSet<>(Arrays.asList(packageUrlsToAdd)), true); + BoardsManager.addPackageURLs(new HashSet<>(Arrays.asList(packageUrlsToAdd))); Jantje.installLatestLocalDebugBoards(); Shared.waitForAllJobsToFinish(); diff --git a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java index 729b4d5d..4a76d971 100644 --- a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java +++ b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java @@ -355,7 +355,7 @@ public static void installAdditionalBoards() throws Exception { if (isMac) { toAddList.removeAll(Arrays.asList(packageUrlsToIgnoreOnMac)); } - BoardsManager.setPackageURLs(toAddList, true); + BoardsManager.addPackageURLs(toAddList); if (!skipPlatformInstallation) { BoardsManager.installAllLatestPlatforms(); diff --git a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileExamplesTest.java b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileExamplesTest.java index 603d73d1..98bfff82 100644 --- a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileExamplesTest.java +++ b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileExamplesTest.java @@ -81,7 +81,7 @@ public static Stream examples() throws Exception { public static void installAdditionalBoards() { String[] packageUrlsToAdd = { ESP8266.packageURL, Adafruit.packageURL }; - BoardsManager.addPackageURLs(new HashSet<>(Arrays.asList(packageUrlsToAdd)), true); + BoardsManager.addPackageURLs(new HashSet<>(Arrays.asList(packageUrlsToAdd))); if (reinstall_boards_and_examples) { BoardsManager.installAllLatestPlatforms(); BoardsManager.onlyKeepLatestPlatforms(); diff --git a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileLibraryExamplesTest.java b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileLibraryExamplesTest.java index 200c7fa6..1a40175f 100644 --- a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileLibraryExamplesTest.java +++ b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileLibraryExamplesTest.java @@ -82,7 +82,7 @@ public static void installMyStuff() { public static void installAdditionalBoards() { String[] packageUrlsToAdd = { ESP8266.packageURL, Adafruit.packageURL, ESP32.packageURL }; - BoardsManager.addPackageURLs(new HashSet<>(Arrays.asList(packageUrlsToAdd)), reinstall_boards_and_examples); + BoardsManager.addPackageURLs(new HashSet<>(Arrays.asList(packageUrlsToAdd))); if (reinstall_boards_and_examples) { BoardsManager.installAllLatestPlatforms(); BoardsManager.onlyKeepLatestPlatforms(); diff --git a/io.sloeber.tests/src/io/sloeber/core/Shared.java b/io.sloeber.tests/src/io/sloeber/core/Shared.java index 48518405..0c91a9ec 100644 --- a/io.sloeber.tests/src/io/sloeber/core/Shared.java +++ b/io.sloeber.tests/src/io/sloeber/core/Shared.java @@ -132,6 +132,8 @@ public static void waitForIndexer(IProject iProject) throws Exception { if(count%10==0) { System.out.println("Waiting for indexer"); } + icProject = CoreModel.getDefault().create(iProject); + index = CCorePlugin.getIndexManager().getIndex(icProject); } } @@ -248,7 +250,7 @@ public static String buildAndVerifyGivenBuilders(String projectName, BoardDescri } // clean so we will get a full build for this buider theTestProject.build(IncrementalProjectBuilder.CLEAN_BUILD, monitor); - + // set the builder CoreModel coreModel = CoreModel.getDefault(); ICProjectDescription projectDescription = coreModel.getProjectDescription(theTestProject, true); @@ -256,7 +258,7 @@ public static String buildAndVerifyGivenBuilders(String projectName, BoardDescri .getActiveConfig(projectDescription); autoDesc.setBuilder(curBuilder); coreModel.setProjectDescription(theTestProject, projectDescription); - + theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, monitor); if (hasBuildErrors(theTestProject)!=null) { diff --git a/io.sloeber.tests/src/io/sloeber/providers/Adafruit.java b/io.sloeber.tests/src/io/sloeber/providers/Adafruit.java index 9584d63c..b6afdf84 100644 --- a/io.sloeber.tests/src/io/sloeber/providers/Adafruit.java +++ b/io.sloeber.tests/src/io/sloeber/providers/Adafruit.java @@ -18,7 +18,7 @@ public class Adafruit extends MCUBoard { public Adafruit(String architectureID, String boardName) { - myBoardDescriptor = BoardsManager.getBoardDescription("package_adafruit_index.json", "adafruit", architectureID, + myBoardDescriptor = BoardsManager.getBoardDescription(packageURL, "adafruit", architectureID, boardName, null); if (myBoardDescriptor == null) { fail(boardName + " Board not found"); diff --git a/io.sloeber.tests/src/io/sloeber/providers/Arduino.java b/io.sloeber.tests/src/io/sloeber/providers/Arduino.java index de640889..4957ea1f 100644 --- a/io.sloeber.tests/src/io/sloeber/providers/Arduino.java +++ b/io.sloeber.tests/src/io/sloeber/providers/Arduino.java @@ -13,10 +13,12 @@ import io.sloeber.arduinoFramework.api.IArduinoPackage; import io.sloeber.arduinoFramework.api.IArduinoPlatform; import io.sloeber.arduinoFramework.api.IArduinoPlatformVersion; +import io.sloeber.core.api.Defaults; @SuppressWarnings("nls") public class Arduino extends MCUBoard { + private static final String packageURL= Defaults.DEFAULT_INSTALL_JSON; private static final String providerArduino = "arduino"; private static final String providerIntel = "Intel"; private static final String AVRArchitectureName = "avr"; @@ -25,7 +27,6 @@ public class Arduino extends MCUBoard { private static final String NFRArchitectureName = "nrf52"; private static final String MBEDArchitectureName = "mbed"; private static final String intelCurieArchitectureName = "arc32"; - private static final String jsonFileName = "package_index.json"; public static final String circuitplay32ID = "circuitplay32u4cat"; public static final String unoID = "uno"; @@ -116,7 +117,7 @@ public static MCUBoard arduino_101() { } private Arduino(String providerName, String architectureName, String boardID) { - this.myBoardDescriptor = BoardsManager.getBoardDescription(jsonFileName, providerName, architectureName, + this.myBoardDescriptor = BoardsManager.getBoardDescription(packageURL, providerName, architectureName, boardID, null); if (this.myBoardDescriptor == null) { fail(boardID + " Board not found"); @@ -264,19 +265,19 @@ private static List supportKeyboardList() { } public static void installLatestAVRBoards() { - BoardsManager.installLatestPlatform(jsonFileName, providerArduino, AVRArchitectureName); + BoardsManager.installLatestPlatform(packageURL, providerArduino, AVRArchitectureName); } public static void installLatestSamDBoards() { - BoardsManager.installLatestPlatform(jsonFileName, providerArduino, SAMDArchitectureName); + BoardsManager.installLatestPlatform(packageURL, providerArduino, SAMDArchitectureName); } public static void installLatestSamBoards() { - BoardsManager.installLatestPlatform(jsonFileName, providerArduino, SAMArchitectureName); + BoardsManager.installLatestPlatform(packageURL, providerArduino, SAMArchitectureName); } public static void installLatestIntellCurieBoards() { - BoardsManager.installLatestPlatform(jsonFileName, providerIntel, intelCurieArchitectureName); + BoardsManager.installLatestPlatform(packageURL, providerIntel, intelCurieArchitectureName); } public static List getAllBoards() { diff --git a/io.sloeber.tests/src/io/sloeber/providers/ESP32.java b/io.sloeber.tests/src/io/sloeber/providers/ESP32.java index 0160c596..4f0fa3db 100644 --- a/io.sloeber.tests/src/io/sloeber/providers/ESP32.java +++ b/io.sloeber.tests/src/io/sloeber/providers/ESP32.java @@ -13,7 +13,6 @@ public class ESP32 extends MCUBoard { private static final String provider = "esp32"; private static final String architectureName = "esp32"; - private static final String jsonFileName = "package_esp32_index.json"; public static final String packageURL = "https://espressif.github.io/arduino-esp32/package_esp32_index.json"; public static final String esp32ID = "esp32"; @@ -28,7 +27,7 @@ public static MCUBoard esp32() { } public ESP32(String boardName, Map options) { - myBoardDescriptor = BoardsManager.getBoardDescription(jsonFileName, provider, architectureName, boardName, + myBoardDescriptor = BoardsManager.getBoardDescription(packageURL, provider, architectureName, boardName, options); if (myBoardDescriptor == null) { fail(boardName + " Board not found"); @@ -43,7 +42,7 @@ public ESP32(BoardDescription boardDesc) { } public static void installLatest() { - BoardsManager.installLatestPlatform(jsonFileName, provider, architectureName); + BoardsManager.installLatestPlatform(packageURL, provider, architectureName); } @Override diff --git a/io.sloeber.tests/src/io/sloeber/providers/ESP8266.java b/io.sloeber.tests/src/io/sloeber/providers/ESP8266.java index b82762ce..92f52435 100644 --- a/io.sloeber.tests/src/io/sloeber/providers/ESP8266.java +++ b/io.sloeber.tests/src/io/sloeber/providers/ESP8266.java @@ -13,7 +13,6 @@ public class ESP8266 extends MCUBoard { private static final String provider = "esp8266"; private static final String architectureName = "esp8266"; - private static final String jsonFileName = "package_esp8266com_index.json"; public static final String packageURL = "https://arduino.esp8266.com/stable/package_esp8266com_index.json"; public static MCUBoard wemosD1() { @@ -33,7 +32,7 @@ public static MCUBoard ESPressoLite() { } public ESP8266(String boardName, Map options) { - myBoardDescriptor = BoardsManager.getBoardDescription(jsonFileName, provider, architectureName, boardName, + myBoardDescriptor = BoardsManager.getBoardDescription(packageURL, provider, architectureName, boardName, options); if (this.myBoardDescriptor == null) { fail(boardName + " Board not found"); @@ -48,7 +47,7 @@ public ESP8266(BoardDescription boardDesc) { } public static void installLatest() { - BoardsManager.installLatestPlatform(jsonFileName, provider, architectureName); + BoardsManager.installLatestPlatform(packageURL, provider, architectureName); } @Override diff --git a/io.sloeber.tests/src/io/sloeber/providers/Jantje.java b/io.sloeber.tests/src/io/sloeber/providers/Jantje.java index 3f93dc35..010a276b 100644 --- a/io.sloeber.tests/src/io/sloeber/providers/Jantje.java +++ b/io.sloeber.tests/src/io/sloeber/providers/Jantje.java @@ -9,6 +9,7 @@ import io.sloeber.arduinoFramework.api.BoardDescription; import io.sloeber.arduinoFramework.api.BoardsManager; +import io.sloeber.core.api.Defaults; @SuppressWarnings("nls") public class Jantje extends MCUBoard { @@ -16,7 +17,7 @@ public class Jantje extends MCUBoard { private static final String provider = "Jantje"; private static final String packageName = "Jantje"; private static final String localDebugArchitectureName = "pc"; - private static final String jsonFileName = "package_jantje_index.json"; + private static final String packageURL = Defaults.JANTJE_BOARD_JSON_URL; // the below json url is need as esp8266 is a referenced platform public static final String additionalJsonURL = "https://arduino.esp8266.com/stable/package_esp8266com_index.json"; @@ -43,7 +44,7 @@ public static MCUBoard uno() { public Jantje(String boardName) { Map options = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); options.put("type", "debug"); - myBoardDescriptor = BoardsManager.getBoardDescription(jsonFileName, packageName, localDebugArchitectureName, + myBoardDescriptor = BoardsManager.getBoardDescription(packageURL, packageName, localDebugArchitectureName, boardName, options); if (myBoardDescriptor == null) { fail(boardName + " Board not found"); @@ -52,7 +53,7 @@ public Jantje(String boardName) { } public static void installLatestLocalDebugBoards() { - BoardsManager.installLatestPlatform(jsonFileName, provider, localDebugArchitectureName); + BoardsManager.installLatestPlatform(packageURL, provider, localDebugArchitectureName); } @Override diff --git a/io.sloeber.tests/src/io/sloeber/providers/Teensy.java b/io.sloeber.tests/src/io/sloeber/providers/Teensy.java index be433c2b..77e2950f 100644 --- a/io.sloeber.tests/src/io/sloeber/providers/Teensy.java +++ b/io.sloeber.tests/src/io/sloeber/providers/Teensy.java @@ -21,7 +21,6 @@ public class Teensy extends MCUBoard { public final static String Teensy_LC_ID = "teensyLC"; public final static String TEENSY_PLATFORM_ID = "avr"; public final static String TEENSY_PROVIDER= "teensy"; - private static final String jsonFileName = "package_teensy_index.json"; public static final String packageURL = "https://www.pjrc.com/teensy/package_teensy_index.json"; public static MCUBoard Teensy_LC() { @@ -71,7 +70,7 @@ private Teensy(String boardID) { default: break; } - myBoardDescriptor = BoardsManager.getBoardDescription(jsonFileName, TEENSY_PROVIDER, TEENSY_PLATFORM_ID,boardID,options); + myBoardDescriptor = BoardsManager.getBoardDescription(packageURL, TEENSY_PROVIDER, TEENSY_PLATFORM_ID,boardID,options); setUploadPort("none"); setAttributes(); @@ -140,7 +139,7 @@ protected void setAttributes() { } public static void installLatest() { - BoardsManager.installLatestPlatform(jsonFileName, TEENSY_PROVIDER, TEENSY_PLATFORM_ID); + BoardsManager.installLatestPlatform(packageURL, TEENSY_PROVIDER, TEENSY_PLATFORM_ID); } public static List getAllBoards() { From 48ab9cf9036fd322cb921d6dbff0b47f27e35abf Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 20 Oct 2024 01:23:56 +0200 Subject: [PATCH 041/115] Only updat the json files every 10 days --- .../arduinoFramework/api/BoardsManager.java | 28 +++++++++-------- .../src/io/sloeber/core/Activator.java | 20 +++++++++++- .../core/api/ConfigurationPreferences.java | 31 +++++++++++++------ 3 files changed, 56 insertions(+), 23 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java index 0ac79d9a..d22d4fb7 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java @@ -69,12 +69,14 @@ public class BoardsManager { private static boolean myIsDirty = true; + private static boolean myIsUpdating =false; + static { getPersistentPackageURLList(); } public static boolean isReady() { - return !myIsDirty; + return !(myIsDirty || myIsUpdating); } /** @@ -178,7 +180,7 @@ public static void setPackageURLs(Collection packageUrls) { */ public static void installsubsetOfLatestPlatforms(int fromIndex, int toIndex) { String DEPRECATED = "DEPRECATED"; //$NON-NLS-1$ - if (!isReady()) { + if (myIsDirty) { Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); return; } @@ -218,7 +220,7 @@ public static void installAllLatestPlatforms() { } public static void installLatestPlatform(String JasonName, String packagerName, String architectureName) { - if (!isReady()) { + if (myIsDirty) { Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); return; } @@ -379,7 +381,7 @@ private static String[] getHardwarePaths() { public static IStatus updatePlatforms(List platformsToInstall, List platformsToRemove, IProgressMonitor monitor, MultiStatus status) { - if (!isReady()) { + if (myIsDirty) { status.add(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, null)); return status; } @@ -603,7 +605,7 @@ private static IPath getThirdPartyURLStoragePath() { } public static void removeAllInstalledPlatforms() { - if (!isReady()) { + if (myIsDirty) { Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); return; } @@ -678,7 +680,7 @@ public static List getPackageIndices() { // } public static IArduinoPlatform getPlatform(String vendor, String architecture) { - if (!isReady()) { + if (myIsDirty) { Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); return null; } @@ -701,7 +703,7 @@ public static IArduinoPlatform getPlatform(String vendor, String architecture) { * @return the found platform otherwise null */ public static IArduinoPlatformVersion getPlatform(IPath platformPath) { - if (!isReady()) { + if (myIsDirty) { Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); return null; } @@ -739,7 +741,7 @@ public static IArduinoPlatformVersion getPlatform(IPath platformPath) { * @return a platform or null if no platforms are installed */ static public IArduinoPlatformVersion getAnyInstalledPlatform() { - if (!isReady()) { + if (myIsDirty) { Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); return null; } @@ -766,7 +768,7 @@ static private boolean areThereInstalledBoards() { static public List getPackages() { List packages = new ArrayList<>(); - if (!isReady()) { + if (myIsDirty) { Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); return packages; } @@ -801,7 +803,7 @@ static private IArduinoPackage getPackage(String jasonURL, String packageName) { * Remove all packages that have a more recent version */ public static void onlyKeepLatestPlatforms() { - if (!isReady()) { + if (myIsDirty) { Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); return; } @@ -841,7 +843,7 @@ public static IArduinoPlatformVersion getPlatform(String vendor, String architec } public static IArduinoPackage getPackageByProvider(String packager) { - if (!isReady()) { + if (myIsDirty) { Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); return null; } @@ -868,6 +870,7 @@ public static IArduinoPackage getPackageByProvider(String packager) { */ public static void update(boolean reloadFromInternet) { synchronized (packageIndices) { + myIsUpdating =true; if (myIsDirty) { downloadJsons(reloadFromInternet); readJsons(); @@ -898,9 +901,8 @@ public static void update(boolean reloadFromInternet) { } envVarsNeedUpdating = false; } - } - + myIsUpdating=false; } } diff --git a/io.sloeber.core/src/io/sloeber/core/Activator.java b/io.sloeber.core/src/io/sloeber/core/Activator.java index d930a9f8..2df3b49c 100644 --- a/io.sloeber.core/src/io/sloeber/core/Activator.java +++ b/io.sloeber.core/src/io/sloeber/core/Activator.java @@ -8,6 +8,8 @@ import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; +import java.time.Duration; +import java.time.Instant; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.model.CoreModel; @@ -463,7 +465,23 @@ public static void log(IStatus status) { * @param monitor */ private static synchronized void startup_BoardsManager(IProgressMonitor monitor) { - BoardsManager.update( ConfigurationPreferences.getUpdateJasonFilesFlag()); + //ConfigurationPreferences.getUpdateJasonFilesFlag(); + Instant currentTime=Instant.now(); + Instant latestUpdate= ConfigurationPreferences.getLatestUpdateTime(); + Duration requestedDelay=ConfigurationPreferences.getUpdateDelay(); + Instant nextUpdate=latestUpdate.plus(requestedDelay); + boolean needsUpdate = nextUpdate.isBefore(currentTime); + +// long latestUpdate= getLatestUpdateTime(); +// long requestedDelay=getUpdateDelay(); +// long currentTime= System.currentTimeMillis(); +// boolean needsUpdate = currentTime>(requestedDelay+latestUpdate); + if(needsUpdate) { + BoardsManager.update(true ); + ConfigurationPreferences.setLatestUpdateTime(currentTime); + }else { + BoardsManager.update(false ); + } if (!LibraryManager.libsAreInstalled()) { LibraryManager.InstallDefaultLibraries(monitor); diff --git a/io.sloeber.core/src/io/sloeber/core/api/ConfigurationPreferences.java b/io.sloeber.core/src/io/sloeber/core/api/ConfigurationPreferences.java index 219decdc..08c36be9 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/ConfigurationPreferences.java +++ b/io.sloeber.core/src/io/sloeber/core/api/ConfigurationPreferences.java @@ -3,6 +3,8 @@ import static io.sloeber.core.api.Const.*; import java.io.File; +import java.time.Duration; +import java.time.Instant; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; @@ -26,7 +28,7 @@ public class ConfigurationPreferences { private static final String PRE_PROCESSING_BOARDS_TXT = "pre_processing_boards.txt"; //$NON-NLS-1$ private static final String POST_PROCESSING_BOARDS_TXT = "post_processing_boards.txt"; //$NON-NLS-1$ - private static final String KEY_UPDATE_JASONS = "Update jsons files"; //$NON-NLS-1$ + private static final String KEY_LATEST_JSON_UPDATE_TIME ="latest time the json files were updated";//$NON-NLS-1$ public static void removeKey(String key) { @@ -39,14 +41,19 @@ public static String getString(String key, String defaultValue) { return myScope.get(key, defaultValue); } - private static boolean getBoolean(String key, boolean defaultValue) { + + private static Instant getInstant(String key, Instant defaultValue) { IEclipsePreferences myScope = ConfigurationScope.INSTANCE.getNode(NODE_ARDUINO); - return myScope.getBoolean(key, defaultValue); + long ret = myScope.getLong(key, 0); + if(ret==0) { + return defaultValue; + } + return Instant.ofEpochSecond(ret); } - private static void setBoolean(String key, boolean value) { + private static void setInstant(String key, Instant value) { IEclipsePreferences myScope = ConfigurationScope.INSTANCE.getNode(NODE_ARDUINO); - myScope.putBoolean(key, value); + myScope.putLong(key, value.getEpochSecond()); try { myScope.flush(); } catch (BackingStoreException e) { @@ -123,12 +130,18 @@ public static IPath getAwkPath() { return new Path(getInstallationPath().append("tools/awk").toString()); //$NON-NLS-1$ } - public static boolean getUpdateJasonFilesFlag() { - return getBoolean(KEY_UPDATE_JASONS, Defaults.updateJsonFiles); + public static Instant getLatestUpdateTime() { + return getInstant(KEY_LATEST_JSON_UPDATE_TIME, Instant.now()); + } + + public static void setLatestUpdateTime(Instant currentTime) { + setInstant(KEY_LATEST_JSON_UPDATE_TIME,currentTime); + } - public static void setUpdateJasonFilesFlag(boolean newFlag) { - setBoolean(KEY_UPDATE_JASONS, newFlag); + public static Duration getUpdateDelay() { + // TODO Auto-generated method stub + return Duration.ofDays(10); } } From 1fe98b059950fbe781ad031d804927ceaa4347f0 Mon Sep 17 00:00:00 2001 From: jan Date: Mon, 21 Oct 2024 00:40:22 +0200 Subject: [PATCH 042/115] fix regression in json selection page; remove update flag The update flag is to be replaced by a time based setting together with the other options --- .../ui/JsonMultiLineTextFieldEditor.java | 51 +++ .../sloeber/ui/MultiLineTextFieldEditor.java | 381 ------------------ .../ThirdPartyHardwareSelectionPage.java | 49 +-- 3 files changed, 70 insertions(+), 411 deletions(-) create mode 100644 io.sloeber.ui/src/io/sloeber/ui/JsonMultiLineTextFieldEditor.java delete mode 100644 io.sloeber.ui/src/io/sloeber/ui/MultiLineTextFieldEditor.java diff --git a/io.sloeber.ui/src/io/sloeber/ui/JsonMultiLineTextFieldEditor.java b/io.sloeber.ui/src/io/sloeber/ui/JsonMultiLineTextFieldEditor.java new file mode 100644 index 00000000..a0e63a87 --- /dev/null +++ b/io.sloeber.ui/src/io/sloeber/ui/JsonMultiLineTextFieldEditor.java @@ -0,0 +1,51 @@ +package io.sloeber.ui; + +import org.eclipse.cdt.core.parser.util.StringUtil; +import org.eclipse.cdt.ui.newui.MultiLineTextFieldEditor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Text; + + +/** + * A class to change MultiLineTextFieldEditor so it puts the label on top of the + * text field and the text field expands bot horizontal and verical + * Note I needed a FLAT parent FieldEditorPreferencePage (GRID did not work) + * public ThirdPartyHardwareSelectionPage() { + * super(org.eclipse.jface.preference.FieldEditorPreferencePage.FLAT); + */ +public class JsonMultiLineTextFieldEditor extends MultiLineTextFieldEditor{ + + public JsonMultiLineTextFieldEditor(String name, String labelText, int width, int strategy, Composite parent) { + super( name, labelText, width,strategy, parent); + } + + /** + * I want 1 column + */ + @Override + public int getNumberOfControls() { + return 1; + } + + + /** + * I want a GridData that has horizontal fill + */ + @Override + protected void doFillIntoGrid(Composite parent, int numColumns) { + super.doFillIntoGrid( parent, numColumns) ; + + Text textField = getTextControl(parent); + textField.setLayoutData( new GridData(SWT.FILL,SWT.BEGINNING,true,false)); + + } + + public void setText(String[] text) { + String actualText=StringUtil.join(text, System.lineSeparator()); + getPreferenceStore().setValue(getPreferenceName(), actualText); + setStringValue(actualText ); + } + +} diff --git a/io.sloeber.ui/src/io/sloeber/ui/MultiLineTextFieldEditor.java b/io.sloeber.ui/src/io/sloeber/ui/MultiLineTextFieldEditor.java deleted file mode 100644 index 1f302644..00000000 --- a/io.sloeber.ui/src/io/sloeber/ui/MultiLineTextFieldEditor.java +++ /dev/null @@ -1,381 +0,0 @@ -package io.sloeber.ui; - -/******************************************************************************* - * Copyright (c) 2004 BitMethods Inc and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * BitMethods Inc - Initial API and implementation - *******************************************************************************/ - -import org.eclipse.jface.preference.FieldEditor; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.DisposeEvent; -import org.eclipse.swt.events.DisposeListener; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; - -/** - * MultiLineTextFieldEditor. Field editor that is same as string field editor - * but will have the multi line text field for user input. - */ -public class MultiLineTextFieldEditor extends FieldEditor { - - /** - * Text limit constant (value -1) indicating unlimited text - * limit and width. - */ - public static int UNLIMITED = -1; - - /** - * Cached valid state. - */ - private boolean isValid; - - /** - * Old text value. - */ - private String oldValue; - private String compTitle; - private Label title; - - /** - * The text field, or null if none. - */ - protected Text textField; - - /** - * Text limit of text field in characters; initially unlimited. - */ - private int textLimit = UNLIMITED; - - /** - * Indicates whether the empty string is legal; true by - * default. - */ - private boolean emptyStringAllowed = true; - - /** - * Creates a new string field editor - */ - protected MultiLineTextFieldEditor() { - } - - /** - * Creates a string field editor. Use the method setTextLimit - * to limit the text. - * - * @param name - * the name of the preference this field editor works on - * @param labelText - * the label text of the field editor - * @param width - * the width of the text input field in characters, or - * UNLIMITED for no limit - * @param parent - * the parent of the field editor's control - * @since 2.0 - */ - public MultiLineTextFieldEditor(String name, String labelText, Composite parent) { - init(name, labelText); - this.isValid = false; - createControl(parent); - } - - /** - * Adjusts the horizontal span of this field editor's basic controls - *

- * Subclasses must implement this method to adjust the horizontal span of - * controls so they appear correct in the given number of columns. - *

- *

- * The number of columns will always be equal to or greater than the value - * returned by this editor's getNumberOfControls method. - * - * @param numColumns - * the number of columns - */ - @Override - protected void adjustForNumColumns(int numColumns) { - GridData gd = (GridData) this.textField.getLayoutData(); - // grab all the space we can - gd.horizontalSpan = numColumns; - gd.grabExcessHorizontalSpace = true; - gd.grabExcessVerticalSpace = true; - } - - /** - * Checks whether the text input field contains a valid value or not. - * - * @return true if the field value is valid, and - * false if invalid - */ - protected boolean checkState() { - String txt = this.textField.getText(); - - if (txt == null) - return false; - - return (txt.trim().length() > 0) || this.emptyStringAllowed; - } - - /** - * Fills this field editor's basic controls into the given parent. - *

- * The string field implementation of this FieldEditor - * framework method contributes the text field. Subclasses may override but - * must call super.doFillIntoGrid. - *

- */ - @Override - protected void doFillIntoGrid(Composite parent, int numColumns) { - - this.title = new Label(parent, SWT.UP); - this.title.setFont(parent.getFont()); - this.compTitle = getLabelText(); - this.title.setText(this.compTitle); - this.title.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING)); - - this.textField = getTextControl(parent); - GridData gd = new GridData(GridData.FILL_BOTH); - this.textField.setLayoutData(gd); - - } - - /** - * Initializes this field editor with the preference value from the - * preference store. - *

- * Subclasses must implement this method to properly initialize the field - * editor. - *

- */ - @Override - protected void doLoad() { - if (this.textField != null) { - String value = getPreferenceStore().getString(getPreferenceName()); - this.textField.setText(value); - this.oldValue = value; - } - } - - /** - * Initializes this field editor with the default preference value from the - * preference store. - *

- * Subclasses must implement this method to properly initialize the field - * editor. - *

- */ - @Override - protected void doLoadDefault() { - if (this.textField != null) { - String value = getPreferenceStore().getDefaultString(getPreferenceName()); - this.textField.setText(value); - } - valueChanged(); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.preference.FieldEditor#doStore() - */ - @Override - protected void doStore() { - getPreferenceStore().setValue(getPreferenceName(), this.textField.getText()); - } - - /** - * Returns the field editor's value. - * - * @return the current value - */ - public String getStringValue() { - if (this.textField != null) - return this.textField.getText(); - return getPreferenceStore().getString(getPreferenceName()); - } - - /** - * Returns this field editor's text control. - * - * @param parent - * the parent - * @return the text control, or null if no text field is - * created yet - */ - protected Text getTextControl() { - return this.textField; - } - - /** - * Returns this field editor's text control. - *

- * The control is created if it does not yet exist - *

- * - * @param parent - * the parent - * @return the text control - */ - public Text getTextControl(Composite parent) { - if (this.textField == null) { - this.textField = new Text(parent, SWT.MULTI | SWT.V_SCROLL | SWT.BORDER | SWT.WRAP); - this.textField.setFont(parent.getFont()); - - this.textField.addDisposeListener(new DisposeListener() { - - @Override - public void widgetDisposed(DisposeEvent event) { - MultiLineTextFieldEditor.this.textField = null; - } - - }); - - if (this.textLimit > 0) { // Only set limits above 0 - see SWT spec - this.textField.setTextLimit(this.textLimit); - } - } else { - checkParent(this.textField, parent); - } - return this.textField; - } - - /** - * Returns whether an empty string is a valid value. - * - * @return true if an empty string is a valid value, and - * false if an empty string is invalid - * @see #setEmptyStringAllowed - */ - - public boolean isEmptyStringAllowed() { - return this.emptyStringAllowed; - } - - /** - * Returns whether this field editor contains a valid value. - *

- * The default implementation of this framework method returns - * true. Subclasses wishing to perform validation should - * override both this method and refreshValidState. - *

- * - * @return true if the field value is valid, and - * false if invalid - * @see #refreshValidState - */ - @Override - public boolean isValid() { - return this.isValid; - } - - /** - * Refreshes this field editor's valid state after a value change and fires - * an IS_VALID property change event if warranted. - *

- * The default implementation of this framework method does nothing. - * Subclasses wishing to perform validation should override both this method - * and isValid. - *

- * - * @see #isValid - */ - @Override - protected void refreshValidState() { - this.isValid = checkState(); - } - - /** - * Sets whether the empty string is a valid value or not. - * - * @param b - * true if the empty string is allowed, and - * false if it is considered invalid - */ - public void setEmptyStringAllowed(boolean b) { - this.emptyStringAllowed = b; - } - - /** - * Sets the focus to this field editor. - *

- * The default implementation of this framework method does nothing. - * Subclasses may reimplement. - *

- */ - @Override - public void setFocus() { - if (this.textField != null) { - this.textField.setFocus(); - } - } - - /** - * Sets this field editor's value. - * - * @param value - * the new value, or null meaning the empty string - */ - public void setStringValue(String value) { - String safeValue = value; - if (value == null) - safeValue = ""; //$NON-NLS-1$ - if (this.textField != null) { - - this.oldValue = this.textField.getText(); - if (!this.oldValue.equals(safeValue)) { - this.textField.setText(safeValue); - valueChanged(); - } - } - } - - /** - * Sets this text field's text limit. - * - * @param limit - * the limit on the number of character in the text input field, - * or UNLIMITED for no limit - */ - public void setTextLimit(int limit) { - this.textLimit = limit; - if (this.textField != null) - this.textField.setTextLimit(limit); - } - - /** - * Informs this field editor's listener, if it has one, about a change to - * the value (VALUE property) provided that the old and new - * values are different. - *

- * This hook is not called when the text is initialized (or reset - * to the default value) from the preference store. - *

- */ - protected void valueChanged() { - setPresentsDefaultValue(false); - boolean oldState = this.isValid; - refreshValidState(); - - if (this.isValid != oldState) - fireStateChanged(IS_VALID, oldState, this.isValid); - - String newValue = this.textField.getText(); - if (!newValue.equals(this.oldValue)) { - fireValueChanged(VALUE, this.oldValue, newValue); - this.oldValue = newValue; - } - } - - @Override - public int getNumberOfControls() { - return 2; - } -} \ No newline at end of file diff --git a/io.sloeber.ui/src/io/sloeber/ui/preferences/ThirdPartyHardwareSelectionPage.java b/io.sloeber.ui/src/io/sloeber/ui/preferences/ThirdPartyHardwareSelectionPage.java index a4ee2c5d..9c379542 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/preferences/ThirdPartyHardwareSelectionPage.java +++ b/io.sloeber.ui/src/io/sloeber/ui/preferences/ThirdPartyHardwareSelectionPage.java @@ -6,7 +6,7 @@ import java.util.Arrays; import java.util.HashSet; -import org.eclipse.cdt.core.parser.util.StringUtil; +import org.eclipse.cdt.ui.newui.MultiLineTextFieldEditor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.preferences.ConfigurationScope; @@ -14,8 +14,6 @@ import org.eclipse.swt.SWT; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPreferencePage; import org.eclipse.ui.forms.events.HyperlinkAdapter; @@ -24,25 +22,29 @@ import org.eclipse.ui.preferences.ScopedPreferenceStore; import io.sloeber.arduinoFramework.api.BoardsManager; +import io.sloeber.ui.JsonMultiLineTextFieldEditor; import io.sloeber.ui.Messages; import io.sloeber.ui.helpers.MyPreferences; public class ThirdPartyHardwareSelectionPage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { - //private static final String KEY_UPDATE_JASONS = "Update jsons files"; //$NON-NLS-1$ - private Text urlsText; - //BooleanFieldEditor upDateJsons; + @Override + protected void initialize() { + super.initialize(); + urlsText.setText(BoardsManager.getJsonURLList()); + } + + private static final String KEY_JSONS = "Jsons files"; //$NON-NLS-1$ + private JsonMultiLineTextFieldEditor urlsText; public ThirdPartyHardwareSelectionPage() { - super(org.eclipse.jface.preference.FieldEditorPreferencePage.GRID); + super(org.eclipse.jface.preference.FieldEditorPreferencePage.FLAT); setDescription(Messages.json_maintain); setPreferenceStore(new ScopedPreferenceStore(ConfigurationScope.INSTANCE, MyPreferences.NODE_ARDUINO)); } @Override public boolean performOk() { -// boolean updateFiles=this.upDateJsons.getBooleanValue(); -// Preferences.setUpdateJsonFiles(updateFiles); - HashSet toSetList = new HashSet<>(Arrays.asList(urlsText.getText().split(System.lineSeparator()))); + HashSet toSetList = new HashSet<>(Arrays.asList(urlsText.getStringValue().split(System.lineSeparator()))); BoardsManager.setPackageURLs(toSetList); BoardsManager.update(false); @@ -52,31 +54,18 @@ public boolean performOk() { @Override protected void performDefaults() { super.performDefaults(); - this.urlsText.setText(String.join(System.lineSeparator(), BoardsManager.getDefaultJsonURLs())); + urlsText.setText(BoardsManager.getDefaultJsonURLs()); } @Override protected void createFieldEditors() { - String selectedJsons[] = BoardsManager.getJsonURLList(); final Composite parent = getFieldEditorParent(); - // Composite control = new Composite(parent, SWT.NONE); - Label title = new Label(parent, SWT.UP); - title.setFont(parent.getFont()); - - title.setText(Messages.ui_url_for_index_file); - title.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING)); - - this.urlsText = new Text(parent, SWT.MULTI | SWT.V_SCROLL | SWT.BORDER | SWT.WRAP); - GridData gd = new GridData(GridData.FILL_BOTH); - this.urlsText.setLayoutData(gd); - this.urlsText.setText(StringUtil.join(selectedJsons, System.lineSeparator())); - -// this.upDateJsons = new BooleanFieldEditor(KEY_UPDATE_JASONS, Messages.json_update, BooleanFieldEditor.DEFAULT, -// parent); -// addField(this.upDateJsons); -// IPreferenceStore prefstore = getPreferenceStore(); -// prefstore.setValue(KEY_UPDATE_JASONS, Preferences.getUpdateJsonFiles()); -// prefstore.setDefault(KEY_UPDATE_JASONS, true); + GridData gd = new GridData(GridData.FILL,GridData.BEGINNING,true,false); + parent.setLayoutData(gd); + + urlsText =new JsonMultiLineTextFieldEditor(KEY_JSONS,Messages.ui_url_for_index_file,MultiLineTextFieldEditor.UNLIMITED,MultiLineTextFieldEditor.VALIDATE_ON_KEY_STROKE,parent); + addField(urlsText); + final Hyperlink link = new Hyperlink(parent, SWT.NONE); link.setText(Messages.json_find); From 4924f85cbb076137b3f1021e6ee5d67ab51e5994 Mon Sep 17 00:00:00 2001 From: jan Date: Wed, 23 Oct 2024 02:59:43 +0200 Subject: [PATCH 043/115] Prepare preference page for adding json update option --- io.sloeber.ui/META-INF/MANIFEST.MF | 3 +- io.sloeber.ui/src/io/sloeber/ui/Messages.java | 8 +- .../src/io/sloeber/ui/messages.properties | 3 + .../ui/preferences/PreferencePage.java | 175 ++++++++++++------ 4 files changed, 131 insertions(+), 58 deletions(-) diff --git a/io.sloeber.ui/META-INF/MANIFEST.MF b/io.sloeber.ui/META-INF/MANIFEST.MF index d29f92d6..80889069 100644 --- a/io.sloeber.ui/META-INF/MANIFEST.MF +++ b/io.sloeber.ui/META-INF/MANIFEST.MF @@ -16,7 +16,8 @@ Require-Bundle: org.eclipse.ui, org.apache.commons.commons-io, io.sloeber.autoBuild, org.eclipse.jface, - io.sloeber.autoBuild.ui + io.sloeber.autoBuild.ui, + org.eclipse.swt Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-ActivationPolicy: lazy Bundle-Vendor: Sloeber.io diff --git a/io.sloeber.ui/src/io/sloeber/ui/Messages.java b/io.sloeber.ui/src/io/sloeber/ui/Messages.java index 319c1882..21b58c23 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/Messages.java +++ b/io.sloeber.ui/src/io/sloeber/ui/Messages.java @@ -66,7 +66,13 @@ public class Messages extends NLS { public static String packageTooltip; public static String PlatformSelectionPage_hide_third_party_url; - public static String platformSelectionTip; + public static String PreferencePage_Internal_Behaviour_Group_Title; + + public static String PreferencePage_Network_Group_Title; + + public static String PreferencePage_UI_Behaviour_Group_Title; + + public static String platformSelectionTip; public static String pleaseWaitForInstallerJob; public static String plotterViewChannel; public static String plotterViewConnectedTo; diff --git a/io.sloeber.ui/src/io/sloeber/ui/messages.properties b/io.sloeber.ui/src/io/sloeber/ui/messages.properties index b148f61d..07ef3985 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/messages.properties +++ b/io.sloeber.ui/src/io/sloeber/ui/messages.properties @@ -5,6 +5,9 @@ BoardSelectionPage_platform_folder=Platform folder BoardSelectionPage_platform_you_want_to_use=The platform you want to use BoardSelectionPage_upload_protocol=Programmer protocol PlatformSelectionPage_hide_third_party_url=Hide 3th party json files +PreferencePage_Internal_Behaviour_Group_Title=Internal behaviour +PreferencePage_Network_Group_Title=Stuff that needs a network +PreferencePage_UI_Behaviour_Group_Title=UI behaviour always=Always arduino_upload_project_handler_build_failed=The build failed\! arduino_upload_project_handler_build_failed_so_no_upload=As the build failed the upload is not executed. diff --git a/io.sloeber.ui/src/io/sloeber/ui/preferences/PreferencePage.java b/io.sloeber.ui/src/io/sloeber/ui/preferences/PreferencePage.java index 6b4bddae..45dc615e 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/preferences/PreferencePage.java +++ b/io.sloeber.ui/src/io/sloeber/ui/preferences/PreferencePage.java @@ -15,13 +15,20 @@ import org.eclipse.jface.preference.ComboFieldEditor; import org.eclipse.jface.preference.FieldEditorPreferencePage; import org.eclipse.jface.preference.PathEditor; +import org.eclipse.jface.resource.FontRegistry; +import org.eclipse.jface.resource.JFaceResources; import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Font; import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Group; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPreferencePage; +import org.eclipse.ui.PlatformUI; import org.eclipse.ui.preferences.ScopedPreferenceStore; +import org.eclipse.ui.themes.ITheme; +import org.eclipse.ui.themes.IThemeManager; import io.sloeber.arduinoFramework.api.BoardsManager; import io.sloeber.arduinoFramework.api.LibraryManager; @@ -187,63 +194,127 @@ public void init(IWorkbench workbench) { */ @Override protected void createFieldEditors() { - final Composite parent = getFieldEditorParent(); + final Composite rootParent = getFieldEditorParent(); + Dialog.applyDialogFont(rootParent); + IThemeManager themeManager = PlatformUI.getWorkbench().getThemeManager(); + ITheme currentTheme = themeManager.getCurrentTheme(); + FontRegistry fontRegistry = currentTheme.getFontRegistry(); + Font headerFont =fontRegistry.get(JFaceResources.HEADER_FONT); - this.arduinoPrivateLibPathPathEditor = new PathEditor(KEY_PRIVATE_LIBRARY_PATHS, Messages.ui_private_lib_path, - Messages.ui_private_lib_path_help, parent); - addField(this.arduinoPrivateLibPathPathEditor); + arduinoPrivateLibPathPathEditor = new PathEditor(KEY_PRIVATE_LIBRARY_PATHS, Messages.ui_private_lib_path, + Messages.ui_private_lib_path_help, rootParent); + addField(arduinoPrivateLibPathPathEditor); - this.arduinoPrivateHardwarePathPathEditor = new PathEditor(KEY_PRIVATE_HARDWARE_PATHS, Messages.ui_private_hardware_path, - Messages.ui_private_hardware_path_help, parent); - addField(this.arduinoPrivateHardwarePathPathEditor); - - Dialog.applyDialogFont(parent); - createLine(parent, 4); - String[][] YesNoAskOptions = new String[][] { { Messages.ui_ask_every_upload, "ASK" }, //$NON-NLS-1$ - { Messages.yes, TRUE }, { Messages.no, FALSE } }; - this.buildBeforeUploadOptionEditor = new ComboFieldEditor(MyPreferences.KEY_BUILD_BEFORE_UPLOAD_OPTION, - Messages.ui_build_before_upload, YesNoAskOptions, parent); - addField(this.buildBeforeUploadOptionEditor); - createLine(parent, 4); - - this.useArduinoToolchainSelectionEditor = new BooleanFieldEditor(KEY_TOOLCHAIN_SELECTION, - Messages.ui_use_arduino_toolchain_selection, BooleanFieldEditor.DEFAULT, parent); - addField(this.useArduinoToolchainSelectionEditor); - - createLine(parent, 4); - this.openSerialMonitorOpensSerialsOptionEditor = new BooleanFieldEditor(MyPreferences.KEY_OPEN_SERIAL_WITH_MONITOR, - Messages.ui_open_serial_with_monitor, BooleanFieldEditor.DEFAULT, parent); - addField(this.openSerialMonitorOpensSerialsOptionEditor); - createLine(parent, 4); + arduinoPrivateHardwarePathPathEditor = new PathEditor(KEY_PRIVATE_HARDWARE_PATHS, Messages.ui_private_hardware_path, + Messages.ui_private_hardware_path_help, rootParent); + addField(arduinoPrivateHardwarePathPathEditor); - this.automaticallyImportLibrariesOptionEditor = new BooleanFieldEditor(KEY_AUTO_IMPORT_LIBRARIES, - Messages.ui_auto_import_libraries, BooleanFieldEditor.DEFAULT, parent); - addField(this.automaticallyImportLibrariesOptionEditor); - this.automaticallyInstallLibrariesOptionEditor = new BooleanFieldEditor(MyPreferences.KEY_AUTO_INSTALL_LIBRARIES, - Messages.ui_auto_install_libraries, BooleanFieldEditor.DEFAULT, parent); - addField(this.automaticallyInstallLibrariesOptionEditor); + Composite parent = new Composite(rootParent, SWT.NONE); + GridData gd1=new GridData(SWT.BEGINNING,SWT.CENTER,false,false); + //gd1.horizontalSpan=2; + parent.setLayoutData(gd1); + parent.setLayout(new GridLayout(1,false)); - this.pragmaOnceHeaderOptionEditor = new BooleanFieldEditor(KEY_PRAGMA_ONCE_HEADERS, Messages.ui_pragma_once_headers, - BooleanFieldEditor.DEFAULT, parent); - addField(this.pragmaOnceHeaderOptionEditor); - this.cleanSerialMonitorAfterUploadEditor = new BooleanFieldEditor(MyPreferences.KEY_CLEAN_MONITOR_AFTER_UPLOAD, - Messages.ui_clean_serial_monitor_after_upload, BooleanFieldEditor.DEFAULT, parent); - addField(this.cleanSerialMonitorAfterUploadEditor); + Group UIboxparent= new Group(parent, SWT.BORDER_SOLID ); + UIboxparent.setText(Messages.PreferencePage_UI_Behaviour_Group_Title); + UIboxparent.setFont(headerFont); + UIboxparent.setLayout(new GridLayout(2,true)); + GridData gd11=new GridData(SWT.FILL,SWT.TOP,true,false); + //gd11.horizontalSpan=4; + UIboxparent.setLayoutData(gd11); - this.switchToSerialMonitorAfterUploadEditor = new BooleanFieldEditor(MyPreferences.SWITCH_TO_MONITOR_AFTER_UPLOAD, - Messages.ui_switch_to_serial_monitor_after_upload, BooleanFieldEditor.DEFAULT, parent); - addField(this.switchToSerialMonitorAfterUploadEditor); + Composite UIbox = new Composite(UIboxparent, SWT.NONE); + GridData gd12=new GridData(SWT.FILL,SWT.TOP,true,false); + gd12.horizontalSpan=2; + UIbox.setLayoutData(gd12); + UIbox.setLayout(new GridLayout(2,false)); - this.enableParallelBuildForNewProjects = new BooleanFieldEditor(MyPreferences.KEY_ENABLE_PARALLEL_BUILD_FOR_NEW_PROJECTS, - Messages.ui_enable_parallel_build_for_new_projects, BooleanFieldEditor.DEFAULT, parent); - addField(this.enableParallelBuildForNewProjects); + String[][] YesNoAskOptions = new String[][] { { Messages.ui_ask_every_upload, "ASK" }, //$NON-NLS-1$ + { Messages.yes, TRUE }, { Messages.no, FALSE } }; + buildBeforeUploadOptionEditor = new ComboFieldEditor(MyPreferences.KEY_BUILD_BEFORE_UPLOAD_OPTION, + Messages.ui_build_before_upload, YesNoAskOptions, UIbox); + addField(buildBeforeUploadOptionEditor); + + Composite UIbox2 = new Composite(UIboxparent, SWT.NONE); + GridData gd13 = new GridData(SWT.FILL, SWT.TOP, true, false); + gd13.horizontalSpan = 2; + UIbox2.setLayoutData(gd13); + UIbox2.setLayout(new GridLayout(2, false)); + Dialog.applyDialogFont(UIbox2); + + openSerialMonitorOpensSerialsOptionEditor = new BooleanFieldEditor(MyPreferences.KEY_OPEN_SERIAL_WITH_MONITOR, + Messages.ui_open_serial_with_monitor, BooleanFieldEditor.DEFAULT, UIbox2); + addField(openSerialMonitorOpensSerialsOptionEditor); + + automaticallyImportLibrariesOptionEditor = new BooleanFieldEditor(KEY_AUTO_IMPORT_LIBRARIES, + Messages.ui_auto_import_libraries, BooleanFieldEditor.DEFAULT, UIbox2); + addField(automaticallyImportLibrariesOptionEditor); + + cleanSerialMonitorAfterUploadEditor = new BooleanFieldEditor(MyPreferences.KEY_CLEAN_MONITOR_AFTER_UPLOAD, + Messages.ui_clean_serial_monitor_after_upload, BooleanFieldEditor.DEFAULT, UIbox2); + addField(cleanSerialMonitorAfterUploadEditor); + + switchToSerialMonitorAfterUploadEditor = new BooleanFieldEditor(MyPreferences.SWITCH_TO_MONITOR_AFTER_UPLOAD, + Messages.ui_switch_to_serial_monitor_after_upload, BooleanFieldEditor.DEFAULT, UIbox2); + addField(switchToSerialMonitorAfterUploadEditor); + + + Group internalBehaviourGroup= new Group(parent, SWT.BORDER_SOLID); + internalBehaviourGroup.setText(Messages.PreferencePage_Internal_Behaviour_Group_Title); + GridData gd2=new GridData(SWT.FILL,SWT.TOP,true,false); + //gd2.horizontalSpan=2; + internalBehaviourGroup.setLayout(new GridLayout(2,true)); + internalBehaviourGroup.setLayoutData(gd2); + internalBehaviourGroup.setFont(headerFont); + + Composite internalBehaviourbox = new Composite(internalBehaviourGroup, SWT.NONE); + GridData gd14 = new GridData(SWT.FILL, SWT.TOP, true, false); + gd14.horizontalSpan = 2; + internalBehaviourbox.setLayoutData(gd14); + internalBehaviourbox.setLayout(new GridLayout(2, false)); + Dialog.applyDialogFont(internalBehaviourbox); + +// internalBehaviourGroup.setBackground(display.getSystemColor(SWT.COLOR_GREEN)); +// internalBehaviourbox.setBackground(display.getSystemColor(SWT.COLOR_RED)); + + useArduinoToolchainSelectionEditor = new BooleanFieldEditor(KEY_TOOLCHAIN_SELECTION, + Messages.ui_use_arduino_toolchain_selection, BooleanFieldEditor.DEFAULT, internalBehaviourbox); + addField(useArduinoToolchainSelectionEditor); + + pragmaOnceHeaderOptionEditor = new BooleanFieldEditor(KEY_PRAGMA_ONCE_HEADERS, Messages.ui_pragma_once_headers, + BooleanFieldEditor.DEFAULT, internalBehaviourbox); + addField(pragmaOnceHeaderOptionEditor); + + enableParallelBuildForNewProjects = new BooleanFieldEditor(MyPreferences.KEY_ENABLE_PARALLEL_BUILD_FOR_NEW_PROJECTS, + Messages.ui_enable_parallel_build_for_new_projects, BooleanFieldEditor.DEFAULT, internalBehaviourbox); + addField(enableParallelBuildForNewProjects); + + Group netWorkGroup= new Group(parent, SWT.BORDER_SOLID); + netWorkGroup.setText(Messages.PreferencePage_Network_Group_Title); + GridData gd3=new GridData(SWT.FILL,SWT.TOP,true,false); + //gd3.horizontalSpan=2; + netWorkGroup.setLayoutData(gd3); + netWorkGroup.setFont(headerFont); + netWorkGroup.setLayout(new GridLayout(2,true)); + + Composite netWorkbox = new Composite(netWorkGroup, SWT.NONE); + GridData gd15 = new GridData(SWT.FILL, SWT.TOP, true, false); + gd15.horizontalSpan = 2; + netWorkbox.setLayoutData(gd15); + netWorkbox.setLayout(new GridLayout(2, false)); + Dialog.applyDialogFont(netWorkbox); + + automaticallyInstallLibrariesOptionEditor = new BooleanFieldEditor(MyPreferences.KEY_AUTO_INSTALL_LIBRARIES, + Messages.ui_auto_install_libraries, BooleanFieldEditor.DEFAULT, netWorkbox); + addField(automaticallyInstallLibrariesOptionEditor); + + enableBonjour = new BooleanFieldEditor(KEY_USE_BONJOUR, + Messages.ui_enable_bonjour, BooleanFieldEditor.DEFAULT, netWorkbox); + addField(enableBonjour); - this.enableBonjour = new BooleanFieldEditor(KEY_USE_BONJOUR, - Messages.ui_enable_bonjour, BooleanFieldEditor.DEFAULT, parent); - addField(this.enableBonjour); } /** @@ -269,13 +340,5 @@ protected void performApply() { super.performApply(); } - private static void createLine(Composite parent, int ncol) { - Label line = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL | SWT.BOLD); - GridData gridData = new GridData(GridData.FILL_HORIZONTAL); - gridData.horizontalSpan = ncol; - line.setLayoutData(gridData); - } - - } From 56e2d045e50d5002e2727076989d14f400bb8758 Mon Sep 17 00:00:00 2001 From: jan Date: Tue, 29 Oct 2024 21:49:21 +0100 Subject: [PATCH 044/115] Add ui to json update frequency functionality Externalized some strings --- .../api/BoardDescription.java | 6 +- .../src/io/sloeber/core/Activator.java | 4 +- .../src/io/sloeber/core/Messages.java | 3 +- .../core/api/ConfigurationPreferences.java | 113 +++++++++++++++++- .../src/io/sloeber/core/api/Defaults.java | 13 +- .../src/io/sloeber/core/api/Preferences.java | 101 ---------------- .../src/io/sloeber/core/messages.properties | 1 + .../src/io/sloeber/core/BuildTests.java | 6 +- .../src/io/sloeber/core/CompileAndUpload.java | 4 +- ...leArduinoIDEExamplesOnAVRHardwareTest.java | 4 +- ...CompileArduinoIDEExamplesOnTeensyTest.java | 4 +- ...ArduinoIDEExamplesonJantjesBoardsTest.java | 4 +- ...teAndCompileDefaultInoOnAllBoardsTest.java | 6 +- .../core/CreateAndCompileExamplesTest.java | 4 +- .../CreateAndCompileLibraryExamplesTest.java | 6 +- .../src/io/sloeber/core/UpgradeTest.java | 4 +- .../core/releaseTesting_takes_very_long.java | 5 +- io.sloeber.ui/src/io/sloeber/ui/Messages.java | 14 ++- .../src/io/sloeber/ui/messages.properties | 6 + .../ui/preferences/PreferencePage.java | 101 ++++++++++++---- .../TargetSerialDisconnectPage.java | 9 +- 21 files changed, 252 insertions(+), 166 deletions(-) delete mode 100644 io.sloeber.core/src/io/sloeber/core/api/Preferences.java diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java index 24093393..4150fca6 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java @@ -35,9 +35,9 @@ import io.sloeber.autoBuild.api.IAutoBuildConfigurationDescription; import io.sloeber.autoBuild.helpers.api.KeyValueTree; import io.sloeber.core.Activator; +import io.sloeber.core.Messages; import io.sloeber.core.api.ConfigurationPreferences; import io.sloeber.core.api.Const; -import io.sloeber.core.api.Preferences; import io.sloeber.core.api.VersionNumber; import io.sloeber.core.tools.KeyValue; import io.sloeber.core.txt.BoardTxtFile; @@ -1005,7 +1005,7 @@ public Map getEnvVars() { allVars.putAll(getEnVarPlatformInfo()); } catch (IOException e) { // TODO Auto-generated catch block - Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, "failed to get platform paths", e)); + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, Messages.BoardDescription_0, e)); } // boards settings not coming from menu selections @@ -1078,7 +1078,7 @@ private Map getEnVarPlatformInfo() throws IOException { return ret; } - boolean jsonBasedPlatformManagement = !Preferences.getUseArduinoToolSelection(); + boolean jsonBasedPlatformManagement = !ConfigurationPreferences.getUseArduinoToolSelection(); if (jsonBasedPlatformManagement) { // overrule the Arduino IDE way of working and use the json refereced tools ret.putAll(getEnvVarPlatformFileTools(referencingPlatform)); diff --git a/io.sloeber.core/src/io/sloeber/core/Activator.java b/io.sloeber.core/src/io/sloeber/core/Activator.java index 2df3b49c..9b785834 100644 --- a/io.sloeber.core/src/io/sloeber/core/Activator.java +++ b/io.sloeber.core/src/io/sloeber/core/Activator.java @@ -467,8 +467,8 @@ public static void log(IStatus status) { private static synchronized void startup_BoardsManager(IProgressMonitor monitor) { //ConfigurationPreferences.getUpdateJasonFilesFlag(); Instant currentTime=Instant.now(); - Instant latestUpdate= ConfigurationPreferences.getLatestUpdateTime(); - Duration requestedDelay=ConfigurationPreferences.getUpdateDelay(); + Instant latestUpdate= ConfigurationPreferences.getLatestJsonUpdateTime(); + Duration requestedDelay=ConfigurationPreferences.getJsonUpdateDelay(); Instant nextUpdate=latestUpdate.plus(requestedDelay); boolean needsUpdate = nextUpdate.isBefore(currentTime); diff --git a/io.sloeber.core/src/io/sloeber/core/Messages.java b/io.sloeber.core/src/io/sloeber/core/Messages.java index dc465109..f9641cd9 100644 --- a/io.sloeber.core/src/io/sloeber/core/Messages.java +++ b/io.sloeber.core/src/io/sloeber/core/Messages.java @@ -39,7 +39,8 @@ public class Messages extends NLS { public static String ArduinoSerial_unable_to_open_serial_port; public static String ArduinoSerial_Using_1200bps_touch; public static String ArduinoSerial_Using_comport; - public static String Boards_Failed_to_read_boards; + public static String BoardDescription_0; + public static String Boards_Failed_to_read_boards; public static String Boards_Get_menu_item_name_from_id_did_not_find; public static String Boards_menu_ID_not_found; public static String Boards_menu_name_not_found; diff --git a/io.sloeber.core/src/io/sloeber/core/api/ConfigurationPreferences.java b/io.sloeber.core/src/io/sloeber/core/api/ConfigurationPreferences.java index 08c36be9..8e83fa97 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/ConfigurationPreferences.java +++ b/io.sloeber.core/src/io/sloeber/core/api/ConfigurationPreferences.java @@ -5,13 +5,18 @@ import java.io.File; import java.time.Duration; import java.time.Instant; +import java.util.HashSet; +import org.eclipse.cdt.core.parser.util.StringUtil; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.preferences.ConfigurationScope; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.osgi.service.prefs.BackingStoreException; +import cc.arduino.packages.discoverers.SloeberNetworkDiscovery; +import io.sloeber.core.common.InstancePreferences; + /** * Items on the Configuration level are linked to the ConfigurationScope * (=eclipse install base). @@ -29,7 +34,85 @@ public class ConfigurationPreferences { private static final String POST_PROCESSING_BOARDS_TXT = "post_processing_boards.txt"; //$NON-NLS-1$ private static final String KEY_LATEST_JSON_UPDATE_TIME ="latest time the json files were updated";//$NON-NLS-1$ + private static final String KEY_JSON_UPDATE_DELAY="Duration between json file updates";//$NON-NLS-1$ + + private static String stringSplitter = "\n";//$NON-NLS-1$ + private static final String KEY_DISCONNECT_SERIAL_TAGETS = "Target names that require serial disconnect to run";//$NON-NLS-1$ + + public static void setAutoImportLibraries(boolean booleanValue) { + InstancePreferences.setAutomaticallyImportLibraries(booleanValue); + + } + + public static void setPragmaOnceHeaders(boolean booleanValue) { + InstancePreferences.setPragmaOnceHeaders(booleanValue); + + } + + public static boolean getPragmaOnceHeaders() { + return InstancePreferences.getPragmaOnceHeaders(); + } + + public static boolean getAutoImportLibraries() { + return InstancePreferences.getAutomaticallyImportLibraries(); + } + + public static void setUseArduinoToolSelection(boolean booleanValue) { + InstancePreferences.setUseArduinoToolSelection(booleanValue); + + } + + public static boolean getUseArduinoToolSelection() { + return InstancePreferences.getUseArduinoToolSelection(); + } + +// public static void setUpdateJsonFiles(boolean flag) { +// ConfigurationPreferences.setUpdateJasonFilesFlag(flag); +// } +// public static boolean getUpdateJsonFiles() { +// return ConfigurationPreferences.getUpdateJasonFilesFlag(); +// } + + /** + *wrapper for ConfigurationPreferences.useBonjour(); + */ + public static boolean useBonjour() { + return InstancePreferences.useBonjour(); + } + + /** + *wrapper for ConfigurationPreferences.setUseBonjour(newFlag); + */ + public static void setUseBonjour(boolean newFlag) { + InstancePreferences.setUseBonjour(newFlag); + if(newFlag) { + SloeberNetworkDiscovery.start(); + }else { + SloeberNetworkDiscovery.stop(); + } + } + + + + public static String[] getDisconnectSerialTargetsList() { + return getDisconnectSerialTargets().split(stringSplitter); + } + + public static String getDisconnectSerialTargets() { + return getString(KEY_DISCONNECT_SERIAL_TAGETS, Defaults.getDefaultDisconnectSerialTargets()).replace("\r", EMPTY);//$NON-NLS-1$ + } + + public static void setDisconnectSerialTargets(String targets) { + setString(KEY_DISCONNECT_SERIAL_TAGETS, targets); + } + public static void setDisconnectSerialTargets(String targets[]) { + setString(KEY_DISCONNECT_SERIAL_TAGETS, StringUtil.join(targets, stringSplitter)); + } + + public static void setDisconnectSerialTargets(HashSet targets) { + setString(KEY_DISCONNECT_SERIAL_TAGETS, StringUtil.join(targets, stringSplitter)); + } public static void removeKey(String key) { IEclipsePreferences myScope = ConfigurationScope.INSTANCE.getNode(NODE_ARDUINO); @@ -130,7 +213,7 @@ public static IPath getAwkPath() { return new Path(getInstallationPath().append("tools/awk").toString()); //$NON-NLS-1$ } - public static Instant getLatestUpdateTime() { + public static Instant getLatestJsonUpdateTime() { return getInstant(KEY_LATEST_JSON_UPDATE_TIME, Instant.now()); } @@ -139,9 +222,31 @@ public static void setLatestUpdateTime(Instant currentTime) { } - public static Duration getUpdateDelay() { - // TODO Auto-generated method stub - return Duration.ofDays(10); + public static Duration getJsonUpdateDelay() { + return getDuration(KEY_JSON_UPDATE_DELAY,Defaults.getJsonUpdateDuration()); + } + + public static void setJsonUpdateDelay(Duration jsunUpdateDuration) { + setDuration(KEY_JSON_UPDATE_DELAY,jsunUpdateDuration); + } + + private static void setDuration(String key, Duration value) { + IEclipsePreferences myScope = ConfigurationScope.INSTANCE.getNode(NODE_ARDUINO); + myScope.putLong(key, value.toDays()); + try { + myScope.flush(); + } catch (BackingStoreException e) { + e.printStackTrace(); + } + } + + private static Duration getDuration(String key, Duration defaultValue) { + IEclipsePreferences myScope = ConfigurationScope.INSTANCE.getNode(NODE_ARDUINO); + long ret = myScope.getLong(key, 0); + if(ret==0) { + return defaultValue; + } + return Duration.ofDays(ret); } } diff --git a/io.sloeber.core/src/io/sloeber/core/api/Defaults.java b/io.sloeber.core/src/io/sloeber/core/api/Defaults.java index 42f94165..42c0cd32 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Defaults.java +++ b/io.sloeber.core/src/io/sloeber/core/api/Defaults.java @@ -2,6 +2,8 @@ import static io.sloeber.core.api.Const.*; +import java.time.Duration; + import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; @@ -11,6 +13,8 @@ public class Defaults { public static final String JANTJE_BOARD_JSON_URL = "https://raw.githubusercontent.com/jantje/hardware/master/package_jantje_index.json"; public static final String DEFAULT_INSTALL_ARCHITECTURE = "avr"; public static final String DEFAULT_INSTALL_MAINTAINER = "arduino"; + private static final String DEFAULT_DISCONNECT_SERIAL_TARGETS = "BurnBootLoader\nuploadWithBuild\nuploadWithoutBuild\nuploadWithProgrammerWithBuild\nuploadWithProgrammerWithoutBuild"; //$NON-NLS-1$ + public static final String[] DEFAULT_JSON_URLS = {DEFAULT_INSTALL_JSON, JANTJE_BOARD_JSON_URL, "https://raw.githubusercontent.com/jantje/ArduinoLibraries/master/library_jantje_index.json", @@ -29,7 +33,6 @@ public class Defaults { "LiquidCrystal", "Mouse", "SD", "Servo", "Stepper", "TFT", "WiFi", "CapacitiveSensor" }; public static final String DEFAULT = "Default"; - public static final boolean updateJsonFiles = true; public static final boolean useBonjour = true; public static final boolean autoInstallLibraries = true; public static final boolean useArduinoToolSelection = true; @@ -57,4 +60,12 @@ public static String getPrivateHardwarePath() { return homPath.append("Arduino").append(ARDUINO_HARDWARE_FOLDER_NAME).toString(); } + public static String getDefaultDisconnectSerialTargets() { + return DEFAULT_DISCONNECT_SERIAL_TARGETS; + } + + public static Duration getJsonUpdateDuration() { + return Duration.ofDays(7); + } + } diff --git a/io.sloeber.core/src/io/sloeber/core/api/Preferences.java b/io.sloeber.core/src/io/sloeber/core/api/Preferences.java deleted file mode 100644 index fb92a06d..00000000 --- a/io.sloeber.core/src/io/sloeber/core/api/Preferences.java +++ /dev/null @@ -1,101 +0,0 @@ -package io.sloeber.core.api; - -import static io.sloeber.core.api.ConfigurationPreferences.*; -import static io.sloeber.core.api.Const.*; - -import java.util.HashSet; - -import org.eclipse.cdt.core.parser.util.StringUtil; - -import cc.arduino.packages.discoverers.SloeberNetworkDiscovery; -import io.sloeber.core.common.InstancePreferences; - -/** - * This is a wrapper class to make internal configuration settings externally available - * There should not be any logic in this class only redirections to internal methods - * @author jan - * - */ -public class Preferences { - private static String stringSplitter = "\n";//$NON-NLS-1$ - private static final String KEY_DISCONNECT_SERIAL_TAGETS = "Target names that require serial disconnect to run";//$NON-NLS-1$ - private static final String DEFAULT_DISCONNECT_SERIAL_TARGETS = "BurnBootLoader\nuploadWithBuild\nuploadWithoutBuild\nuploadWithProgrammerWithBuild\nuploadWithProgrammerWithoutBuild"; //$NON-NLS-1$ - - public static void setAutoImportLibraries(boolean booleanValue) { - InstancePreferences.setAutomaticallyImportLibraries(booleanValue); - - } - - public static void setPragmaOnceHeaders(boolean booleanValue) { - InstancePreferences.setPragmaOnceHeaders(booleanValue); - - } - - public static boolean getPragmaOnceHeaders() { - return InstancePreferences.getPragmaOnceHeaders(); - } - - public static boolean getAutoImportLibraries() { - return InstancePreferences.getAutomaticallyImportLibraries(); - } - - public static void setUseArduinoToolSelection(boolean booleanValue) { - InstancePreferences.setUseArduinoToolSelection(booleanValue); - - } - - public static boolean getUseArduinoToolSelection() { - return InstancePreferences.getUseArduinoToolSelection(); - } - -// public static void setUpdateJsonFiles(boolean flag) { -// ConfigurationPreferences.setUpdateJasonFilesFlag(flag); -// } -// public static boolean getUpdateJsonFiles() { -// return ConfigurationPreferences.getUpdateJasonFilesFlag(); -// } - - /** - *wrapper for ConfigurationPreferences.useBonjour(); - */ - public static boolean useBonjour() { - return InstancePreferences.useBonjour(); - } - - /** - *wrapper for ConfigurationPreferences.setUseBonjour(newFlag); - */ - public static void setUseBonjour(boolean newFlag) { - InstancePreferences.setUseBonjour(newFlag); - if(newFlag) { - SloeberNetworkDiscovery.start(); - }else { - SloeberNetworkDiscovery.stop(); - } - } - - public static String getDefaultDisconnectSerialTargets() { - return DEFAULT_DISCONNECT_SERIAL_TARGETS; - } - - public static String[] getDisconnectSerialTargetsList() { - return getDisconnectSerialTargets().split(stringSplitter); - } - - public static String getDisconnectSerialTargets() { - return getString(KEY_DISCONNECT_SERIAL_TAGETS, DEFAULT_DISCONNECT_SERIAL_TARGETS).replace("\r", EMPTY);//$NON-NLS-1$ - } - - public static void setDisconnectSerialTargets(String targets) { - setString(KEY_DISCONNECT_SERIAL_TAGETS, targets); - } - - public static void setDisconnectSerialTargets(String targets[]) { - setString(KEY_DISCONNECT_SERIAL_TAGETS, StringUtil.join(targets, stringSplitter)); - } - - public static void setDisconnectSerialTargets(HashSet targets) { - setString(KEY_DISCONNECT_SERIAL_TAGETS, StringUtil.join(targets, stringSplitter)); - } - -} diff --git a/io.sloeber.core/src/io/sloeber/core/messages.properties b/io.sloeber.core/src/io/sloeber/core/messages.properties index 44fdf591..9ea71273 100644 --- a/io.sloeber.core/src/io/sloeber/core/messages.properties +++ b/io.sloeber.core/src/io/sloeber/core/messages.properties @@ -12,6 +12,7 @@ ArduinoSerial_reset_failed=reset using 1200bps touch failed ArduinoSerial_unable_to_open_serial_port=Unable to open Serial port "{PORT}" ArduinoSerial_Using_1200bps_touch=Forcing reset using 1200bps open/close on port : "{PORT}" ArduinoSerial_Using_comport=Using port "{PORT}" from now onwards +BoardDescription_0=failed to get platform paths Boards_Failed_to_read_boards=Failed to read arduino boards file "{FILE}"!! Boards_Get_menu_item_name_from_id_did_not_find=getMenuItemNameFromMenuItemID did not find menu item with ID "{MENUITEMID}" menu ID "{MENUID}" for board "{BOARDID}" Boards_menu_ID_not_found=menu ID "{ID}" not found diff --git a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java index 0d6782c7..8f39e997 100644 --- a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java +++ b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java @@ -52,7 +52,7 @@ import io.sloeber.core.api.CompileDescription.WarningLevels; import io.sloeber.core.api.ISloeberConfiguration; import io.sloeber.core.api.OtherDescription; -import io.sloeber.core.api.Preferences; +import io.sloeber.core.api.ConfigurationPreferences; import io.sloeber.core.api.SloeberProject; import io.sloeber.providers.Arduino; import io.sloeber.providers.ESP32; @@ -76,7 +76,7 @@ public class BuildTests { public static void beforeClass() throws Exception { Shared.waitForBoardsManager(); Shared.setDeleteProjects(false); - Preferences.setUseBonjour(false); + ConfigurationPreferences.setUseBonjour(false); installAdditionalBoards(); installAdditionalLibs(); } @@ -754,7 +754,7 @@ static Stream openAndClosePreservesSettingsValueCmd() throws Exceptio } public static Stream NightlyBoardPatronTestData() throws Exception { - Preferences.setUseArduinoToolSelection(true); + ConfigurationPreferences.setUseArduinoToolSelection(true); CompileDescription compileOptions = new CompileDescription(); MCUBoard zeroBoard = Arduino.zeroProgrammingPort(); diff --git a/io.sloeber.tests/src/io/sloeber/core/CompileAndUpload.java b/io.sloeber.tests/src/io/sloeber/core/CompileAndUpload.java index 584245af..e09de5db 100644 --- a/io.sloeber.tests/src/io/sloeber/core/CompileAndUpload.java +++ b/io.sloeber.tests/src/io/sloeber/core/CompileAndUpload.java @@ -39,7 +39,7 @@ import io.sloeber.core.api.CompileDescription; import io.sloeber.core.api.ConfigurationPreferences; import io.sloeber.core.api.ISloeberConfiguration; -import io.sloeber.core.api.Preferences; +import io.sloeber.core.api.ConfigurationPreferences; import io.sloeber.core.api.Sketch; import io.sloeber.core.api.SloeberProject; import io.sloeber.providers.Arduino; @@ -98,7 +98,7 @@ static Stream uploadBourds() throws Exception { public static void installAdditionalBoards() { - Preferences.setUseBonjour(false); + ConfigurationPreferences.setUseBonjour(false); String[] packageUrlsToAdd = { ESP32.packageURL, ESP8266.packageURL }; BoardsManager.addPackageURLs(new HashSet<>(Arrays.asList(packageUrlsToAdd))); diff --git a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileArduinoIDEExamplesOnAVRHardwareTest.java b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileArduinoIDEExamplesOnAVRHardwareTest.java index 748cc971..3f57db78 100644 --- a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileArduinoIDEExamplesOnAVRHardwareTest.java +++ b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileArduinoIDEExamplesOnAVRHardwareTest.java @@ -31,7 +31,7 @@ import io.sloeber.arduinoFramework.api.LibraryManager; import io.sloeber.core.api.CodeDescription; import io.sloeber.core.api.CompileDescription; -import io.sloeber.core.api.Preferences; +import io.sloeber.core.api.ConfigurationPreferences; import io.sloeber.providers.Arduino; import io.sloeber.providers.MCUBoard; @@ -46,7 +46,7 @@ public static void setup() throws Exception { Shared.waitForBoardsManager(); Shared.setUseParralBuildProjects(Boolean.TRUE); Shared.waitForAllJobsToFinish(); - Preferences.setUseBonjour(false); + ConfigurationPreferences.setUseBonjour(false); } diff --git a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileArduinoIDEExamplesOnTeensyTest.java b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileArduinoIDEExamplesOnTeensyTest.java index c38e0ec5..b6b49ba4 100644 --- a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileArduinoIDEExamplesOnTeensyTest.java +++ b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileArduinoIDEExamplesOnTeensyTest.java @@ -35,7 +35,7 @@ import io.sloeber.arduinoFramework.api.LibraryManager; import io.sloeber.core.api.CodeDescription; import io.sloeber.core.api.CompileDescription; -import io.sloeber.core.api.Preferences; +import io.sloeber.core.api.ConfigurationPreferences; import io.sloeber.providers.MCUBoard; import io.sloeber.providers.Teensy; @@ -55,7 +55,7 @@ public static void setup() throws Exception { //Shared.setDefaultBuilder(AutoBuildProject.MAKE_BUILDER_ID); Shared.waitForAllJobsToFinish(); - Preferences.setUseBonjour(false); + ConfigurationPreferences.setUseBonjour(false); } diff --git a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileArduinoIDEExamplesonJantjesBoardsTest.java b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileArduinoIDEExamplesonJantjesBoardsTest.java index cddf2047..4c86d1ea 100644 --- a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileArduinoIDEExamplesonJantjesBoardsTest.java +++ b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileArduinoIDEExamplesonJantjesBoardsTest.java @@ -23,7 +23,7 @@ import io.sloeber.arduinoFramework.api.IExample; import io.sloeber.arduinoFramework.api.LibraryManager; import io.sloeber.core.api.CodeDescription; -import io.sloeber.core.api.Preferences; +import io.sloeber.core.api.ConfigurationPreferences; import io.sloeber.providers.Jantje; import io.sloeber.providers.MCUBoard; @@ -35,7 +35,7 @@ public class CreateAndCompileArduinoIDEExamplesonJantjesBoardsTest { private static int mySkipAtStart = 0; @BeforeAll public static void setup() throws Exception { - Preferences.setUseBonjour(false); + ConfigurationPreferences.setUseBonjour(false); Shared.waitForAllJobsToFinish(); String[] packageUrlsToAdd = { Jantje.additionalJsonURL }; BoardsManager.addPackageURLs(new HashSet<>(Arrays.asList(packageUrlsToAdd))); diff --git a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java index 4a76d971..1a2f9657 100644 --- a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java +++ b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java @@ -24,7 +24,7 @@ import io.sloeber.arduinoFramework.api.BoardsManager; import io.sloeber.arduinoFramework.api.LibraryManager; import io.sloeber.core.api.CodeDescription; -import io.sloeber.core.api.Preferences; +import io.sloeber.core.api.ConfigurationPreferences; @SuppressWarnings({"nls","static-method"}) public class CreateAndCompileDefaultInoOnAllBoardsTest { @@ -47,8 +47,8 @@ static public void beforeAll() throws Exception { Shared.setUseParralBuildProjects(Boolean.TRUE); CCorePlugin.getIndexManager().setDefaultIndexerId( IPDOMManager.ID_NO_INDEXER ); // build the Arduino way - Preferences.setUseArduinoToolSelection(true); - Preferences.setUseBonjour(false); + ConfigurationPreferences.setUseArduinoToolSelection(true); + ConfigurationPreferences.setUseBonjour(false); installAdditionalBoards(); } diff --git a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileExamplesTest.java b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileExamplesTest.java index 98bfff82..c3e99af3 100644 --- a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileExamplesTest.java +++ b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileExamplesTest.java @@ -22,7 +22,7 @@ import io.sloeber.arduinoFramework.api.LibraryManager; import io.sloeber.core.api.CodeDescription; import io.sloeber.core.api.CompileDescription; -import io.sloeber.core.api.Preferences; +import io.sloeber.core.api.ConfigurationPreferences; import io.sloeber.providers.Adafruit; import io.sloeber.providers.Arduino; import io.sloeber.providers.ESP8266; @@ -39,7 +39,7 @@ public class CreateAndCompileExamplesTest { public static Stream examples() throws Exception { installAdditionalBoards(); Shared.waitForAllJobsToFinish(); - Preferences.setUseBonjour(false); + ConfigurationPreferences.setUseBonjour(false); MCUBoard myBoards[] = { Arduino.leonardo(), Arduino.uno(), diff --git a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileLibraryExamplesTest.java b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileLibraryExamplesTest.java index 1a40175f..8956e1d8 100644 --- a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileLibraryExamplesTest.java +++ b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileLibraryExamplesTest.java @@ -22,7 +22,7 @@ import io.sloeber.arduinoFramework.api.IExample; import io.sloeber.arduinoFramework.api.LibraryManager; import io.sloeber.core.api.CodeDescription; -import io.sloeber.core.api.Preferences; +import io.sloeber.core.api.ConfigurationPreferences; import io.sloeber.providers.Adafruit; import io.sloeber.providers.Arduino; import io.sloeber.providers.ESP32; @@ -39,8 +39,8 @@ public class CreateAndCompileLibraryExamplesTest { private int myTotalFails = 0; public static Stream CreateAndCompileLibraryExamplesTestData() throws Exception { - Preferences.setUseBonjour(false); - Preferences.setUseArduinoToolSelection(true); + ConfigurationPreferences.setUseBonjour(false); + ConfigurationPreferences.setUseArduinoToolSelection(true); Shared.waitForAllJobsToFinish(); installMyStuff(); diff --git a/io.sloeber.tests/src/io/sloeber/core/UpgradeTest.java b/io.sloeber.tests/src/io/sloeber/core/UpgradeTest.java index 56073151..276b7d0e 100644 --- a/io.sloeber.tests/src/io/sloeber/core/UpgradeTest.java +++ b/io.sloeber.tests/src/io/sloeber/core/UpgradeTest.java @@ -10,7 +10,7 @@ import static org.junit.jupiter.api.Assertions.*; -import io.sloeber.core.api.Preferences; +import io.sloeber.core.api.ConfigurationPreferences; import io.sloeber.core.api.SloeberProject; import io.sloeber.providers.Arduino; @@ -34,7 +34,7 @@ public class UpgradeTest { @BeforeAll public static void setup() { // stop bonjour as it clutters the console log - Preferences.setUseBonjour(false); + ConfigurationPreferences.setUseBonjour(false); Shared.waitForAllJobsToFinish(); // TOFIX: this will have to change into a specific version // or we will have to add the install based on stored data diff --git a/io.sloeber.tests/src/io/sloeber/core/releaseTesting_takes_very_long.java b/io.sloeber.tests/src/io/sloeber/core/releaseTesting_takes_very_long.java index 205a656b..25a6e30c 100644 --- a/io.sloeber.tests/src/io/sloeber/core/releaseTesting_takes_very_long.java +++ b/io.sloeber.tests/src/io/sloeber/core/releaseTesting_takes_very_long.java @@ -1,7 +1,6 @@ package io.sloeber.core; -import io.sloeber.core.api.Preferences; import org.junit.jupiter.api.BeforeAll; import org.junit.platform.suite.api.SelectClasses; @@ -9,6 +8,8 @@ import org.junit.platform.suite.api.Suite; import org.junit.platform.suite.api.SuiteDisplayName; +import io.sloeber.core.api.ConfigurationPreferences; + @SuiteDisplayName("Sloeber Nightly suite") @SelectClasses ({ @@ -26,7 +27,7 @@ public class releaseTesting_takes_very_long { @BeforeAll public static void setUp() { - Preferences.setUseBonjour(false); + ConfigurationPreferences.setUseBonjour(false); Shared.setDeleteProjects(true); } } diff --git a/io.sloeber.ui/src/io/sloeber/ui/Messages.java b/io.sloeber.ui/src/io/sloeber/ui/Messages.java index 21b58c23..6d0ea0ae 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/Messages.java +++ b/io.sloeber.ui/src/io/sloeber/ui/Messages.java @@ -66,12 +66,24 @@ public class Messages extends NLS { public static String packageTooltip; public static String PlatformSelectionPage_hide_third_party_url; - public static String PreferencePage_Internal_Behaviour_Group_Title; + public static String PreferencePage_centuries; + + public static String PreferencePage_days; + + public static String PreferencePage_Internal_Behaviour_Group_Title; + + public static String PreferencePage_months; public static String PreferencePage_Network_Group_Title; public static String PreferencePage_UI_Behaviour_Group_Title; + public static String PreferencePage_update_interval; + + public static String PreferencePage_weeks; + + public static String PreferencePage_years; + public static String platformSelectionTip; public static String pleaseWaitForInstallerJob; public static String plotterViewChannel; diff --git a/io.sloeber.ui/src/io/sloeber/ui/messages.properties b/io.sloeber.ui/src/io/sloeber/ui/messages.properties index 07ef3985..01ed2858 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/messages.properties +++ b/io.sloeber.ui/src/io/sloeber/ui/messages.properties @@ -5,9 +5,15 @@ BoardSelectionPage_platform_folder=Platform folder BoardSelectionPage_platform_you_want_to_use=The platform you want to use BoardSelectionPage_upload_protocol=Programmer protocol PlatformSelectionPage_hide_third_party_url=Hide 3th party json files +PreferencePage_centuries=centuries +PreferencePage_days=days PreferencePage_Internal_Behaviour_Group_Title=Internal behaviour +PreferencePage_months=months PreferencePage_Network_Group_Title=Stuff that needs a network PreferencePage_UI_Behaviour_Group_Title=UI behaviour +PreferencePage_update_interval=Update json files every +PreferencePage_weeks=weeks +PreferencePage_years=years always=Always arduino_upload_project_handler_build_failed=The build failed\! arduino_upload_project_handler_build_failed_so_no_upload=As the build failed the upload is not executed. diff --git a/io.sloeber.ui/src/io/sloeber/ui/preferences/PreferencePage.java b/io.sloeber.ui/src/io/sloeber/ui/preferences/PreferencePage.java index 45dc615e..6a33433e 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/preferences/PreferencePage.java +++ b/io.sloeber.ui/src/io/sloeber/ui/preferences/PreferencePage.java @@ -3,6 +3,7 @@ import static io.sloeber.ui.Activator.*; import java.io.File; +import java.time.Duration; import java.util.ArrayList; import org.eclipse.core.runtime.IPath; @@ -21,8 +22,12 @@ import org.eclipse.swt.graphics.Font; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Spinner; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPreferencePage; import org.eclipse.ui.PlatformUI; @@ -33,7 +38,7 @@ import io.sloeber.arduinoFramework.api.BoardsManager; import io.sloeber.arduinoFramework.api.LibraryManager; import io.sloeber.core.api.Defaults; -import io.sloeber.core.api.Preferences; +import io.sloeber.core.api.ConfigurationPreferences; import io.sloeber.ui.Messages; import io.sloeber.ui.helpers.MyPreferences; @@ -50,6 +55,8 @@ */ public class PreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { + + private static final String TRUE = "TRUE"; //$NON-NLS-1$ private static final String FALSE = "FALSE"; //$NON-NLS-1$ private static final String KEY_AUTO_IMPORT_LIBRARIES = "Gui entry for import libraries"; //$NON-NLS-1$ @@ -57,20 +64,25 @@ public class PreferencePage extends FieldEditorPreferencePage implements IWorkbe private static final String KEY_PRIVATE_HARDWARE_PATHS = "Gui entry for private hardware paths"; //$NON-NLS-1$ private static final String KEY_PRIVATE_LIBRARY_PATHS = "Gui entry for private library paths"; //$NON-NLS-1$ private static final String KEY_TOOLCHAIN_SELECTION = "Gui entry for toolchain selection"; //$NON-NLS-1$ - private static final String KEY_USE_BONJOUR = "Gui entry for usage of bonjour"; //$NON-NLS-1$ + //private static final String KEY_USE_BONJOUR = "Gui entry for usage of bonjour"; //$NON-NLS-1$ private PathEditor arduinoPrivateLibPathPathEditor; private PathEditor arduinoPrivateHardwarePathPathEditor; private ComboFieldEditor buildBeforeUploadOptionEditor; private BooleanFieldEditor openSerialMonitorOpensSerialsOptionEditor; private BooleanFieldEditor automaticallyImportLibrariesOptionEditor; - private BooleanFieldEditor automaticallyInstallLibrariesOptionEditor; + //private BooleanFieldEditor automaticallyInstallLibrariesOptionEditor; private BooleanFieldEditor useArduinoToolchainSelectionEditor; private BooleanFieldEditor pragmaOnceHeaderOptionEditor; private BooleanFieldEditor cleanSerialMonitorAfterUploadEditor; private BooleanFieldEditor switchToSerialMonitorAfterUploadEditor; private BooleanFieldEditor enableParallelBuildForNewProjects; - private BooleanFieldEditor enableBonjour; + //private BooleanFieldEditor enableBonjour; + private Button myBonjourCheckBox; + private Spinner myDelayNumberSpinner; + private Combo myDelayUnitCombo; + + private static int[] durationUnits = new int[] { 1, 7, 30, 365, 36500 }; public PreferencePage() { super(org.eclipse.jface.preference.FieldEditorPreferencePage.GRID); @@ -82,7 +94,7 @@ public PreferencePage() { MyPreferences.DEFAULT_OPEN_SERIAL_WITH_MONITOR); preferences.setDefault(KEY_AUTO_IMPORT_LIBRARIES, true); preferences.setDefault(KEY_PRAGMA_ONCE_HEADERS, true); - preferences.setDefault(KEY_USE_BONJOUR, Defaults.useBonjour); + preferences.setDefault(KEY_PRIVATE_HARDWARE_PATHS, Defaults.getPrivateHardwarePath()); preferences.setDefault(KEY_PRIVATE_LIBRARY_PATHS, Defaults.getPrivateLibraryPath()); preferences.setDefault(KEY_TOOLCHAIN_SELECTION, Defaults.useArduinoToolSelection); @@ -133,10 +145,12 @@ public boolean performOk() { libraryPaths=filteredList; log(new Status(IStatus.ERROR, PLUGIN_ID, Messages.Invalid_Private_Library_folder)); } - Preferences.setUseArduinoToolSelection(this.useArduinoToolchainSelectionEditor.getBooleanValue()); - Preferences.setAutoImportLibraries(this.automaticallyImportLibrariesOptionEditor.getBooleanValue()); - Preferences.setPragmaOnceHeaders(this.pragmaOnceHeaderOptionEditor.getBooleanValue()); - Preferences.setUseBonjour(enableBonjour.getBooleanValue()); + ConfigurationPreferences.setUseArduinoToolSelection(this.useArduinoToolchainSelectionEditor.getBooleanValue()); + ConfigurationPreferences.setAutoImportLibraries(this.automaticallyImportLibrariesOptionEditor.getBooleanValue()); + ConfigurationPreferences.setPragmaOnceHeaders(this.pragmaOnceHeaderOptionEditor.getBooleanValue()); + ConfigurationPreferences.setUseBonjour(myBonjourCheckBox.getSelection()); + //TOFIX line below + ConfigurationPreferences.setJsonUpdateDelay(Duration.ofDays(myDelayNumberSpinner.getSelection() * durationUnits[myDelayUnitCombo.getSelectionIndex()])); BoardsManager.setPrivateHardwarePaths(hardWarePaths); LibraryManager.setPrivateLibraryPaths(libraryPaths); return ret; @@ -171,10 +185,9 @@ private static String[] filterUnwantedPaths(String[] paths) { public void init(IWorkbench workbench) { String hardWarePaths = BoardsManager.getPrivateHardwarePathsString(); String libraryPaths = LibraryManager.getPrivateLibraryPathsString(); - boolean autoImport = Preferences.getAutoImportLibraries(); - boolean pragmaOnceHeaders = Preferences.getPragmaOnceHeaders(); - boolean useArduinoToolchainSelection =Preferences.getUseArduinoToolSelection(); - boolean useBonjour=Preferences.useBonjour(); + boolean autoImport = ConfigurationPreferences.getAutoImportLibraries(); + boolean pragmaOnceHeaders = ConfigurationPreferences.getPragmaOnceHeaders(); + boolean useArduinoToolchainSelection =ConfigurationPreferences.getUseArduinoToolSelection(); getPreferenceStore().setValue(KEY_AUTO_IMPORT_LIBRARIES, autoImport); @@ -182,7 +195,6 @@ public void init(IWorkbench workbench) { getPreferenceStore().setValue(KEY_PRIVATE_HARDWARE_PATHS, hardWarePaths); getPreferenceStore().setValue(KEY_PRIVATE_LIBRARY_PATHS, libraryPaths); getPreferenceStore().setValue(KEY_TOOLCHAIN_SELECTION, useArduinoToolchainSelection); - getPreferenceStore().setValue(KEY_USE_BONJOUR, useBonjour); } @@ -222,7 +234,6 @@ protected void createFieldEditors() { UIboxparent.setFont(headerFont); UIboxparent.setLayout(new GridLayout(2,true)); GridData gd11=new GridData(SWT.FILL,SWT.TOP,true,false); - //gd11.horizontalSpan=4; UIboxparent.setLayoutData(gd11); Composite UIbox = new Composite(UIboxparent, SWT.NONE); @@ -277,9 +288,6 @@ protected void createFieldEditors() { internalBehaviourbox.setLayout(new GridLayout(2, false)); Dialog.applyDialogFont(internalBehaviourbox); -// internalBehaviourGroup.setBackground(display.getSystemColor(SWT.COLOR_GREEN)); -// internalBehaviourbox.setBackground(display.getSystemColor(SWT.COLOR_RED)); - useArduinoToolchainSelectionEditor = new BooleanFieldEditor(KEY_TOOLCHAIN_SELECTION, Messages.ui_use_arduino_toolchain_selection, BooleanFieldEditor.DEFAULT, internalBehaviourbox); addField(useArduinoToolchainSelectionEditor); @@ -304,16 +312,38 @@ protected void createFieldEditors() { GridData gd15 = new GridData(SWT.FILL, SWT.TOP, true, false); gd15.horizontalSpan = 2; netWorkbox.setLayoutData(gd15); - netWorkbox.setLayout(new GridLayout(2, false)); + netWorkbox.setLayout(new GridLayout(4, false)); Dialog.applyDialogFont(netWorkbox); - automaticallyInstallLibrariesOptionEditor = new BooleanFieldEditor(MyPreferences.KEY_AUTO_INSTALL_LIBRARIES, - Messages.ui_auto_install_libraries, BooleanFieldEditor.DEFAULT, netWorkbox); - addField(automaticallyInstallLibrariesOptionEditor); - - enableBonjour = new BooleanFieldEditor(KEY_USE_BONJOUR, - Messages.ui_enable_bonjour, BooleanFieldEditor.DEFAULT, netWorkbox); - addField(enableBonjour); + //Currently sloeber does not autoinstall librtaries +// Button myInstallLibCheckBox = new Button(netWorkbox, SWT.CHECK | SWT.LEFT); +// Label inStallLabel = new Label(netWorkbox, SWT.BEGINNING); +// inStallLabel.setText(Messages.ui_auto_install_libraries); +// GridData gd16 = new GridData(SWT.BEGINNING, SWT.TOP, true, false); +// gd16.horizontalSpan=3; +// inStallLabel.setLayoutData(gd16); + + + myBonjourCheckBox = new Button(netWorkbox, SWT.CHECK | SWT.LEFT ); + Label bonjourLabel = new Label(netWorkbox, SWT.BEGINNING); + bonjourLabel.setText(Messages.ui_enable_bonjour); + GridData gd18 = new GridData(SWT.BEGINNING, SWT.TOP, true, false); + gd18.horizontalSpan=3; + bonjourLabel.setLayoutData(gd18); + + String[] jsonUpdateDelays2 = new String[] { Messages.PreferencePage_days, Messages.PreferencePage_weeks, Messages.PreferencePage_months, Messages.PreferencePage_years, Messages.PreferencePage_centuries }; + Label nuberLabel = new Label(netWorkbox, SWT.BEGINNING); + nuberLabel.setText(Messages.PreferencePage_update_interval); + myDelayNumberSpinner = new Spinner(netWorkbox, SWT.BEGINNING); + myDelayNumberSpinner.setDigits(0); + myDelayUnitCombo = new Combo(netWorkbox, SWT.BEGINNING); + myDelayUnitCombo.setItems(jsonUpdateDelays2); + GridData gd17 = new GridData(SWT.BEGINNING, SWT.TOP, false, false); + gd17.horizontalSpan=2; + nuberLabel.setLayoutData(gd17); + + myBonjourCheckBox.setSelection(ConfigurationPreferences.useBonjour()); + setJsonDurationComposites(ConfigurationPreferences.getJsonUpdateDelay()); } @@ -340,5 +370,24 @@ protected void performApply() { super.performApply(); } + @Override + protected void performDefaults() { + super.performDefaults(); + myBonjourCheckBox.setSelection( Defaults.useBonjour); + setJsonDurationComposites(Defaults.getJsonUpdateDuration()); + + } + + private void setJsonDurationComposites(Duration dur) { + long durationDays = dur.toDays(); + for (int durationUnit = durationUnits.length-1; durationUnit >= 0; durationUnit--) { + if ((durationDays % durationUnits[durationUnit]) == 0) { + myDelayNumberSpinner.setSelection((int) durationDays / durationUnits[durationUnit]); + myDelayUnitCombo.select(durationUnit); + break; + } + } + + } } diff --git a/io.sloeber.ui/src/io/sloeber/ui/preferences/TargetSerialDisconnectPage.java b/io.sloeber.ui/src/io/sloeber/ui/preferences/TargetSerialDisconnectPage.java index 1ea58517..f055af24 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/preferences/TargetSerialDisconnectPage.java +++ b/io.sloeber.ui/src/io/sloeber/ui/preferences/TargetSerialDisconnectPage.java @@ -12,7 +12,8 @@ import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPreferencePage; -import io.sloeber.core.api.Preferences; +import io.sloeber.core.api.ConfigurationPreferences; +import io.sloeber.core.api.Defaults; import io.sloeber.ui.Messages; public class TargetSerialDisconnectPage extends PreferencePage implements IWorkbenchPreferencePage { @@ -21,21 +22,21 @@ public class TargetSerialDisconnectPage extends PreferencePage implements IWorkb @Override public boolean performOk() { - Preferences.setDisconnectSerialTargets(targetsText.getText()); + ConfigurationPreferences.setDisconnectSerialTargets(targetsText.getText()); return true; } @Override protected void performDefaults() { super.performDefaults(); - this.targetsText.setText(Preferences.getDefaultDisconnectSerialTargets()); + this.targetsText.setText(Defaults.getDefaultDisconnectSerialTargets()); } @Override protected Control createContents(Composite parent) { Composite control = new Composite(parent, SWT.NONE); control.setLayout(new GridLayout()); - String selectedTargets = Preferences.getDisconnectSerialTargets(); + String selectedTargets = ConfigurationPreferences.getDisconnectSerialTargets(); // Composite control = new Composite(parent, SWT.NONE); Label title = new Label(control, SWT.UP); From 7065845773a605d31310e6d4b41d406bc9e4b8fe Mon Sep 17 00:00:00 2001 From: jan Date: Sat, 2 Nov 2024 03:59:58 +0100 Subject: [PATCH 045/115] Fix regression due to boardsmanager changes --- .../core/CreateAndCompileDefaultInoOnAllBoardsTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java index 1a2f9657..3747366f 100644 --- a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java +++ b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java @@ -356,7 +356,8 @@ public static void installAdditionalBoards() throws Exception { toAddList.removeAll(Arrays.asList(packageUrlsToIgnoreOnMac)); } BoardsManager.addPackageURLs(toAddList); - + BoardsManager.update(false); + if (!skipPlatformInstallation) { BoardsManager.installAllLatestPlatforms(); // PackageManager.installsubsetOfLatestPlatforms(0,5); From 4b7a26a9dc03657c0efa531941547e9290e5033c Mon Sep 17 00:00:00 2001 From: jan Date: Sat, 2 Nov 2024 04:01:50 +0100 Subject: [PATCH 046/115] Add button to update the json files --- .../arduinoFramework/api/BoardsManager.java | 6 ++- .../src/io/sloeber/core/Activator.java | 1 - .../core/api/ConfigurationPreferences.java | 1 + io.sloeber.ui/src/io/sloeber/ui/Messages.java | 4 ++ .../src/io/sloeber/ui/messages.properties | 2 + .../ui/preferences/PreferencePage.java | 38 +++++++++++++++++++ 6 files changed, 50 insertions(+), 2 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java index d22d4fb7..cc904a7e 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java @@ -16,6 +16,7 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; +import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -871,9 +872,12 @@ public static IArduinoPackage getPackageByProvider(String packager) { public static void update(boolean reloadFromInternet) { synchronized (packageIndices) { myIsUpdating =true; - if (myIsDirty) { + if (myIsDirty || reloadFromInternet) { downloadJsons(reloadFromInternet); readJsons(); + if(reloadFromInternet) { + ConfigurationPreferences.setLatestUpdateTime(Instant.now()); + } } diff --git a/io.sloeber.core/src/io/sloeber/core/Activator.java b/io.sloeber.core/src/io/sloeber/core/Activator.java index 9b785834..1fd8c78d 100644 --- a/io.sloeber.core/src/io/sloeber/core/Activator.java +++ b/io.sloeber.core/src/io/sloeber/core/Activator.java @@ -478,7 +478,6 @@ private static synchronized void startup_BoardsManager(IProgressMonitor monitor) // boolean needsUpdate = currentTime>(requestedDelay+latestUpdate); if(needsUpdate) { BoardsManager.update(true ); - ConfigurationPreferences.setLatestUpdateTime(currentTime); }else { BoardsManager.update(false ); } diff --git a/io.sloeber.core/src/io/sloeber/core/api/ConfigurationPreferences.java b/io.sloeber.core/src/io/sloeber/core/api/ConfigurationPreferences.java index 8e83fa97..272d2f3a 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/ConfigurationPreferences.java +++ b/io.sloeber.core/src/io/sloeber/core/api/ConfigurationPreferences.java @@ -129,6 +129,7 @@ private static Instant getInstant(String key, Instant defaultValue) { IEclipsePreferences myScope = ConfigurationScope.INSTANCE.getNode(NODE_ARDUINO); long ret = myScope.getLong(key, 0); if(ret==0) { + setInstant( key, defaultValue); return defaultValue; } return Instant.ofEpochSecond(ret); diff --git a/io.sloeber.ui/src/io/sloeber/ui/Messages.java b/io.sloeber.ui/src/io/sloeber/ui/Messages.java index 6d0ea0ae..6ae51322 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/Messages.java +++ b/io.sloeber.ui/src/io/sloeber/ui/Messages.java @@ -72,12 +72,16 @@ public class Messages extends NLS { public static String PreferencePage_Internal_Behaviour_Group_Title; + public static String PreferencePage_json_Download_date; + public static String PreferencePage_months; public static String PreferencePage_Network_Group_Title; public static String PreferencePage_UI_Behaviour_Group_Title; + public static String PreferencePage_Update_json_files_now; + public static String PreferencePage_update_interval; public static String PreferencePage_weeks; diff --git a/io.sloeber.ui/src/io/sloeber/ui/messages.properties b/io.sloeber.ui/src/io/sloeber/ui/messages.properties index 01ed2858..ab4c98c3 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/messages.properties +++ b/io.sloeber.ui/src/io/sloeber/ui/messages.properties @@ -8,9 +8,11 @@ PlatformSelectionPage_hide_third_party_url=Hide 3th party json files PreferencePage_centuries=centuries PreferencePage_days=days PreferencePage_Internal_Behaviour_Group_Title=Internal behaviour +PreferencePage_json_Download_date=json download date PreferencePage_months=months PreferencePage_Network_Group_Title=Stuff that needs a network PreferencePage_UI_Behaviour_Group_Title=UI behaviour +PreferencePage_Update_json_files_now=update json files now PreferencePage_update_interval=Update json files every PreferencePage_weeks=weeks PreferencePage_years=years diff --git a/io.sloeber.ui/src/io/sloeber/ui/preferences/PreferencePage.java b/io.sloeber.ui/src/io/sloeber/ui/preferences/PreferencePage.java index 6a33433e..2db1a428 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/preferences/PreferencePage.java +++ b/io.sloeber.ui/src/io/sloeber/ui/preferences/PreferencePage.java @@ -4,6 +4,9 @@ import java.io.File; import java.time.Duration; +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import org.eclipse.core.runtime.IPath; @@ -19,6 +22,8 @@ import org.eclipse.jface.resource.FontRegistry; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; @@ -342,11 +347,44 @@ protected void createFieldEditors() { gd17.horizontalSpan=2; nuberLabel.setLayoutData(gd17); + + Label lastJsonUpdateTimeLabel = new Label(netWorkbox, SWT.BEGINNING); + lastJsonUpdateTimeLabel.setText(Messages.PreferencePage_json_Download_date); + GridData gd19 = new GridData(SWT.BEGINNING, SWT.TOP, false, false); + gd19.horizontalSpan=2; + lastJsonUpdateTimeLabel.setLayoutData(gd19); + Label lastJsonUpdateTime = new Label(netWorkbox, SWT.BEGINNING); + lastJsonUpdateTime.setText(getLatestJsonUpdateTime()); + Button updateJsonFileNowButton =new Button(netWorkbox, SWT.PUSH | SWT.LEFT ); + updateJsonFileNowButton.setText(Messages.PreferencePage_Update_json_files_now); + updateJsonFileNowButton.addSelectionListener(new SelectionListener() { + + @Override + public void widgetSelected(SelectionEvent e) { + BoardsManager.update(true); + lastJsonUpdateTime.setText(getLatestJsonUpdateTime()); + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + // TODO Auto-generated method stub + + } + }); + + myBonjourCheckBox.setSelection(ConfigurationPreferences.useBonjour()); setJsonDurationComposites(ConfigurationPreferences.getJsonUpdateDelay()); } + private static String getLatestJsonUpdateTime() { + DateTimeFormatter formatter = + DateTimeFormatter.ofPattern( "uuuu-MM-dd HH-mm" ) //$NON-NLS-1$ + .withZone(ZoneId.systemDefault()); + Instant instant =ConfigurationPreferences.getLatestJsonUpdateTime(); + return formatter.format(instant); + } /** * testStatus test whether the provided information is OK. Here the code * checks whether there is a hardware\arduino\board.txt file under the From a2843522748904c1bae3e2da384396d9ff7dcc89 Mon Sep 17 00:00:00 2001 From: jan Date: Mon, 4 Nov 2024 18:09:51 +0100 Subject: [PATCH 047/115] First sloeber run does not start with the sloeber perspective --- io.sloeber.product/sloeber.product | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/io.sloeber.product/sloeber.product b/io.sloeber.product/sloeber.product index a9fd3c9d..0f46a223 100644 --- a/io.sloeber.product/sloeber.product +++ b/io.sloeber.product/sloeber.product @@ -22,14 +22,14 @@ https://github.com/sloeber/arduino-eclipse-plugin/graphs/contributors - --launcher.defaultAction openFile + -perspective (io.sloeber.product.perspective) +--launcher.defaultAction openFile --launcher.appendVmargs ---perspective io.sloeber.product.perspective - -Dorg.eclipse.update.reconcile=false --Dlog4j.configuration=log4j/log4j.xml --Xms256m --Xmx4g + -Dorg.eclipse.update.reconcile=false +-Dlog4j.configuration=log4j/log4j.xml +-Xms256m +-Xmx4g --add-modules=ALL-SYSTEM -XstartOnFirstThread -Dorg.eclipse.swt.internal.carbon.smallFonts From e7ac01d1db63611c5942cd4e52759adfebbfd937 Mon Sep 17 00:00:00 2001 From: jan Date: Mon, 4 Nov 2024 21:42:04 +0100 Subject: [PATCH 048/115] Make jsunURL available for read --- .../src/io/sloeber/arduinoFramework/api/BoardDescription.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java index 4150fca6..8bf1796a 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java @@ -71,6 +71,10 @@ public class BoardDescription { private boolean myIsDirty = true; + public String jsonURL() { + return myJsonURL; + } + @Override public String toString() { return getReferencingBoardsFile() + " \"" + getBoardName() + "\" " + getUploadPort(); //$NON-NLS-1$//$NON-NLS-2$ From ec95b71b5aab9386f16bd7bfd37d43ab3cd11a8c Mon Sep 17 00:00:00 2001 From: jan Date: Sat, 16 Nov 2024 16:14:56 +0100 Subject: [PATCH 049/115] Trying to fix inconsistent uploadPattern fail 1 Seems Sloeber takes the arduino provided esp board when the test fails. As the ESP32 is loaded at the beginning of the test run I realy do not understand why. --- .../src/io/sloeber/arduinoFramework/api/BoardsManager.java | 2 +- io.sloeber.tests/src/io/sloeber/core/BuildTests.java | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java index cc904a7e..67cbd308 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java @@ -77,7 +77,7 @@ public class BoardsManager { } public static boolean isReady() { - return !(myIsDirty || myIsUpdating); + return !(myIsDirty || myIsUpdating || envVarsNeedUpdating); } /** diff --git a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java index 8f39e997..c6ba4bf2 100644 --- a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java +++ b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java @@ -109,6 +109,8 @@ public static void installAdditionalBoards() throws Exception { Teensy.installLatest(); Arduino.installLatestSamDBoards(); LibraryManager.installLibrary("RTCZero"); + BoardsManager.update(false); + Shared.waitForBoardsManager(); } @@ -605,7 +607,7 @@ public void uploadPattern() throws Exception { boardDescriptor = ESP32.esp32().getBoardDescriptor(); boardDescriptor.setUploadPort("host 10.10.10.10"); recipeKey = boardDescriptor.getUploadPatternKey(); - assertEquals( "tools.esptool_py.upload.network_pattern", recipeKey,"ESP OTA upload recipe key is wrong"); + assertEquals( "tools.esptool_py.upload.network_pattern", recipeKey,"ESP OTA upload recipe key is wrong "+boardDescriptor.jsonURL()); } From ea212140661bb25e9e850d07353310bac4f708a5 Mon Sep 17 00:00:00 2001 From: jan Date: Sat, 16 Nov 2024 17:21:56 +0100 Subject: [PATCH 050/115] disable the esp upload command test --- io.sloeber.tests/src/io/sloeber/core/BuildTests.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java index c6ba4bf2..6e284fb7 100644 --- a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java +++ b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java @@ -604,10 +604,11 @@ public void uploadPattern() throws Exception { BoardDescription boardDescriptor = Arduino.uno().getBoardDescriptor(); String recipeKey = boardDescriptor.getUploadPatternKey(); assertEquals( "tools.avrdude.upload.pattern", recipeKey,"uno upload recipe key is wrong"); - boardDescriptor = ESP32.esp32().getBoardDescriptor(); - boardDescriptor.setUploadPort("host 10.10.10.10"); - recipeKey = boardDescriptor.getUploadPatternKey(); - assertEquals( "tools.esptool_py.upload.network_pattern", recipeKey,"ESP OTA upload recipe key is wrong "+boardDescriptor.jsonURL()); + //TOFIX find out why the somethimes this returns a Arduino esp board +// boardDescriptor = ESP32.esp32().getBoardDescriptor(); +// boardDescriptor.setUploadPort("host 10.10.10.10"); +// recipeKey = boardDescriptor.getUploadPatternKey(); +// assertEquals( "tools.esptool_py.upload.network_pattern", recipeKey,"ESP OTA upload recipe key is wrong "+boardDescriptor.jsonURL()); } From f4c3a8606610c6f2d3957151c6bd7a0d9a3ee720 Mon Sep 17 00:00:00 2001 From: jan Date: Sat, 16 Nov 2024 18:08:36 +0100 Subject: [PATCH 051/115] Fix warning (externalize string + remove unused export) --- io.sloeber.core/src/io/sloeber/core/Messages.java | 1 + io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java | 4 ++-- io.sloeber.core/src/io/sloeber/core/api/messages.properties | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 io.sloeber.core/src/io/sloeber/core/api/messages.properties diff --git a/io.sloeber.core/src/io/sloeber/core/Messages.java b/io.sloeber.core/src/io/sloeber/core/Messages.java index f9641cd9..910674fe 100644 --- a/io.sloeber.core/src/io/sloeber/core/Messages.java +++ b/io.sloeber.core/src/io/sloeber/core/Messages.java @@ -118,6 +118,7 @@ public class Messages extends NLS { public static String CompileDescription_WarningsDefault; public static String CompileDescription_WarningsMore; public static String CompileDescription_WarningsNone; + public static String SloeberProject_Project_is_null; static { // initialize resource bundle diff --git a/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java b/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java index 72a79c43..dedbedf8 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java +++ b/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java @@ -16,7 +16,6 @@ import org.eclipse.cdt.core.settings.model.ICProjectDescription; import org.eclipse.cdt.core.settings.model.ICSettingEntry; import org.eclipse.cdt.core.settings.model.ICSourceEntry; -import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProjectDescription; @@ -45,6 +44,7 @@ import io.sloeber.autoBuild.schema.api.IConfiguration; import io.sloeber.autoBuild.schema.api.IProjectType; import io.sloeber.core.Activator; +import io.sloeber.core.Messages; import io.sloeber.core.internal.SloeberConfiguration; import io.sloeber.core.listeners.IndexerController; import io.sloeber.core.natures.SloeberNature; @@ -60,7 +60,7 @@ public class SloeberProject extends Common { public static void convertToArduinoProject(IProject project, IProgressMonitor monitor) { if (project == null) { Activator.log(new Status(IStatus.ERROR, Activator.getId(), - "The provided project is null. Sloeber can not upgrade.")); + Messages.SloeberProject_Project_is_null)); return; } final IWorkspace workspace = ResourcesPlugin.getWorkspace(); diff --git a/io.sloeber.core/src/io/sloeber/core/api/messages.properties b/io.sloeber.core/src/io/sloeber/core/api/messages.properties new file mode 100644 index 00000000..c140323a --- /dev/null +++ b/io.sloeber.core/src/io/sloeber/core/api/messages.properties @@ -0,0 +1 @@ +SloeberProject_Project_is_null=The provided project is null. Sloeber can not upgrade. From 485b5fb37afa514da6cdc758283c8d61341c57d8 Mon Sep 17 00:00:00 2001 From: jan Date: Wed, 20 Nov 2024 00:57:17 +0100 Subject: [PATCH 052/115] fix missing SloeberProject_Project_is_null message --- io.sloeber.core/src/io/sloeber/core/api/messages.properties | 1 - io.sloeber.core/src/io/sloeber/core/messages.properties | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 io.sloeber.core/src/io/sloeber/core/api/messages.properties diff --git a/io.sloeber.core/src/io/sloeber/core/api/messages.properties b/io.sloeber.core/src/io/sloeber/core/api/messages.properties deleted file mode 100644 index c140323a..00000000 --- a/io.sloeber.core/src/io/sloeber/core/api/messages.properties +++ /dev/null @@ -1 +0,0 @@ -SloeberProject_Project_is_null=The provided project is null. Sloeber can not upgrade. diff --git a/io.sloeber.core/src/io/sloeber/core/messages.properties b/io.sloeber.core/src/io/sloeber/core/messages.properties index 9ea71273..f51fe28c 100644 --- a/io.sloeber.core/src/io/sloeber/core/messages.properties +++ b/io.sloeber.core/src/io/sloeber/core/messages.properties @@ -91,4 +91,4 @@ CompileDescription_WarningsCustom=Custom CompileDescription_WarningsDefault=Default CompileDescription_WarningsMore=More CompileDescription_WarningsNone=None - +SloeberProject_Project_is_null=The provided project is null. Sloeber can not upgrade. From a47544f44bb83ba8f64a29011d869a3e955bdf47 Mon Sep 17 00:00:00 2001 From: jan Date: Wed, 20 Nov 2024 00:58:45 +0100 Subject: [PATCH 053/115] Update happened to early making the test fail --- .../core/CreateAndCompileDefaultInoOnAllBoardsTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java index 3747366f..543b9536 100644 --- a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java +++ b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java @@ -356,7 +356,7 @@ public static void installAdditionalBoards() throws Exception { toAddList.removeAll(Arrays.asList(packageUrlsToIgnoreOnMac)); } BoardsManager.addPackageURLs(toAddList); - BoardsManager.update(false); + if (!skipPlatformInstallation) { BoardsManager.installAllLatestPlatforms(); @@ -367,6 +367,7 @@ public static void installAdditionalBoards() throws Exception { if (apply_known_work_Arounds) { Shared.applyKnownWorkArounds(); } + BoardsManager.update(false); Shared.waitForAllJobsToFinish(); } From f9169f48da8d7201f3ac3f1b70105fc90cbe2b86 Mon Sep 17 00:00:00 2001 From: jan Date: Wed, 20 Nov 2024 00:59:39 +0100 Subject: [PATCH 054/115] reenable esp upload pattern test --- io.sloeber.tests/src/io/sloeber/core/BuildTests.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java index 6e284fb7..c6ba4bf2 100644 --- a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java +++ b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java @@ -604,11 +604,10 @@ public void uploadPattern() throws Exception { BoardDescription boardDescriptor = Arduino.uno().getBoardDescriptor(); String recipeKey = boardDescriptor.getUploadPatternKey(); assertEquals( "tools.avrdude.upload.pattern", recipeKey,"uno upload recipe key is wrong"); - //TOFIX find out why the somethimes this returns a Arduino esp board -// boardDescriptor = ESP32.esp32().getBoardDescriptor(); -// boardDescriptor.setUploadPort("host 10.10.10.10"); -// recipeKey = boardDescriptor.getUploadPatternKey(); -// assertEquals( "tools.esptool_py.upload.network_pattern", recipeKey,"ESP OTA upload recipe key is wrong "+boardDescriptor.jsonURL()); + boardDescriptor = ESP32.esp32().getBoardDescriptor(); + boardDescriptor.setUploadPort("host 10.10.10.10"); + recipeKey = boardDescriptor.getUploadPatternKey(); + assertEquals( "tools.esptool_py.upload.network_pattern", recipeKey,"ESP OTA upload recipe key is wrong "+boardDescriptor.jsonURL()); } From 59ee10fc58d74e94c6ad7fb963bca5db9763e143 Mon Sep 17 00:00:00 2001 From: jan Date: Fri, 22 Nov 2024 02:25:28 +0100 Subject: [PATCH 055/115] Fiddle with when to use jsonurl and when not Also includes some library code during project migration This may break "private hardware" based projects. Now teensy is no longer a private hardware I need a replacement for it --- .../api/BoardDescription.java | 68 +++---------------- .../arduinoFramework/api/BoardsManager.java | 25 +++++-- .../io/sloeber/core/api/SloeberProject.java | 28 +++++++- 3 files changed, 59 insertions(+), 62 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java index 8bf1796a..03da7d95 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java @@ -37,7 +37,6 @@ import io.sloeber.core.Activator; import io.sloeber.core.Messages; import io.sloeber.core.api.ConfigurationPreferences; -import io.sloeber.core.api.Const; import io.sloeber.core.api.VersionNumber; import io.sloeber.core.tools.KeyValue; import io.sloeber.core.txt.BoardTxtFile; @@ -66,14 +65,10 @@ public class BoardDescription { private IArduinoPlatformVersion myReferencedPlatformVariant = null; private IArduinoPlatformVersion myReferencedPlatformCore = null; private IArduinoPlatformVersion myReferencedPlatformUpload = null; - private String myJsonFileName = null; private String myJsonURL = null; private boolean myIsDirty = true; - public String jsonURL() { - return myJsonURL; - } @Override public String toString() { @@ -281,7 +276,7 @@ public static List makeBoardDescriptors(File boardFile) { Map boardSection = txtFile.getSection(curboardID); if (boardSection != null) { if (!"true".equalsIgnoreCase(boardSection.get("hide"))) { //$NON-NLS-1$ //$NON-NLS-2$ - boards.add(new BoardDescription(boardFile, curboardID, null)); + boards.add(new BoardDescription(null,boardFile, curboardID, null)); } } } @@ -296,16 +291,21 @@ public static List makeBoardDescriptors(File boardFile) { * @param options * if null default options are taken */ - public BoardDescription(File boardsFile, String boardID, Map options) { + public BoardDescription(String jsonURL,File boardsFile, String boardID, Map options) { + File expandedBoardsFile = resolvePathEnvironmentString(boardsFile); if (!expandedBoardsFile.exists()) { Activator.log(new Status(IStatus.ERROR, Activator.getId(), "BoardsFile " + boardsFile + " does not exist")); //$NON-NLS-1$//$NON-NLS-2$ return; } + if (jsonURL != null) { + myJsonURL = jsonURL; + } else { + myJsonURL = BoardsManager.getjsonURLFormBoardsFile(expandedBoardsFile); + } myBoardID = boardID; myUserSelectedBoardsTxtFile = boardsFile; mySloeberBoardTxtFile = new BoardTxtFile(expandedBoardsFile); - getJSonInfo(); setDefaultOptions(); if (options != null) { myOptions.putAll(options); @@ -316,13 +316,13 @@ public BoardDescription() { myUserSelectedBoardsTxtFile = new File(myStorageNode.get(KEY_LAST_USED_BOARDS_FILE, EMPTY)); if (!myUserSelectedBoardsTxtFile.exists()) { - IArduinoPlatformVersion platform = BoardsManager.getNewestInstalledPlatform(VENDOR_ARDUINO, AVR); + IArduinoPlatformVersion platform = BoardsManager.getNewestInstalledPlatform( VENDOR_ARDUINO, AVR); if (platform == null) { platform = BoardsManager.getAnyInstalledPlatform(); } myUserSelectedBoardsTxtFile = platform.getBoardsFile(); mySloeberBoardTxtFile = new BoardTxtFile(myUserSelectedBoardsTxtFile); - getJSonInfo(platform); + myJsonURL= BoardsManager.getjsonURLFormBoardsFile(myUserSelectedBoardsTxtFile); if (mySloeberBoardTxtFile.getAllBoardIDs().contains(UNO)) { myBoardID = UNO; @@ -335,38 +335,16 @@ public BoardDescription() { myBoardID = myStorageNode.get(KEY_LAST_USED_BOARD, EMPTY); myUploadPort = myStorageNode.get(KEY_LAST_USED_UPLOAD_PORT, EMPTY); myProgrammer = myStorageNode.get(KEY_LAST_USED_UPLOAD_PROTOCOL, EMPTY); - myJsonFileName = myStorageNode.get(KEY_LAST_USED_JSON_FILENAME, EMPTY); myJsonURL = myStorageNode.get(KEY_LAST_USED_JSON_URL, EMPTY); myOptions = KeyValue.makeMap(myStorageNode.get(KEY_LAST_USED_BOARD_MENU_OPTIONS, EMPTY)); } } - private void getJSonInfo(IArduinoPlatformVersion platform) { - IArduinoPlatformPackageIndex packageIndex = platform.getParent().getParent().getPackageIndex(); - myJsonFileName = packageIndex.getJsonFile().getName(); - myJsonURL = packageIndex.getJsonURL(); - } - - private void getJSonInfo() { - IArduinoPlatformVersion platform = BoardsManager.getNewestInstalledPlatform(getVendor(), getArchitecture()); - if (platform == null) { - platform = BoardsManager.getNewestInstalledPlatform(VENDOR_ARDUINO, AVR); - } - if (platform == null) { - platform = BoardsManager.getAnyInstalledPlatform(); - } - if (platform != null) { - myUserSelectedBoardsTxtFile = platform.getBoardsFile(); - mySloeberBoardTxtFile = new BoardTxtFile(myUserSelectedBoardsTxtFile); - getJSonInfo(platform); - } - } public BoardDescription(BoardDescription srcObject) { myUserSelectedBoardsTxtFile = srcObject.myUserSelectedBoardsTxtFile; mySloeberBoardTxtFile = srcObject.mySloeberBoardTxtFile; - myJsonFileName = srcObject.myJsonFileName; myJsonURL = srcObject.myJsonURL; myBoardID = srcObject.myBoardID; myUploadPort = srcObject.myUploadPort; @@ -424,7 +402,6 @@ public void saveUserSelection() { myStorageNode.put(KEY_LAST_USED_BOARD, myBoardID); myStorageNode.put(KEY_LAST_USED_UPLOAD_PORT, myUploadPort); myStorageNode.put(KEY_LAST_USED_UPLOAD_PROTOCOL, myProgrammer); - myStorageNode.put(KEY_LAST_USED_JSON_FILENAME, myJsonFileName); myStorageNode.put(KEY_LAST_USED_JSON_URL, myJsonURL); myStorageNode.put(KEY_LAST_USED_BOARD_MENU_OPTIONS, KeyValue.makeString(myOptions)); } @@ -768,13 +745,6 @@ public boolean isNetworkUpload() { return getHost() != null; } - protected BoardDescription(File boardsFile, String boardID) { - myBoardID = boardID; - myUserSelectedBoardsTxtFile = boardsFile; - mySloeberBoardTxtFile = new BoardTxtFile(myUserSelectedBoardsTxtFile); - calculateDerivedFields(); - getJSonInfo(); - } BoardDescription(TxtFile configFile, String prefix) { @@ -788,7 +758,6 @@ protected BoardDescription(File boardsFile, String boardID) { Map options = optionsTree.toKeyValues(EMPTY); KeyValueTree boardSection = tree.getChild(BOARD); - myJsonFileName = boardSection.getValue(JSON_NAME); myJsonURL = boardSection.getValue(JSON_URL); myUserSelectedBoardsTxtFile = resolvePathEnvironmentString(new File(board_txt)); @@ -814,7 +783,6 @@ public BoardDescription(KeyValueTree keyValues) { String txtFile = boardvalueTree.getValue(KEY_SLOEBER_BOARD_TXT); KeyValueTree jSonvalueTree = keyValues.getChild(KEY_JSON); - myJsonFileName = jSonvalueTree.getValue(KEY_JSON_FILENAME); myJsonURL = jSonvalueTree.getValue(KEY_JSON_URL); myUserSelectedBoardsTxtFile = resolvePathEnvironmentString(new File(txtFile)); @@ -869,26 +837,12 @@ public void serialize(KeyValueTree keyValueTree) { boardvalueTree.addChild(KEY_SLOEBER_BOARD_TXT, board_txt); KeyValueTree jSonvalueTree = keyValueTree.addChild(KEY_JSON); - jSonvalueTree.addChild(KEY_JSON_FILENAME, myJsonFileName); jSonvalueTree.addChild(KEY_JSON_URL, myJsonURL); KeyValueTree menuvalueTree = keyValueTree.addChild(KEY_SLOEBER_MENU_SELECTION); for (Entry curOption : myOptions.entrySet()) { menuvalueTree.addValue(curOption.getKey(), curOption.getValue()); } - -// allVars.put(KEY_SLOEBER_PROGRAMMER, myProgrammer); -// allVars.put(KEY_SLOEBER_BOARD_ID, myBoardID); -// allVars.put(KEY_SLOEBER_BOARD_TXT, board_txt); -// allVars.put(KEY_SLOEBER_UPLOAD_PORT, myUploadPort); -// -// allVars.put(KEY_JSON_FILENAME, myJsonFileName); -// allVars.put(KEY_JSON_URL, myJsonURL); -// -// for (Entry curOption : myOptions.entrySet()) { -// allVars.put(KEY_SLOEBER_MENU_SELECTION + DOT + curOption.getKey(), curOption.getValue()); -// } -// return allVars; } private Map onlyKeepValidOptions(Map options) { @@ -1065,7 +1019,7 @@ private Map getEnVarPlatformInfo() throws IOException { ret.putAll(getEnvVarPlatformFileTools(myReferencedPlatformCore)); BoardsManager.update(false);// This way we know the boardsmanager is started or we wait for the lock - IArduinoPlatformVersion latestArduinoPlatform = BoardsManager.getNewestInstalledPlatform(Const.VENDOR_ARDUINO, + IArduinoPlatformVersion latestArduinoPlatform = BoardsManager.getNewestInstalledPlatform(VENDOR_ARDUINO, getArchitecture()); ret.putAll(getEnvVarPlatformFileTools(latestArduinoPlatform)); diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java index 67cbd308..6e1cc4ff 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java @@ -103,7 +103,7 @@ public static boolean isReady() { static public BoardDescription getBoardDescription(String jsonURL, String packageName, String architectureID, String boardID, Map options) { if (LOCAL.equals(jsonURL)) { - return new BoardDescription(new File(packageName), boardID, options); + return new BoardDescription(jsonURL,new File(packageName), boardID, options); } update(false); return getNewestBoardIDFromBoardsManager(jsonURL, packageName, architectureID, boardID, options); @@ -124,7 +124,7 @@ static private BoardDescription getNewestBoardIDFromBoardsManager(String jsonURL } IArduinoPlatformVersion platformVersion = platform.getNewestVersion(); java.io.File boardsFile = platformVersion.getBoardsFile(); - BoardDescription boardid = new BoardDescription(boardsFile, boardID, options); + BoardDescription boardid = new BoardDescription(jsonURL,boardsFile, boardID, options); return boardid; } @@ -680,7 +680,7 @@ public static List getPackageIndices() { // return platforms; // } - public static IArduinoPlatform getPlatform(String vendor, String architecture) { + public static IArduinoPlatform getPlatform( String vendor, String architecture) { if (myIsDirty) { Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, BoardsManagerIsBussy, new Exception())); return null; @@ -835,7 +835,7 @@ private static void onlyKeepLatestPlatforms(IArduinoPackage curPackage) { } } - public static IArduinoPlatformVersion getPlatform(String vendor, String architecture, VersionNumber refVersion) { + public static IArduinoPlatformVersion getPlatform( String vendor, String architecture, VersionNumber refVersion) { IArduinoPlatform platform = getPlatform(vendor, architecture); if (platform != null) { return platform.getVersion(refVersion); @@ -909,4 +909,21 @@ public static void update(boolean reloadFromInternet) { myIsUpdating=false; } + public static String getjsonURLFormBoardsFile(File boardsFile) { + File expandedBoardsFile = resolvePathEnvironmentString(boardsFile); + String ret=null; + for (IArduinoPlatformPackageIndex index : packageIndices) { + ret=index.getJsonURL(); + for (IArduinoPackage pkg : index.getPackages()) { + for (IArduinoPlatformVersion platformVersion : pkg.getInstalledPlatforms()) { + + if(platformVersion.getBoardsFile().equals(expandedBoardsFile)) { + return ret; + } + } + } + } + return ret; + } + } diff --git a/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java b/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java index dedbedf8..3b0dda98 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java +++ b/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java @@ -9,6 +9,8 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.TreeMap; + import org.eclipse.cdt.core.CCProjectNature; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.settings.model.CSourceEntry; @@ -34,6 +36,7 @@ import io.sloeber.arduinoFramework.api.BoardDescription; import io.sloeber.arduinoFramework.api.IArduinoLibraryVersion; +import io.sloeber.arduinoFramework.api.LibraryManager; import io.sloeber.autoBuild.api.AutoBuildProject; import io.sloeber.autoBuild.api.IAutoBuildConfigurationDescription; import io.sloeber.autoBuild.buildTools.api.IBuildTools; @@ -129,6 +132,18 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { SloeberNature.addNature(project, internalMonitor); ICProjectDescription prjCDesc = cCorePlugin.getProjectDescription(project, true); + // Get the libraries used + Set libNames = new HashSet<>(); + IFolder libFolder = OldCoreFolder.getFolder("libraries"); //$NON-NLS-1$ + if (libFolder.exists()) { + for (IResource curResource : libFolder.members()) { + if (curResource instanceof IFolder) { + IFolder curFolder = (IFolder) curResource; + libNames.add(curFolder.getName()); + } + } + } + IConfiguration defaultConfig = projectType.getConfigurations()[0]; for(String cfgName:cfgNames) { @@ -194,6 +209,17 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { autoConf.setIsParallelBuild(compileDescriptor.isParallelBuildEnabled()); SloeberConfiguration sloeberConfiguration = new SloeberConfiguration(boardDescriptor, otherDesc, compileDescriptor); + + // Add the libraries + TreeMap availableLibs = LibraryManager + .getLibrariesAll(boardDescriptor); + // find the libs we can add + Set toInstallLibs = new HashSet<>(); + for (String curLibName : libNames) { + toInstallLibs.add(availableLibs.get(curLibName)); + } + sloeberConfiguration.addLibraries(toInstallLibs); + //Save the sloeber configuration in the autoBuild configuration autoConf.setAutoBuildConfigurationExtensionDescription(sloeberConfiguration); } @@ -301,7 +327,7 @@ private static BoardDescription getBoardDescription(KeyValueTree oldConfig) { } File boardsFile=foundBoardsFilePath.toFile();// new File(boardsFileString); - BoardDescription ret= new BoardDescription( boardsFile, boardID, options); + BoardDescription ret= new BoardDescription( null,boardsFile, boardID, options); String uploadPort=oldConfig.getValue("board.UPLOAD.PORT"); //$NON-NLS-1$ ret.setUploadPort(uploadPort); From 31d6f46a7ceac0235176d09b59909914e8933a25 Mon Sep 17 00:00:00 2001 From: jan Date: Fri, 22 Nov 2024 02:25:54 +0100 Subject: [PATCH 056/115] Do not allow double qoutes in project names --- io.sloeber.core/src/io/sloeber/core/api/Common.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io.sloeber.core/src/io/sloeber/core/api/Common.java b/io.sloeber.core/src/io/sloeber/core/api/Common.java index 165b1e2e..ecde87d5 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Common.java +++ b/io.sloeber.core/src/io/sloeber/core/api/Common.java @@ -114,7 +114,7 @@ private static String getSloeberHome() { * @return a name safe to create files or folders */ public static String makeNameCompileSafe(String name) { - char[] badChars = { ' ', '/', '.', ':', '\\', '(', ')', '*', '?', '%', '|', '<', '>', ',', '-', '#' }; + char[] badChars = { ' ', '/', '.', ':', '\\', '(', ')', '*', '?', '%', '|', '<', '>', ',', '-', '#', '"' }; String ret = name.trim(); for (char curchar : badChars) { ret = ret.replace(curchar, '_'); From 4fcbb947ab6b8de12c4bf9d9cb3995c21bf500b4 Mon Sep 17 00:00:00 2001 From: jan Date: Fri, 22 Nov 2024 02:27:49 +0100 Subject: [PATCH 057/115] Test part of reconsidering jsonURL usage --- io.sloeber.tests/src/io/sloeber/core/BuildTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java index c6ba4bf2..962330d5 100644 --- a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java +++ b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java @@ -607,7 +607,7 @@ public void uploadPattern() throws Exception { boardDescriptor = ESP32.esp32().getBoardDescriptor(); boardDescriptor.setUploadPort("host 10.10.10.10"); recipeKey = boardDescriptor.getUploadPatternKey(); - assertEquals( "tools.esptool_py.upload.network_pattern", recipeKey,"ESP OTA upload recipe key is wrong "+boardDescriptor.jsonURL()); + assertEquals( "tools.esptool_py.upload.network_pattern", recipeKey,"ESP OTA upload recipe key is wrong "); } From 6c6b0aa9b1adfbdedda30243a1b14c3843c48d85 Mon Sep 17 00:00:00 2001 From: jan Date: Fri, 22 Nov 2024 02:33:38 +0100 Subject: [PATCH 058/115] Add getCounterName based on BoardDescription --- io.sloeber.tests/src/io/sloeber/core/Shared.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/io.sloeber.tests/src/io/sloeber/core/Shared.java b/io.sloeber.tests/src/io/sloeber/core/Shared.java index 0c91a9ec..f65a7d00 100644 --- a/io.sloeber.tests/src/io/sloeber/core/Shared.java +++ b/io.sloeber.tests/src/io/sloeber/core/Shared.java @@ -199,7 +199,7 @@ public static String buildAndVerify(BoardDescription boardDescriptor, CodeDescri public static String buildAndVerify(BoardDescription boardDescriptor, CodeDescription codeDescriptor, CompileDescription compileOptions) throws Exception { - String projectName = getCounterName(boardDescriptor.getBoardID()); + String projectName = getCounterName(boardDescriptor); IExample example = codeDescriptor.getLinkedExample(); if (example != null) { Collection lib = example.getArduinoLibraries(); @@ -336,6 +336,11 @@ public static String getCounterName(String name) { return getCounterName("%05d_%s", name); } + public static String getCounterName( BoardDescription boardDescriptor) { + return String.format("%05d_%s_%s", Integer.valueOf(myTestCounter++), boardDescriptor.getVendor(),boardDescriptor.getBoardName()); + } + + public static String getCounterName(String format, String name) { return String.format(format, Integer.valueOf(myTestCounter++), name); } From 1a370d7a973309afbf5da9c00982d3b1f886f011 Mon Sep 17 00:00:00 2001 From: jan Date: Fri, 22 Nov 2024 16:12:37 +0100 Subject: [PATCH 059/115] Do not allow & in project names for cmd ESP tests resulted in the following error cmd /c if exist "R:\junit-workspace\00306_esp32_WeMos_WiFi&Bluetooth_Battery\src\partitions.csv" COPY /y "R:\junit-workspace\00306_esp32_WeMos_WiFi&Bluetooth_Battery\src\partitions.csv" "R:\junit-workspace\00306_esp32_WeMos_WiFi&Bluetooth_Battery\bin\Release\partitions.csv" & was unexpected at this time. --- io.sloeber.core/src/io/sloeber/core/api/Common.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/io.sloeber.core/src/io/sloeber/core/api/Common.java b/io.sloeber.core/src/io/sloeber/core/api/Common.java index ecde87d5..22c4ba4a 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Common.java +++ b/io.sloeber.core/src/io/sloeber/core/api/Common.java @@ -109,12 +109,14 @@ private static String getSloeberHome() { * allowed in Unix filenames, see Note 1 > greater than used to redirect output, * allowed in Unix filenames, see Note 1 . period or dot * + * Though " and & are allowed they confuse cmd commands + * * @param name * the string that needs to be checked * @return a name safe to create files or folders */ public static String makeNameCompileSafe(String name) { - char[] badChars = { ' ', '/', '.', ':', '\\', '(', ')', '*', '?', '%', '|', '<', '>', ',', '-', '#', '"' }; + char[] badChars = { ' ', '/', '.', ':', '\\', '(', ')', '*', '?', '%', '|', '<', '>', ',', '-', '#', '"', '&' }; String ret = name.trim(); for (char curchar : badChars) { ret = ret.replace(curchar, '_'); From 8e991f379924928201c5febb23617c167a2f959f Mon Sep 17 00:00:00 2001 From: jan Date: Fri, 22 Nov 2024 16:17:22 +0100 Subject: [PATCH 060/115] Fix warning for possible memory leak. --- io.sloeber.core/src/io/sloeber/core/api/Common.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/io.sloeber.core/src/io/sloeber/core/api/Common.java b/io.sloeber.core/src/io/sloeber/core/api/Common.java index 22c4ba4a..f007b3fd 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Common.java +++ b/io.sloeber.core/src/io/sloeber/core/api/Common.java @@ -11,6 +11,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.Comparator; +import java.util.stream.Stream; import org.eclipse.cdt.core.model.CoreModel; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; @@ -250,7 +251,9 @@ public static void deleteDirectory(org.eclipse.core.runtime.IPath directory) thr } public static void deleteDirectory(Path directory) throws IOException { - Files.walk(directory).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete); + try( Stream stream = Files.walk(directory)){ + stream.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete); + } } /** From 39b0ec7d530232c3ad6893781538b954a47afcc6 Mon Sep 17 00:00:00 2001 From: jan Date: Sat, 23 Nov 2024 14:36:10 +0100 Subject: [PATCH 061/115] Remove 2 boards that also fail in arduino IDE --- .../core/CreateAndCompileDefaultInoOnAllBoardsTest.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java index 543b9536..facadf64 100644 --- a/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java +++ b/io.sloeber.tests/src/io/sloeber/core/CreateAndCompileDefaultInoOnAllBoardsTest.java @@ -159,6 +159,8 @@ static public void beforeAll() throws Exception { "Generic STM32F103Z series",// confirmed failing in arduino IDE 2020 05 30 "Arduino Nano ESP32",// requires recipe.hooks.core.prebuild and recipe.hooks.core.postbuild + "Snō",//fails in arduino ide 2024 11 22 + "OpenXLR8 - Snō",//fails in arduino ide 2024 11 22 }; private static final String[] boardsToIgnoreOnWindows = { @@ -307,7 +309,8 @@ public static Stream allBoards() throws Exception { List boards = new ArrayList<>(); for (File curBoardFile : BoardsManager.getAllBoardsFiles()) { System.out.println("Adding boards of " + curBoardFile.toString()); - boards.addAll(BoardDescription.makeBoardDescriptors(curBoardFile)); + boards.addAll(BoardDescription.makeBoardDescriptors(curBoardFile)); + } HashSet boardsToIgnoreList = new HashSet<>(Arrays.asList(boardsToIgnoreOnAllOses)); @@ -356,8 +359,8 @@ public static void installAdditionalBoards() throws Exception { toAddList.removeAll(Arrays.asList(packageUrlsToIgnoreOnMac)); } BoardsManager.addPackageURLs(toAddList); - - + + if (!skipPlatformInstallation) { BoardsManager.installAllLatestPlatforms(); // PackageManager.installsubsetOfLatestPlatforms(0,5); From 9559d8482bf1fd2e6b2dea3166a1306936290e03 Mon Sep 17 00:00:00 2001 From: jan Date: Sat, 23 Nov 2024 14:43:24 +0100 Subject: [PATCH 062/115] Use OS specific path delimiters (to make esp32 V3.0.7 work Use toOSString instead of toString Introduce the global variable PATH_SEPERATOR Use ${PathDelimiter} or PATH_SEPERATOR instead of / This also requires a new version string for the sloeber.txt file --- .../src/io/sloeber/autoBuild/api/AutoBuildCommon.java | 2 +- .../sloeber/autoBuild/buildTools/internal/CDTBuildTools.java | 2 +- .../autoBuild/buildTools/internal/MinGWBuildTools.java | 2 +- .../autoBuild/extensionPoint/providers/AutoBuildMakeRule.java | 4 ++-- .../io/sloeber/autoBuild/helpers/api/AutoBuildConstants.java | 1 + .../src/io/sloeber/autoBuild/schema/internal/Tool.java | 2 +- io.sloeber.core/config/pre_processing_platform_default.txt | 2 +- .../src/io/sloeber/arduinoFramework/api/BoardDescription.java | 4 ++-- .../src/io/sloeber/arduinoFramework/api/BoardsManager.java | 2 +- .../src/io/sloeber/arduinoFramework/api/LibraryManager.java | 2 +- io.sloeber.core/src/io/sloeber/core/api/Common.java | 4 ++-- .../src/io/sloeber/core/api/ConfigurationPreferences.java | 4 ++-- io.sloeber.core/src/io/sloeber/core/api/Defaults.java | 4 ++-- 13 files changed, 18 insertions(+), 17 deletions(-) diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/api/AutoBuildCommon.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/api/AutoBuildCommon.java index c6af150d..5980e8dc 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/api/AutoBuildCommon.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/api/AutoBuildCommon.java @@ -368,7 +368,7 @@ static public String GetNiceFileName(IFolder buildPath, IFile path) { static public String GetNiceFileName(IPath buildPath, IPath filePath) { String ret; if (buildPath.isPrefixOf(filePath) || buildPath.removeLastSegments(3).isPrefixOf(filePath)) { - ret = filePath.makeRelativeTo(buildPath).toString(); + ret = filePath.makeRelativeTo(buildPath).toOSString(); } else { ret = filePath.toString(); } diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/buildTools/internal/CDTBuildTools.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/buildTools/internal/CDTBuildTools.java index d0d001d6..ce5c929d 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/buildTools/internal/CDTBuildTools.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/buildTools/internal/CDTBuildTools.java @@ -96,7 +96,7 @@ public String getPathExtension() { @Override public String getDiscoveryCommand(ToolType toolType) { - return getToolLocation().append( getCommand(toolType)).toString() + DISCOVERY_PARAMETERS; + return getToolLocation().append( getCommand(toolType)).toOSString() + DISCOVERY_PARAMETERS; } @Override diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/buildTools/internal/MinGWBuildTools.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/buildTools/internal/MinGWBuildTools.java index a14f19b1..34e94c64 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/buildTools/internal/MinGWBuildTools.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/buildTools/internal/MinGWBuildTools.java @@ -92,7 +92,7 @@ public String getPathExtension() { @Override public String getDiscoveryCommand(ToolType toolType) { - return getToolLocation().append( getCommand(toolType)).toString() +DISCOVERY_PARAMETERS; + return getToolLocation().append( getCommand(toolType)).toOSString() +DISCOVERY_PARAMETERS; } @Override diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/AutoBuildMakeRule.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/AutoBuildMakeRule.java index f3f1ac5c..87a059b0 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/AutoBuildMakeRule.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/AutoBuildMakeRule.java @@ -288,7 +288,7 @@ public String[] getRecipes(IFolder buildFolder, AutoBuildConfigurationDescriptio IFile file = project.getWorkspace().getRoot() .getFile(IPath.forPosix(curEntry.getValue())); includeFiles = includeFiles + WHITESPACE + DOUBLE_QUOTE + CMD_LINE_INCLUDE_FILE - + file.getLocation().toString() + DOUBLE_QUOTE; + + file.getLocation().toOSString() + DOUBLE_QUOTE; break; } case ICSettingEntry.INCLUDE_PATH: { @@ -300,7 +300,7 @@ public String[] getRecipes(IFolder buildFolder, AutoBuildConfigurationDescriptio }else { includePath = includePath + WHITESPACE + DOUBLE_QUOTE + CMD_LINE_INCLUDE_FOLDER - + path.toString() + DOUBLE_QUOTE; + + path.toOSString() + DOUBLE_QUOTE; } break; } diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/helpers/api/AutoBuildConstants.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/helpers/api/AutoBuildConstants.java index 439cf8a9..45556d07 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/helpers/api/AutoBuildConstants.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/helpers/api/AutoBuildConstants.java @@ -43,6 +43,7 @@ public class AutoBuildConstants { public static final String PROCENT = "%"; public static final String SLACH = "/"; public static final String BACKSLACH = "\\"; + public static final String PATH_SEPERATOR=isWindows?BACKSLACH:SLACH; public static final String FALSE = "FALSE"; public static final String TRUE = "TRUE"; public static final String COLON = ":"; diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/schema/internal/Tool.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/schema/internal/Tool.java index 46fb4c75..61574cf9 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/schema/internal/Tool.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/schema/internal/Tool.java @@ -562,7 +562,7 @@ public String[] getRecipes(IAutoBuildConfigurationDescription autoBuildConfData, IPath toolPath = buildTools.getToolLocation(); if (toolPath != null && !toolPath.toString().isBlank()) { //store the path - toolCommandVars.put(CMD_LINE_TOOL_PATH, toolPath.toString().trim() + SLACH); + toolCommandVars.put(CMD_LINE_TOOL_PATH, toolPath.toOSString().trim() + PATH_SEPERATOR); } Map toolVariables = buildTools.getToolVariables(); if (toolVariables != null && toolVariables.size() > 0) { diff --git a/io.sloeber.core/config/pre_processing_platform_default.txt b/io.sloeber.core/config/pre_processing_platform_default.txt index 6b47ace9..b27b1580 100644 --- a/io.sloeber.core/config/pre_processing_platform_default.txt +++ b/io.sloeber.core/config/pre_processing_platform_default.txt @@ -1,7 +1,7 @@ #this file contains default/fallback/rescue values software=ARDUINO archive_file=arduino.ar -archive_file_path=${build.path}/${archive_file} +archive_file_path=${build.path}${PathDelimiter}${archive_file} ide_version=20302 serial.port=${com_port} diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java index 03da7d95..41eb6893 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java @@ -45,7 +45,7 @@ import io.sloeber.core.txt.TxtFile; public class BoardDescription { - private static final String FIRST_SLOEBER_LINE = "#Sloeber created file please do not modify V1.00.test 06 "; //$NON-NLS-1$ + private static final String FIRST_SLOEBER_LINE = "#Sloeber created file please do not modify V1.00.test 07 "; //$NON-NLS-1$ private static final IEclipsePreferences myStorageNode = InstanceScope.INSTANCE.getNode(NODE_ARDUINO); /* @@ -1072,7 +1072,7 @@ private Map getEnvVarPlatformFileTools(IArduinoPlatformVersion p for (ArduinoPlatformTooldDependency tool : platformVersion.getToolsDependencies()) { IPath installPath = tool.getInstallPath(); if (installPath.toFile().exists()) { - String value = installPath.toString(); + String value = installPath.toOSString(); String keyString = ENV_KEY_RUNTIME_TOOLS + tool.getName() + tool.getVersion() + DOT_PATH; vars = vars + NEWLINE + keyString + EQUAL + value; keyString = ENV_KEY_RUNTIME_TOOLS + tool.getName() + '-' + tool.getVersion() + DOT_PATH; diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java index 6e1cc4ff..bbf3e406 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardsManager.java @@ -416,7 +416,7 @@ private static IStatus uninstall(IArduinoPlatformVersion curPlatform) { deleteDirectory(installFolder); envVarsNeedUpdating = true; } catch (IOException e) { - return new Status(IStatus.ERROR, Activator.getId(), "Failed to remove folder" + installFolder.toString(), //$NON-NLS-1$ + return new Status(IStatus.ERROR, Activator.getId(), "Failed to remove folder" + installFolder.toOSString(), //$NON-NLS-1$ e); } diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java index ef797ca0..09476933 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java @@ -174,7 +174,7 @@ public static IStatus unInstall(IArduinoLibraryVersion lib, IProgressMonitor mon deleteDirectory(lib.getInstallPath().removeLastSegments(1)); } catch (IOException e) { return new Status(IStatus.ERROR, Activator.getId(), - "Failed to remove folder" + lib.getInstallPath().toString(), //$NON-NLS-1$ + "Failed to remove folder" + lib.getInstallPath().toOSString(), //$NON-NLS-1$ e); } diff --git a/io.sloeber.core/src/io/sloeber/core/api/Common.java b/io.sloeber.core/src/io/sloeber/core/api/Common.java index f007b3fd..b29e8131 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Common.java +++ b/io.sloeber.core/src/io/sloeber/core/api/Common.java @@ -31,7 +31,7 @@ public class Common { public final static String sloeberHome = getSloeberHome(); public final static IPath sloeberHomePath = new org.eclipse.core.runtime.Path(sloeberHome); - public final static String sloeberHomePathToString = sloeberHomePath.toString(); + public final static String sloeberHomePathToString = sloeberHomePath.toOSString(); private static String getSloeberHome() { @@ -213,7 +213,7 @@ public static IPath getWorkspaceRoot() { */ public static String makePathVersionString(File file) { if(sloeberHomePath.isPrefixOf(IPath.fromFile(file))) { - return SLOEBER_HOME_VAR+SLACH+IPath.fromFile(file) .makeRelativeTo(sloeberHomePath).toString(); + return SLOEBER_HOME_VAR+SLACH+IPath.fromFile(file).makeRelativeTo(sloeberHomePath).toString(); } return file.toString(); } diff --git a/io.sloeber.core/src/io/sloeber/core/api/ConfigurationPreferences.java b/io.sloeber.core/src/io/sloeber/core/api/ConfigurationPreferences.java index 272d2f3a..c81028ab 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/ConfigurationPreferences.java +++ b/io.sloeber.core/src/io/sloeber/core/api/ConfigurationPreferences.java @@ -206,12 +206,12 @@ public static File getPostProcessingBoardsFile() { } public static Path getMakePath() { - return new Path(getInstallationPath().append("tools/make").toString()); //$NON-NLS-1$ + return new Path(getInstallationPath().append("tools/make").toOSString()); //$NON-NLS-1$ } public static IPath getAwkPath() { - return new Path(getInstallationPath().append("tools/awk").toString()); //$NON-NLS-1$ + return new Path(getInstallationPath().append("tools/awk").toOSString()); //$NON-NLS-1$ } public static Instant getLatestJsonUpdateTime() { diff --git a/io.sloeber.core/src/io/sloeber/core/api/Defaults.java b/io.sloeber.core/src/io/sloeber/core/api/Defaults.java index 42c0cd32..db6c4249 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Defaults.java +++ b/io.sloeber.core/src/io/sloeber/core/api/Defaults.java @@ -49,7 +49,7 @@ public static String getPrivateLibraryPath() { if (isMac || isWindows) { homPath = homPath.append("Documents"); } - return homPath.append("Arduino").append(ARDUINO_LIBRARY_FOLDER_NAME).toString(); + return homPath.append("Arduino").append(ARDUINO_LIBRARY_FOLDER_NAME).toOSString(); } public static String getPrivateHardwarePath() { @@ -57,7 +57,7 @@ public static String getPrivateHardwarePath() { if (isMac || isWindows) { homPath = homPath.append("Documents"); } - return homPath.append("Arduino").append(ARDUINO_HARDWARE_FOLDER_NAME).toString(); + return homPath.append("Arduino").append(ARDUINO_HARDWARE_FOLDER_NAME).toOSString(); } public static String getDefaultDisconnectSerialTargets() { From 0891845f704b887b6a897f6cf7eb9b1ddd654735 Mon Sep 17 00:00:00 2001 From: jan Date: Sat, 23 Nov 2024 19:52:56 +0100 Subject: [PATCH 063/115] Add ; to the list of not supported chars for project names 1184 boards tested successfully with the default ino test :-) --- .../src/io/sloeber/core/api/Common.java | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/core/api/Common.java b/io.sloeber.core/src/io/sloeber/core/api/Common.java index b29e8131..c44fcb2d 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Common.java +++ b/io.sloeber.core/src/io/sloeber/core/api/Common.java @@ -110,18 +110,23 @@ private static String getSloeberHome() { * allowed in Unix filenames, see Note 1 > greater than used to redirect output, * allowed in Unix filenames, see Note 1 . period or dot * - * Though " and & are allowed they confuse cmd commands + * + * Though { " & + @ } ; are allowed according to the above info they do confuse cmd + * commands so I also remove them + * + * multiple underscores are also concatenated to 1 underscore * * @param name * the string that needs to be checked * @return a name safe to create files or folders */ public static String makeNameCompileSafe(String name) { - char[] badChars = { ' ', '/', '.', ':', '\\', '(', ')', '*', '?', '%', '|', '<', '>', ',', '-', '#', '"', '&' }; + char[] badChars = { ' ', '/', '.', ':', '\\', '(', ')', '*', '?', '%', '|', '<', '>', ',', '-', '#', '"', '&' ,'+' ,'@', ';' }; String ret = name.trim(); for (char curchar : badChars) { ret = ret.replace(curchar, '_'); } + ret = ret.replace("__", "_"); //$NON-NLS-1$ //$NON-NLS-2$ return ret; } @@ -176,8 +181,8 @@ static public String getBuildEnvironmentVariable(ISloeberConfiguration sloeberCo static public String getBuildEnvironmentVariable(ISloeberConfiguration sloeberConf, String envName, String defaultvalue, boolean expanded) { if (sloeberConf != null) { - IAutoBuildConfigurationDescription autoDesc= sloeberConf.getAutoBuildDesc(); - return AutoBuildCommon.getVariableValue(envName, defaultvalue, expanded, autoDesc); + IAutoBuildConfigurationDescription autoDesc= sloeberConf.getAutoBuildDesc(); + return AutoBuildCommon.getVariableValue(envName, defaultvalue, expanded, autoDesc); // IEnvironmentVariableManager envManager = CCorePlugin.getDefault().getBuildEnvironmentManager(); // try { // ICConfigurationDescription configurationDescription = sloeberConf.getAutoBuildDesc() @@ -212,9 +217,9 @@ public static IPath getWorkspaceRoot() { * @return modified string or the original */ public static String makePathVersionString(File file) { - if(sloeberHomePath.isPrefixOf(IPath.fromFile(file))) { - return SLOEBER_HOME_VAR+SLACH+IPath.fromFile(file).makeRelativeTo(sloeberHomePath).toString(); - } + if(sloeberHomePath.isPrefixOf(IPath.fromFile(file))) { + return SLOEBER_HOME_VAR+SLACH+IPath.fromFile(file).makeRelativeTo(sloeberHomePath).toString(); + } return file.toString(); } @@ -228,9 +233,9 @@ public static File resolvePathEnvironmentString(File file) { String retString = file.getPath(); if (retString.startsWith(SLOEBER_HOME_VAR)) { - retString=retString.replace(SLOEBER_HOME_VAR, EMPTY_STRING); - return sloeberHomePath.append(retString).toFile(); - //.replace(SLOEBER_HOME_VAR, sloeberHomePathToString); + retString=retString.replace(SLOEBER_HOME_VAR, EMPTY_STRING); + return sloeberHomePath.append(retString).toFile(); + //.replace(SLOEBER_HOME_VAR, sloeberHomePathToString); } return new File(retString); } @@ -252,7 +257,7 @@ public static void deleteDirectory(org.eclipse.core.runtime.IPath directory) thr public static void deleteDirectory(Path directory) throws IOException { try( Stream stream = Files.walk(directory)){ - stream.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete); + stream.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete); } } From 6b02f9d9b8e0db40afc55cc0e171210b4fb2481b Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 24 Nov 2024 22:20:58 +0100 Subject: [PATCH 064/115] Fix regression on download and install lib on usage. --- .../arduinoFramework/api/LibraryManager.java | 19 ++++++++++++++++ .../core/api/ConfigurationPreferences.java | 17 ++++++++++++++ .../src/io/sloeber/core/api/Defaults.java | 1 + .../core/common/InstancePreferences.java | 9 ++++++++ .../core/listeners/IndexerListener.java | 3 ++- .../ui/preferences/PreferencePage.java | 22 +++++++++++-------- 6 files changed, 61 insertions(+), 10 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java index 09476933..f5de8a28 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java @@ -142,6 +142,7 @@ public static void installLibrary(String libName) { } } + public static IStatus install(IArduinoLibraryVersion inLib, IProgressMonitor monitor) { if (!(inLib instanceof ArduinoLibraryVersion)) { return Status.error("Trying to install a library that is not a installable library" + inLib.getName()); //$NON-NLS-1$ @@ -470,4 +471,22 @@ public static IArduinoLibraryVersion getLibraryVersionFromLocation(IFolder libFo return getLibrariesPrivate().get(libFolder.getName()); } + /** + * Remove a lib based on the name of the lib + * + * @param libName the name of the lib + * @return true if the lib has been removed or was not found + * false if the lib was found and the removal failed. + */ + public static boolean uninstallLibrary(String libName) { + Map installedLibs=getLibrariesAll(null); + IPath libFQN=ArduinoLibraryVersion.calculateFQN(libName); + IArduinoLibraryVersion libVersion = installedLibs.get(libFQN.toString()); + if(libVersion==null) { + return true; + + } + return unInstall(libVersion, new NullProgressMonitor()).isOK(); + } + } \ No newline at end of file diff --git a/io.sloeber.core/src/io/sloeber/core/api/ConfigurationPreferences.java b/io.sloeber.core/src/io/sloeber/core/api/ConfigurationPreferences.java index c81028ab..7e399de6 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/ConfigurationPreferences.java +++ b/io.sloeber.core/src/io/sloeber/core/api/ConfigurationPreferences.java @@ -250,4 +250,21 @@ private static Duration getDuration(String key, Duration defaultValue) { return Duration.ofDays(ret); } + /** + * Allow the library manager to download and install libraries based on include directive in the source code. + * + * @param selection true if you want to install libraries based on their usage else false + */ + public static void setInstallLibraries(boolean selection) { + InstancePreferences.setInstallLibraries(selection); + } + + /** + * + * @return true when libraries can be downloaded and installed when referenced in the code. + */ + public static boolean getInstallLibraries() { + return InstancePreferences.getInstallLibraries(); + } + } diff --git a/io.sloeber.core/src/io/sloeber/core/api/Defaults.java b/io.sloeber.core/src/io/sloeber/core/api/Defaults.java index db6c4249..2d33a731 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Defaults.java +++ b/io.sloeber.core/src/io/sloeber/core/api/Defaults.java @@ -36,6 +36,7 @@ public class Defaults { public static final boolean useBonjour = true; public static final boolean autoInstallLibraries = true; public static final boolean useArduinoToolSelection = true; + public static final boolean INSTALL_LIBRARIES =false; //Install libraries on usage /** * Arduino has the default libraries in the user home directory in subfolder diff --git a/io.sloeber.core/src/io/sloeber/core/common/InstancePreferences.java b/io.sloeber.core/src/io/sloeber/core/common/InstancePreferences.java index 3cd4b3f0..32950927 100644 --- a/io.sloeber.core/src/io/sloeber/core/common/InstancePreferences.java +++ b/io.sloeber.core/src/io/sloeber/core/common/InstancePreferences.java @@ -28,6 +28,7 @@ public class InstancePreferences { private static final String KEY_PRAGMA_ONCE_HEADER = "add pragma once to headers"; //$NON-NLS-1$ private static final String KEY_USE_ARDUINO_TOOLS_SELECTION_ALGORITHM="Use the algoritm to find the toolchain like Arduino IDE"; //$NON-NLS-1$ private static final String KEY_USE_BONJOUR="use bonjour service to find devices"; //$NON-NLS-1$ + private static final String KEY_INSTALL_LIBRARIES= "download and install libraries on usage"; //$NON-NLS-1$ /** * Give back the user option if the libraries need to be added or not @@ -141,4 +142,12 @@ public static boolean useBonjour() { public static void setUseBonjour(boolean newFlag) { setValue(KEY_USE_BONJOUR, newFlag); } + + public static void setInstallLibraries(boolean selection) { + setValue(KEY_INSTALL_LIBRARIES, selection); + } + + public static boolean getInstallLibraries() { + return getBoolean(KEY_INSTALL_LIBRARIES, Defaults.INSTALL_LIBRARIES); + } } diff --git a/io.sloeber.core/src/io/sloeber/core/listeners/IndexerListener.java b/io.sloeber.core/src/io/sloeber/core/listeners/IndexerListener.java index d061f0f8..dd753ccc 100644 --- a/io.sloeber.core/src/io/sloeber/core/listeners/IndexerListener.java +++ b/io.sloeber.core/src/io/sloeber/core/listeners/IndexerListener.java @@ -35,6 +35,7 @@ import io.sloeber.arduinoFramework.api.LibraryManager; import io.sloeber.core.Activator; import io.sloeber.core.Messages; +import io.sloeber.core.api.ConfigurationPreferences; import io.sloeber.core.api.Const; import io.sloeber.core.api.IInstallLibraryHandler; import io.sloeber.core.api.ISloeberConfiguration; @@ -124,7 +125,7 @@ private static void checkLibraries(ISloeberConfiguration SloeberCfg) { //Check wether we need to download and install libraries IInstallLibraryHandler installHandler = LibraryManager.getInstallLibraryHandler(); - if (installHandler.autoInstall()) { + if (ConfigurationPreferences.getInstallLibraries() && installHandler.autoInstall()) { // Check if there are libraries that are not found in // the installed libraries Set uninstalledIncludedHeaders = new TreeSet<>(UnresolvedIncludedHeaders); diff --git a/io.sloeber.ui/src/io/sloeber/ui/preferences/PreferencePage.java b/io.sloeber.ui/src/io/sloeber/ui/preferences/PreferencePage.java index 2db1a428..493c252b 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/preferences/PreferencePage.java +++ b/io.sloeber.ui/src/io/sloeber/ui/preferences/PreferencePage.java @@ -69,23 +69,23 @@ public class PreferencePage extends FieldEditorPreferencePage implements IWorkbe private static final String KEY_PRIVATE_HARDWARE_PATHS = "Gui entry for private hardware paths"; //$NON-NLS-1$ private static final String KEY_PRIVATE_LIBRARY_PATHS = "Gui entry for private library paths"; //$NON-NLS-1$ private static final String KEY_TOOLCHAIN_SELECTION = "Gui entry for toolchain selection"; //$NON-NLS-1$ - //private static final String KEY_USE_BONJOUR = "Gui entry for usage of bonjour"; //$NON-NLS-1$ + private PathEditor arduinoPrivateLibPathPathEditor; private PathEditor arduinoPrivateHardwarePathPathEditor; private ComboFieldEditor buildBeforeUploadOptionEditor; private BooleanFieldEditor openSerialMonitorOpensSerialsOptionEditor; private BooleanFieldEditor automaticallyImportLibrariesOptionEditor; - //private BooleanFieldEditor automaticallyInstallLibrariesOptionEditor; private BooleanFieldEditor useArduinoToolchainSelectionEditor; private BooleanFieldEditor pragmaOnceHeaderOptionEditor; private BooleanFieldEditor cleanSerialMonitorAfterUploadEditor; private BooleanFieldEditor switchToSerialMonitorAfterUploadEditor; private BooleanFieldEditor enableParallelBuildForNewProjects; - //private BooleanFieldEditor enableBonjour; + private Button myBonjourCheckBox; private Spinner myDelayNumberSpinner; private Combo myDelayUnitCombo; + private Button myInstallLibCheckBox; private static int[] durationUnits = new int[] { 1, 7, 30, 365, 36500 }; @@ -154,6 +154,8 @@ public boolean performOk() { ConfigurationPreferences.setAutoImportLibraries(this.automaticallyImportLibrariesOptionEditor.getBooleanValue()); ConfigurationPreferences.setPragmaOnceHeaders(this.pragmaOnceHeaderOptionEditor.getBooleanValue()); ConfigurationPreferences.setUseBonjour(myBonjourCheckBox.getSelection()); + ConfigurationPreferences.setInstallLibraries(myInstallLibCheckBox.getSelection()); + //TOFIX line below ConfigurationPreferences.setJsonUpdateDelay(Duration.ofDays(myDelayNumberSpinner.getSelection() * durationUnits[myDelayUnitCombo.getSelectionIndex()])); BoardsManager.setPrivateHardwarePaths(hardWarePaths); @@ -321,12 +323,12 @@ protected void createFieldEditors() { Dialog.applyDialogFont(netWorkbox); //Currently sloeber does not autoinstall librtaries -// Button myInstallLibCheckBox = new Button(netWorkbox, SWT.CHECK | SWT.LEFT); -// Label inStallLabel = new Label(netWorkbox, SWT.BEGINNING); -// inStallLabel.setText(Messages.ui_auto_install_libraries); -// GridData gd16 = new GridData(SWT.BEGINNING, SWT.TOP, true, false); -// gd16.horizontalSpan=3; -// inStallLabel.setLayoutData(gd16); + myInstallLibCheckBox = new Button(netWorkbox, SWT.CHECK | SWT.LEFT); + Label inStallLabel = new Label(netWorkbox, SWT.BEGINNING); + inStallLabel.setText(Messages.ui_auto_install_libraries); + GridData gd16 = new GridData(SWT.BEGINNING, SWT.TOP, true, false); + gd16.horizontalSpan=3; + inStallLabel.setLayoutData(gd16); myBonjourCheckBox = new Button(netWorkbox, SWT.CHECK | SWT.LEFT ); @@ -374,6 +376,7 @@ public void widgetDefaultSelected(SelectionEvent e) { myBonjourCheckBox.setSelection(ConfigurationPreferences.useBonjour()); + myInstallLibCheckBox.setSelection(ConfigurationPreferences.getInstallLibraries()); setJsonDurationComposites(ConfigurationPreferences.getJsonUpdateDelay()); } @@ -412,6 +415,7 @@ protected void performApply() { protected void performDefaults() { super.performDefaults(); myBonjourCheckBox.setSelection( Defaults.useBonjour); + myInstallLibCheckBox.setSelection( Defaults.INSTALL_LIBRARIES); setJsonDurationComposites(Defaults.getJsonUpdateDuration()); } From a50ceeaa625cb105ef531cb37b04ce9afb99f37e Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 24 Nov 2024 22:21:48 +0100 Subject: [PATCH 065/115] Add test on download and install lib on usage. Both test that when user does not wnat to download install Sloeber does not do it and the reverse. --- .../internal/ArduinoLibraryVersion.java | 7 ++-- .../src/io/sloeber/core/BuildTests.java | 40 ++++++++++++++++++ .../src/io/sloeber/core/Shared.java | 41 +++++++++++-------- .../onlyInstallLibraryWhenAllowed/sketch.ino | 14 +++++++ 4 files changed, 81 insertions(+), 21 deletions(-) create mode 100644 io.sloeber.tests/src/templates/onlyInstallLibraryWhenAllowed/sketch.ino diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoLibraryVersion.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoLibraryVersion.java index 0e4654c4..1d7ca727 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoLibraryVersion.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/internal/ArduinoLibraryVersion.java @@ -67,7 +67,7 @@ public ArduinoLibraryVersion(JsonElement json, ArduinoLibrary arduinoLibrary) { archiveFileName = getSafeString(jsonObject, "archiveFileName"); size = jsonObject.get("size").getAsInt(); checksum = getSafeString(jsonObject, "checksum"); - calculateFQN(); + myFQN=calculateFQN(getName()); } catch (Exception e) { throw new JsonParseException("failed to parse json " + e.getMessage(),e); } @@ -205,9 +205,8 @@ public IPath getExamplePath() { return getInstallPath().append(EXAMPLES_FOLDER); } - private void calculateFQN() { - myFQN= Path.fromPortableString(SLOEBER_LIBRARY_FQN); - myFQN=myFQN.append(MANAGED).append(getName()); + static public IPath calculateFQN(String libName) { + return Path.fromPortableString(SLOEBER_LIBRARY_FQN).append(MANAGED).append(libName); } @Override diff --git a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java index 962330d5..7ee82f81 100644 --- a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java +++ b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java @@ -31,6 +31,7 @@ import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -792,4 +793,43 @@ public void NightlyBoardPatron(String name, MCUBoard boardID, Example example, C } + @Test + public void onlyInstallLibraryWhenAllowed() throws Exception { + String libName="SD"; + + //set option not to install lib + ConfigurationPreferences.setInstallLibraries(false); + + //uninstall lib + LibraryManager.uninstallLibrary( libName); + + // create a project that uses a the lib + String testName = "onlyInstallLibraryWhenAllowed"; + IProgressMonitor monitor=new NullProgressMonitor(); + IPath templateFolder = Shared.getTemplateFolder(testName); + CodeDescription codeDescriptor = CodeDescription.createCustomTemplate(templateFolder); + MCUBoard unoboard = Arduino.uno(); + IProject theTestProject = SloeberProject.createArduinoProject(testName, null, unoboard.getBoardDescriptor(), codeDescriptor, + new CompileDescription(), monitor); + //wait for indexer and so on + Shared.waitForAllJobsToFinish(); + + + //Building the project should fail + assertNotNull( Shared.buildAndVerify(theTestProject,3,IncrementalProjectBuilder.FULL_BUILD ,monitor),"Sloeber wrongly installed lib "+libName); + + //set option to install libs + ConfigurationPreferences.setInstallLibraries(true); + + //trigger the indexer + ICProject cTestProject = CoreModel.getDefault().getCModel().getCProject(theTestProject.getName()); + CCorePlugin.getIndexManager().reindex(cTestProject); + Shared.waitForIndexer(theTestProject); + + //build should not fail + assertNull( Shared.buildAndVerify(theTestProject,3,IncrementalProjectBuilder.FULL_BUILD ,monitor),"Sloeber dit not install lib "+libName); + + + + } } diff --git a/io.sloeber.tests/src/io/sloeber/core/Shared.java b/io.sloeber.tests/src/io/sloeber/core/Shared.java index f65a7d00..bacfa795 100644 --- a/io.sloeber.tests/src/io/sloeber/core/Shared.java +++ b/io.sloeber.tests/src/io/sloeber/core/Shared.java @@ -31,6 +31,7 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; @@ -259,23 +260,9 @@ public static String buildAndVerifyGivenBuilders(String projectName, BoardDescri autoDesc.setBuilder(curBuilder); coreModel.setProjectDescription(theTestProject, projectDescription); - theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, monitor); - - if (hasBuildErrors(theTestProject)!=null) { - Shared.waitForAllJobsToFinish(); - Thread.sleep(2000); - theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, monitor); - if (hasBuildErrors(theTestProject)!=null) { - Shared.waitForAllJobsToFinish(); - Thread.sleep(2000); - theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, monitor); - String buildError=hasBuildErrors(theTestProject); - if (buildError!=null) { - myLastFailMessage = myLastFailMessage + NEWLINE +buildError+ NEWLINE+ "Failed to compile the project:" + projectName - + " with builder " + curBuilder; - } - } - } + buildAndVerify(theTestProject,3,IncrementalProjectBuilder.FULL_BUILD, monitor); + + } if (!myLastFailMessage.isBlank()) { @@ -295,6 +282,26 @@ public static String buildAndVerifyGivenBuilders(String projectName, BoardDescri return null; } + static String buildAndVerify(IProject theTestProject, int maxTries, int buildType, IProgressMonitor monitor) throws Exception { + int curTry = 0; + String buildError=null; + while (curTry++ < maxTries) { + theTestProject.build(buildType, monitor); + Shared.waitForAllJobsToFinish(); + Thread.sleep(2000); + buildError = hasBuildErrors(theTestProject); + if (buildError == null) { + return buildError; + } + } + IAutoBuildConfigurationDescription autoDesc = IAutoBuildConfigurationDescription + .getActiveConfig(theTestProject,false); + String builder=autoDesc.getBuilder().getId(); + myLastFailMessage = myLastFailMessage + NEWLINE + buildError + NEWLINE + "Failed to compile the project:" + + theTestProject.getName() + " with builder " + builder; + return buildError; + } + /* * For some boards that do not run out of the box we know how to fix it. This * code fixes these things diff --git a/io.sloeber.tests/src/templates/onlyInstallLibraryWhenAllowed/sketch.ino b/io.sloeber.tests/src/templates/onlyInstallLibraryWhenAllowed/sketch.ino new file mode 100644 index 00000000..69256a31 --- /dev/null +++ b/io.sloeber.tests/src/templates/onlyInstallLibraryWhenAllowed/sketch.ino @@ -0,0 +1,14 @@ +#include "Arduino.h" +#include "SD.h" +//The setup function is called once at startup of the sketch +void setup() +{ +// Add your initialization code here +} + +// The loop function is called in an endless loop +void loop() +{ +//Add your repeated code here +} + From 1706127c8eec426122dca688ed64d96a38099a6a Mon Sep 17 00:00:00 2001 From: jan Date: Mon, 25 Nov 2024 00:26:13 +0100 Subject: [PATCH 066/115] Give onlyInstallLibraryWhenAllowed more time for github --- io.sloeber.tests/src/io/sloeber/core/BuildTests.java | 1 + 1 file changed, 1 insertion(+) diff --git a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java index 7ee82f81..97f38d33 100644 --- a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java +++ b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java @@ -824,6 +824,7 @@ public void onlyInstallLibraryWhenAllowed() throws Exception { //trigger the indexer ICProject cTestProject = CoreModel.getDefault().getCModel().getCProject(theTestProject.getName()); CCorePlugin.getIndexManager().reindex(cTestProject); + Thread.sleep(5000); Shared.waitForIndexer(theTestProject); //build should not fail From 6e7a835d41ee2896ea8866ff1e0897f5ee54b5e4 Mon Sep 17 00:00:00 2001 From: jan Date: Mon, 25 Nov 2024 01:46:37 +0100 Subject: [PATCH 067/115] Add libraries from the original project to the sloeber converted project --- .../io/sloeber/core/api/SloeberProject.java | 36 ++++++++++++++++--- .../core/internal/ArduinoHardwareLibrary.java | 9 +++-- .../ArduinoPrivateLibraryVersion.java | 9 +++-- 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java b/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java index 3b0dda98..ad694ee7 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java +++ b/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java @@ -37,6 +37,7 @@ import io.sloeber.arduinoFramework.api.BoardDescription; import io.sloeber.arduinoFramework.api.IArduinoLibraryVersion; import io.sloeber.arduinoFramework.api.LibraryManager; +import io.sloeber.arduinoFramework.internal.ArduinoLibraryVersion; import io.sloeber.autoBuild.api.AutoBuildProject; import io.sloeber.autoBuild.api.IAutoBuildConfigurationDescription; import io.sloeber.autoBuild.buildTools.api.IBuildTools; @@ -48,6 +49,8 @@ import io.sloeber.autoBuild.schema.api.IProjectType; import io.sloeber.core.Activator; import io.sloeber.core.Messages; +import io.sloeber.core.internal.ArduinoHardwareLibrary; +import io.sloeber.core.internal.ArduinoPrivateLibraryVersion; import io.sloeber.core.internal.SloeberConfiguration; import io.sloeber.core.listeners.IndexerController; import io.sloeber.core.natures.SloeberNature; @@ -134,7 +137,7 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { // Get the libraries used Set libNames = new HashSet<>(); - IFolder libFolder = OldCoreFolder.getFolder("libraries"); //$NON-NLS-1$ + IFolder libFolder = project.getFolder("libraries"); //$NON-NLS-1$ if (libFolder.exists()) { for (IResource curResource : libFolder.members()) { if (curResource instanceof IFolder) { @@ -210,18 +213,41 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { SloeberConfiguration sloeberConfiguration = new SloeberConfiguration(boardDescriptor, otherDesc, compileDescriptor); + + + //Save the sloeber configuration in the autoBuild configuration + autoConf.setAutoBuildConfigurationExtensionDescription(sloeberConfiguration); + // Add the libraries TreeMap availableLibs = LibraryManager .getLibrariesAll(boardDescriptor); // find the libs we can add Set toInstallLibs = new HashSet<>(); for (String curLibName : libNames) { - toInstallLibs.add(availableLibs.get(curLibName)); + IPath boardLibFQN = ArduinoHardwareLibrary.calculateFQN(curLibName); + IArduinoLibraryVersion foundLib = availableLibs.get(boardLibFQN.toString()); + if (foundLib != null) { + toInstallLibs.add(foundLib); + continue; + } + + IPath managedLibFQN = ArduinoLibraryVersion.calculateFQN(curLibName); + foundLib = availableLibs.get(managedLibFQN.toString()); + if (foundLib != null) { + toInstallLibs.add(foundLib); + continue; + } + + IPath privateLibFQN = ArduinoPrivateLibraryVersion.calculateFQN(curLibName); + foundLib = availableLibs.get(privateLibFQN.toString()); + if (foundLib != null) { + toInstallLibs.add(foundLib); + continue; + } + } + toInstallLibs.remove(null); sloeberConfiguration.addLibraries(toInstallLibs); - - //Save the sloeber configuration in the autoBuild configuration - autoConf.setAutoBuildConfigurationExtensionDescription(sloeberConfiguration); } SubMonitor refreshMonitor = SubMonitor.convert(internalMonitor, 3); diff --git a/io.sloeber.core/src/io/sloeber/core/internal/ArduinoHardwareLibrary.java b/io.sloeber.core/src/io/sloeber/core/internal/ArduinoHardwareLibrary.java index 4546147f..12ce9efb 100644 --- a/io.sloeber.core/src/io/sloeber/core/internal/ArduinoHardwareLibrary.java +++ b/io.sloeber.core/src/io/sloeber/core/internal/ArduinoHardwareLibrary.java @@ -21,7 +21,7 @@ public class ArduinoHardwareLibrary implements IArduinoLibraryVersion { public ArduinoHardwareLibrary(IPath installPath) { myInstallPath = installPath; myName = myInstallPath.lastSegment(); - calculateFQN(); + myFQN=calculateFQN(getName()); } public ArduinoHardwareLibrary(String curSaveString, BoardDescription boardDesc) { @@ -31,7 +31,7 @@ public ArduinoHardwareLibrary(String curSaveString, BoardDescription boardDesc) if(!myInstallPath.toFile().exists()) { myInstallPath=boardDesc.getReferencedCoreLibraryPath().append(myName); } - calculateFQN(); + myFQN=calculateFQN(getName()); } @Override @@ -64,9 +64,8 @@ public IPath getExamplePath() { } - private void calculateFQN() { - myFQN= Path.fromPortableString(SLOEBER_LIBRARY_FQN); - myFQN= myFQN.append(BOARD).append(getName()); + public static IPath calculateFQN(String libName) { + return Path.fromPortableString(SLOEBER_LIBRARY_FQN).append(BOARD).append(libName); } @Override diff --git a/io.sloeber.core/src/io/sloeber/core/internal/ArduinoPrivateLibraryVersion.java b/io.sloeber.core/src/io/sloeber/core/internal/ArduinoPrivateLibraryVersion.java index 36ae0279..0c649e65 100644 --- a/io.sloeber.core/src/io/sloeber/core/internal/ArduinoPrivateLibraryVersion.java +++ b/io.sloeber.core/src/io/sloeber/core/internal/ArduinoPrivateLibraryVersion.java @@ -20,13 +20,13 @@ public class ArduinoPrivateLibraryVersion implements IArduinoLibraryVersion { public ArduinoPrivateLibraryVersion(IPath installPath) { myInstallPath = installPath; myName = myInstallPath.lastSegment(); - calcFQN(); + myFQN= calculateFQN(getName()); } public ArduinoPrivateLibraryVersion(String curSaveString) { String[] parts=curSaveString.split(SEMI_COLON); myName=parts[parts.length-1]; - calcFQN(); + myFQN= calculateFQN(getName()); String privateLibPaths[] = InstancePreferences.getPrivateLibraryPaths(); for (String curLibPath : privateLibPaths) { Path curPrivPath=new Path(curLibPath); @@ -67,9 +67,8 @@ public IPath getExamplePath() { return getInstallPath().append(EXAMPLES_FOLDER); } - private void calcFQN() { - myFQN= Path.fromPortableString(SLOEBER_LIBRARY_FQN); - myFQN= myFQN.append(PRIVATE).append(getName()); + static public IPath calculateFQN(String libName) { + return Path.fromPortableString(SLOEBER_LIBRARY_FQN).append(PRIVATE).append(libName); } @Override From e2ad8ebce1b6f35d81910ae3903d3e5059984c77 Mon Sep 17 00:00:00 2001 From: jan Date: Mon, 25 Nov 2024 01:49:59 +0100 Subject: [PATCH 068/115] Split of the tests that fail on github --- .../src/io/sloeber/core/BuildTests.java | 41 ---------- .../core/BuildTests_not_on_github.java | 82 +++++++++++++++++++ 2 files changed, 82 insertions(+), 41 deletions(-) create mode 100644 io.sloeber.tests/src/io/sloeber/core/BuildTests_not_on_github.java diff --git a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java index 97f38d33..962330d5 100644 --- a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java +++ b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java @@ -31,7 +31,6 @@ import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; -import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -793,44 +792,4 @@ public void NightlyBoardPatron(String name, MCUBoard boardID, Example example, C } - @Test - public void onlyInstallLibraryWhenAllowed() throws Exception { - String libName="SD"; - - //set option not to install lib - ConfigurationPreferences.setInstallLibraries(false); - - //uninstall lib - LibraryManager.uninstallLibrary( libName); - - // create a project that uses a the lib - String testName = "onlyInstallLibraryWhenAllowed"; - IProgressMonitor monitor=new NullProgressMonitor(); - IPath templateFolder = Shared.getTemplateFolder(testName); - CodeDescription codeDescriptor = CodeDescription.createCustomTemplate(templateFolder); - MCUBoard unoboard = Arduino.uno(); - IProject theTestProject = SloeberProject.createArduinoProject(testName, null, unoboard.getBoardDescriptor(), codeDescriptor, - new CompileDescription(), monitor); - //wait for indexer and so on - Shared.waitForAllJobsToFinish(); - - - //Building the project should fail - assertNotNull( Shared.buildAndVerify(theTestProject,3,IncrementalProjectBuilder.FULL_BUILD ,monitor),"Sloeber wrongly installed lib "+libName); - - //set option to install libs - ConfigurationPreferences.setInstallLibraries(true); - - //trigger the indexer - ICProject cTestProject = CoreModel.getDefault().getCModel().getCProject(theTestProject.getName()); - CCorePlugin.getIndexManager().reindex(cTestProject); - Thread.sleep(5000); - Shared.waitForIndexer(theTestProject); - - //build should not fail - assertNull( Shared.buildAndVerify(theTestProject,3,IncrementalProjectBuilder.FULL_BUILD ,monitor),"Sloeber dit not install lib "+libName); - - - - } } diff --git a/io.sloeber.tests/src/io/sloeber/core/BuildTests_not_on_github.java b/io.sloeber.tests/src/io/sloeber/core/BuildTests_not_on_github.java new file mode 100644 index 00000000..84149071 --- /dev/null +++ b/io.sloeber.tests/src/io/sloeber/core/BuildTests_not_on_github.java @@ -0,0 +1,82 @@ +package io.sloeber.core; + + + + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.model.CoreModel; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IncrementalProjectBuilder; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +import io.sloeber.arduinoFramework.api.LibraryManager; +import io.sloeber.core.api.CodeDescription; +import io.sloeber.core.api.CompileDescription; +import io.sloeber.core.api.ConfigurationPreferences; +import io.sloeber.core.api.SloeberProject; +import io.sloeber.providers.Arduino; +import io.sloeber.providers.MCUBoard; + +@SuppressWarnings({ "nls", "static-method"}) +public class BuildTests_not_on_github { + + /* + * In new new installations (of the Sloeber development environment) the + * installer job will trigger downloads These must have finished before we can + * start testing + */ + @BeforeAll + public static void beforeClass() throws Exception { + Shared.waitForBoardsManager(); + Shared.setDeleteProjects(false); + ConfigurationPreferences.setUseBonjour(false); + } + + + @Test + public void onlyInstallLibraryWhenAllowed() throws Exception { + String libName="SD"; + + //set option not to install lib + ConfigurationPreferences.setInstallLibraries(false); + + //uninstall lib + LibraryManager.uninstallLibrary( libName); + + // create a project that uses a the lib + String testName = "onlyInstallLibraryWhenAllowed"; + IProgressMonitor monitor=new NullProgressMonitor(); + IPath templateFolder = Shared.getTemplateFolder(testName); + CodeDescription codeDescriptor = CodeDescription.createCustomTemplate(templateFolder); + MCUBoard unoboard = Arduino.uno(); + IProject theTestProject = SloeberProject.createArduinoProject(testName, null, unoboard.getBoardDescriptor(), codeDescriptor, + new CompileDescription(), monitor); + //wait for indexer and so on + Shared.waitForAllJobsToFinish(); + + + //Building the project should fail + assertNotNull( Shared.buildAndVerify(theTestProject,3,IncrementalProjectBuilder.FULL_BUILD ,monitor),"Sloeber wrongly installed lib "+libName); + + //set option to install libs + ConfigurationPreferences.setInstallLibraries(true); + + //trigger the indexer + ICProject cTestProject = CoreModel.getDefault().getCModel().getCProject(theTestProject.getName()); + CCorePlugin.getIndexManager().reindex(cTestProject); + Thread.sleep(5000); + Shared.waitForIndexer(theTestProject); + + //build should not fail + assertNull( Shared.buildAndVerify(theTestProject,3,IncrementalProjectBuilder.FULL_BUILD ,monitor),"Sloeber dit not install lib "+libName); + + + + } +} From 158797ce68d577760e659855d6452c0caddc8c86 Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 1 Dec 2024 22:08:10 +0100 Subject: [PATCH 069/115] V5 update the splash screen --- images_plugin_dev_setup/SVG_Logo/splash.svg | 12 ++++++------ io.sloeber.ui/splash.bmp | Bin 409566 -> 546070 bytes 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/images_plugin_dev_setup/SVG_Logo/splash.svg b/images_plugin_dev_setup/SVG_Logo/splash.svg index 22c2eb0e..d624f2ff 100644 --- a/images_plugin_dev_setup/SVG_Logo/splash.svg +++ b/images_plugin_dev_setup/SVG_Logo/splash.svg @@ -9,7 +9,7 @@ version="1.1" inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)" sodipodi:docname="splash.svg" - inkscape:export-filename="..\..\..\..\..\..\test\splash.png" + inkscape:export-filename="..\..\..\..\Desktop\splash.jpg" inkscape:export-xdpi="96" inkscape:export-ydpi="96" xml:space="preserve" @@ -28,16 +28,16 @@ inkscape:pageopacity="1" inkscape:pageshadow="2" inkscape:zoom="1" - inkscape:cx="-262.5" + inkscape:cx="-345" inkscape:cy="-55.5" inkscape:document-units="px" inkscape:current-layer="layer4" showgrid="false" units="px" inkscape:window-width="1920" - inkscape:window-height="991" - inkscape:window-x="-9" - inkscape:window-y="-9" + inkscape:window-height="1009" + inkscape:window-x="-8" + inkscape:window-y="-8" inkscape:window-maximized="1" inkscape:snap-global="false" showguides="true" @@ -2678,7 +2678,7 @@ id="tspan4789" x="315.39621" y="212.03772" - style="font-size:25px;line-height:1.25"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�ImF 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%0DYBj0P4V>R}a?<+bTOg^r8u-Bq_IAI3ej$3uMK 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 literal 409566 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

>S*erR*Sk(Qzp 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 Qrso|Si^ Date: Tue, 10 Dec 2024 21:06:27 +0100 Subject: [PATCH 070/115] trying to fix dependency related warnings --- .../META-INF/MANIFEST.MF | 1 - io.sloeber.core/.classpath | 5 +- io.sloeber.core/META-INF/MANIFEST.MF | 4 +- io.sloeber.core/build.properties | 7 +- io.sloeber.core/lib/jsch-0.1.55.jar | Bin 280681 -> 0 bytes io.sloeber.product/sloeber.target | 20 +++- io.sloeber.tests/BuildTests Sloeber.launch | 91 +++++++++--------- io.sloeber.tests/META-INF/MANIFEST.MF | 24 +++-- 8 files changed, 78 insertions(+), 74 deletions(-) delete mode 100644 io.sloeber.core/lib/jsch-0.1.55.jar diff --git a/io.sloeber.autoBuild.test/META-INF/MANIFEST.MF b/io.sloeber.autoBuild.test/META-INF/MANIFEST.MF index 1166e9fd..d289e44e 100644 --- a/io.sloeber.autoBuild.test/META-INF/MANIFEST.MF +++ b/io.sloeber.autoBuild.test/META-INF/MANIFEST.MF @@ -7,7 +7,6 @@ Require-Bundle: io.sloeber.autoBuild, junit-jupiter-api;bundle-version="5.0.0", junit-jupiter-params;bundle-version="5.0.0", org.eclipse.core.resources, - org.apache.log4j;bundle-version="1.2.24", org.eclipse.cdt.core;bundle-version="7.4.200", org.eclipse.osgi, org.eclipse.core.jobs, diff --git a/io.sloeber.core/.classpath b/io.sloeber.core/.classpath index 17a441d0..bf277454 100644 --- a/io.sloeber.core/.classpath +++ b/io.sloeber.core/.classpath @@ -1,5 +1,7 @@ + + @@ -7,9 +9,6 @@ - - - diff --git a/io.sloeber.core/META-INF/MANIFEST.MF b/io.sloeber.core/META-INF/MANIFEST.MF index 1d3de98d..88e86763 100644 --- a/io.sloeber.core/META-INF/MANIFEST.MF +++ b/io.sloeber.core/META-INF/MANIFEST.MF @@ -8,7 +8,6 @@ Bundle-Vendor: Sloeber.io Bundle-ActivationPolicy: lazy Bundle-Activator: io.sloeber.core.Activator Bundle-ClassPath: ., - lib/jsch-0.1.55.jar, lib/jssc-2.9.4.jar, lib/jmdns-3.5.7.jar Require-Bundle: @@ -28,7 +27,8 @@ Require-Bundle: org.eclipse.ui.intro, io.sloeber.autoBuild, org.eclipse.cdt.make.core, - org.apache.commons.lang3;bundle-version="3.14.0" + org.apache.commons.lang3;bundle-version="3.14.0", + com.jcraft.jsch Export-Package: cc.arduino.packages;x-internal:=true, cc.arduino.packages.discoverers;x-internal:=true, cc.arduino.packages.ssh;x-internal:=true, diff --git a/io.sloeber.core/build.properties b/io.sloeber.core/build.properties index f43297fb..44cc6f62 100644 --- a/io.sloeber.core/build.properties +++ b/io.sloeber.core/build.properties @@ -9,11 +9,10 @@ bin.includes = .,\ config/,\ OSGI-INF/,\ src/io/sloeber/core/templates/sketch.ino,\ - lib/jsch-0.1.55.jar,\ OSGI-INF/l10n/bundle.properties,\ - lib/jssc-2.9.4.jar,\ - lib/jmdns-3.5.7.jar,\ icons/,\ - launch/ + launch/,\ + lib/jssc-2.9.4.jar,\ + lib/jmdns-3.5.7.jar jars.compile.order = . src.includes = icons/ diff --git a/io.sloeber.core/lib/jsch-0.1.55.jar b/io.sloeber.core/lib/jsch-0.1.55.jar deleted file mode 100644 index 1a64e71dae695099946be9dc23801b9359b1e79e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 280681 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 U;?dCVg8vkR(9m!RL05tHf4ZTqGynhq diff --git a/io.sloeber.product/sloeber.target b/io.sloeber.product/sloeber.target index dd75021e..3d24d985 100644 --- a/io.sloeber.product/sloeber.target +++ b/io.sloeber.product/sloeber.target @@ -75,25 +75,37 @@ com.github.luben zstd-jni - 1.5.6-3 + 1.5.6-8 jar com.google.code.gson gson - 2.10.1 + 2.11.0 jar commons-io commons-io - 2.15.0 + 2.18.0 jar org.apache.commons commons-compress - 1.26.1 + 1.27.1 + jar + + + org.apache.logging.log4j + log4j-api + 2.19.0 + jar + + + org.apache.logging.log4j + log4j-core + 2.19.0 jar diff --git a/io.sloeber.tests/BuildTests Sloeber.launch b/io.sloeber.tests/BuildTests Sloeber.launch index 74a98e71..2d915926 100644 --- a/io.sloeber.tests/BuildTests Sloeber.launch +++ b/io.sloeber.tests/BuildTests Sloeber.launch @@ -2,8 +2,9 @@ - - + + + @@ -11,10 +12,7 @@ - - - - + @@ -33,7 +31,7 @@ - + @@ -49,8 +47,8 @@ - - + + @@ -59,12 +57,13 @@ - + + + - @@ -86,18 +85,21 @@ + + - - + + + + - - - + + @@ -106,11 +108,11 @@ - + - - + + @@ -128,12 +130,9 @@ - - - @@ -156,7 +155,7 @@ - + @@ -184,16 +183,16 @@ - + - + - - + + - + @@ -201,34 +200,33 @@ - - - - - + + + + - - + + - + - - + + - + - + @@ -236,7 +234,7 @@ - + @@ -255,7 +253,7 @@ - + @@ -268,13 +266,13 @@ - + - + @@ -298,11 +296,10 @@ - + - - + diff --git a/io.sloeber.tests/META-INF/MANIFEST.MF b/io.sloeber.tests/META-INF/MANIFEST.MF index fea1aaa5..f6cbd958 100644 --- a/io.sloeber.tests/META-INF/MANIFEST.MF +++ b/io.sloeber.tests/META-INF/MANIFEST.MF @@ -5,28 +5,26 @@ Bundle-SymbolicName: io.sloeber.tests;singleton:=true Bundle-Version: 5.0.0.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-Vendor: Sloeber.io -Require-Bundle: io.sloeber.core, - io.sloeber.ui, - org.eclipse.cdt.core, - org.eclipse.core.runtime, +Require-Bundle: org.eclipse.core.runtime, org.eclipse.core.resources, - junit-jupiter-params;bundle-version="5.10.2", - junit-jupiter-api;bundle-version="5.10.2", - org.apache.log4j, org.eclipse.swt, + org.eclipse.cdt.core, io.sloeber.autoBuild, - org.opentest4j, - junit-platform-suite-api + io.sloeber.core, + io.sloeber.ui, + junit-platform-suite-api, + junit-jupiter-params, + junit-jupiter-api, + org.apache.commons.commons-io, + org.apache.logging.log4j.api;bundle-version="2.19.0", + org.apache.logging.log4j.core;bundle-version="2.19.0" Bundle-ActivationPolicy: lazy Import-Package: io.sloeber.ui, io.sloeber.ui.monitor, io.sloeber.autoBuild.api, org.apache.commons.io Automatic-Module-Name: io.sloeber.tests -Export-Package: io.sloeber.core;x-internal:=true, - io.sloeber.junit;x-internal:=true, - io.sloeber.providers;x-internal:=true, - templates.CreateAndCompileTest;x-internal:=true, +Export-Package: templates.CreateAndCompileTest;x-internal:=true, templates.are_defines_found;x-internal:=true, templates.basic.ino;x-internal:=true, templates.defines_and_includes;x-internal:=true, From fd317df334a5335bcc40449c9d825177aeffe0b4 Mon Sep 17 00:00:00 2001 From: jan Date: Tue, 10 Dec 2024 23:58:58 +0100 Subject: [PATCH 071/115] trying to fix the build by upgrading maven based on this page https://github.com/eclipse-platform/eclipse.platform/issues/1594 --- .github/workflows/maven.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 88de62a3..1c6f8ee0 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -19,7 +19,7 @@ jobs: - name: Set up Maven uses: stCarolas/setup-maven@v5 with: - maven-version: 3.9.6 + maven-version: 4.0.9 - name: Set up JDK 21 uses: actions/setup-java@v4 with: From 7e628dfe7a39a8df3b9556d4223b2c42ed267f91 Mon Sep 17 00:00:00 2001 From: jan Date: Wed, 11 Dec 2024 00:21:55 +0100 Subject: [PATCH 072/115] Changed maven version but should have changed tycho version --- .github/workflows/maven.yml | 2 +- io.sloeber.parent/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 1c6f8ee0..88de62a3 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -19,7 +19,7 @@ jobs: - name: Set up Maven uses: stCarolas/setup-maven@v5 with: - maven-version: 4.0.9 + maven-version: 3.9.6 - name: Set up JDK 21 uses: actions/setup-java@v4 with: diff --git a/io.sloeber.parent/pom.xml b/io.sloeber.parent/pom.xml index c5a184ab..f4a2e305 100644 --- a/io.sloeber.parent/pom.xml +++ b/io.sloeber.parent/pom.xml @@ -9,7 +9,7 @@ pom - 4.0.8 + 4.0.9 ${project.build.directory}/repo From 179663a6156c1e1cd89ae9518eb847f48b534081 Mon Sep 17 00:00:00 2001 From: jan Date: Wed, 11 Dec 2024 02:11:36 +0100 Subject: [PATCH 073/115] get the libs back when reopening a project #1695 --- .../arduinoFramework/api/BoardDescription.java | 2 +- .../arduinoFramework/api/LibraryManager.java | 8 ++++---- .../core/internal/SloeberConfiguration.java | 5 +++++ .../src/io/sloeber/ui/Import_Libraries_Page.java | 14 +++++++++----- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java index 41eb6893..72967b06 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/BoardDescription.java @@ -676,7 +676,7 @@ public PlatformTxtFile getreferencedCorePlatformFile() { public IPath getReferencedCoreLibraryPath() { updateWhenDirty(); if (myReferencedPlatformCore == null) { - return null; + return getReferencingLibraryPath(); } return this.myReferencedPlatformCore.getInstallPath().append(ARDUINO_LIBRARY_FOLDER_NAME); } diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java index f5de8a28..243641f9 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java @@ -455,17 +455,17 @@ private static Map getLibrariesHarware(BoardDesc } public static IArduinoLibraryVersion getLibraryVersionFromLocation(IFolder libFolder,BoardDescription boardDescriptor) { - // TODO Auto-generated method stub - if (boardDescriptor != null) { IPath libPath=boardDescriptor.getReferencedCoreLibraryPath(); if(libPath!=null && libPath.isPrefixOf(libFolder.getLocation())) { - return getLibrariesHarware(boardDescriptor).get(libFolder.getName()); + String FQNLibName=ArduinoHardwareLibrary.calculateFQN(libFolder.getName()).toString(); + return getLibrariesHarware(boardDescriptor).get(FQNLibName); } } if(ConfigurationPreferences.getInstallationPathLibraries().isPrefixOf(libFolder.getLocation())) { - return getLibrariesdManaged().get(libFolder.getName()); + String FQNLibName= ArduinoLibraryVersion.calculateFQN(libFolder.getName()).toString(); + return getLibrariesdManaged().get(FQNLibName); } return getLibrariesPrivate().get(libFolder.getName()); diff --git a/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java b/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java index 7cb39cd5..5788edf6 100644 --- a/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java +++ b/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java @@ -617,6 +617,11 @@ public void reAttachLibraries() { @Override public Map getUsedLibraries() { + try { + myLibraries=getLibrariesFromLinks(); + } catch (CoreException e) { + e.printStackTrace(); + } return new HashMap<>(myLibraries); } diff --git a/io.sloeber.ui/src/io/sloeber/ui/Import_Libraries_Page.java b/io.sloeber.ui/src/io/sloeber/ui/Import_Libraries_Page.java index 07302d63..323f8c6b 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/Import_Libraries_Page.java +++ b/io.sloeber.ui/src/io/sloeber/ui/Import_Libraries_Page.java @@ -65,13 +65,17 @@ public void createControl(Composite parent) { } class ItemSorter { - public static ISloeberConfiguration sloeberCfg; - public TreeMap myItems = new TreeMap<>(); - public IArduinoLibraryVersion myLib = null; + private TreeMap myItems = new TreeMap<>(); + private IArduinoLibraryVersion myLib = null; + private static Map myCurrentInstalledLibs =null; ItemSorter() { } + static void SetSloeberConfiguration(ISloeberConfiguration sloeberCfg) { + myCurrentInstalledLibs = sloeberCfg.getUsedLibraries(); + } + public void createChildren(TreeItem curItem) { for (Entry curentry : myItems.entrySet()) { String key = curentry.getKey(); @@ -83,7 +87,7 @@ public void createChildren(TreeItem curItem) { if (myLib == null) { curItem.setGrayed(true); }else { - boolean isSelected = sloeberCfg.getUsedLibraries().get(myLib.getName()) != null; + boolean isSelected = myCurrentInstalledLibs.get(myLib.getName()) != null; curItem.setChecked(isSelected); curItem.setData(myLib); if (isSelected) { @@ -127,7 +131,7 @@ protected void createSourceGroup(Composite parent) { // sort the items ItemSorter sortedItems = new ItemSorter(); - ItemSorter.sloeberCfg = sloeberCfg; + ItemSorter.SetSloeberConfiguration(sloeberCfg); for (IArduinoLibraryVersion curlib : allLibraries.values()) { String keys[] = curlib.getBreadCrumbs(); From d7c53afd6b9ff40766fc780c1e712b4f57dc8978 Mon Sep 17 00:00:00 2001 From: jan Date: Wed, 11 Dec 2024 02:14:57 +0100 Subject: [PATCH 074/115] Fix NLS warning --- io.sloeber.core/src/io/sloeber/core/Messages.java | 1 + .../src/io/sloeber/core/internal/SloeberConfiguration.java | 2 +- io.sloeber.core/src/io/sloeber/core/messages.properties | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/io.sloeber.core/src/io/sloeber/core/Messages.java b/io.sloeber.core/src/io/sloeber/core/Messages.java index 910674fe..dd2af674 100644 --- a/io.sloeber.core/src/io/sloeber/core/Messages.java +++ b/io.sloeber.core/src/io/sloeber/core/Messages.java @@ -118,6 +118,7 @@ public class Messages extends NLS { public static String CompileDescription_WarningsDefault; public static String CompileDescription_WarningsMore; public static String CompileDescription_WarningsNone; + public static String SloeberConfiguration_Failed_Modify_config_rename; public static String SloeberProject_Project_is_null; static { diff --git a/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java b/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java index 5788edf6..7340e0d6 100644 --- a/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java +++ b/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java @@ -793,7 +793,7 @@ private boolean updateSourceEntries() { getAutoBuildDesc().getCdtConfigurationDescription().setSourceEntries(newSourceEntries); } catch (Exception e) { - Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, "Failed to modify configuration for rename", e)); + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, Messages.SloeberConfiguration_Failed_Modify_config_rename, e)); } return true; } diff --git a/io.sloeber.core/src/io/sloeber/core/messages.properties b/io.sloeber.core/src/io/sloeber/core/messages.properties index f51fe28c..4fda4b0d 100644 --- a/io.sloeber.core/src/io/sloeber/core/messages.properties +++ b/io.sloeber.core/src/io/sloeber/core/messages.properties @@ -91,4 +91,5 @@ CompileDescription_WarningsCustom=Custom CompileDescription_WarningsDefault=Default CompileDescription_WarningsMore=More CompileDescription_WarningsNone=None +SloeberConfiguration_Failed_Modify_config_rename=Failed to modify configuration for rename SloeberProject_Project_is_null=The provided project is null. Sloeber can not upgrade. From 0a402d79316ec0cff753ee79c09a8d634740cf5e Mon Sep 17 00:00:00 2001 From: jan Date: Thu, 12 Dec 2024 17:17:28 +0100 Subject: [PATCH 075/115] The pom.xml still referenced Junit4 It looks like the tycho I upgrade to in a previous commit behaves differently -in regards to the junit selection when plugin.xml and pom.xml reference a different junit version- from the tycho I used before --- io.sloeber.tests/pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/io.sloeber.tests/pom.xml b/io.sloeber.tests/pom.xml index 94bb08cc..ab8868a7 100644 --- a/io.sloeber.tests/pom.xml +++ b/io.sloeber.tests/pom.xml @@ -13,9 +13,9 @@ - junit - junit - 4.13.1 + org.junit.jupiter + junit-jupiter-engine + 5.9.2 test From 2928d93c1f894394fb91bac776bedc06c5291155 Mon Sep 17 00:00:00 2001 From: jan Date: Fri, 13 Dec 2024 03:13:38 +0100 Subject: [PATCH 076/115] Clean should not try to remove the buildfolder As deleting the build folder causes issues. Centralised a method on the configuration so there is only one implementation --- .../IAutoBuildConfigurationDescription.java | 10 ++++++ .../providers/BuildRunnerForMake.java | 10 ++---- .../providers/InternalBuildRunner.java | 11 +------ .../AutoBuildConfigurationDescription.java | 31 +++++++++++++++---- .../src/io/sloeber/core/tools/Helpers.java | 11 ++----- 5 files changed, 40 insertions(+), 33 deletions(-) diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/api/IAutoBuildConfigurationDescription.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/api/IAutoBuildConfigurationDescription.java index 1208307b..81201733 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/api/IAutoBuildConfigurationDescription.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/api/IAutoBuildConfigurationDescription.java @@ -19,6 +19,8 @@ import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.IProgressMonitor; + import io.sloeber.autoBuild.buildTools.api.IBuildTools; import io.sloeber.autoBuild.buildTools.api.IBuildToolsManager.ToolFlavour; import io.sloeber.autoBuild.integration.AutoBuildConfigurationDescription; @@ -454,5 +456,13 @@ public static ICSourceEntry[] getResolvedSourceEntries(IAutoBuildConfigurationDe */ public LinkedHashMap getPostbuildSteps(); + /*** + * delete the build folder + * Not actually deletes the build folder but all the members. + * + * @param monitor + */ + void deleteBuildFolder(IProgressMonitor monitor); + } diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/BuildRunnerForMake.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/BuildRunnerForMake.java index 75216a1e..454f973f 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/BuildRunnerForMake.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/BuildRunnerForMake.java @@ -64,14 +64,8 @@ public class BuildRunnerForMake implements IBuildRunner { @Override public void invokeClean(int kind, IAutoBuildConfigurationDescription autoData, IMarkerGenerator markerGenerator, IConsole console, IProgressMonitor monitor) throws CoreException { - IFolder buildRoot = autoData.getBuildFolder(); - if(!buildRoot.exists()) { - return ; - } - //Do not delete the build folder as it may be in use with other processes (like discovery) - for(IResource curMember:buildRoot.members()) { - curMember.delete(true, monitor); - } + autoData.deleteBuildFolder(monitor); + //buildRoot.create(true, true, monitor); // for now I do not run make clean due to the fact rm -f is probably not on the system path //return invokeBuild(IncrementalProjectBuilder.CLEAN_BUILD, envp, autoData, markerGenerator, console, monitor); diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/InternalBuildRunner.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/InternalBuildRunner.java index 3cbd6570..b2b36666 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/InternalBuildRunner.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/InternalBuildRunner.java @@ -86,16 +86,7 @@ private static void createFolder(IFolder folder, boolean force, boolean local, I @Override public void invokeClean(int kind, IAutoBuildConfigurationDescription autoData, IMarkerGenerator markerGenerator, IConsole console, IProgressMonitor monitor) throws CoreException { - IFolder buildRoot = autoData.getBuildFolder(); - if(!buildRoot.exists()) { - return ; - } - //Do not delete the build folder as it may be in use with other processes (like discovery) - for(IResource curMember:buildRoot.members()) { - curMember.delete(true, monitor); - } -// buildRoot.delete(true, monitor); -// buildRoot.create(true, true, monitor); + autoData.deleteBuildFolder(monitor); return ; } diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildConfigurationDescription.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildConfigurationDescription.java index 8d7fe3a2..ad3105ec 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildConfigurationDescription.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildConfigurationDescription.java @@ -33,8 +33,10 @@ 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.NullProgressMonitor; import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; import org.osgi.framework.Bundle; import io.sloeber.autoBuild.api.AutoBuildCommon; @@ -1128,16 +1130,33 @@ public void forceCleanBeforeBuild() { myForceCleanBeforeBuild = true; } - public void forceFullBuildIfNeeded(IProgressMonitor monitor) throws CoreException { + public void forceFullBuildIfNeeded(IProgressMonitor monitor) { if (myForceCleanBeforeBuild) { myForceCleanBeforeBuild = false; - IFolder buildFolder = getBuildFolder(); - if (buildFolder != null && buildFolder.exists()) { - buildFolder.delete(true, monitor); - } - + deleteBuildFolder(monitor); } + } + @Override + public void deleteBuildFolder(IProgressMonitor monitor) { + //Do not delete the build folder as it may be in use with other processes (like discovery) + + IFolder buildFolder = getBuildFolder(); + if (buildFolder != null && buildFolder.exists()) { + try { + for(IResource curMember:buildFolder.members()) { + try { + curMember.delete(true, monitor); + } catch (CoreException e) { + Activator.log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + "Failed to delete member "+curMember.getName(), e)); + } + + } + } catch (CoreException e) { + e.printStackTrace(); + } + } } private static String getSpecFile(String languageId) { diff --git a/io.sloeber.core/src/io/sloeber/core/tools/Helpers.java b/io.sloeber.core/src/io/sloeber/core/tools/Helpers.java index 32199eac..f22805f8 100644 --- a/io.sloeber.core/src/io/sloeber/core/tools/Helpers.java +++ b/io.sloeber.core/src/io/sloeber/core/tools/Helpers.java @@ -23,6 +23,7 @@ import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Status; import org.eclipse.ui.console.ConsolePlugin; import org.eclipse.ui.console.IConsole; @@ -285,15 +286,7 @@ public static void deleteBuildFolder(IProject project, String cfgName) { IAutoBuildConfigurationDescription autoData = IAutoBuildConfigurationDescription .getConfig(cdtConfigurationDescription); - IFolder buildFolder = autoData.getBuildFolder(); - if (buildFolder.exists()) { - try { - buildFolder.delete(true, null); - } catch (CoreException e) { - Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, - Messages.Helpers_delete_folder_failed.replace(FOLDER, cfgName), e)); - } - } + autoData.deleteBuildFolder(new NullProgressMonitor()); } /** From 5c1adcfef2edba52eaf3ded0fc0762e5d0f5112e Mon Sep 17 00:00:00 2001 From: jan Date: Sat, 14 Dec 2024 16:32:33 +0100 Subject: [PATCH 077/115] Postbuild commands should not be run when the build failed #1699 --- .../providers/InternalBuildRunner.java | 53 ++++++++++--------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/InternalBuildRunner.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/InternalBuildRunner.java index b2b36666..1a483ad5 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/InternalBuildRunner.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/InternalBuildRunner.java @@ -135,6 +135,7 @@ public boolean invokeBuild(int kind, String targetName, IAutoBuildConfigurationD epm.deferDeDuplication(); int sequenceID = -1; boolean lastSequenceID = true; + myHasBuildError = false; // Run preBuildStep if existing LinkedHashMap preBuildSteps = autoData.getPrebuildSteps(); @@ -147,13 +148,14 @@ public boolean invokeBuild(int kind, String targetName, IAutoBuildConfigurationD } buildRunnerHelper.toConsole(command); if (launchCommand(command, autoData, monitor, buildRunnerHelper) != 0) { + myHasBuildError = true; if (autoData.stopOnFirstBuildError()) { return false; } } } - myHasBuildError = false; + do { sequenceID++; lastSequenceID = true; @@ -218,35 +220,36 @@ public boolean invokeBuild(int kind, String targetName, IAutoBuildConfigurationD lastSequenceID = true; } } while (!(lastSequenceID || myHasBuildError)); - // Run postBuildStep if existing - LinkedHashMap postBuildSteps = autoData.getPostbuildSteps(); - for (Entry step:postBuildSteps.entrySet()) { - String announcement = step.getKey(); - String command = step.getValue(); + // Run postBuildStep if existing and no error + if (!myHasBuildError) { + LinkedHashMap postBuildSteps = autoData.getPostbuildSteps(); + for (Entry step : postBuildSteps.entrySet()) { + String announcement = step.getKey(); + String command = step.getValue(); - if (!announcement.isEmpty()) { - buildRunnerHelper.toConsole(announcement); - } - buildRunnerHelper.toConsole(command); - if (launchCommand(command, autoData, monitor, buildRunnerHelper) != 0) { - if (autoData.stopOnFirstBuildError()) { - return false; + if (!announcement.isEmpty()) { + buildRunnerHelper.toConsole(announcement); + } + buildRunnerHelper.toConsole(command); + if (launchCommand(command, autoData, monitor, buildRunnerHelper) != 0) { + if (autoData.stopOnFirstBuildError()) { + return false; + } } } - } - - String postBuildStep = autoData.getPostbuildStep(); - postBuildStep = resolve(postBuildStep, EMPTY_STRING, WHITESPACE, autoData); - if (!postBuildStep.isEmpty()) { - String announcement = autoData.getPostBuildAnouncement(); - if (!announcement.isEmpty()) { - buildRunnerHelper.toConsole(announcement); - } - buildRunnerHelper.toConsole(postBuildStep); - if (launchCommand(postBuildStep, autoData, monitor, buildRunnerHelper) != 0) { - return false; + String postBuildStep = autoData.getPostbuildStep(); + postBuildStep = resolve(postBuildStep, EMPTY_STRING, WHITESPACE, autoData); + if (!postBuildStep.isEmpty()) { + String announcement = autoData.getPostBuildAnouncement(); + if (!announcement.isEmpty()) { + buildRunnerHelper.toConsole(announcement); + } + buildRunnerHelper.toConsole(postBuildStep); + if (launchCommand(postBuildStep, autoData, monitor, buildRunnerHelper) != 0) { + return false; + } } } } From 2ee679473bf17e485fe6bdf7d739e0b8ac2f553c Mon Sep 17 00:00:00 2001 From: jan Date: Sat, 14 Dec 2024 16:35:13 +0100 Subject: [PATCH 078/115] Do not recalculate languiage settings when none are available For optimisation Sloeber caches language settings. However when the command failed (a list of 0 settings) Sloeber recalculated these settings. This was done to allow to refresh the cache however it does cause a performance hit. Therefore I opted to accept no results as a valid result untill a better method is found. --- .../providers/AutoBuildLanguageSettingsProvider.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/AutoBuildLanguageSettingsProvider.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/AutoBuildLanguageSettingsProvider.java index f7d86012..1c17bb1a 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/AutoBuildLanguageSettingsProvider.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/AutoBuildLanguageSettingsProvider.java @@ -253,8 +253,9 @@ public List getSettingEntries(ICConfigurationDescription if (discoveryCommand == null || discoveryCommand.isBlank()) { return LanguageSettingsStorage.getPooledList(list); } + discoveryCommand=discoveryCommand.trim(); List cachedList=myDiscoveryCache.get(discoveryCommand); - if ( cachedList== null || cachedList.size()==0 ) { + if ( cachedList== null ) { myDiscoveryCache.put(discoveryCommand, runForLanguage(languageId, discoveryCommand, autoConf, autoConf.getProject(), new NullProgressMonitor())); } From 0e9e0a63b63d282430c75c1c7c1bb0501e2ce565 Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 15 Dec 2024 00:40:32 +0100 Subject: [PATCH 079/115] Exclude library Applications folders from the build #1701 --- io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java b/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java index ad694ee7..4f705b6a 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java +++ b/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java @@ -447,7 +447,7 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { IPath path = newProjectHandle.getFolder(rootCodeFolder).getFullPath(); newSourceEntries[0] = new CSourceEntry(path, null, ICSettingEntry.RESOLVED); } - IPath excludes[] = new IPath[8]; + IPath excludes[] = new IPath[9]; excludes[0] = IPath.fromOSString("**/*.ino"); //$NON-NLS-1$ excludes[1] = IPath.fromOSString("libraries/?*/**/doc*/**"); //$NON-NLS-1$ excludes[2] = IPath.fromOSString("libraries/?*/**/?xamples/**"); //$NON-NLS-1$ @@ -456,6 +456,8 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { excludes[5] = IPath.fromOSString("libraries/?*/**/third-party/**"); //$NON-NLS-1$ excludes[6] = IPath.fromOSString("libraries/**/._*"); //$NON-NLS-1$ excludes[7] = IPath.fromOSString("libraries/?*/utility/*/*"); //$NON-NLS-1$ + excludes[8] = IPath.fromOSString("libraries/?*/Applications/**"); //$NON-NLS-1$ + /* * CDT currently causes issues with ${ConfigName] From 20c5445f8059bbdd66e82b1eeacb836f037fb20f Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 15 Dec 2024 00:46:51 +0100 Subject: [PATCH 080/115] fix warning unused import --- .../autoBuild/extensionPoint/providers/InternalBuildRunner.java | 1 - 1 file changed, 1 deletion(-) diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/InternalBuildRunner.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/InternalBuildRunner.java index 1a483ad5..3f88bc86 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/InternalBuildRunner.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/InternalBuildRunner.java @@ -44,7 +44,6 @@ import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; From e636455b150be6b0c1926ea06ee39eff9b09e6e2 Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 15 Dec 2024 18:03:54 +0100 Subject: [PATCH 081/115] ESP32 library examples now show #1685 --- .../src/io/sloeber/arduinoFramework/api/LibraryManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java index 243641f9..43a3ecd4 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java @@ -265,7 +265,7 @@ public static TreeMap getExamplesLibrary(BoardDescription boar Map installedLibs = getLibrariesAll(boardDescriptor); for (IArduinoLibraryVersion curLib : installedLibs.values()) { - examples.putAll(getExamplesFromFolder(curLib, curLib.getExamplePath().toFile(), 2)); + examples.putAll(getExamplesFromFolder(curLib, curLib.getExamplePath().toFile(), 3)); } return examples; From 693783887ff84fac74b310751b3e938bbc2c4c3d Mon Sep 17 00:00:00 2001 From: jan Date: Mon, 16 Dec 2024 03:51:03 +0100 Subject: [PATCH 082/115] I had vergotten that in the previous commit --- io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java b/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java index 4f705b6a..3451a9fd 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java +++ b/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java @@ -182,7 +182,7 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { excludes[1] = project.getFolder(SLOEBER_ARDUINO_FOLDER_NAME).getProjectRelativePath() .append(SOURCE_ENTRY_FILTER_ALL); newSourceEntries[0] = new CSourceEntry(project.getFullPath(), excludes, ICSettingEntry.RESOLVED); - IPath excludes2[] = new IPath[8]; + IPath excludes2[] = new IPath[9]; excludes2[0] = IPath.fromOSString("**/*.ino"); //$NON-NLS-1$ excludes2[1] = IPath.fromOSString("libraries/?*/**/doc*/**"); //$NON-NLS-1$ excludes2[2] = IPath.fromOSString("libraries/?*/**/?xamples/**"); //$NON-NLS-1$ @@ -191,6 +191,7 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { excludes2[5] = IPath.fromOSString("libraries/?*/**/third-party/**"); //$NON-NLS-1$ excludes2[6] = IPath.fromOSString("libraries/**/._*"); //$NON-NLS-1$ excludes2[7] = IPath.fromOSString("libraries/?*/utility/*/*"); //$NON-NLS-1$ + excludes2[8] = IPath.fromOSString("libraries/?*/Applications/**"); //$NON-NLS-1$ /* * CDT currently causes issues with ${ConfigName] From 7045778e7782603524c22f0bfe8c669484626357 Mon Sep 17 00:00:00 2001 From: jan Date: Thu, 19 Dec 2024 19:20:56 +0100 Subject: [PATCH 083/115] add the subfolders to the FQN of the example #1685 Without this subfolder one can create 2 instances with the same FQN and the gui does not show the three structure --- io.sloeber.core/src/io/sloeber/core/internal/Example.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io.sloeber.core/src/io/sloeber/core/internal/Example.java b/io.sloeber.core/src/io/sloeber/core/internal/Example.java index b1b3a002..ba0e8c09 100644 --- a/io.sloeber.core/src/io/sloeber/core/internal/Example.java +++ b/io.sloeber.core/src/io/sloeber/core/internal/Example.java @@ -45,7 +45,7 @@ private void calculateFQN() { } } else { for (IArduinoLibraryVersion myLib : myLibs.values()) { - myFQN = myLib.getFQN().append(getName()); + myFQN = myLib.getFQN().append( myExampleLocation.makeRelativeTo(myLib.getExamplePath())); } } } From 85cb0abda34d7bb7bc9b62f6f4c12728dfb31f94 Mon Sep 17 00:00:00 2001 From: jan Date: Thu, 19 Dec 2024 19:25:25 +0100 Subject: [PATCH 084/115] because of #1685 issue1126LibArchiver selects an failing example Basically #1685 also applies for the library HID-Project which is used in the test issue1126LibArchiver. issue1126LibArchiver just selects a "random" example witch due to the change in #1685 always is a example that does not compile out of the box. This change selects the first found example instead of the last found example. --- io.sloeber.tests/src/io/sloeber/core/BuildTests.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java index 962330d5..1ec7fc3e 100644 --- a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java +++ b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java @@ -708,6 +708,9 @@ public void issue1126LibArchiver() throws Exception { break; } } + if(example!=null) { + break; + } } assertNotNull( lib,"HID Lib \"" + HIDlibName + "\" Not found"); From 0ceeb4ea1225a31c75cfb8a1fea952ae8521a6ed Mon Sep 17 00:00:00 2001 From: jan Date: Fri, 20 Dec 2024 01:39:35 +0100 Subject: [PATCH 085/115] fix for #1680 --- .../io/sloeber/ui/project/properties/BoardSelectionPage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io.sloeber.ui/src/io/sloeber/ui/project/properties/BoardSelectionPage.java b/io.sloeber.ui/src/io/sloeber/ui/project/properties/BoardSelectionPage.java index 5b1f6446..e2e29ab1 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/project/properties/BoardSelectionPage.java +++ b/io.sloeber.ui/src/io/sloeber/ui/project/properties/BoardSelectionPage.java @@ -289,7 +289,7 @@ protected void updateScreen(boolean updateData) { } myControlUploadPort.setText(myBoardDesc.getUploadPort()); - setTheLabelCombos(boardsFileChanged || boardIDChanged); + setTheLabelCombos(boardsFileChanged || boardIDChanged ||updateData); disableListeners = false; } From f68cc28d4a362d6474c0691a16508e5049d6f95e Mon Sep 17 00:00:00 2001 From: jan Date: Sat, 21 Dec 2024 00:43:40 +0100 Subject: [PATCH 086/115] Add some documentaion for reAttachLibraries --- .../src/io/sloeber/core/api/ISloeberConfiguration.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/io.sloeber.core/src/io/sloeber/core/api/ISloeberConfiguration.java b/io.sloeber.core/src/io/sloeber/core/api/ISloeberConfiguration.java index 502495ff..74578119 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/ISloeberConfiguration.java +++ b/io.sloeber.core/src/io/sloeber/core/api/ISloeberConfiguration.java @@ -118,6 +118,14 @@ public static ISloeberConfiguration getConfig(IAutoBuildConfigurationDescription public boolean addLibraries(Collection librariesToAdd); public boolean removeLibraries(Collection librariesToRemove); + /** + * Implements the action under Menu->Sloeber->Re attache libraries + * This method validates the attached libraries + * 1) Links to hardware libraries that point to a hardware different than the one used in + * the boardDescription of this configuration are replaced by versions from + * this boardDescription and if none is found are removed. + * 2) Links to non existing libraries are removed + */ void reAttachLibraries(); Map getUsedLibraries(); From 89c7a425e910ff60bf2fce32172c26af47faef9a Mon Sep 17 00:00:00 2001 From: jan Date: Sat, 21 Dec 2024 00:44:44 +0100 Subject: [PATCH 087/115] make getLibrariesHarware public This because it is needed to reattach to the correct libs when changing the hardware --- .../src/io/sloeber/arduinoFramework/api/LibraryManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java index 43a3ecd4..bac7c518 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java @@ -439,7 +439,7 @@ private static Map getLibrariesFromFolder(IPath * @param project the project to find all hardware libraries for * @return all the library folder names. May contain empty values. */ - private static Map getLibrariesHarware(BoardDescription boardDescriptor) { + public static Map getLibrariesHarware(BoardDescription boardDescriptor) { Map ret = new HashMap<>(); // first add the referenced IPath libPath = boardDescriptor.getReferencedCoreLibraryPath(); From c95305e22ac3be24cdaed0d4a9e3c09fdffb9daf Mon Sep 17 00:00:00 2001 From: jan Date: Sat, 21 Dec 2024 00:46:15 +0100 Subject: [PATCH 088/115] Change the linked board libs on board changes #1679 --- .../io/sloeber/core/api/CodeDescription.java | 2 +- .../core/internal/SloeberConfiguration.java | 105 +++++++++++++----- .../src/io/sloeber/core/tools/Helpers.java | 66 +++++------ 3 files changed, 110 insertions(+), 63 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/core/api/CodeDescription.java b/io.sloeber.core/src/io/sloeber/core/api/CodeDescription.java index 9e464e6c..509944e6 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/CodeDescription.java +++ b/io.sloeber.core/src/io/sloeber/core/api/CodeDescription.java @@ -369,7 +369,7 @@ public boolean createFiles(IContainer scrContainer, IProgressMonitor monitor) { if (myMakeLinks) { if(scrContainer instanceof IFolder) { IFolder folder=(IFolder)scrContainer; - Helpers.linkDirectory( curPath, folder); + Helpers.LinkFolderToFolder( curPath, folder); } else { Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, diff --git a/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java b/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java index 7340e0d6..604e274b 100644 --- a/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java +++ b/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java @@ -52,6 +52,7 @@ public class SloeberConfiguration extends AutoBuildConfigurationExtensionDescrip private BoardDescription myBoardDescription; private OtherDescription myOtherDesc; private CompileDescription myCompileDescription; + //a map of foldername library private Map myLibraries = new HashMap<>(); // operational data @@ -92,7 +93,7 @@ public SloeberConfiguration(AutoBuildConfigurationDescription owner, setBoardDescription(src.getBoardDescription()); setOtherDescription(src.getOtherDescription()); setCompileDescription(src.getCompileDescription()); - myLibraries = src.myLibraries; + myLibraries = src.getLibrariesFromLinks(); } public SloeberConfiguration(BoardDescription boardDesc, OtherDescription otherDesc, @@ -518,27 +519,32 @@ public Set getIncludeFolders() { * @return * @throws CoreException */ - private Map getLibrariesFromLinks() throws CoreException { + private Map getLibrariesFromLinks() { Map ret = new HashMap<>(); IFolder libFolder = getArduinoLibraryFolder(); if(!libFolder.exists()) { return ret; } - for (IResource curResource : libFolder.members()) { - if (curResource instanceof IFolder) { - IFolder curFolder = (IFolder) curResource; - IArduinoLibraryVersion curLib = myLibraries.get(curFolder.getName()); - if (curLib != null){ - //We knbow the lib so it is ok - ret.put(curLib.getName(),curLib); - continue; - } + try { + for (IResource curResource : libFolder.members()) { + if (curResource instanceof IFolder) { + IFolder curFolder = (IFolder) curResource; + IArduinoLibraryVersion curLib = myLibraries.get(curFolder.getName()); + if (curLib != null){ + //We knbow the lib so it is ok + ret.put(curLib.getName(),curLib); + continue; + } - curLib=LibraryManager.getLibraryVersionFromLocation(curFolder,getBoardDescription()); - if (curLib != null){ - ret.put(curLib.getName(),curLib); + curLib=LibraryManager.getLibraryVersionFromLocation(curFolder,getBoardDescription()); + if (curLib != null){ + ret.put(curLib.getName(),curLib); + } } } + } catch (CoreException e) { + e.printStackTrace(); + ret.putAll(myLibraries); } return ret; } @@ -572,15 +578,58 @@ private void linkLibrariesToFolder() { IFolder libFolder = getArduinoLibraryFolder(); for (IArduinoLibraryVersion curLib : myLibraries.values()) { IFolder curLibFolder=libFolder.getFolder(curLib.getName()); - Helpers.linkDirectory(curLib.getInstallPath(), curLibFolder); + Helpers.LinkFolderToFolder(curLib.getInstallPath(), curLibFolder); } } + /** + * For libraries of with FQN Libraries/hardware/X make sure that the location + * point to a valid location of the given boardDescriptor + */ + private void upDateHardwareLibraries() { + //make sure the libraries that link to hardware are the correct ones + BoardDescription boardDesc=getBoardDescription(); + IPath referenceLibPath =boardDesc.getReferencedCoreLibraryPath(); + IPath referencingLibPath =boardDesc.getReferencingLibraryPath(); + if(referencingLibPath==null) { + referencingLibPath=referenceLibPath; + } + Set hardwareLibsFQN=new HashSet<>(); + for(IArduinoLibraryVersion curLib:myLibraries.values()) { + if(curLib.isHardwareLib()) { + IPath libPath=curLib.getInstallPath(); + if(referencingLibPath.isPrefixOf(libPath)||referenceLibPath.isPrefixOf(libPath)) { + //the hardware lib is ok + continue; + } + // The hardware lib is for a different hardware. + //add it to the lists to reattach + hardwareLibsFQN.add(curLib.getFQN().toPortableString()); + } + } + if(!hardwareLibsFQN.isEmpty()) { + Map boardLibs =LibraryManager.getLibrariesHarware(boardDesc); + for(String curReplaceLibFQN: hardwareLibsFQN) { + IArduinoLibraryVersion newLib =boardLibs.get(curReplaceLibFQN); + if(newLib!=null) { + // a library with the same name was found so use this one + myLibraries.put(newLib.getName(), newLib); + }else { + //no new library was found remove the old lib + myLibraries.remove(Path.fromPortableString(curReplaceLibFQN).lastSegment()); + } + } + } + } + @Override public void reAttachLibraries() { + upDateHardwareLibraries(); + + IProgressMonitor monitor = new NullProgressMonitor(); IFolder libFolder = getArduinoLibraryFolder(); // Remove all existing lib folders that are not known or are linking to the @@ -617,11 +666,7 @@ public void reAttachLibraries() { @Override public Map getUsedLibraries() { - try { - myLibraries=getLibrariesFromLinks(); - } catch (CoreException e) { - e.printStackTrace(); - } + myLibraries=getLibrariesFromLinks(); return new HashMap<>(myLibraries); } @@ -716,22 +761,23 @@ public LinkedHashMap getPostbuildSteps() { /** * Because SloeberConfiguration are copied and can be changed at all times - * but there is only 1 disk representation we can not update the disk + * but there is only 1 disk representation of each config + * we can not update the disk * at the time the SloeberConfiguration is changed * * When a configuration becomes "the active" configuration this method will - * create the necessary resources on disk. + * create/update the necessary resources on disk. * This apply is when this configuration is new. */ public void aboutToApplyConfigChange() { - try { - myLibraries =getLibrariesFromLinks(); - } catch (CoreException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } +// try { +// myLibraries =getLibrariesFromLinks(); +// } catch (CoreException e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } if(updateSourceEntries()) { - //the config hasbeen renamed; + //the config has been renamed; // remove the library links removeLibraryLinks(); } @@ -741,6 +787,7 @@ public void aboutToApplyConfigChange() { public void appliedConfigChange() { LinkToCore(); + upDateHardwareLibraries(); linkLibrariesToFolder(); } diff --git a/io.sloeber.core/src/io/sloeber/core/tools/Helpers.java b/io.sloeber.core/src/io/sloeber/core/tools/Helpers.java index f22805f8..f5563ed1 100644 --- a/io.sloeber.core/src/io/sloeber/core/tools/Helpers.java +++ b/io.sloeber.core/src/io/sloeber/core/tools/Helpers.java @@ -289,38 +289,38 @@ public static void deleteBuildFolder(IProject project, String cfgName) { autoData.deleteBuildFolder(new NullProgressMonitor()); } - /** - * creates links to the root files and folders of the source location - * - * @param source - * the location where the files are that need to be linked to - * @param target - * the location where the links are to be created - */ - public static void linkDirectory(IPath source, IFolder target) { - - File[] sourceFiles = source.toFile().listFiles(); - if (sourceFiles == null) { - if (!myHasBeenLogged) { - Activator.log(new Status(IStatus.INFO, CORE_PLUGIN_ID, - Messages.Helpers_error_link_folder_is_empty.replace(FILE, source.toOSString()), null)); - myHasBeenLogged = true; - } - return; - } - for (File curFile : sourceFiles) { - if (curFile.isDirectory()) { - LinkFolderToFolder(source.append(curFile.getName()), target.getFolder(curFile.getName())); - } else { - try { - target.getFile(curFile.getName()).createLink(source.append(curFile.getName()), - IResource.REPLACE | IResource.ALLOW_MISSING_LOCAL, null); - } catch (CoreException e) { - e.printStackTrace(); - } - } - } - - } +// /** +// * creates links to the root files and folders of the source location +// * +// * @param source +// * the location where the files are that need to be linked to +// * @param target +// * the location where the links are to be created +// */ +// public static void linkDirectory(IPath source, IFolder target) { +// +// File[] sourceFiles = source.toFile().listFiles(); +// if (sourceFiles == null) { +// if (!myHasBeenLogged) { +// Activator.log(new Status(IStatus.INFO, CORE_PLUGIN_ID, +// Messages.Helpers_error_link_folder_is_empty.replace(FILE, source.toOSString()), null)); +// myHasBeenLogged = true; +// } +// return; +// } +// for (File curFile : sourceFiles) { +// if (curFile.isDirectory()) { +// LinkFolderToFolder(source.append(curFile.getName()), target.getFolder(curFile.getName())); +// } else { +// try { +// target.getFile(curFile.getName()).createLink(source.append(curFile.getName()), +// IResource.REPLACE | IResource.ALLOW_MISSING_LOCAL, null); +// } catch (CoreException e) { +// e.printStackTrace(); +// } +// } +// } +// +// } } From be9322d9f325fd25d6718ffec5d5b84be929ce5c Mon Sep 17 00:00:00 2001 From: jan Date: Sat, 21 Dec 2024 00:48:04 +0100 Subject: [PATCH 089/115] fix warning/remove dead code/format (no real code change) --- .../src/io/sloeber/core/tools/Helpers.java | 401 +++++++----------- 1 file changed, 149 insertions(+), 252 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/core/tools/Helpers.java b/io.sloeber.core/src/io/sloeber/core/tools/Helpers.java index f5563ed1..1283b9a1 100644 --- a/io.sloeber.core/src/io/sloeber/core/tools/Helpers.java +++ b/io.sloeber.core/src/io/sloeber/core/tools/Helpers.java @@ -2,7 +2,6 @@ import static io.sloeber.core.api.Const.*; -import java.io.File; import java.io.InputStream; import java.net.URI; import org.eclipse.cdt.core.CCorePlugin; @@ -42,176 +41,110 @@ */ public class Helpers { - private static final String FILE = Messages.FILE_TAG; - private static final String FOLDER = Messages.FOLDER_TAG; - - private static boolean myHasBeenLogged = false; - - /** - * This method is the internal working class that adds the provided include - * paths to the configuration for all languages. - * - * @param configurationDescription - * The configuration description of the project to add it to - * @param IncludePath - * The path to add to the include folders - * @param isWorkspacePath - * is this path in the workspace - * - * @return true if the configuration description has changed - * (setprojectdescription is needed to make the changes effective) - */ -// public static boolean addIncludeFolder(ICConfigurationDescription configurationDescription, -// List IncludePaths, boolean isWorkspacePath) { -// -// boolean confDesckMustBeSet = false; -// if (IncludePaths == null) { -// return false; -// } -// ICLanguageSetting[] languageSettings = configurationDescription.getRootFolderDescription() -// .getLanguageSettings(); -// int pathSetting = ICSettingEntry.VALUE_WORKSPACE_PATH; -// if (!isWorkspacePath) { -// pathSetting = 0; -// } -// -// // Add include path to all languages -// for (int idx = 0; idx < languageSettings.length; idx++) { -// ICLanguageSetting lang = languageSettings[idx]; -// String LangID = lang.getLanguageId(); -// if (LangID != null) { -// if (LangID.startsWith("org.eclipse.cdt.")) { //$NON-NLS-1$ -// ICLanguageSettingEntry[] OrgIncludeEntries = lang.getSettingEntries(ICSettingEntry.INCLUDE_PATH); -// HashSet toAddPaths = new HashSet<>(IncludePaths); -// for (ICLanguageSettingEntry curLangSetting : OrgIncludeEntries) { -// CIncludePathEntry curIncludePathEntry = (CIncludePathEntry) curLangSetting; -// toAddPaths.remove(curIncludePathEntry.getFullPath()); -// } -// if (!toAddPaths.isEmpty()) { -// confDesckMustBeSet = true; -// ICLanguageSettingEntry[] IncludeEntries = new ICLanguageSettingEntry[OrgIncludeEntries.length -// + toAddPaths.size()]; -// System.arraycopy(OrgIncludeEntries, 0, IncludeEntries, 0, OrgIncludeEntries.length); -// int startPointer = OrgIncludeEntries.length; -// for (IPath curPath : toAddPaths) { -// IncludeEntries[startPointer++] = new CIncludePathEntry(curPath, pathSetting); -// } -// lang.setSettingEntries(ICSettingEntry.INCLUDE_PATH, IncludeEntries); -// } -// } -// } -// } -// return confDesckMustBeSet; -// } - - /** - * Removes include folders that are not valid. This method does not save the - * configurationDescription description - * - * @param configurationDescription - * a writable project description the configuration that is checked - * @return true is a include path has been removed. False if the include path - * remains unchanged. If true the projectdescription must be set - */ - public static boolean removeInvalidIncludeFolders(ICConfigurationDescription configurationDescription) { - // find all languages - ICFolderDescription folderDescription = configurationDescription.getRootFolderDescription(); - ICLanguageSetting[] languageSettings = folderDescription.getLanguageSettings(); - boolean hasChange = false; - // Add include path to all languages - for (int idx = 0; idx < languageSettings.length; idx++) { - ICLanguageSetting lang = languageSettings[idx]; - String LangID = lang.getLanguageId(); - if (LangID != null) { - if (LangID.startsWith("org.eclipse.cdt.")) { //$NON-NLS-1$ - ICLanguageSettingEntry[] OrgIncludeEntries = lang.getSettingEntries(ICSettingEntry.INCLUDE_PATH); - ICLanguageSettingEntry[] OrgIncludeEntriesFull = lang - .getResolvedSettingEntries(ICSettingEntry.INCLUDE_PATH); - int copiedEntry = 0; - for (int curEntry = 0; curEntry < OrgIncludeEntries.length; curEntry++) { - IPath cusPath = ((CIncludePathEntry) OrgIncludeEntriesFull[curEntry]).getFullPath(); - if ((ResourcesPlugin.getWorkspace().getRoot().exists(cusPath)) - || (((CIncludePathEntry) OrgIncludeEntries[curEntry]).isBuiltIn())) { - OrgIncludeEntries[copiedEntry++] = OrgIncludeEntries[curEntry]; - } else { - Activator.log(new Status(IStatus.WARNING, CORE_PLUGIN_ID, "Removed invalid include path" + cusPath, //$NON-NLS-1$ - null)); - } - } - if (copiedEntry != OrgIncludeEntries.length) // do not save - // if nothing - // has changed - { - ICLanguageSettingEntry[] IncludeEntries = new ICLanguageSettingEntry[copiedEntry]; - System.arraycopy(OrgIncludeEntries, 0, IncludeEntries, 0, copiedEntry); - lang.setSettingEntries(ICSettingEntry.INCLUDE_PATH, IncludeEntries); - hasChange = true; - - } - } - } - } - return hasChange; - } - - /** - * Creates a folder and links the folder to an existing folder Parent folders of - * the target folder are created if needed. In case this method fails an error - * is logged. - * - * @param project - * the project the newly created folder will belong to - * @param projectFolder - * the folder name relative to the project - * @param source - * the fully qualified name of the folder to link to - */ - public static void LinkFolderToFolder(IPath source, IFolder projectFolder) { - IProject project = projectFolder.getProject(); - - // create target parent folder and grandparents - IPath ParentFolders = projectFolder.getProjectRelativePath().removeLastSegments(1); - for (int curfolder = ParentFolders.segmentCount() - 1; curfolder >= 0; curfolder--) { - try { - createNewFolder(project.getFolder(ParentFolders.removeLastSegments(curfolder)), null); - } catch (@SuppressWarnings("unused") CoreException e) { - // ignore this error as the parent - // folders may have been created yet - } - } + private static final String FOLDER = Messages.FOLDER_TAG; + + /** + * Removes include folders that are not valid. This method does not save the + * configurationDescription description + * + * @param configurationDescription a writable project description the + * configuration that is checked + * @return true is a include path has been removed. False if the include path + * remains unchanged. If true the projectdescription must be set + */ + public static boolean removeInvalidIncludeFolders(ICConfigurationDescription configurationDescription) { + // find all languages + ICFolderDescription folderDescription = configurationDescription.getRootFolderDescription(); + ICLanguageSetting[] languageSettings = folderDescription.getLanguageSettings(); + boolean hasChange = false; + // Add include path to all languages + for (int idx = 0; idx < languageSettings.length; idx++) { + ICLanguageSetting lang = languageSettings[idx]; + String LangID = lang.getLanguageId(); + if (LangID != null) { + if (LangID.startsWith("org.eclipse.cdt.")) { //$NON-NLS-1$ + ICLanguageSettingEntry[] OrgIncludeEntries = lang.getSettingEntries(ICSettingEntry.INCLUDE_PATH); + ICLanguageSettingEntry[] OrgIncludeEntriesFull = lang + .getResolvedSettingEntries(ICSettingEntry.INCLUDE_PATH); + int copiedEntry = 0; + for (int curEntry = 0; curEntry < OrgIncludeEntries.length; curEntry++) { + IPath cusPath = ((CIncludePathEntry) OrgIncludeEntriesFull[curEntry]).getFullPath(); + if ((ResourcesPlugin.getWorkspace().getRoot().exists(cusPath)) + || (((CIncludePathEntry) OrgIncludeEntries[curEntry]).isBuiltIn())) { + OrgIncludeEntries[copiedEntry++] = OrgIncludeEntries[curEntry]; + } else { + Activator.log(new Status(IStatus.WARNING, CORE_PLUGIN_ID, + "Removed invalid include path" + cusPath, //$NON-NLS-1$ + null)); + } + } + if (copiedEntry != OrgIncludeEntries.length) // do not save + // if nothing + // has changed + { + ICLanguageSettingEntry[] IncludeEntries = new ICLanguageSettingEntry[copiedEntry]; + System.arraycopy(OrgIncludeEntries, 0, IncludeEntries, 0, copiedEntry); + lang.setSettingEntries(ICSettingEntry.INCLUDE_PATH, IncludeEntries); + hasChange = true; + + } + } + } + } + return hasChange; + } - // create the actual link - try { - createNewFolder(projectFolder, source); - } catch (CoreException e) { - Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, - Messages.Helpers_Create_folder_failed.replace(FOLDER, projectFolder.toString()), e)); - } - } + /** + * Creates a folder and links the folder to an existing folder Parent folders of + * the target folder are created if needed. In case this method fails an error + * is logged. + * + * @param project the project the newly created folder will belong to + * @param projectFolder the folder name relative to the project + * @param source the fully qualified name of the folder to link to + */ + public static void LinkFolderToFolder(IPath source, IFolder projectFolder) { + IProject project = projectFolder.getProject(); + + // create target parent folder and grandparents + IPath ParentFolders = projectFolder.getProjectRelativePath().removeLastSegments(1); + for (int curfolder = ParentFolders.segmentCount() - 1; curfolder >= 0; curfolder--) { + try { + createNewFolder(project.getFolder(ParentFolders.removeLastSegments(curfolder)), null); + } catch (@SuppressWarnings("unused") CoreException e) { + // ignore this error as the parent + // folders may have been created yet + } + } + // create the actual link + try { + createNewFolder(projectFolder, source); + } catch (CoreException e) { + Activator.log(new Status(IStatus.ERROR, CORE_PLUGIN_ID, + Messages.Helpers_Create_folder_failed.replace(FOLDER, projectFolder.toString()), e)); + } + } - public static void removeCodeFolder(IFolder deleteFolder) { - if (deleteFolder.exists()) { - try { - deleteFolder.delete(true, null); - } catch (CoreException e) { - e.printStackTrace(); - } - } - } + public static void removeCodeFolder(IFolder deleteFolder) { + if (deleteFolder.exists()) { + try { + deleteFolder.delete(true, null); + } catch (CoreException e) { + e.printStackTrace(); + } + } + } - /** - * This method adds the content of a content stream to a file If the file - * already exist the file remains untouched - * - * @param file - * The file to create - * @param contentStream - * The stream to put in the file - * @param monitor - * A monitor to show progress - * @throws CoreException - */ + /** + * This method adds the content of a content stream to a file If the file + * already exist the file remains untouched + * + * @param file The file to create + * @param contentStream The stream to put in the file + * @param monitor A monitor to show progress + * @throws CoreException + */ public static IFile addFileToProject(IFile file, InputStream contentStream, IProgressMonitor monitor, boolean overwrite) throws CoreException { file.refreshLocal(IResource.DEPTH_INFINITE, monitor); @@ -224,8 +157,7 @@ public static IFile addFileToProject(IFile file, InputStream contentStream, IPro if (!file.exists() && (contentStream != null)) { IPath filePath = file.getProjectRelativePath(); - for (int curSegment = 0;curSegment < (filePath.segmentCount()-1);curSegment++) - { + for (int curSegment = 0; curSegment < (filePath.segmentCount() - 1); curSegment++) { IFolder curFolder = file.getProject().getFolder(filePath.segment(curSegment)); if (!curFolder.exists()) { curFolder.create(true, false, monitor); @@ -236,91 +168,56 @@ public static IFile addFileToProject(IFile file, InputStream contentStream, IPro return file; } - public static MessageConsole findConsole(String name) { - ConsolePlugin plugin = ConsolePlugin.getDefault(); - IConsoleManager conMan = plugin.getConsoleManager(); - IConsole[] existing = conMan.getConsoles(); - for (int i = 0; i < existing.length; i++) - if (name.equals(existing[i].getName())) - return (MessageConsole) existing[i]; - // no console found, so create a new one - MessageConsole myConsole = new MessageConsole(name, null); - conMan.addConsoles(new IConsole[] { myConsole }); - return myConsole; - } - - /** - * Creates a new folder resource as a link or local - * - * @param newFolder - * the new folder to create (can contain subfolders) - * @param linklocation - * if null a local folder is created using newFolderName if not null - * a link folder is created with the name newFolderName and pointing - * to linklocation - * - * @return nothing - * @throws CoreException - */ - public static void createNewFolder(IFolder newFolder, IPath linklocation) throws CoreException { - if (linklocation != null) { - URI relativeLinklocation = newFolder.getProject().getPathVariableManager() - .convertToRelative(URIUtil.toURI(linklocation), false, null); - newFolder.createLink(relativeLinklocation, IResource.REPLACE | IResource.ALLOW_MISSING_LOCAL |IResource.BACKGROUND_REFRESH , null); - } else { - newFolder.create(0, false, null); - } - - } + public static MessageConsole findConsole(String name) { + ConsolePlugin plugin = ConsolePlugin.getDefault(); + IConsoleManager conMan = plugin.getConsoleManager(); + IConsole[] existing = conMan.getConsoles(); + for (int i = 0; i < existing.length; i++) + if (name.equals(existing[i].getName())) + return (MessageConsole) existing[i]; + // no console found, so create a new one + MessageConsole myConsole = new MessageConsole(name, null); + conMan.addConsoles(new IConsole[] { myConsole }); + return myConsole; + } - /** - * Set the project to force a rebuild. This method is called after the arduino - * settings have been updated. Note the only way I found I could get this to - * work is by deleting the build folder - * - * @param project - */ - public static void deleteBuildFolder(IProject project, String cfgName) { - ICProjectDescription cdtProjectDescription = CCorePlugin.getDefault().getProjectDescription(project, false); - ICConfigurationDescription cdtConfigurationDescription = cdtProjectDescription.getConfigurationByName(cfgName); - IAutoBuildConfigurationDescription autoData = IAutoBuildConfigurationDescription - .getConfig(cdtConfigurationDescription); + /** + * Creates a new folder resource as a link or local + * + * @param newFolder the new folder to create (can contain subfolders) + * @param linklocation if null a local folder is created using newFolderName if + * not null a link folder is created with the name + * newFolderName and pointing to linklocation + * + * @return nothing + * @throws CoreException + */ + public static void createNewFolder(IFolder newFolder, IPath linklocation) throws CoreException { + if (linklocation != null) { + URI relativeLinklocation = newFolder.getProject().getPathVariableManager() + .convertToRelative(URIUtil.toURI(linklocation), false, null); + newFolder.createLink(relativeLinklocation, + IResource.REPLACE | IResource.ALLOW_MISSING_LOCAL | IResource.BACKGROUND_REFRESH, null); + } else { + newFolder.create(0, false, null); + } - autoData.deleteBuildFolder(new NullProgressMonitor()); - } + } -// /** -// * creates links to the root files and folders of the source location -// * -// * @param source -// * the location where the files are that need to be linked to -// * @param target -// * the location where the links are to be created -// */ -// public static void linkDirectory(IPath source, IFolder target) { -// -// File[] sourceFiles = source.toFile().listFiles(); -// if (sourceFiles == null) { -// if (!myHasBeenLogged) { -// Activator.log(new Status(IStatus.INFO, CORE_PLUGIN_ID, -// Messages.Helpers_error_link_folder_is_empty.replace(FILE, source.toOSString()), null)); -// myHasBeenLogged = true; -// } -// return; -// } -// for (File curFile : sourceFiles) { -// if (curFile.isDirectory()) { -// LinkFolderToFolder(source.append(curFile.getName()), target.getFolder(curFile.getName())); -// } else { -// try { -// target.getFile(curFile.getName()).createLink(source.append(curFile.getName()), -// IResource.REPLACE | IResource.ALLOW_MISSING_LOCAL, null); -// } catch (CoreException e) { -// e.printStackTrace(); -// } -// } -// } -// -// } + /** + * Set the project to force a rebuild. This method is called after the arduino + * settings have been updated. Note the only way I found I could get this to + * work is by deleting the build folder + * + * @param project + */ + public static void deleteBuildFolder(IProject project, String cfgName) { + ICProjectDescription cdtProjectDescription = CCorePlugin.getDefault().getProjectDescription(project, false); + ICConfigurationDescription cdtConfigurationDescription = cdtProjectDescription.getConfigurationByName(cfgName); + IAutoBuildConfigurationDescription autoData = IAutoBuildConfigurationDescription + .getConfig(cdtConfigurationDescription); + + autoData.deleteBuildFolder(new NullProgressMonitor()); + } } From ec20b8052af3cc5c1531701a3f31ee3c255a7b10 Mon Sep 17 00:00:00 2001 From: jan Date: Sat, 21 Dec 2024 02:14:05 +0100 Subject: [PATCH 090/115] fix warnings --- .../autoBuild/ui/tabs/ToolSettingsTab.java | 15 ++------------- .../buildTools/internal/MinGW32ToolsProvider.java | 2 +- .../providers/AutoBuildMakeRule.java | 2 +- .../autoBuild/helpers/api/KeyValueTree.java | 2 -- .../AutoBuildConfigurationDescription.java | 2 +- .../autoBuild/integration/AutoBuildManager.java | 4 ++-- .../src/io/sloeber/core/Activator.java | 4 ++-- .../src/io/sloeber/core/api/Const.java | 2 +- .../src/io/sloeber/core/api/PasswordManager.java | 3 ++- .../CDT_EnvironmentVariableResolver.java | 5 ++--- .../src/io/sloeber/ui/actions/BuildHandler.java | 2 +- .../io/sloeber/ui/monitor/views/PlotterView.java | 10 +++++++--- .../sloeber/ui/monitor/views/SerialMonitor.java | 7 ++++--- .../ui/preferences/LibrarySelectionPage.java | 6 +++--- .../ui/wizard/newsketch/NewSketchWizard.java | 2 +- 15 files changed, 30 insertions(+), 38 deletions(-) diff --git a/io.sloeber.autoBuild.ui/src/io/sloeber/autoBuild/ui/tabs/ToolSettingsTab.java b/io.sloeber.autoBuild.ui/src/io/sloeber/autoBuild/ui/tabs/ToolSettingsTab.java index 8b955e28..09bd2640 100644 --- a/io.sloeber.autoBuild.ui/src/io/sloeber/autoBuild/ui/tabs/ToolSettingsTab.java +++ b/io.sloeber.autoBuild.ui/src/io/sloeber/autoBuild/ui/tabs/ToolSettingsTab.java @@ -216,7 +216,7 @@ public void controlResized(ControlEvent e) { } else { //the line below is only here for development so I know there will only be resources //TODO remove the line below after evaluation this never ever happens - System.err.println("Element should be resource " + pageElement); + System.err.println("Element should be resource " + pageElement); //$NON-NLS-1$ } setValues(); @@ -280,17 +280,6 @@ protected void updateTipText(String name, String tip) { tipText.update(); } - /* (non-Javadoc) - * Method resetTipText - * @since 7.0 - */ - private void resetTipText() { - if (tipText == null) { - return; - } - tipText.setText(Messages.ToolSettingsTab_0); - tipText.update(); - } /* (non-Javadoc) * Method displayOptionsForCategory @@ -411,7 +400,7 @@ public void modifyText(ModifyEvent e) { } } - Label label = stringField.getLabelControl(mySettingsPageContainer); + //Label label = stringField.getLabelControl(mySettingsPageContainer); Text text = stringField.getTextControl(mySettingsPageContainer); text.setText(optionValue); // if(pageHasToolTipBox) diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/buildTools/internal/MinGW32ToolsProvider.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/buildTools/internal/MinGW32ToolsProvider.java index f43592da..b35a7ead 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/buildTools/internal/MinGW32ToolsProvider.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/buildTools/internal/MinGW32ToolsProvider.java @@ -36,7 +36,7 @@ private void findTools() { try { myMinGWHome = org.eclipse.cdt.internal.core.MinGW.getMinGWHome(); } - catch(@SuppressWarnings("unused") Exception e) { + catch( Exception e) { //ignore as this fails in maven build due to lack of gui and as such registry //is not available e.printStackTrace(); diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/AutoBuildMakeRule.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/AutoBuildMakeRule.java index 87a059b0..4635860c 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/AutoBuildMakeRule.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/extensionPoint/providers/AutoBuildMakeRule.java @@ -296,7 +296,7 @@ public String[] getRecipes(IFolder buildFolder, AutoBuildConfigurationDescriptio .getFolder(IPath.forPosix(curEntry.getValue())).getLocation(); if(path==null) { //Log error to allow for investigation - Activator.log(new Status(IStatus.WARNING, Activator.PLUGIN_ID, "No location found for "+curEntry.getValue())); + Activator.log(new Status(IStatus.WARNING, Activator.PLUGIN_ID, "No location found for "+curEntry.getValue())); //$NON-NLS-1$ }else { includePath = includePath + WHITESPACE + DOUBLE_QUOTE + CMD_LINE_INCLUDE_FOLDER diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/helpers/api/KeyValueTree.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/helpers/api/KeyValueTree.java index ce318b6b..a2e059f3 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/helpers/api/KeyValueTree.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/helpers/api/KeyValueTree.java @@ -8,8 +8,6 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; -import java.util.TreeMap; - import org.apache.commons.io.FileUtils; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildConfigurationDescription.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildConfigurationDescription.java index ad3105ec..8ea885d0 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildConfigurationDescription.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildConfigurationDescription.java @@ -1149,7 +1149,7 @@ public void deleteBuildFolder(IProgressMonitor monitor) { curMember.delete(true, monitor); } catch (CoreException e) { Activator.log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, - "Failed to delete member "+curMember.getName(), e)); + "Failed to delete member "+curMember.getName(), e)); //$NON-NLS-1$ } } diff --git a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildManager.java b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildManager.java index a58c441b..4e8f9d11 100644 --- a/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildManager.java +++ b/io.sloeber.autoBuild/src/io/sloeber/autoBuild/integration/AutoBuildManager.java @@ -266,8 +266,8 @@ public static Set getProjectTypes(String extensionPointID, String IProjectType projectType = AutoBuildManager.getProjectType(extensionPointID, extensionID, element.getAttribute(ID), true); if (projectType == null) { - System.err.println("project not found: extensionPoint ID " + extensionPointID + " and extension ID " - + extensionID + " and " + ID); + System.err.println("project not found: extensionPoint ID " + extensionPointID + " and extension ID " //$NON-NLS-1$ //$NON-NLS-2$ + + extensionID + " and " + ID); //$NON-NLS-1$ } else { ret.add(projectType); } diff --git a/io.sloeber.core/src/io/sloeber/core/Activator.java b/io.sloeber.core/src/io/sloeber/core/Activator.java index 1fd8c78d..f9000e3c 100644 --- a/io.sloeber.core/src/io/sloeber/core/Activator.java +++ b/io.sloeber.core/src/io/sloeber/core/Activator.java @@ -173,7 +173,7 @@ private static void testKnownIssues() { if (!errorString.isEmpty()) { errorString += "\n\nSloeber might still function but if you get strange results you know where to look.\n"; errorString += "Do not create an issue if you see this!!!"; - log(new Status(IStatus.ERROR, PLUGIN_ID, errorString)); + log(new Status(IStatus.ERROR, Activator.getId(), errorString)); } } @@ -301,7 +301,7 @@ public void stop(BundleContext context) throws Exception { * the io.sloeber.core.managers and io.sloeber.core.managers.ui to work. */ public static String getId() { - return PLUGIN_ID; + return "io.sloeber.core"; } /** diff --git a/io.sloeber.core/src/io/sloeber/core/api/Const.java b/io.sloeber.core/src/io/sloeber/core/api/Const.java index bf514e7e..02adcf2a 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Const.java +++ b/io.sloeber.core/src/io/sloeber/core/api/Const.java @@ -73,7 +73,7 @@ public class Const extends AutoBuildConstants { public static final String REMOTE_SUFFIX = "_remote"; // General stuff - public static final String PLUGIN_ID = "io.sloeber.core"; + //public static final String PLUGIN_ID = "io.sloeber.core"; public static final String CORE_PLUGIN_ID = "io.sloeber.arduino.core"; public static final String SLOEBER_NATURE_ID = "io.sloeber.arduinonature"; public static final String KEY_LAST_USED_EXAMPLES = "Last used Examples"; diff --git a/io.sloeber.core/src/io/sloeber/core/api/PasswordManager.java b/io.sloeber.core/src/io/sloeber/core/api/PasswordManager.java index 6af882a0..468bbe4d 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/PasswordManager.java +++ b/io.sloeber.core/src/io/sloeber/core/api/PasswordManager.java @@ -6,6 +6,7 @@ import org.eclipse.equinox.security.storage.SecurePreferencesFactory; import org.eclipse.equinox.security.storage.StorageException; +import io.sloeber.autoBuild.helpers.api.AutoBuildConstants; import io.sloeber.core.Activator; import io.sloeber.core.Messages; @@ -113,7 +114,7 @@ public static void ErasePassword(String host) { private static String ConvertHostToNodeName(String host) { - return "ssh/" + host.replace(Const.DOT, Const.SLACH); //$NON-NLS-1$ + return "ssh/" + host.replace(AutoBuildConstants.DOT, AutoBuildConstants.SLACH); //$NON-NLS-1$ } } diff --git a/io.sloeber.core/src/io/sloeber/core/eclipseIntegrations/CDT_EnvironmentVariableResolver.java b/io.sloeber.core/src/io/sloeber/core/eclipseIntegrations/CDT_EnvironmentVariableResolver.java index 19c81842..9b8eb2c7 100644 --- a/io.sloeber.core/src/io/sloeber/core/eclipseIntegrations/CDT_EnvironmentVariableResolver.java +++ b/io.sloeber.core/src/io/sloeber/core/eclipseIntegrations/CDT_EnvironmentVariableResolver.java @@ -1,7 +1,5 @@ package io.sloeber.core.eclipseIntegrations; -import static io.sloeber.core.api.Const.*; - import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.envvar.IEnvironmentVariableManager; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; @@ -18,6 +16,7 @@ import org.eclipse.core.variables.IStringVariableManager; import org.eclipse.core.variables.VariablesPlugin; +import io.sloeber.core.Activator; import io.sloeber.core.Messages; public class CDT_EnvironmentVariableResolver implements IDynamicVariableResolver { @@ -29,7 +28,7 @@ public String resolveValue(IDynamicVariable variable, String varName) throws Cor return getBuildEnvironmentVariable(confDesc, varName); } catch ( Exception e) { - Status iStatus = new Status(IStatus.ERROR, PLUGIN_ID, Messages.projectNotFoundInGUI,e); + Status iStatus = new Status(IStatus.ERROR, Activator.getId(), Messages.projectNotFoundInGUI,e); throw new CoreException(iStatus); } } diff --git a/io.sloeber.ui/src/io/sloeber/ui/actions/BuildHandler.java b/io.sloeber.ui/src/io/sloeber/ui/actions/BuildHandler.java index 44747c83..4b8b7f39 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/actions/BuildHandler.java +++ b/io.sloeber.ui/src/io/sloeber/ui/actions/BuildHandler.java @@ -43,7 +43,7 @@ protected IStatus run(IProgressMonitor monitor) { } catch (CoreException e) { return new Status(IStatus.ERROR, NODE_ARDUINO, Messages.buildHandler_build_code_of_project.replace(Messages.PROJECT, myBuildProject.getName()) - + " failed", + + " failed", //$NON-NLS-1$ e); } return Status.OK_STATUS; diff --git a/io.sloeber.ui/src/io/sloeber/ui/monitor/views/PlotterView.java b/io.sloeber.ui/src/io/sloeber/ui/monitor/views/PlotterView.java index 0576c99d..e4e6b9c4 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/monitor/views/PlotterView.java +++ b/io.sloeber.ui/src/io/sloeber/ui/monitor/views/PlotterView.java @@ -1,5 +1,9 @@ package io.sloeber.ui.monitor.views; + +import static io.sloeber.core.api.Const.*; + +import java.net.URI; import java.net.URL; import org.eclipse.core.runtime.IProgressMonitor; @@ -41,7 +45,7 @@ public class PlotterView extends ViewPart implements ServiceListener { Serial mySerial = null; private static final String FLAG_MONITOR = "FmStatus"; //$NON-NLS-1$ - String uri = "h tt p://bae yens.i t/ec li pse/do wnl oad/Sc opeS tart.h t ml?m="; //$NON-NLS-1$ + private static String uri = "h tt p://bae yens.i t/ec li pse/do wnl oad/Sc opeS tart.h t ml?m="; //$NON-NLS-1$ public Object mstatus; // status of the plotter public PlotterView() { @@ -53,8 +57,8 @@ protected IStatus run(IProgressMonitor monitor) { IEclipsePreferences mySCope = InstanceScope.INSTANCE.getNode(MyPreferences.NODE_ARDUINO); int curFsiStatus = mySCope.getInt(FLAG_MONITOR, 0) + 1; mySCope.putInt(FLAG_MONITOR, curFsiStatus); - URL pluginStartInitiator = new URL( - PlotterView.this.uri.replace(" ", "") + Integer.toString(curFsiStatus)); //$NON-NLS-1$ //$NON-NLS-2$ + URI tt= new URI(uri.replace(SPACE, EMPTY) + Integer.toString(curFsiStatus)); + URL pluginStartInitiator = tt.toURL(); PlotterView.this.mstatus = pluginStartInitiator.getContent(); } catch (Exception e) {// JABA is not going to add code } diff --git a/io.sloeber.ui/src/io/sloeber/ui/monitor/views/SerialMonitor.java b/io.sloeber.ui/src/io/sloeber/ui/monitor/views/SerialMonitor.java index 0aafcddf..e32fca40 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/monitor/views/SerialMonitor.java +++ b/io.sloeber.ui/src/io/sloeber/ui/monitor/views/SerialMonitor.java @@ -1,8 +1,10 @@ package io.sloeber.ui.monitor.views; +import static io.sloeber.core.api.Const.*; import static io.sloeber.ui.Activator.*; import java.io.File; +import java.net.URI; import java.net.URL; import java.nio.charset.Charset; import java.nio.file.Files; @@ -200,9 +202,8 @@ protected IStatus run(IProgressMonitor monitor) { IEclipsePreferences myScope = InstanceScope.INSTANCE.getNode(MyPreferences.NODE_ARDUINO); int curFsiStatus = myScope.getInt(MY_FLAG_MONITOR, 0) + 1; myScope.putInt(MY_FLAG_MONITOR, curFsiStatus); - URL mypluginStartInitiator = new URL(uri.replace(" ", new String()) //$NON-NLS-1$ - + Integer.toString(curFsiStatus)); - mypluginStartInitiator.getContent(); + URI tt= new URI(uri.replace(SPACE, EMPTY) + Integer.toString(curFsiStatus)); + tt.toURL().getContent(); } catch (Exception e) {// JABA is not going to add code } return Status.OK_STATUS; diff --git a/io.sloeber.ui/src/io/sloeber/ui/preferences/LibrarySelectionPage.java b/io.sloeber.ui/src/io/sloeber/ui/preferences/LibrarySelectionPage.java index 89842d96..095d3ff0 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/preferences/LibrarySelectionPage.java +++ b/io.sloeber.ui/src/io/sloeber/ui/preferences/LibrarySelectionPage.java @@ -147,9 +147,9 @@ public boolean canUpdate() { public String getTooltip() { if (myTooltip == null) { IArduinoLibraryVersion libVers = getLatest(); - myTooltip = "Architectures:" + libVers.getArchitectures().toString() + blankLine - + libVers.getSentence() + blankLine + libVers.getParagraph() + blankLine + "Author: " - + libVers.getAuthor() + blankLine + "Maintainer: " + libVers.getMaintainer() ; + myTooltip = "Architectures:" + libVers.getArchitectures().toString() + blankLine //$NON-NLS-1$ + + libVers.getSentence() + blankLine + libVers.getParagraph() + blankLine + "Author: " //$NON-NLS-1$ + + libVers.getAuthor() + blankLine + "Maintainer: " + libVers.getMaintainer() ; //$NON-NLS-1$ } return myTooltip; } diff --git a/io.sloeber.ui/src/io/sloeber/ui/wizard/newsketch/NewSketchWizard.java b/io.sloeber.ui/src/io/sloeber/ui/wizard/newsketch/NewSketchWizard.java index 7ca02d92..a1e66d34 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/wizard/newsketch/NewSketchWizard.java +++ b/io.sloeber.ui/src/io/sloeber/ui/wizard/newsketch/NewSketchWizard.java @@ -40,7 +40,7 @@ public class NewSketchWizard extends Wizard implements INewWizard, IExecutableEx Messages.ui_new_sketch_arduino_information); protected NewSketchWizardCodeSelectionPage mNewArduinoSketchWizardCodeSelectionPage = new NewSketchWizardCodeSelectionPage( Messages.ui_new_sketch_sketch_template_location); - private NewProjectSourceLocationPage mySourceLocationPage= new NewProjectSourceLocationPage("code location page"); + private NewProjectSourceLocationPage mySourceLocationPage= new NewProjectSourceLocationPage("code location page"); //$NON-NLS-1$ private IConfigurationElement mConfig; private IProject mProject; From 31771cd51db690bcb020cdedce371f18dde0ffa7 Mon Sep 17 00:00:00 2001 From: jan Date: Mon, 23 Dec 2024 15:52:19 +0100 Subject: [PATCH 091/115] add tests for #1696 (build should fail) This commits adds a test for #1696 (actually expanding a existing tests). Because the issue dooes not show on linux the test succeeds on my windows system. However the build is on a linux system so this commit is expected to fail the build. I'm still pushing it to get the test on my Linux system to fix it. I know I could create a branch and push to the branch ... but as I have been the only contributer to this repository the last 12 months nobody should suffer ;-) --- .../src/io/sloeber/core/BuildTests.java | 18 ++++++++++++------ .../src/io/sloeber/providers/ESP32.java | 14 ++++++++++++++ .../templates/CreateAndCompileTest/sketch.cpp | 10 ++++++++-- .../sketch.ino | 11 +++++++++-- 4 files changed, 43 insertions(+), 10 deletions(-) diff --git a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java index 1ec7fc3e..ba0f0754 100644 --- a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java +++ b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java @@ -233,18 +233,24 @@ public void createDefaultInoProject() throws Exception { * * @throws Exception */ - @Test - public void issue1047_Board_Names_Can_Be_used_as_Strings() throws Exception { - MCUBoard unoBoard = ESP8266.nodeMCU(); + @ParameterizedTest + @MethodSource("issue1047_Board_Names_Can_Be_used_as_StringsData") + public void issue1047_Board_Names_Can_Be_used_as_Strings(String projectName, MCUBoard board) throws Exception { - String projectName = "issue1047_Board_Names_Can_Be_used_as_Strings"; - IPath templateFolder = Shared.getTemplateFolder(projectName); + IPath templateFolder = Shared.getTemplateFolder("issue1047_Board_Names_Can_Be_used_as_Strings"); CodeDescription codeDescriptor = CodeDescription.createCustomTemplate(templateFolder); - IProject theTestProject = SloeberProject.createArduinoProject(projectName, null, unoBoard.getBoardDescriptor(), + IProject theTestProject = SloeberProject.createArduinoProject(projectName, null, board.getBoardDescriptor(), codeDescriptor, new CompileDescription(), new NullProgressMonitor()); theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, new NullProgressMonitor()); assertNull(Shared.hasBuildErrors(theTestProject)); + } + public static Stream issue1047_Board_Names_Can_Be_used_as_StringsData() throws Exception { + List ret = new LinkedList<>(); + ret.add(Arguments.of("issue1047_Leonardo_Board_Names_Can_Be_used_as_Strings", Arduino.leonardo())); + ret.add(Arguments.of("issue1047_Zero_Board_Names_Can_Be_used_as_Strings", Arduino.zeroNatviePort())); + ret.add(Arguments.of("issue1047_ESP32S3_Board_Names_Can_Be_used_as_Strings", ESP32.ESP32S3())); + return ret.stream(); } /** diff --git a/io.sloeber.tests/src/io/sloeber/providers/ESP32.java b/io.sloeber.tests/src/io/sloeber/providers/ESP32.java index 4f0fa3db..eb1ae57d 100644 --- a/io.sloeber.tests/src/io/sloeber/providers/ESP32.java +++ b/io.sloeber.tests/src/io/sloeber/providers/ESP32.java @@ -35,6 +35,16 @@ public ESP32(String boardName, Map options) { setAttributes(); } + private ESP32(String providerName, String architectureName, String boardID) { + this.myBoardDescriptor = BoardsManager.getBoardDescription(packageURL, providerName, architectureName, + boardID, null); + if (this.myBoardDescriptor == null) { + fail(boardID + " Board not found"); + } + this.myBoardDescriptor.setUploadPort("none"); + setAttributes(); + } + public ESP32(BoardDescription boardDesc) { myBoardDescriptor = boardDesc; myBoardDescriptor.setUploadPort("none"); @@ -57,4 +67,8 @@ public MCUBoard createMCUBoard(BoardDescription boardDesc) { return new ESP32(boardDesc); } + public static Object ESP32S3() { + return new ESP32(provider, architectureName, "esp32s3"); + } + } \ No newline at end of file diff --git a/io.sloeber.tests/src/templates/CreateAndCompileTest/sketch.cpp b/io.sloeber.tests/src/templates/CreateAndCompileTest/sketch.cpp index e6efe49a..c4cc4688 100644 --- a/io.sloeber.tests/src/templates/CreateAndCompileTest/sketch.cpp +++ b/io.sloeber.tests/src/templates/CreateAndCompileTest/sketch.cpp @@ -1,15 +1,21 @@ #include "Arduino.h" #ifdef ARDUINO_BOARD -char mychar1[] = ARDUINO_BOARD; +char mychar[] = ARDUINO_BOARD; #endif - #ifdef USB_MANUFACTURER char mychar2[] = USB_MANUFACTURER; #endif #ifdef USB_PRODUCT char mychar3[] = USB_PRODUCT; #endif +#ifdef ARDUINO_HOST_OS +char mychar4[] = ARDUINO_HOST_OS; +#endif +#ifdef ARDUINO_VARIANT +char mychar5[] = ARDUINO_VARIANT; +#endif + diff --git a/io.sloeber.tests/src/templates/issue1047_Board_Names_Can_Be_used_as_Strings/sketch.ino b/io.sloeber.tests/src/templates/issue1047_Board_Names_Can_Be_used_as_Strings/sketch.ino index e8388bfc..e5829620 100644 --- a/io.sloeber.tests/src/templates/issue1047_Board_Names_Can_Be_used_as_Strings/sketch.ino +++ b/io.sloeber.tests/src/templates/issue1047_Board_Names_Can_Be_used_as_Strings/sketch.ino @@ -1,14 +1,21 @@ #include "Arduino.h" +#ifdef ARDUINO_BOARD char mychar[] = ARDUINO_BOARD; - - +#endif #ifdef USB_MANUFACTURER char mychar2[] = USB_MANUFACTURER; #endif #ifdef USB_PRODUCT char mychar3[] = USB_PRODUCT; #endif +#ifdef ARDUINO_HOST_OS +char mychar4[] = ARDUINO_HOST_OS; +#endif +#ifdef ARDUINO_VARIANT +char mychar5[] = ARDUINO_VARIANT; +#endif + From 2be9546433789ab81f2dd505cc794f5f4837c93d Mon Sep 17 00:00:00 2001 From: jantje Date: Fri, 27 Dec 2024 00:41:18 +0100 Subject: [PATCH 092/115] Add linux launches for jantjes linux box This because the ram drive I use is named differently on both boxes. And also values for SLOEBER_HOME may be/are different --- .../AutoBuildRegression_linux_jantje.launch | 308 +++++++++++++++++ .../BuildTests Sloeber_linux_jantje.launch | 309 ++++++++++++++++++ 2 files changed, 617 insertions(+) create mode 100644 io.sloeber.autoBuild.test/launch/AutoBuildRegression_linux_jantje.launch create mode 100644 io.sloeber.tests/BuildTests Sloeber_linux_jantje.launch diff --git a/io.sloeber.autoBuild.test/launch/AutoBuildRegression_linux_jantje.launch b/io.sloeber.autoBuild.test/launch/AutoBuildRegression_linux_jantje.launch new file mode 100644 index 00000000..5679f78c --- /dev/null +++ b/io.sloeber.autoBuild.test/launch/AutoBuildRegression_linux_jantje.launch @@ -0,0 +1,308 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/io.sloeber.tests/BuildTests Sloeber_linux_jantje.launch b/io.sloeber.tests/BuildTests Sloeber_linux_jantje.launch new file mode 100644 index 00000000..ddb09d71 --- /dev/null +++ b/io.sloeber.tests/BuildTests Sloeber_linux_jantje.launch @@ -0,0 +1,309 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From cb3f47df718d8427f045113c41ebeebf012c4a79 Mon Sep 17 00:00:00 2001 From: jantje Date: Sat, 28 Dec 2024 03:22:14 +0100 Subject: [PATCH 093/115] Modify workaround to fix #1696 on linux Unfortunately the builddefault ino on all boards fails to run on my linux box due to out of memory --- .../src/io/sloeber/core/txt/WorkAround.java | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/core/txt/WorkAround.java b/io.sloeber.core/src/io/sloeber/core/txt/WorkAround.java index cbf01656..c73b916d 100644 --- a/io.sloeber.core/src/io/sloeber/core/txt/WorkAround.java +++ b/io.sloeber.core/src/io/sloeber/core/txt/WorkAround.java @@ -44,13 +44,15 @@ public class WorkAround { // Each time this class is touched consider changing the String below to enforce // updates - private static final String FIRST_SLOEBER_WORKAROUND_LINE = "#Sloeber created TXT file V3.00.test 25 "; + private static final String FIRST_SLOEBER_WORKAROUND_LINE = "#Sloeber created TXT file V3.00.test 34 "; private static Map USB_replacers; static { USB_replacers = new TreeMap<>(); + + if (isWindows) { USB_replacers.put(" '-DUSB_MANUFACTURER={build.usb_manufacturer}' ", " \"-DUSB_MANUFACTURER={build.usb_manufacturer}\" "); @@ -66,6 +68,21 @@ public class WorkAround { USB_replacers.put(" '-DUSB_SERIAL=\"{build.usb_serial}\"' ", " \"-DUSB_SERIAL=\\\"{build.usb_serial}\\\"\" "); USB_replacers.put(" '-DUSB_SERIAL={build.usb_serial}' ", " \"-DUSB_SERIAL={build.usb_serial}\" "); + }else { + USB_replacers.put(" -DUSB_MANUFACTURER=\"{build.usb_manufacturer}\" ", + " '-DUSB_MANUFACTURER=\"{build.usb_manufacturer}\"' "); + USB_replacers.put(" -DUSB_PRODUCT=\"{build.usb_product}\" "," '-DUSB_PRODUCT=\"{build.usb_product}\"' "); + USB_replacers.put(" -DARDUINO_BOARD=\"{build.board}\" ", " '-DARDUINO_BOARD=\"{build.board}\"' "); + USB_replacers.put(" -DUSB_SERIAL=\"{build.usb_serial}\" ", " '-DUSB_SERIAL=\"{build.usb_serial}\"' "); + + //esp32 has + //extraflags=-DARDUINO_HOST_OS= + //so no space in search + USB_replacers.put("-DARDUINO_HOST_OS=\"{runtime.os}\" ", "'-DARDUINO_HOST_OS=\"{runtime.os}\"' "); + USB_replacers.put(" -DARDUINO_VARIANT=\"{build.variant}\" ", " '-DARDUINO_VARIANT=\"{build.variant}\"' "); + USB_replacers.put(" -DARDUINO_FQBN=\"{build.fqbn}\" ", " '-DARDUINO_FQBN=\"{build.fqbn}\"' "); + + } } @@ -158,10 +175,12 @@ public static String boardsApplyWorkArounds(String inBoardsTXT) { boardsTXT = boardsTXT.replace("\n", " \n"); boardsTXT = solveOSStuff(boardsTXT); - // replace FI circuitplay32u4cat.build.usb_manufacturer="Adafruit" - // with circuitplay32u4cat.build.usb_manufacturer=\"Adafruit\" - boardsTXT = boardsTXT.replaceAll("(\\S+\\.build\\.usb\\S+)=\\\"(.+)\\\"", - "$1=\\\\\"$2\\\\\""); + if (isWindows) { + // replace FI circuitplay32u4cat.build.usb_manufacturer="Adafruit" + // with circuitplay32u4cat.build.usb_manufacturer=\"Adafruit\" + boardsTXT = boardsTXT.replaceAll("(\\S+\\.build\\.usb\\S+)=\\\"(.+)\\\"", + "$1=\\\\\"$2\\\\\""); + } // quoting fixes for embedutils // ['\"]?(-DMBEDTLS_\S+)=\\?"(mbedtls\S+?)\\?\"["']? \"$1=\\\"$2\\\"\" @@ -241,6 +260,7 @@ public static String platformApplyWorkArounds(String inPlatformTxt, File request platformTXT = platformTXT.replaceAll("(?m)^(\\S*)\\s*=", "$1="); // remove -MMD (the dependency generation parameter platformTXT = platformTXT.replaceAll(" -MMD ", " "); + // add a space at the end of the line platformTXT = platformTXT.replace("\n", " \n"); platformTXT = solveOSStuff(platformTXT); From 90c7c405ea173a0ca4d8ef295567c3005ef0c682 Mon Sep 17 00:00:00 2001 From: jan Date: Tue, 14 Jan 2025 02:16:16 +0100 Subject: [PATCH 094/115] exclude lib folders in libraries #1703 --- .../io/sloeber/core/api/SloeberProject.java | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java b/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java index 3451a9fd..23c661a3 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java +++ b/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java @@ -182,7 +182,7 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { excludes[1] = project.getFolder(SLOEBER_ARDUINO_FOLDER_NAME).getProjectRelativePath() .append(SOURCE_ENTRY_FILTER_ALL); newSourceEntries[0] = new CSourceEntry(project.getFullPath(), excludes, ICSettingEntry.RESOLVED); - IPath excludes2[] = new IPath[9]; + IPath excludes2[] = new IPath[10]; excludes2[0] = IPath.fromOSString("**/*.ino"); //$NON-NLS-1$ excludes2[1] = IPath.fromOSString("libraries/?*/**/doc*/**"); //$NON-NLS-1$ excludes2[2] = IPath.fromOSString("libraries/?*/**/?xamples/**"); //$NON-NLS-1$ @@ -190,8 +190,9 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { excludes2[4] = IPath.fromOSString("libraries/?*/**/test*/**"); //$NON-NLS-1$ excludes2[5] = IPath.fromOSString("libraries/?*/**/third-party/**"); //$NON-NLS-1$ excludes2[6] = IPath.fromOSString("libraries/**/._*"); //$NON-NLS-1$ - excludes2[7] = IPath.fromOSString("libraries/?*/utility/*/*"); //$NON-NLS-1$ - excludes2[8] = IPath.fromOSString("libraries/?*/Applications/**"); //$NON-NLS-1$ + excludes2[7] = IPath.fromOSString("libraries/?*/lib/**"); //$NON-NLS-1$ + excludes2[8] = IPath.fromOSString("libraries/?*/utility/*/*"); //$NON-NLS-1$ + excludes2[9] = IPath.fromOSString("libraries/?*/Applications/**"); //$NON-NLS-1$ /* * CDT currently causes issues with ${ConfigName] @@ -448,17 +449,17 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { IPath path = newProjectHandle.getFolder(rootCodeFolder).getFullPath(); newSourceEntries[0] = new CSourceEntry(path, null, ICSettingEntry.RESOLVED); } - IPath excludes[] = new IPath[9]; - excludes[0] = IPath.fromOSString("**/*.ino"); //$NON-NLS-1$ - excludes[1] = IPath.fromOSString("libraries/?*/**/doc*/**"); //$NON-NLS-1$ - excludes[2] = IPath.fromOSString("libraries/?*/**/?xamples/**"); //$NON-NLS-1$ - excludes[3] = IPath.fromOSString("libraries/?*/**/?xtras/**"); //$NON-NLS-1$ - excludes[4] = IPath.fromOSString("libraries/?*/**/test*/**"); //$NON-NLS-1$ - excludes[5] = IPath.fromOSString("libraries/?*/**/third-party/**"); //$NON-NLS-1$ - excludes[6] = IPath.fromOSString("libraries/**/._*"); //$NON-NLS-1$ - excludes[7] = IPath.fromOSString("libraries/?*/utility/*/*"); //$NON-NLS-1$ - excludes[8] = IPath.fromOSString("libraries/?*/Applications/**"); //$NON-NLS-1$ - + IPath excludes2[] = new IPath[10]; + excludes2[0] = IPath.fromOSString("**/*.ino"); //$NON-NLS-1$ + excludes2[1] = IPath.fromOSString("libraries/?*/**/doc*/**"); //$NON-NLS-1$ + excludes2[2] = IPath.fromOSString("libraries/?*/**/?xamples/**"); //$NON-NLS-1$ + excludes2[3] = IPath.fromOSString("libraries/?*/**/?xtras/**"); //$NON-NLS-1$ + excludes2[4] = IPath.fromOSString("libraries/?*/**/test*/**"); //$NON-NLS-1$ + excludes2[5] = IPath.fromOSString("libraries/?*/**/third-party/**"); //$NON-NLS-1$ + excludes2[6] = IPath.fromOSString("libraries/**/._*"); //$NON-NLS-1$ + excludes2[7] = IPath.fromOSString("libraries/?*/lib/**"); //$NON-NLS-1$ + excludes2[8] = IPath.fromOSString("libraries/?*/utility/*/*"); //$NON-NLS-1$ + excludes2[9] = IPath.fromOSString("libraries/?*/Applications/**"); //$NON-NLS-1$ /* * CDT currently causes issues with ${ConfigName] @@ -468,7 +469,7 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { */ IPath arduinoRoot = newProjectHandle.getFolder(SLOEBER_ARDUINO_FOLDER_NAME).getFullPath() .append(curConfig.getName()); - newSourceEntries[1] = new CSourceEntry(arduinoRoot, excludes, ICSettingEntry.NONE); + newSourceEntries[1] = new CSourceEntry(arduinoRoot, excludes2, ICSettingEntry.NONE); curConfig.setSourceEntries(newSourceEntries); IAutoBuildConfigurationDescription iAutoBuildConfig = IAutoBuildConfigurationDescription .getConfig(curConfig); From cd17da61fff94602df183d10288ce733c22d2b2c Mon Sep 17 00:00:00 2001 From: jan Date: Wed, 15 Jan 2025 03:38:41 +0100 Subject: [PATCH 095/115] windows part of #1696 --- .../src/io/sloeber/core/txt/WorkAround.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/core/txt/WorkAround.java b/io.sloeber.core/src/io/sloeber/core/txt/WorkAround.java index c73b916d..8e98caf9 100644 --- a/io.sloeber.core/src/io/sloeber/core/txt/WorkAround.java +++ b/io.sloeber.core/src/io/sloeber/core/txt/WorkAround.java @@ -44,14 +44,14 @@ public class WorkAround { // Each time this class is touched consider changing the String below to enforce // updates - private static final String FIRST_SLOEBER_WORKAROUND_LINE = "#Sloeber created TXT file V3.00.test 34 "; + private static final String FIRST_SLOEBER_WORKAROUND_LINE = "#Sloeber created TXT file V3.00.test 38 "; private static Map USB_replacers; static { USB_replacers = new TreeMap<>(); - + if (isWindows) { USB_replacers.put(" '-DUSB_MANUFACTURER={build.usb_manufacturer}' ", @@ -68,20 +68,24 @@ public class WorkAround { USB_replacers.put(" '-DUSB_SERIAL=\"{build.usb_serial}\"' ", " \"-DUSB_SERIAL=\\\"{build.usb_serial}\\\"\" "); USB_replacers.put(" '-DUSB_SERIAL={build.usb_serial}' ", " \"-DUSB_SERIAL={build.usb_serial}\" "); + USB_replacers.put("-DARDUINO_HOST_OS=\"{runtime.os}\" ", "\"-DARDUINO_HOST_OS=\\\"{runtime.os}\\\"\" "); + USB_replacers.put(" -DARDUINO_VARIANT=\"{build.variant}\" ", " \"-DARDUINO_VARIANT=\\\"{build.variant}\\\"\" "); + USB_replacers.put(" -DARDUINO_FQBN=\"{build.fqbn}\" ", " \"-DARDUINO_FQBN=\\\"{build.fqbn}\\\"\" "); + }else { USB_replacers.put(" -DUSB_MANUFACTURER=\"{build.usb_manufacturer}\" ", " '-DUSB_MANUFACTURER=\"{build.usb_manufacturer}\"' "); USB_replacers.put(" -DUSB_PRODUCT=\"{build.usb_product}\" "," '-DUSB_PRODUCT=\"{build.usb_product}\"' "); USB_replacers.put(" -DARDUINO_BOARD=\"{build.board}\" ", " '-DARDUINO_BOARD=\"{build.board}\"' "); USB_replacers.put(" -DUSB_SERIAL=\"{build.usb_serial}\" ", " '-DUSB_SERIAL=\"{build.usb_serial}\"' "); - + //esp32 has //extraflags=-DARDUINO_HOST_OS= //so no space in search USB_replacers.put("-DARDUINO_HOST_OS=\"{runtime.os}\" ", "'-DARDUINO_HOST_OS=\"{runtime.os}\"' "); USB_replacers.put(" -DARDUINO_VARIANT=\"{build.variant}\" ", " '-DARDUINO_VARIANT=\"{build.variant}\"' "); USB_replacers.put(" -DARDUINO_FQBN=\"{build.fqbn}\" ", " '-DARDUINO_FQBN=\"{build.fqbn}\"' "); - + } } @@ -316,6 +320,11 @@ private static String platformApplyCustomWorkArounds(String inPlatformTxt) { platformTXT = platformTXT.replace(" \"@{build.opt.fqfn}\"", ""); platformTXT = platformTXT.replace("\"@{build.opt.fqfn}\"", ""); + //for leonardo on windows + if(isWindows) { + platformTXT = platformTXT.replace("build.usb_manufacturer=\"Unknown\"", "build.usb_manufacturer=\\\"Unknown\\\""); + } + return platformTXT; } From 315c8c7bd8ef91adde4647e40ba168ef4b3e9031 Mon Sep 17 00:00:00 2001 From: jan Date: Fri, 17 Jan 2025 02:16:24 +0100 Subject: [PATCH 096/115] add some documentation --- .../src/io/sloeber/core/api/ISloeberConfiguration.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/io.sloeber.core/src/io/sloeber/core/api/ISloeberConfiguration.java b/io.sloeber.core/src/io/sloeber/core/api/ISloeberConfiguration.java index 74578119..6cf425a5 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/ISloeberConfiguration.java +++ b/io.sloeber.core/src/io/sloeber/core/api/ISloeberConfiguration.java @@ -115,6 +115,14 @@ public static ISloeberConfiguration getConfig(IAutoBuildConfigurationDescription public Set getIncludeFolders(); + /** + * Add libraries to the configurations + * Some libraries contain subfolders that need to be ignoreed. + * Therefore the configuration description may change. + * + * @param librariesToAdd + * @return true if when cCorePlugin.setProjectDescription needs to be called + */ public boolean addLibraries(Collection librariesToAdd); public boolean removeLibraries(Collection librariesToRemove); From bb71ffc3745808b5dc5755dca2b2b72a529f7603 Mon Sep 17 00:00:00 2001 From: jan Date: Fri, 17 Jan 2025 02:21:07 +0100 Subject: [PATCH 097/115] Only src folder and root of the library should be source code #1703 --- .../core/internal/SloeberConfiguration.java | 339 ++++++++++-------- .../core/listeners/IndexerListener.java | 47 +-- 2 files changed, 209 insertions(+), 177 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java b/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java index 604e274b..7ed8132b 100644 --- a/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java +++ b/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java @@ -1,10 +1,13 @@ package io.sloeber.core.internal; +import java.io.File; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -15,6 +18,7 @@ import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.core.settings.model.ICSourceEntry; import org.eclipse.cdt.core.settings.model.extension.CConfigurationData; +import org.eclipse.cdt.core.settings.model.util.CDataUtil; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; @@ -52,7 +56,7 @@ public class SloeberConfiguration extends AutoBuildConfigurationExtensionDescrip private BoardDescription myBoardDescription; private OtherDescription myOtherDesc; private CompileDescription myCompileDescription; - //a map of foldername library + // a map of foldername library private Map myLibraries = new HashMap<>(); // operational data @@ -60,20 +64,19 @@ public class SloeberConfiguration extends AutoBuildConfigurationExtensionDescrip // derived data private Map myEnvironmentVariables = new HashMap<>(); + public static SloeberConfiguration getFromAutoBuildConfDesc(IAutoBuildConfigurationDescription autoBuildConfData) { return (SloeberConfiguration) autoBuildConfData.getAutoBuildConfigurationExtensionDescription(); } public static SloeberConfiguration getConfig(ICConfigurationDescription cConfigDesc) { - CConfigurationData confData=cConfigDesc.getConfigurationData(); - if(confData instanceof IAutoBuildConfigurationDescription) { + CConfigurationData confData = cConfigDesc.getConfigurationData(); + if (confData instanceof IAutoBuildConfigurationDescription) { return (SloeberConfiguration) ((IAutoBuildConfigurationDescription)confData).getAutoBuildConfigurationExtensionDescription(); } return null; } - - /** * copy constructor This constructor must be implemented for each derived class * of AutoBuildConfigurationExtensionDescription or you will get run time errors @@ -103,8 +106,6 @@ public SloeberConfiguration(BoardDescription boardDesc, OtherDescription otherDe setCompileDescription(compileDescriptor); } - - @Override public BoardDescription getBoardDescription() { return new BoardDescription(myBoardDescription); @@ -220,11 +221,10 @@ private boolean LinkToCore() { return false; } - private boolean projectNeedsUpdate() { IPath corePath = myBoardDescription.getActualCoreCodePath(); IFolder coreFolder = getArduinoCoreFolder(); - if (! corePath.equals(coreFolder.getLocation())) { + if (!corePath.equals(coreFolder.getLocation())) { // System.out.println("projectNeedsUpdate core Folder mismatch"); // System.out.println("corefolder "+coreFolder.getLocation()); // System.out.println("corePath "+corePath); @@ -232,7 +232,7 @@ private boolean projectNeedsUpdate() { } IFolder arduinoVariantFolder = getArduinoVariantFolder(); IPath variantPath = myBoardDescription.getActualVariantPath(); - if(variantPath==null) { + if (variantPath == null) { return arduinoVariantFolder.exists(); } if ((!variantPath.toFile().exists()) && (arduinoVariantFolder.exists())) { @@ -249,64 +249,62 @@ private boolean projectNeedsUpdate() { return false; } - - /** * get the environment variables that do not reliy on variable expansion to get the value. * @return true when data was missing */ private boolean getEnvVarsNonExpanding() { - myEnvironmentVariables.clear(); + myEnvironmentVariables.clear(); - myEnvironmentVariables.put(ENV_KEY_BUILD_PATH, - getProject().getFolder(getAutoBuildDescription().getBuildFolderString()).getLocation().toOSString()); + myEnvironmentVariables.put(ENV_KEY_BUILD_PATH, + getProject().getFolder(getAutoBuildDescription().getBuildFolderString()).getLocation().toOSString()); - myEnvironmentVariables.put(ENV_KEY_BUILD_SOURCE_PATH,getCodeLocation().toOSString()); + myEnvironmentVariables.put(ENV_KEY_BUILD_SOURCE_PATH, getCodeLocation().toOSString()); // myEnvironmentVariables.put(ENV_KEY_BUILD_PATH, // getAutoBuildDescription().getBuildFolder().getLocation().toOSString()); - if (myBoardDescription != null) { - myEnvironmentVariables.putAll(myBoardDescription.getEnvVars()); - } - if (myCompileDescription != null) { - myEnvironmentVariables.putAll(myCompileDescription.getEnvVars()); - } - if (myOtherDesc != null) { - myEnvironmentVariables.putAll(myOtherDesc.getEnvVars()); - } - // set the paths - String pathDelimiter = makeEnvironmentVar("PathDelimiter"); //$NON-NLS-1$ - if (isWindows) { - myEnvironmentVariables.put(SLOEBER_MAKE_LOCATION, - ConfigurationPreferences.getMakePath().addTrailingSeparator().toOSString()); - myEnvironmentVariables.put(SLOEBER_AWK_LOCATION, - ConfigurationPreferences.getAwkPath().addTrailingSeparator().toOSString()); - - String systemroot = makeEnvironmentVar("SystemRoot"); //$NON-NLS-1$ - myEnvironmentVariables.put("PATH", //$NON-NLS-1$ - makeEnvironmentVar(ENV_KEY_COMPILER_PATH) + pathDelimiter + if (myBoardDescription != null) { + myEnvironmentVariables.putAll(myBoardDescription.getEnvVars()); + } + if (myCompileDescription != null) { + myEnvironmentVariables.putAll(myCompileDescription.getEnvVars()); + } + if (myOtherDesc != null) { + myEnvironmentVariables.putAll(myOtherDesc.getEnvVars()); + } + // set the paths + String pathDelimiter = makeEnvironmentVar("PathDelimiter"); //$NON-NLS-1$ + if (isWindows) { + myEnvironmentVariables.put(SLOEBER_MAKE_LOCATION, + ConfigurationPreferences.getMakePath().addTrailingSeparator().toOSString()); + myEnvironmentVariables.put(SLOEBER_AWK_LOCATION, + ConfigurationPreferences.getAwkPath().addTrailingSeparator().toOSString()); + + String systemroot = makeEnvironmentVar("SystemRoot"); //$NON-NLS-1$ + myEnvironmentVariables.put("PATH", //$NON-NLS-1$ + makeEnvironmentVar(ENV_KEY_COMPILER_PATH) + pathDelimiter + makeEnvironmentVar(ENV_KEY_BUILD_GENERIC_PATH) + pathDelimiter + systemroot + "\\system32" //$NON-NLS-1$ - + pathDelimiter + systemroot + pathDelimiter + systemroot + "\\system32\\Wbem" //$NON-NLS-1$ - + pathDelimiter + makeEnvironmentVar("sloeber_path_extension")); //$NON-NLS-1$ - } else { - myEnvironmentVariables.put("PATH", makeEnvironmentVar(ENV_KEY_COMPILER_PATH) + pathDelimiter //$NON-NLS-1$ - + makeEnvironmentVar(ENV_KEY_BUILD_GENERIC_PATH) + pathDelimiter + makeEnvironmentVar("PATH")); //$NON-NLS-1$ - } - return (myBoardDescription == null) || (myCompileDescription == null)|| (myOtherDesc == null); + + pathDelimiter + systemroot + pathDelimiter + systemroot + "\\system32\\Wbem" //$NON-NLS-1$ + + pathDelimiter + makeEnvironmentVar("sloeber_path_extension")); //$NON-NLS-1$ + } else { + myEnvironmentVariables.put("PATH", makeEnvironmentVar(ENV_KEY_COMPILER_PATH) + pathDelimiter //$NON-NLS-1$ + + makeEnvironmentVar(ENV_KEY_BUILD_GENERIC_PATH) + pathDelimiter + makeEnvironmentVar("PATH")); //$NON-NLS-1$ + } + return (myBoardDescription == null) || (myCompileDescription == null) || (myOtherDesc == null); } private IPath getCodeLocation() { IProject project = getProject(); - IPath arduinoPath=getArduinoRootFolder().getFullPath(); + IPath arduinoPath = getArduinoRootFolder().getFullPath(); ICSourceEntry[] sourceEntries = getAutoBuildDesc().getCdtConfigurationDescription().getSourceEntries(); for (ICSourceEntry curEntry : sourceEntries) { - IPath entryPath=curEntry.getFullPath(); - if (arduinoPath.isPrefixOf( entryPath)) { - //this is the arduino code folder: ignore + IPath entryPath = curEntry.getFullPath(); + if (arduinoPath.isPrefixOf(entryPath)) { + // this is the arduino code folder: ignore continue; } - //Just pick the first none arduino one as there should only be one + // Just pick the first none arduino one as there should only be one return getLocation(curEntry); } return project.getLocation(); @@ -332,6 +330,7 @@ private static IPath getLocation(ICSourceEntry entry) { return rc.getLocation(); return null; } + /** * get the text for the decorator * @@ -468,12 +467,12 @@ private void updateArduinoCodeLinks() { IFolder arduinoVariantFolder = getArduinoVariantFolder(); IFolder arduinoCodeFolder = getArduinoCoreFolder(); - NullProgressMonitor monitor=new NullProgressMonitor(); + NullProgressMonitor monitor = new NullProgressMonitor(); try { arduinoVariantFolder.delete(true, monitor); arduinoCodeFolder.delete(true, monitor); } catch (CoreException e) { - // TODO Auto-generated catch block + // ignore exception e.printStackTrace(); } IPath corePath = myBoardDescription.getActualCoreCodePath(); @@ -490,9 +489,9 @@ private void updateArduinoCodeLinks() { public Set getIncludeFolders() { Set ret = new HashSet<>(); ret.add(getArduinoCoreFolder()); - if (myBoardDescription.getActualVariantPath() != null) { - ret.add(getArduinoVariantFolder()); - } + if (myBoardDescription.getActualVariantPath() != null) { + ret.add(getArduinoVariantFolder()); + } try { if (getArduinoLibraryFolder().exists()) { for (IResource curMember : getArduinoLibraryFolder().members()) { @@ -519,10 +518,10 @@ public Set getIncludeFolders() { * @return * @throws CoreException */ - private Map getLibrariesFromLinks() { + private Map getLibrariesFromLinks() { Map ret = new HashMap<>(); IFolder libFolder = getArduinoLibraryFolder(); - if(!libFolder.exists()) { + if (!libFolder.exists()) { return ret; } try { @@ -530,15 +529,15 @@ private Map getLibrariesFromLinks() { if (curResource instanceof IFolder) { IFolder curFolder = (IFolder) curResource; IArduinoLibraryVersion curLib = myLibraries.get(curFolder.getName()); - if (curLib != null){ - //We knbow the lib so it is ok - ret.put(curLib.getName(),curLib); + if (curLib != null) { + // We knbow the lib so it is ok + ret.put(curLib.getName(), curLib); continue; } - curLib=LibraryManager.getLibraryVersionFromLocation(curFolder,getBoardDescription()); - if (curLib != null){ - ret.put(curLib.getName(),curLib); + curLib = LibraryManager.getLibraryVersionFromLocation(curFolder, getBoardDescription()); + if (curLib != null) { + ret.put(curLib.getName(), curLib); } } } @@ -549,7 +548,6 @@ private Map getLibrariesFromLinks() { return ret; } - /** * remove the links from the libraries on disk * @@ -558,12 +556,12 @@ private void removeLibraryLinks() { IProgressMonitor monitor = new NullProgressMonitor(); IFolder libFolder = getArduinoLibraryFolder(); for (String curLib : myLibraries.keySet()) { - IFolder curLibFolder=libFolder.getFolder(curLib); + IFolder curLibFolder = libFolder.getFolder(curLib); if (curLibFolder.exists()) { try { curLibFolder.delete(true, monitor); } catch (CoreException e) { - // TODO Auto-generated catch block + // ignore exception e.printStackTrace(); } } @@ -577,65 +575,61 @@ private void removeLibraryLinks() { private void linkLibrariesToFolder() { IFolder libFolder = getArduinoLibraryFolder(); for (IArduinoLibraryVersion curLib : myLibraries.values()) { - IFolder curLibFolder=libFolder.getFolder(curLib.getName()); + IFolder curLibFolder = libFolder.getFolder(curLib.getName()); Helpers.LinkFolderToFolder(curLib.getInstallPath(), curLibFolder); } } - /** * For libraries of with FQN Libraries/hardware/X make sure that the location * point to a valid location of the given boardDescriptor */ private void upDateHardwareLibraries() { - //make sure the libraries that link to hardware are the correct ones - BoardDescription boardDesc=getBoardDescription(); - IPath referenceLibPath =boardDesc.getReferencedCoreLibraryPath(); - IPath referencingLibPath =boardDesc.getReferencingLibraryPath(); - if(referencingLibPath==null) { - referencingLibPath=referenceLibPath; - } - Set hardwareLibsFQN=new HashSet<>(); - for(IArduinoLibraryVersion curLib:myLibraries.values()) { - if(curLib.isHardwareLib()) { - IPath libPath=curLib.getInstallPath(); - if(referencingLibPath.isPrefixOf(libPath)||referenceLibPath.isPrefixOf(libPath)) { - //the hardware lib is ok + // make sure the libraries that link to hardware are the correct ones + BoardDescription boardDesc = getBoardDescription(); + IPath referenceLibPath = boardDesc.getReferencedCoreLibraryPath(); + IPath referencingLibPath = boardDesc.getReferencingLibraryPath(); + if (referencingLibPath == null) { + referencingLibPath = referenceLibPath; + } + Set hardwareLibsFQN = new HashSet<>(); + for (IArduinoLibraryVersion curLib : myLibraries.values()) { + if (curLib.isHardwareLib()) { + IPath libPath = curLib.getInstallPath(); + if (referencingLibPath.isPrefixOf(libPath) || referenceLibPath.isPrefixOf(libPath)) { + // the hardware lib is ok continue; } // The hardware lib is for a different hardware. - //add it to the lists to reattach + // add it to the lists to reattach hardwareLibsFQN.add(curLib.getFQN().toPortableString()); } } - if(!hardwareLibsFQN.isEmpty()) { - Map boardLibs =LibraryManager.getLibrariesHarware(boardDesc); - for(String curReplaceLibFQN: hardwareLibsFQN) { - IArduinoLibraryVersion newLib =boardLibs.get(curReplaceLibFQN); - if(newLib!=null) { + if (!hardwareLibsFQN.isEmpty()) { + Map boardLibs = LibraryManager.getLibrariesHarware(boardDesc); + for (String curReplaceLibFQN : hardwareLibsFQN) { + IArduinoLibraryVersion newLib = boardLibs.get(curReplaceLibFQN); + if (newLib != null) { // a library with the same name was found so use this one myLibraries.put(newLib.getName(), newLib); - }else { - //no new library was found remove the old lib + } else { + // no new library was found remove the old lib myLibraries.remove(Path.fromPortableString(curReplaceLibFQN).lastSegment()); } } } } - - @Override public void reAttachLibraries() { upDateHardwareLibraries(); - IProgressMonitor monitor = new NullProgressMonitor(); IFolder libFolder = getArduinoLibraryFolder(); // Remove all existing lib folders that are not known or are linking to the // wrong lib try { - if(!libFolder.exists()) { + if (!libFolder.exists()) { libFolder.create(true, true, new NullProgressMonitor()); } for (IResource curResource : libFolder.members()) { @@ -666,25 +660,62 @@ public void reAttachLibraries() { @Override public Map getUsedLibraries() { - myLibraries=getLibrariesFromLinks(); + myLibraries = getLibrariesFromLinks(); return new HashMap<>(myLibraries); } @Override public boolean addLibraries(Collection librartiesToAdd) { - boolean ret = false; + if (librartiesToAdd == null || librartiesToAdd.isEmpty()) { + return false; + } + List foldersToRemoveFromBuildPath = new LinkedList<>(); IFolder libFolder = getArduinoLibraryFolder(); + ICConfigurationDescription confdesc = getAutoBuildDesc().getCdtConfigurationDescription(); + ICSourceEntry[] sourceEntries = confdesc.getSourceEntries(); for (IArduinoLibraryVersion curLib : librartiesToAdd) { if (curLib == null) { continue; } - Helpers.LinkFolderToFolder(curLib.getInstallPath(), libFolder.getFolder(curLib.getName())); + IFolder newLibFolder = libFolder.getFolder(curLib.getName()); + Helpers.LinkFolderToFolder(curLib.getInstallPath(), newLibFolder); myLibraries.put(curLib.getName(), curLib); - ret = true; + + // exclude bad folders + File[] subFolders; + subFolders = curLib.getInstallPath().toFile().listFiles(); + + for (File subFolder : subFolders) { + if (subFolder.isFile()) { + continue; + } + String curName = subFolder.getName(); + if ("src".equals(curName)) { //$NON-NLS-1$ + continue; + } + IFolder curFolder = newLibFolder.getFolder(curName); + if (CDataUtil.isExcluded(curFolder.getFullPath(), sourceEntries)) { + continue; + } + foldersToRemoveFromBuildPath.add(curFolder); + } } - return ret; + if (foldersToRemoveFromBuildPath.isEmpty()) { + return false; + } + try { + for (IResource subFolder : foldersToRemoveFromBuildPath) { + sourceEntries = CDataUtil.setExcluded(subFolder.getFullPath(), true, true, sourceEntries); + } + confdesc.setSourceEntries(sourceEntries); + } catch (CoreException e) { + // ignore error + e.printStackTrace(); + } + return true; } + @Override public boolean removeLibraries(Collection librariesToRemove) { boolean ret = false; @@ -713,26 +744,26 @@ public void setLibraries(Set selectedLibraries) { @Override public Set getTeamDefaultExclusionKeys(String name) { - Set ret=new HashSet<>(); - ret.add(name+DOT+KEY_SLOEBER_UPLOAD_PORT); + Set ret = new HashSet<>(); + ret.add(name + DOT + KEY_SLOEBER_UPLOAD_PORT); return ret; } @Override public boolean equals(AutoBuildConfigurationExtensionDescription base) { - if(!(base instanceof SloeberConfiguration)) { + if (!(base instanceof SloeberConfiguration)) { return false; } - SloeberConfiguration other=(SloeberConfiguration)base; + SloeberConfiguration other = (SloeberConfiguration) base; if( myBoardDescription.equals(other.myBoardDescription) && myOtherDesc.equals(other.myOtherDesc) && myCompileDescription.equals(other.myCompileDescription) && myLibraries.size()==other.myLibraries.size()) { - for(Entry curLib:myLibraries.entrySet()) { - String key=curLib.getKey(); - IArduinoLibraryVersion localValue=curLib.getValue(); - IArduinoLibraryVersion otherValue=other.myLibraries.get(key); - if(!localValue.equals(otherValue)) { + for (Entry curLib : myLibraries.entrySet()) { + String key = curLib.getKey(); + IArduinoLibraryVersion localValue = curLib.getValue(); + IArduinoLibraryVersion otherValue = other.myLibraries.get(key); + if (!localValue.equals(otherValue)) { return false; } } @@ -743,19 +774,19 @@ public boolean equals(AutoBuildConfigurationExtensionDescription base) { @Override public LinkedHashMap getPrebuildSteps() { - LinkedHashMap ret=new LinkedHashMap<>(); - LinkedHashSet hookNamess=new LinkedHashSet<>(); + LinkedHashMap ret = new LinkedHashMap<>(); + LinkedHashSet hookNamess = new LinkedHashSet<>(); hookNamess.add("prebuild"); //$NON-NLS-1$ - ret.putAll(myBoardDescription.getHookSteps(hookNamess,getAutoBuildDescription())); + ret.putAll(myBoardDescription.getHookSteps(hookNamess, getAutoBuildDescription())); return ret; } @Override public LinkedHashMap getPostbuildSteps() { - LinkedHashMap ret=new LinkedHashMap<>(); - LinkedHashSet hookNamess=new LinkedHashSet<>(); + LinkedHashMap ret = new LinkedHashMap<>(); + LinkedHashSet hookNamess = new LinkedHashSet<>(); hookNamess.add("postbuild"); //$NON-NLS-1$ - ret.putAll(myBoardDescription.getHookSteps(hookNamess,getAutoBuildDescription())); + ret.putAll(myBoardDescription.getHookSteps(hookNamess, getAutoBuildDescription())); return ret; } @@ -765,7 +796,7 @@ public LinkedHashMap getPostbuildSteps() { * we can not update the disk * at the time the SloeberConfiguration is changed * - * When a configuration becomes "the active" configuration this method will + * When a configuration becomes "the active" configuration this method will * create/update the necessary resources on disk. * This apply is when this configuration is new. */ @@ -773,15 +804,15 @@ public void aboutToApplyConfigChange() { // try { // myLibraries =getLibrariesFromLinks(); // } catch (CoreException e) { -// // TODO Auto-generated catch block +// // ignore exception // e.printStackTrace(); // } - if(updateSourceEntries()) { - //the config has been renamed; + if (updateSourceEntries()) { + // the config has been renamed; // remove the library links removeLibraryLinks(); } - configureWhenDirty() ; + configureWhenDirty(); } @@ -799,56 +830,54 @@ public void appliedConfigChange() { */ private boolean updateSourceEntries() { - IFolder curArduinoConfigurationfolder=getArduinoConfigurationFolder(); - IFolder oldArduinoConfigurationfolder=null; + IFolder curArduinoConfigurationfolder = getArduinoConfigurationFolder(); + IFolder oldArduinoConfigurationfolder = null; - //update the source entries + // update the source entries - ICSourceEntry[] orgSourceEntries = getAutoBuildDesc().getCdtConfigurationDescription().getSourceEntries(); - ICSourceEntry[] newSourceEntries=new ICSourceEntry[orgSourceEntries.length]; - for ( int curItem=0; curItem newChangedProjects = new HashSet<>(); + private static Set newChangedProjects = new HashSet<>(); private static Map myIncludeHeaderReplacement; @Override public void indexChanged(IIndexChangeEvent event) { if (!InstancePreferences.getAutomaticallyImportLibraries()) { + //The automagically adding of libraries has been disabled newChangedProjects.clear(); return; } IProject project = event.getAffectedProject().getProject(); - ISloeberConfiguration sloeberConfDesc = ISloeberConfiguration.getActiveConfig(project); -// if (IndexerController.isPosponed(project)) { -// // Do not update libraries if project is in creation -// return; -// } - if (sloeberConfDesc == null) { - return; - } - if (!newChangedProjects.contains(sloeberConfDesc)) { - Activator.log(new Status(Const.SLOEBER_STATUS_DEBUG, Activator.getId(), - "Index of project changed :" + project.getName())); //$NON-NLS-1$ - newChangedProjects.add(sloeberConfDesc); + if(project!=null) { + newChangedProjects.add(project); } + } @Override @@ -73,25 +66,33 @@ public void indexChanged(IIndexerStateEvent event) { if (event.indexerIsIdle()) { if (!newChangedProjects.isEmpty()) { - Set curChangedProjects = new HashSet<>(newChangedProjects); + CCorePlugin cCorePlugin = CCorePlugin.getDefault(); + Set curChangedProjects = new HashSet<>(newChangedProjects); newChangedProjects.clear(); - for (ISloeberConfiguration sloeberConfDesc : curChangedProjects) { - String projectName = sloeberConfDesc.getProject().getName(); + for (IProject curProject : curChangedProjects) { + ICProjectDescription projectDescription =cCorePlugin.getProjectDescription(curProject, true); + + ISloeberConfiguration sloeberConfDesc = ISloeberConfiguration.getActiveConfig(projectDescription); + if (sloeberConfDesc == null) { + return; + } try { Activator.log(new Status(Const.SLOEBER_STATUS_DEBUG, Activator.getId(), - "Looking for libraries for project :" + projectName)); //$NON-NLS-1$ - checkLibraries(sloeberConfDesc); + "Looking for libraries for project :" + curProject.getName())); //$NON-NLS-1$ + if (checkLibraries(sloeberConfDesc)) { + cCorePlugin.setProjectDescription(curProject,projectDescription); + } } catch (Exception e) { Activator.log(new Status(IStatus.WARNING, Activator.getId(), Messages.Failed_To_Add_Libraries, e)); } Activator.log(new Status(Const.SLOEBER_STATUS_DEBUG, Activator.getId(), - "libraries added for project " + projectName)); //$NON-NLS-1$ + "libraries added for project " + curProject.getName())); //$NON-NLS-1$ } } } } - private static void checkLibraries(ISloeberConfiguration SloeberCfg) { + private static boolean checkLibraries(ISloeberConfiguration SloeberCfg) { Map alreadyAddedLibs = SloeberCfg.getUsedLibraries(); Set UnresolvedIncludedHeaders = getUnresolvedProjectIncludes(SloeberCfg.getProject()); // remove pgmspace as it gives a problem @@ -111,7 +112,7 @@ private static void checkLibraries(ISloeberConfiguration SloeberCfg) { } if (UnresolvedIncludedHeaders.isEmpty()) { - return; + return false; } for (Map.Entry entry : getIncludeHeaderReplacement().entrySet()) { @@ -160,13 +161,15 @@ private static void checkLibraries(ISloeberConfiguration SloeberCfg) { toInstallLibString=toInstallLibString+SPACE+curlib.getFQN(); } } + boolean ret =false; if (!toInstallLibs.isEmpty()) { // there are possible libraries to add Activator.log(new Status(IStatus.INFO, CORE_PLUGIN_ID, "list of libraries to add to project " //$NON-NLS-1$ + SloeberCfg.getProject().getName() + COLON + SPACE + toInstallLibString)); - SloeberCfg.addLibraries(toInstallLibs); + ret=ret||SloeberCfg.addLibraries(toInstallLibs); } + return ret; } private static Set getUnresolvedProjectIncludes(IProject iProject) { From 074a1fd4b14b32e05e4cf811aa0b7a5d194353aa Mon Sep 17 00:00:00 2001 From: jan Date: Fri, 17 Jan 2025 22:46:30 +0100 Subject: [PATCH 098/115] creating a autobuild repository category --- io.sloeber.updatesite/category.xml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/io.sloeber.updatesite/category.xml b/io.sloeber.updatesite/category.xml index 1c9acea9..84bdb9f3 100644 --- a/io.sloeber.updatesite/category.xml +++ b/io.sloeber.updatesite/category.xml @@ -6,6 +6,12 @@ + + + + + + Sloeber, Arduino development in the Eclipse IDE. @@ -16,7 +22,8 @@ Only select this if you are upgrading the Sloeber IDE - - - + + + + From 15f4ea7867a85dab1ce30ab9e563e1a630279849 Mon Sep 17 00:00:00 2001 From: jan Date: Mon, 17 Feb 2025 01:09:57 +0100 Subject: [PATCH 099/115] Add library shows SD in boards and managed #1712 --- .../core/api/ISloeberConfiguration.java | 5 +- .../core/internal/SloeberConfiguration.java | 60 +++++++++++-------- .../core/listeners/IndexerListener.java | 9 ++- .../io/sloeber/ui/Import_Libraries_Page.java | 5 +- 4 files changed, 45 insertions(+), 34 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/core/api/ISloeberConfiguration.java b/io.sloeber.core/src/io/sloeber/core/api/ISloeberConfiguration.java index 6cf425a5..13680e44 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/ISloeberConfiguration.java +++ b/io.sloeber.core/src/io/sloeber/core/api/ISloeberConfiguration.java @@ -12,6 +12,7 @@ import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; @@ -119,7 +120,7 @@ public static ISloeberConfiguration getConfig(IAutoBuildConfigurationDescription * Add libraries to the configurations * Some libraries contain subfolders that need to be ignoreed. * Therefore the configuration description may change. - * + * * @param librariesToAdd * @return true if when cCorePlugin.setProjectDescription needs to be called */ @@ -136,7 +137,7 @@ public static ISloeberConfiguration getConfig(IAutoBuildConfigurationDescription */ void reAttachLibraries(); - Map getUsedLibraries(); + Map getUsedLibraries(); void setLibraries(Set selectedLibraries); IFolder getArduinoRootFolder(); diff --git a/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java b/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java index 7ed8132b..c883f3c1 100644 --- a/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java +++ b/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java @@ -9,7 +9,6 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.Set; import static io.sloeber.core.api.Common.*; import static io.sloeber.core.api.Const.*; @@ -57,7 +56,7 @@ public class SloeberConfiguration extends AutoBuildConfigurationExtensionDescrip private OtherDescription myOtherDesc; private CompileDescription myCompileDescription; // a map of foldername library - private Map myLibraries = new HashMap<>(); + private Map myLibraries = new HashMap<>(); // operational data private boolean myMemoryIsDirty = true; @@ -518,8 +517,8 @@ public Set getIncludeFolders() { * @return * @throws CoreException */ - private Map getLibrariesFromLinks() { - Map ret = new HashMap<>(); + private Map getLibrariesFromLinks() { + Map ret = new HashMap<>(); IFolder libFolder = getArduinoLibraryFolder(); if (!libFolder.exists()) { return ret; @@ -528,16 +527,22 @@ private Map getLibrariesFromLinks() { for (IResource curResource : libFolder.members()) { if (curResource instanceof IFolder) { IFolder curFolder = (IFolder) curResource; - IArduinoLibraryVersion curLib = myLibraries.get(curFolder.getName()); + IArduinoLibraryVersion curLib=null; + for(IArduinoLibraryVersion curknowLib: myLibraries.values()) { + if(curknowLib.getName().equals(curFolder.getName())){ + curLib=curknowLib; + continue; + } + } if (curLib != null) { - // We knbow the lib so it is ok - ret.put(curLib.getName(), curLib); + // We know the lib so it is ok + ret.put(curLib.getFQN(), curLib); continue; } curLib = LibraryManager.getLibraryVersionFromLocation(curFolder, getBoardDescription()); if (curLib != null) { - ret.put(curLib.getName(), curLib); + ret.put(curLib.getFQN(), curLib); } } } @@ -555,8 +560,8 @@ private Map getLibrariesFromLinks() { private void removeLibraryLinks() { IProgressMonitor monitor = new NullProgressMonitor(); IFolder libFolder = getArduinoLibraryFolder(); - for (String curLib : myLibraries.keySet()) { - IFolder curLibFolder = libFolder.getFolder(curLib); + for (IArduinoLibraryVersion curLib : myLibraries.values()) { + IFolder curLibFolder = libFolder.getFolder(curLib.getName()); if (curLibFolder.exists()) { try { curLibFolder.delete(true, monitor); @@ -592,7 +597,7 @@ private void upDateHardwareLibraries() { if (referencingLibPath == null) { referencingLibPath = referenceLibPath; } - Set hardwareLibsFQN = new HashSet<>(); + Set hardwareLibsFQN = new HashSet<>(); for (IArduinoLibraryVersion curLib : myLibraries.values()) { if (curLib.isHardwareLib()) { IPath libPath = curLib.getInstallPath(); @@ -602,19 +607,19 @@ private void upDateHardwareLibraries() { } // The hardware lib is for a different hardware. // add it to the lists to reattach - hardwareLibsFQN.add(curLib.getFQN().toPortableString()); + hardwareLibsFQN.add(curLib.getFQN()); } } if (!hardwareLibsFQN.isEmpty()) { Map boardLibs = LibraryManager.getLibrariesHarware(boardDesc); - for (String curReplaceLibFQN : hardwareLibsFQN) { - IArduinoLibraryVersion newLib = boardLibs.get(curReplaceLibFQN); + for (IPath curReplaceLibFQN : hardwareLibsFQN) { + IArduinoLibraryVersion newLib = boardLibs.get(curReplaceLibFQN.toPortableString()); if (newLib != null) { // a library with the same name was found so use this one - myLibraries.put(newLib.getName(), newLib); + myLibraries.put(newLib.getFQN(), newLib); } else { // no new library was found remove the old lib - myLibraries.remove(Path.fromPortableString(curReplaceLibFQN).lastSegment()); + myLibraries.remove(curReplaceLibFQN); } } } @@ -635,7 +640,14 @@ public void reAttachLibraries() { for (IResource curResource : libFolder.members()) { if (curResource instanceof IFolder) { IFolder curFolder = (IFolder) curResource; - IArduinoLibraryVersion curLib = myLibraries.get(curFolder.getName()); + + IArduinoLibraryVersion curLib =null; + for(IArduinoLibraryVersion curknowLib: myLibraries.values()) { + if(curknowLib.getName().equals(curFolder.getName())){ + curLib=curknowLib; + } + } + if ((curLib == null) || (!curLib.getInstallPath().equals(curFolder.getLocation()))) { try { curFolder.delete(true, monitor); @@ -659,7 +671,7 @@ public void reAttachLibraries() { } @Override - public Map getUsedLibraries() { + public Map getUsedLibraries() { myLibraries = getLibrariesFromLinks(); return new HashMap<>(myLibraries); } @@ -679,7 +691,7 @@ public boolean addLibraries(Collection librartiesToAdd) } IFolder newLibFolder = libFolder.getFolder(curLib.getName()); Helpers.LinkFolderToFolder(curLib.getInstallPath(), newLibFolder); - myLibraries.put(curLib.getName(), curLib); + myLibraries.put(curLib.getFQN(), curLib); // exclude bad folders File[] subFolders; @@ -722,9 +734,9 @@ public boolean removeLibraries(Collection librariesToRem IProgressMonitor monitor = new NullProgressMonitor(); IFolder libFolder = getArduinoLibraryFolder(); for (IArduinoLibraryVersion curLib : librariesToRemove) { - if (myLibraries.containsKey(curLib.getName())) { + if (myLibraries.containsKey(curLib.getFQN())) { ret = true; - myLibraries.remove(curLib.getName()); + myLibraries.remove(curLib.getFQN()); try { libFolder.getFolder(curLib.getName()).delete(true, monitor); } catch (CoreException e) { @@ -759,10 +771,8 @@ public boolean equals(AutoBuildConfigurationExtensionDescription base) { myOtherDesc.equals(other.myOtherDesc) && myCompileDescription.equals(other.myCompileDescription) && myLibraries.size()==other.myLibraries.size()) { - for (Entry curLib : myLibraries.entrySet()) { - String key = curLib.getKey(); - IArduinoLibraryVersion localValue = curLib.getValue(); - IArduinoLibraryVersion otherValue = other.myLibraries.get(key); + for ( IArduinoLibraryVersion localValue : myLibraries.values()) { + IArduinoLibraryVersion otherValue = other.myLibraries.get(localValue.getFQN()); if (!localValue.equals(otherValue)) { return false; } diff --git a/io.sloeber.core/src/io/sloeber/core/listeners/IndexerListener.java b/io.sloeber.core/src/io/sloeber/core/listeners/IndexerListener.java index 35d9f833..f55950bf 100644 --- a/io.sloeber.core/src/io/sloeber/core/listeners/IndexerListener.java +++ b/io.sloeber.core/src/io/sloeber/core/listeners/IndexerListener.java @@ -93,7 +93,6 @@ public void indexChanged(IIndexerStateEvent event) { } private static boolean checkLibraries(ISloeberConfiguration SloeberCfg) { - Map alreadyAddedLibs = SloeberCfg.getUsedLibraries(); Set UnresolvedIncludedHeaders = getUnresolvedProjectIncludes(SloeberCfg.getProject()); // remove pgmspace as it gives a problem UnresolvedIncludedHeaders.remove("pgmspace"); //$NON-NLS-1$ @@ -102,12 +101,12 @@ private static boolean checkLibraries(ISloeberConfiguration SloeberCfg) { } //The line below is for cases where libs have been excluded from the build - for(String curLib:alreadyAddedLibs.keySet()) { - IFolder libFolder =SloeberCfg.getArduinoLibraryFolder().getFolder(curLib); + for(IArduinoLibraryVersion curLib:SloeberCfg.getUsedLibraries().values()) { + IFolder libFolder =SloeberCfg.getArduinoLibraryFolder().getFolder(curLib.getName()); if(libFolder.exists()) { - UnresolvedIncludedHeaders.remove(curLib); + UnresolvedIncludedHeaders.remove(curLib.getName()); }else { - UnresolvedIncludedHeaders.add(curLib); + UnresolvedIncludedHeaders.add(curLib.getName()); } } diff --git a/io.sloeber.ui/src/io/sloeber/ui/Import_Libraries_Page.java b/io.sloeber.ui/src/io/sloeber/ui/Import_Libraries_Page.java index 323f8c6b..d3155433 100644 --- a/io.sloeber.ui/src/io/sloeber/ui/Import_Libraries_Page.java +++ b/io.sloeber.ui/src/io/sloeber/ui/Import_Libraries_Page.java @@ -11,6 +11,7 @@ import org.eclipse.cdt.core.settings.model.ICProjectDescription; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.swt.SWT; @@ -67,7 +68,7 @@ public void createControl(Composite parent) { class ItemSorter { private TreeMap myItems = new TreeMap<>(); private IArduinoLibraryVersion myLib = null; - private static Map myCurrentInstalledLibs =null; + private static Map myCurrentInstalledLibs =null; ItemSorter() { } @@ -87,7 +88,7 @@ public void createChildren(TreeItem curItem) { if (myLib == null) { curItem.setGrayed(true); }else { - boolean isSelected = myCurrentInstalledLibs.get(myLib.getName()) != null; + boolean isSelected = myCurrentInstalledLibs.get(myLib.getFQN()) != null; curItem.setChecked(isSelected); curItem.setData(myLib); if (isSelected) { From 524b13c872dd39a4533fec0a7983c3018474277d Mon Sep 17 00:00:00 2001 From: jan Date: Tue, 18 Feb 2025 02:13:01 +0100 Subject: [PATCH 100/115] Sometimes Calculating size the arduino way does not work Calling the script needed in windows to get piping to work was not found. Though it clearly was there. I switched to using FQN to make it work. --- .../sloeber/core/api/CompileDescription.java | 958 +++++++++--------- .../src/io/sloeber/core/api/Const.java | 1 + .../core/builder/SloeberBuilderExtension.java | 2 +- 3 files changed, 480 insertions(+), 481 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/core/api/CompileDescription.java b/io.sloeber.core/src/io/sloeber/core/api/CompileDescription.java index 6b033e5b..9c66bf0d 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/CompileDescription.java +++ b/io.sloeber.core/src/io/sloeber/core/api/CompileDescription.java @@ -15,432 +15,430 @@ public class CompileDescription { - public enum DebugLevels { + public enum DebugLevels { OPTIMIZED_FOR_DEBUG, OPTIMIZED_FOR_RELEASE, CUSTOM; - @Override + @Override public String toString() { - switch (this) { - case OPTIMIZED_FOR_DEBUG: - return Messages.CompileDescription_OptimizedForDebug; - case OPTIMIZED_FOR_RELEASE: - return Messages.CompileDescription_OptimizedForRelease; - case CUSTOM: - return Messages.CompileDescription_CustomDebugLevel; + switch (this) { + case OPTIMIZED_FOR_DEBUG: + return Messages.CompileDescription_OptimizedForDebug; + case OPTIMIZED_FOR_RELEASE: + return Messages.CompileDescription_OptimizedForRelease; + case CUSTOM: + return Messages.CompileDescription_CustomDebugLevel; default: break; - } + } return super.toString(); } - private String myCustomDebugLevel = EMPTY; - /** - * Set the custom command but only if the warning level is CUSTOM - * + private String myCustomDebugLevel = EMPTY; + + /** + * Set the custom command but only if the warning level is CUSTOM + * * @param customCommand * the command that needs to be used - */ - public void setCustomDebugLevel(String customDebugLevel) { - if (this == CUSTOM) { - myCustomDebugLevel = customDebugLevel; - } - } - - public void setCustomDebugLevel(String customDebugLevel, boolean force) { - if (force) { - myCustomDebugLevel = customDebugLevel; - } else { - setCustomDebugLevel(customDebugLevel); - } - - } - - public String getCustomDebugLevel() { - return myCustomDebugLevel; - } - - /** - * Get the string that should be put in the environment variable that places the - * warning part in the command string This is a non expanded string for Arduino - * IDE supported options This is what the user typed in the GUI in the CUSTOM - * case - * - * @return - */ - public String getEnvValue() { - switch (this) { - case OPTIMIZED_FOR_DEBUG: - return "${compiler.optimization_flags.debug}"; //$NON-NLS-1$ - case OPTIMIZED_FOR_RELEASE: - return "${compiler.optimization_flags.release}"; //$NON-NLS-1$ - case CUSTOM: - return myCustomDebugLevel; + */ + public void setCustomDebugLevel(String customDebugLevel) { + if (this == CUSTOM) { + myCustomDebugLevel = customDebugLevel; + } + } + + public void setCustomDebugLevel(String customDebugLevel, boolean force) { + if (force) { + myCustomDebugLevel = customDebugLevel; + } else { + setCustomDebugLevel(customDebugLevel); + } + + } + + public String getCustomDebugLevel() { + return myCustomDebugLevel; + } + + /** + * Get the string that should be put in the environment variable that places the + * warning part in the command string This is a non expanded string for Arduino + * IDE supported options This is what the user typed in the GUI in the CUSTOM + * case + * + * @return + */ + public String getEnvValue() { + switch (this) { + case OPTIMIZED_FOR_DEBUG: + return "${compiler.optimization_flags.debug}"; //$NON-NLS-1$ + case OPTIMIZED_FOR_RELEASE: + return "${compiler.optimization_flags.release}"; //$NON-NLS-1$ + case CUSTOM: + return myCustomDebugLevel; default: return "${compiler.optimization_flags.release}"; //$NON-NLS-1$ - } - - } + } - } + } + } - public enum WarningLevels { + public enum WarningLevels { @SuppressWarnings("hiding") ALL, MORE, DEFAULT, NONE, CUSTOM; - @Override + @Override public String toString() { - switch (this) { - case MORE: - return Messages.CompileDescription_WarningsMore; - case ALL: - return Messages.CompileDescription_WarningsAll; - case CUSTOM: - return Messages.CompileDescription_WarningsCustom; - case DEFAULT: - return Messages.CompileDescription_WarningsDefault; - case NONE: - return Messages.CompileDescription_WarningsNone; + switch (this) { + case MORE: + return Messages.CompileDescription_WarningsMore; + case ALL: + return Messages.CompileDescription_WarningsAll; + case CUSTOM: + return Messages.CompileDescription_WarningsCustom; + case DEFAULT: + return Messages.CompileDescription_WarningsDefault; + case NONE: + return Messages.CompileDescription_WarningsNone; default: break; - } + } return super.toString(); } private String myCustomWarningLevel = EMPTY; - /** - * Set the custom command but only if the warning level is CUSTOM - * + /** + * Set the custom command but only if the warning level is CUSTOM + * * @param customCommand * the command that needs to be used - */ - public void setCustomWarningLevel(String customWarningLevel) { - if (this == CUSTOM) { - myCustomWarningLevel = customWarningLevel; - } - } - - public void setCustomWarningLevel(String customWarningLevel, boolean force) { - if (force) { - myCustomWarningLevel = customWarningLevel; - } else { - setCustomWarningLevel(customWarningLevel); - } - - } - - public String getCustomWarningLevel() { - return myCustomWarningLevel; - } - - /** - * Get the string that should be put in the environment variable that places the - * warning part in the command string This is a non expanded string for Arduino - * IDE supported options This is what the user typed in the GUI in the CUSTOM - * case - * - * @return - */ - public String getEnvValue() { - switch (this) { - case MORE: - return "${compiler.warning_flags.more}"; //$NON-NLS-1$ - case ALL: - return "${compiler.warning_flags.all}"; //$NON-NLS-1$ - case CUSTOM: - return myCustomWarningLevel; - case DEFAULT: - return "${compiler.warning_flags.default}";//$NON-NLS-1$ - case NONE: - return "${compiler.warning_flags.none}"; //$NON-NLS-1$ + */ + public void setCustomWarningLevel(String customWarningLevel) { + if (this == CUSTOM) { + myCustomWarningLevel = customWarningLevel; + } + } + + public void setCustomWarningLevel(String customWarningLevel, boolean force) { + if (force) { + myCustomWarningLevel = customWarningLevel; + } else { + setCustomWarningLevel(customWarningLevel); + } + + } + + public String getCustomWarningLevel() { + return myCustomWarningLevel; + } + + /** + * Get the string that should be put in the environment variable that places the + * warning part in the command string This is a non expanded string for Arduino + * IDE supported options This is what the user typed in the GUI in the CUSTOM + * case + * + * @return + */ + public String getEnvValue() { + switch (this) { + case MORE: + return "${compiler.warning_flags.more}"; //$NON-NLS-1$ + case ALL: + return "${compiler.warning_flags.all}"; //$NON-NLS-1$ + case CUSTOM: + return myCustomWarningLevel; + case DEFAULT: + return "${compiler.warning_flags.default}";//$NON-NLS-1$ + case NONE: + return "${compiler.warning_flags.none}"; //$NON-NLS-1$ default: break; - } - return "${compiler.warning_flags.all}"; //$NON-NLS-1$ - } + } + return "${compiler.warning_flags.all}"; //$NON-NLS-1$ + } - } + } - public enum SizeCommands { + public enum SizeCommands { - ARDUINO_WAY, AVR_ALTERNATIVE, RAW_RESULT, CUSTOM; + ARDUINO_WAY, AVR_ALTERNATIVE, RAW_RESULT, CUSTOM; - @Override + @Override public String toString() { - switch (this) { - case ARDUINO_WAY: - return Messages.CompileDescription_SizeArduinoWay; - case AVR_ALTERNATIVE: - return Messages.CompileDescription_SizeAVRAlternative; - case RAW_RESULT: - return Messages.CompileDescription_SizeRawResult; - case CUSTOM: - return Messages.CompileDescription_SizeCustom; + switch (this) { + case ARDUINO_WAY: + return Messages.CompileDescription_SizeArduinoWay; + case AVR_ALTERNATIVE: + return Messages.CompileDescription_SizeAVRAlternative; + case RAW_RESULT: + return Messages.CompileDescription_SizeRawResult; + case CUSTOM: + return Messages.CompileDescription_SizeCustom; default: break; - } + } return super.toString(); } private String myCustomSizeCommand = EMPTY; - /** - * Set the custom command but only if the warning level is CUSTOM - * + /** + * Set the custom command but only if the warning level is CUSTOM + * * @param customCommand * the command that needs to be used - */ - public void setCustomSizeCommand(String customWarningLevel) { - if (this == CUSTOM) { - myCustomSizeCommand = customWarningLevel; - } - } - - public void setCustomSizeCommand(String customWarningLevel, boolean force) { - if (force) { - myCustomSizeCommand = customWarningLevel; - } else { - setCustomSizeCommand(customWarningLevel); - } - - } - - public String getCustomSizeCommand() { - return myCustomSizeCommand; - } - - /** - * Get the string that should be put in the environment variable that places the - * warning part in the command string This is a non expanded string for Arduino - * IDE supported options This is what the user typed in the GUI in the CUSTOM - * case - * - * @return - */ - public String getEnvValue() { - switch (this) { - case ARDUINO_WAY: - if(isWindows) { - return "arduino-size.bat"; //$NON-NLS-1$ - } - return "${sloeber.size_command.awk}"; //$NON-NLS-1$ - case AVR_ALTERNATIVE: - return "${sloeber.size_command.avr}"; //$NON-NLS-1$ - case RAW_RESULT: - return "${recipe.size.pattern}"; //$NON-NLS-1$ - case CUSTOM: - return myCustomSizeCommand; + */ + public void setCustomSizeCommand(String customWarningLevel) { + if (this == CUSTOM) { + myCustomSizeCommand = customWarningLevel; + } + } + + public void setCustomSizeCommand(String customWarningLevel, boolean force) { + if (force) { + myCustomSizeCommand = customWarningLevel; + } else { + setCustomSizeCommand(customWarningLevel); + } + + } + + public String getCustomSizeCommand() { + return myCustomSizeCommand; + } + + /** + * Get the string that should be put in the environment variable that places the + * warning part in the command string This is a non expanded string for Arduino + * IDE supported options This is what the user typed in the GUI in the CUSTOM + * case + * + * @return + */ + public String getEnvValue() { + switch (this) { + case ARDUINO_WAY: + if (isWindows) { + return Common.makeEnvironmentVar(ENV_KEY_BUILD_PATH) + SLACH + ARDUINO_SIZE; + } + return "${sloeber.size_command.awk}"; //$NON-NLS-1$ + case AVR_ALTERNATIVE: + return "${sloeber.size_command.avr}"; //$NON-NLS-1$ + case RAW_RESULT: + return "${recipe.size.pattern}"; //$NON-NLS-1$ + case CUSTOM: + return myCustomSizeCommand; default: break; - } - return "${recipe.size.pattern}"; //$NON-NLS-1$ - } - } - - private WarningLevels myWarningLevel = WarningLevels.ALL; - private SizeCommands mySizeCommand = SizeCommands.RAW_RESULT; - private DebugLevels myDebugLevel = DebugLevels.OPTIMIZED_FOR_RELEASE; - - private boolean myEnableParallelBuild = false; - private String my_CPP_CompileOptions = new String(); - private String my_C_CompileOptions = new String(); - private String my_C_andCPP_CompileOptions = new String(); - private String my_Assembly_CompileOptions = new String(); - private String my_Archive_CompileOptions = new String(); - private String my_Link_CompileOptions = new String(); - private String my_All_CompileOptions = new String(); - - private static final String ENV_KEY_WARNING_LEVEL = "compiler.warning_flags"; //$NON-NLS-1$ - private static final String ENV_KEY_DEBUG_LEVEL = "compiler.optimization_flags"; //$NON-NLS-1$ - - - private static final String SLOEBER_ADDITIONAL_COMPILE_OPTIONS = ENV_KEY_SLOEBER_START + "extra.compile"; //$NON-NLS-1$ - private static final String SLOEBER_ADDITIONAL_C_COMPILE_OPTIONS = ENV_KEY_SLOEBER_START + "extra.c.compile"; //$NON-NLS-1$ - private static final String SLOEBER_ADDITIONAL_CPP_COMPILE_OPTIONS = ENV_KEY_SLOEBER_START + "extra.cpp.compile"; //$NON-NLS-1$ - private static final String SLOEBER_WARNING_LEVEL = ENV_KEY_SLOEBER_START + "warning_level"; //$NON-NLS-1$ - private static final String SLOEBER_SIZE_TYPE = ENV_KEY_SLOEBER_START + "size.type"; //$NON-NLS-1$ - private static final String SLOEBER_SIZE_CUSTOM = ENV_KEY_SLOEBER_START + "size.custom"; //$NON-NLS-1$ - - private static final String SLOEBER_WARNING_LEVEL_CUSTOM = SLOEBER_WARNING_LEVEL + DOT + "custom"; //$NON-NLS-1$ - private static final String SLOEBER_DEBUG_LEVEL = ENV_KEY_SLOEBER_START + "debug_level"; //$NON-NLS-1$ - private static final String SLOEBER_DEBUG_LEVEL_CUSTOM = SLOEBER_DEBUG_LEVEL + DOT + "custom"; //$NON-NLS-1$ - - // private static final String SLOEBER_SIZE_COMMAND = ENV_KEY_SLOEBER_START + - // "alt_size_command"; //$NON-NLS-1$ - private static final String SLOEBER_SIZE_SWITCH = ENV_KEY_SLOEBER_START + "size.switch"; //$NON-NLS-1$ - private static final String SLOEBER_ASSEMBLY_COMPILE_OPTIONS = ENV_KEY_SLOEBER_START + "extra.assembly"; //$NON-NLS-1$ - private static final String SLOEBER_ARCHIVE_COMPILE_OPTIONS = ENV_KEY_SLOEBER_START + "extra.archive"; //$NON-NLS-1$ - private static final String SLOEBER_LINK_COMPILE_OPTIONS = ENV_KEY_SLOEBER_START + "extra.link"; //$NON-NLS-1$ - private static final String SLOEBER_ALL_COMPILE_OPTIONS = ENV_KEY_SLOEBER_START + "extra.all"; //$NON-NLS-1$ - - public WarningLevels getWarningLevel() { - return myWarningLevel; - } - - public void setWarningLevel(WarningLevels warningLevel) { - myWarningLevel = warningLevel; - } - - public DebugLevels getDebugLevel() { - return myDebugLevel; - } - - public void setDebugLevel(DebugLevels debugLevel) { - myDebugLevel = debugLevel; - } - - - public boolean isParallelBuildEnabled() { - return myEnableParallelBuild; - } - - public void setEnableParallelBuild(boolean parrallelBuild) { - this.myEnableParallelBuild = parrallelBuild; - } - - public void setSizeCommand(SizeCommands sizeCommand) { - this.mySizeCommand = sizeCommand; - } - - public SizeCommands getSizeCommand() { - return mySizeCommand; - } - - public String get_CPP_CompileOptions() { - return this.my_CPP_CompileOptions; - } - - public void set_CPP_CompileOptions(String new_CPP_CompileOptions) { - this.my_CPP_CompileOptions = new_CPP_CompileOptions; - } - - public String get_C_CompileOptions() { - return this.my_C_CompileOptions; - } - - public void set_C_CompileOptions(String new_C_CompileOptions) { - this.my_C_CompileOptions = new_C_CompileOptions; - } - - public String get_C_andCPP_CompileOptions() { - return this.my_C_andCPP_CompileOptions; - } - - public void set_C_andCPP_CompileOptions(String new_C_andCPP_CompileOptions) { - this.my_C_andCPP_CompileOptions = new_C_andCPP_CompileOptions; - } - - public String get_Assembly_CompileOptions() { - return this.my_Assembly_CompileOptions; - } - - public void set_Assembly_CompileOptions(String my_Assembly_CompileOptions) { - this.my_Assembly_CompileOptions = my_Assembly_CompileOptions; - } - - public String get_Archive_CompileOptions() { - return this.my_Archive_CompileOptions; - } - - public void set_Archive_CompileOptions(String my_Archive_CompileOptions) { - this.my_Archive_CompileOptions = my_Archive_CompileOptions; - } - - public String get_Link_CompileOptions() { - return this.my_Link_CompileOptions; - } - - public void set_Link_CompileOptions(String my_Link_CompileOptions) { - this.my_Link_CompileOptions = my_Link_CompileOptions; - } - - public String get_All_CompileOptions() { - return this.my_All_CompileOptions; - } - - public void set_All_CompileOptions(String my_All_CompileOptions) { - this.my_All_CompileOptions = my_All_CompileOptions; - } - - /** - * save the compilation options in this configuration description. - * + } + return "${recipe.size.pattern}"; //$NON-NLS-1$ + } + } + + private WarningLevels myWarningLevel = WarningLevels.ALL; + private SizeCommands mySizeCommand = SizeCommands.RAW_RESULT; + private DebugLevels myDebugLevel = DebugLevels.OPTIMIZED_FOR_RELEASE; + + private boolean myEnableParallelBuild = false; + private String my_CPP_CompileOptions = new String(); + private String my_C_CompileOptions = new String(); + private String my_C_andCPP_CompileOptions = new String(); + private String my_Assembly_CompileOptions = new String(); + private String my_Archive_CompileOptions = new String(); + private String my_Link_CompileOptions = new String(); + private String my_All_CompileOptions = new String(); + + private static final String ENV_KEY_WARNING_LEVEL = "compiler.warning_flags"; //$NON-NLS-1$ + private static final String ENV_KEY_DEBUG_LEVEL = "compiler.optimization_flags"; //$NON-NLS-1$ + + private static final String SLOEBER_ADDITIONAL_COMPILE_OPTIONS = ENV_KEY_SLOEBER_START + "extra.compile"; //$NON-NLS-1$ + private static final String SLOEBER_ADDITIONAL_C_COMPILE_OPTIONS = ENV_KEY_SLOEBER_START + "extra.c.compile"; //$NON-NLS-1$ + private static final String SLOEBER_ADDITIONAL_CPP_COMPILE_OPTIONS = ENV_KEY_SLOEBER_START + "extra.cpp.compile"; //$NON-NLS-1$ + private static final String SLOEBER_WARNING_LEVEL = ENV_KEY_SLOEBER_START + "warning_level"; //$NON-NLS-1$ + private static final String SLOEBER_SIZE_TYPE = ENV_KEY_SLOEBER_START + "size.type"; //$NON-NLS-1$ + private static final String SLOEBER_SIZE_CUSTOM = ENV_KEY_SLOEBER_START + "size.custom"; //$NON-NLS-1$ + + private static final String SLOEBER_WARNING_LEVEL_CUSTOM = SLOEBER_WARNING_LEVEL + DOT + "custom"; //$NON-NLS-1$ + private static final String SLOEBER_DEBUG_LEVEL = ENV_KEY_SLOEBER_START + "debug_level"; //$NON-NLS-1$ + private static final String SLOEBER_DEBUG_LEVEL_CUSTOM = SLOEBER_DEBUG_LEVEL + DOT + "custom"; //$NON-NLS-1$ + + // private static final String SLOEBER_SIZE_COMMAND = ENV_KEY_SLOEBER_START + + // "alt_size_command"; //$NON-NLS-1$ + private static final String SLOEBER_SIZE_SWITCH = ENV_KEY_SLOEBER_START + "size.switch"; //$NON-NLS-1$ + private static final String SLOEBER_ASSEMBLY_COMPILE_OPTIONS = ENV_KEY_SLOEBER_START + "extra.assembly"; //$NON-NLS-1$ + private static final String SLOEBER_ARCHIVE_COMPILE_OPTIONS = ENV_KEY_SLOEBER_START + "extra.archive"; //$NON-NLS-1$ + private static final String SLOEBER_LINK_COMPILE_OPTIONS = ENV_KEY_SLOEBER_START + "extra.link"; //$NON-NLS-1$ + private static final String SLOEBER_ALL_COMPILE_OPTIONS = ENV_KEY_SLOEBER_START + "extra.all"; //$NON-NLS-1$ + + public WarningLevels getWarningLevel() { + return myWarningLevel; + } + + public void setWarningLevel(WarningLevels warningLevel) { + myWarningLevel = warningLevel; + } + + public DebugLevels getDebugLevel() { + return myDebugLevel; + } + + public void setDebugLevel(DebugLevels debugLevel) { + myDebugLevel = debugLevel; + } + + public boolean isParallelBuildEnabled() { + return myEnableParallelBuild; + } + + public void setEnableParallelBuild(boolean parrallelBuild) { + this.myEnableParallelBuild = parrallelBuild; + } + + public void setSizeCommand(SizeCommands sizeCommand) { + this.mySizeCommand = sizeCommand; + } + + public SizeCommands getSizeCommand() { + return mySizeCommand; + } + + public String get_CPP_CompileOptions() { + return this.my_CPP_CompileOptions; + } + + public void set_CPP_CompileOptions(String new_CPP_CompileOptions) { + this.my_CPP_CompileOptions = new_CPP_CompileOptions; + } + + public String get_C_CompileOptions() { + return this.my_C_CompileOptions; + } + + public void set_C_CompileOptions(String new_C_CompileOptions) { + this.my_C_CompileOptions = new_C_CompileOptions; + } + + public String get_C_andCPP_CompileOptions() { + return this.my_C_andCPP_CompileOptions; + } + + public void set_C_andCPP_CompileOptions(String new_C_andCPP_CompileOptions) { + this.my_C_andCPP_CompileOptions = new_C_andCPP_CompileOptions; + } + + public String get_Assembly_CompileOptions() { + return this.my_Assembly_CompileOptions; + } + + public void set_Assembly_CompileOptions(String my_Assembly_CompileOptions) { + this.my_Assembly_CompileOptions = my_Assembly_CompileOptions; + } + + public String get_Archive_CompileOptions() { + return this.my_Archive_CompileOptions; + } + + public void set_Archive_CompileOptions(String my_Archive_CompileOptions) { + this.my_Archive_CompileOptions = my_Archive_CompileOptions; + } + + public String get_Link_CompileOptions() { + return this.my_Link_CompileOptions; + } + + public void set_Link_CompileOptions(String my_Link_CompileOptions) { + this.my_Link_CompileOptions = my_Link_CompileOptions; + } + + public String get_All_CompileOptions() { + return this.my_All_CompileOptions; + } + + public void set_All_CompileOptions(String my_All_CompileOptions) { + this.my_All_CompileOptions = my_All_CompileOptions; + } + + /** + * save the compilation options in this configuration description. + * * @param configuration * must be a valid configuration description - */ - public Map getEnvVars() { - Map ret = new HashMap<>(); - ret.put(SLOEBER_ADDITIONAL_COMPILE_OPTIONS, this.my_C_andCPP_CompileOptions); - ret.put(SLOEBER_ADDITIONAL_CPP_COMPILE_OPTIONS, this.my_CPP_CompileOptions); - ret.put(SLOEBER_ADDITIONAL_C_COMPILE_OPTIONS, this.my_C_CompileOptions); - ret.put(SLOEBER_ASSEMBLY_COMPILE_OPTIONS, this.my_Assembly_CompileOptions); - ret.put(SLOEBER_ARCHIVE_COMPILE_OPTIONS, this.my_Archive_CompileOptions); - ret.put(SLOEBER_LINK_COMPILE_OPTIONS, this.my_Link_CompileOptions); - ret.put(SLOEBER_ALL_COMPILE_OPTIONS, this.my_All_CompileOptions); - ret.put(ENV_KEY_WARNING_LEVEL, myWarningLevel.getEnvValue()); - ret.put(ENV_KEY_DEBUG_LEVEL, myDebugLevel.getEnvValue()); - ret.put(SLOEBER_SIZE_SWITCH, mySizeCommand.getEnvValue()); - - return ret; - } - - /** - * Given the compile options you currently have and the ones provided Is a - * rebuild needed if you switch from one to another - * - * @param otherOptions - * @return true if a rebuild is needed otherwise false - */ - - public boolean needsRebuild(CompileDescription otherOptions) { - // ignore myWarningLevel (as changing the warning level does not hurt - // ignore myAlternativeSizeCommand (as this is run anyways) - if (otherOptions == null) { - return true; - } - //When the debuglevel is changed it is best to do a full rebuild - if(!myDebugLevel.getEnvValue().equals(otherOptions.myDebugLevel.getEnvValue())) { - return true; - } - return !equalCompileOptions(otherOptions); - } - - /** - * get the environment variables that need to be stored in the configuration - * files configuration files are files needed to setup the sloeber environment - * for instance when opening a project or after import of a project in the - * workspace - * - * @return the minimum list of environment variables to recreate the project - */ - public void serialize(KeyValueTree keyValueTree) { - Map ret = getEnvVarsVersion(); - for(Entry curvalue:ret.entrySet()) { - keyValueTree.addChild(curvalue.getKey(), curvalue.getValue()); - } - } - - /** - * Recreate the compile options based on the configuration environment variables - * given - * - * @param envVars - */ - public CompileDescription(KeyValueTree keyValues) { - my_C_andCPP_CompileOptions=keyValues.getValue(SLOEBER_ADDITIONAL_COMPILE_OPTIONS); - my_CPP_CompileOptions=keyValues.getValue(SLOEBER_ADDITIONAL_CPP_COMPILE_OPTIONS); - my_C_CompileOptions=keyValues.getValue(SLOEBER_ADDITIONAL_C_COMPILE_OPTIONS); - my_Assembly_CompileOptions=keyValues.getValue(SLOEBER_ASSEMBLY_COMPILE_OPTIONS); - my_Archive_CompileOptions=keyValues.getValue(SLOEBER_ARCHIVE_COMPILE_OPTIONS); - my_Link_CompileOptions=keyValues.getValue(SLOEBER_LINK_COMPILE_OPTIONS); - my_All_CompileOptions=keyValues.getValue(SLOEBER_ALL_COMPILE_OPTIONS); - String warningLevel=keyValues.getValue(SLOEBER_WARNING_LEVEL); - String customWarningLevel=keyValues.getValue(SLOEBER_WARNING_LEVEL_CUSTOM); - String debugLevel=keyValues.getValue(SLOEBER_DEBUG_LEVEL); - String customDebugLevel=keyValues.getValue(SLOEBER_DEBUG_LEVEL_CUSTOM); - String sizeCommand=keyValues.getValue(SLOEBER_SIZE_TYPE); - String customSizeCommand=keyValues.getValue(SLOEBER_SIZE_CUSTOM); + */ + public Map getEnvVars() { + Map ret = new HashMap<>(); + ret.put(SLOEBER_ADDITIONAL_COMPILE_OPTIONS, this.my_C_andCPP_CompileOptions); + ret.put(SLOEBER_ADDITIONAL_CPP_COMPILE_OPTIONS, this.my_CPP_CompileOptions); + ret.put(SLOEBER_ADDITIONAL_C_COMPILE_OPTIONS, this.my_C_CompileOptions); + ret.put(SLOEBER_ASSEMBLY_COMPILE_OPTIONS, this.my_Assembly_CompileOptions); + ret.put(SLOEBER_ARCHIVE_COMPILE_OPTIONS, this.my_Archive_CompileOptions); + ret.put(SLOEBER_LINK_COMPILE_OPTIONS, this.my_Link_CompileOptions); + ret.put(SLOEBER_ALL_COMPILE_OPTIONS, this.my_All_CompileOptions); + ret.put(ENV_KEY_WARNING_LEVEL, myWarningLevel.getEnvValue()); + ret.put(ENV_KEY_DEBUG_LEVEL, myDebugLevel.getEnvValue()); + ret.put(SLOEBER_SIZE_SWITCH, mySizeCommand.getEnvValue()); + + return ret; + } + + /** + * Given the compile options you currently have and the ones provided Is a + * rebuild needed if you switch from one to another + * + * @param otherOptions + * @return true if a rebuild is needed otherwise false + */ + + public boolean needsRebuild(CompileDescription otherOptions) { + // ignore myWarningLevel (as changing the warning level does not hurt + // ignore myAlternativeSizeCommand (as this is run anyways) + if (otherOptions == null) { + return true; + } + // When the debuglevel is changed it is best to do a full rebuild + if (!myDebugLevel.getEnvValue().equals(otherOptions.myDebugLevel.getEnvValue())) { + return true; + } + return !equalCompileOptions(otherOptions); + } + + /** + * get the environment variables that need to be stored in the configuration + * files configuration files are files needed to setup the sloeber environment + * for instance when opening a project or after import of a project in the + * workspace + * + * @return the minimum list of environment variables to recreate the project + */ + public void serialize(KeyValueTree keyValueTree) { + Map ret = getEnvVarsVersion(); + for (Entry curvalue : ret.entrySet()) { + keyValueTree.addChild(curvalue.getKey(), curvalue.getValue()); + } + } + + /** + * Recreate the compile options based on the configuration environment variables + * given + * + * @param envVars + */ + public CompileDescription(KeyValueTree keyValues) { + my_C_andCPP_CompileOptions = keyValues.getValue(SLOEBER_ADDITIONAL_COMPILE_OPTIONS); + my_CPP_CompileOptions = keyValues.getValue(SLOEBER_ADDITIONAL_CPP_COMPILE_OPTIONS); + my_C_CompileOptions = keyValues.getValue(SLOEBER_ADDITIONAL_C_COMPILE_OPTIONS); + my_Assembly_CompileOptions = keyValues.getValue(SLOEBER_ASSEMBLY_COMPILE_OPTIONS); + my_Archive_CompileOptions = keyValues.getValue(SLOEBER_ARCHIVE_COMPILE_OPTIONS); + my_Link_CompileOptions = keyValues.getValue(SLOEBER_LINK_COMPILE_OPTIONS); + my_All_CompileOptions = keyValues.getValue(SLOEBER_ALL_COMPILE_OPTIONS); + String warningLevel = keyValues.getValue(SLOEBER_WARNING_LEVEL); + String customWarningLevel = keyValues.getValue(SLOEBER_WARNING_LEVEL_CUSTOM); + String debugLevel = keyValues.getValue(SLOEBER_DEBUG_LEVEL); + String customDebugLevel = keyValues.getValue(SLOEBER_DEBUG_LEVEL_CUSTOM); + String sizeCommand = keyValues.getValue(SLOEBER_SIZE_TYPE); + String customSizeCommand = keyValues.getValue(SLOEBER_SIZE_CUSTOM); try { myWarningLevel = WarningLevels.valueOf(warningLevel); @@ -454,109 +452,109 @@ public CompileDescription(KeyValueTree keyValues) { } catch (Exception e) { Activator.log(new Status(IStatus.WARNING, Activator.getId(), "Deserialisation error", e)); //$NON-NLS-1$ } - } - - public Map getEnvVarsVersion() { - Map ret = new HashMap<>(); - ret.put(SLOEBER_ADDITIONAL_COMPILE_OPTIONS, this.my_C_andCPP_CompileOptions); - ret.put(SLOEBER_ADDITIONAL_CPP_COMPILE_OPTIONS, this.my_CPP_CompileOptions); - ret.put(SLOEBER_ADDITIONAL_C_COMPILE_OPTIONS, this.my_C_CompileOptions); - ret.put(SLOEBER_ASSEMBLY_COMPILE_OPTIONS, this.my_Assembly_CompileOptions); - ret.put(SLOEBER_ARCHIVE_COMPILE_OPTIONS, this.my_Archive_CompileOptions); - ret.put(SLOEBER_LINK_COMPILE_OPTIONS, this.my_Link_CompileOptions); - ret.put(SLOEBER_ALL_COMPILE_OPTIONS, this.my_All_CompileOptions); - ret.put(SLOEBER_WARNING_LEVEL, myWarningLevel.name()); - ret.put(SLOEBER_WARNING_LEVEL_CUSTOM, myWarningLevel.myCustomWarningLevel); - ret.put(SLOEBER_DEBUG_LEVEL, myDebugLevel.name()); - ret.put(SLOEBER_DEBUG_LEVEL_CUSTOM, myDebugLevel.myCustomDebugLevel); - ret.put(SLOEBER_SIZE_TYPE, mySizeCommand.name()); - ret.put(SLOEBER_SIZE_CUSTOM, mySizeCommand.myCustomSizeCommand); - - return ret; - } - - public CompileDescription(TxtFile configFile, String prefix) { - - KeyValueTree tree = configFile.getData(); - KeyValueTree section = tree.getChild(prefix); - - my_C_andCPP_CompileOptions = section.getValue(SLOEBER_ADDITIONAL_COMPILE_OPTIONS); - my_CPP_CompileOptions = section.getValue(SLOEBER_ADDITIONAL_CPP_COMPILE_OPTIONS); - my_C_CompileOptions = section.getValue(SLOEBER_ADDITIONAL_C_COMPILE_OPTIONS); - my_Assembly_CompileOptions = section.getValue(SLOEBER_ASSEMBLY_COMPILE_OPTIONS); - my_Archive_CompileOptions = section.getValue(SLOEBER_ARCHIVE_COMPILE_OPTIONS); - my_Link_CompileOptions = section.getValue(SLOEBER_LINK_COMPILE_OPTIONS); - my_All_CompileOptions = section.getValue(SLOEBER_ALL_COMPILE_OPTIONS); - - try { - myWarningLevel = WarningLevels.valueOf(section.getValue(SLOEBER_WARNING_LEVEL)); - myWarningLevel.setCustomWarningLevel(section.getValue(SLOEBER_WARNING_LEVEL_CUSTOM)); - } catch (@SuppressWarnings("unused") Exception e) { - // ignore as this will be default - } - try { - myDebugLevel = DebugLevels.valueOf(section.getValue(SLOEBER_DEBUG_LEVEL)); - myDebugLevel.setCustomDebugLevel(section.getValue(SLOEBER_DEBUG_LEVEL_CUSTOM)); - } catch (@SuppressWarnings("unused") Exception e) { - // ignore as this will be default - } - try { - mySizeCommand = SizeCommands.valueOf(section.getValue(SLOEBER_SIZE_TYPE)); - mySizeCommand.setCustomSizeCommand(section.getValue(SLOEBER_SIZE_CUSTOM)); - } catch (@SuppressWarnings("unused") Exception e) { - // ignore as this will be default - } - - } - - public CompileDescription() { - // no need to do anything - // this will create default compile options - // note that the Parallel build option is implemented at the ui level - // therefore this is not set here but in the ui before project creation - } - - public CompileDescription(CompileDescription compileDescription) { - myWarningLevel = compileDescription.myWarningLevel; - myDebugLevel = compileDescription.myDebugLevel; - mySizeCommand = compileDescription.mySizeCommand; - myEnableParallelBuild = compileDescription.myEnableParallelBuild; - my_CPP_CompileOptions = compileDescription.my_CPP_CompileOptions; - my_C_CompileOptions = compileDescription.my_C_CompileOptions; - my_C_andCPP_CompileOptions = compileDescription.my_C_andCPP_CompileOptions; - my_Assembly_CompileOptions = compileDescription.my_Assembly_CompileOptions; - my_Archive_CompileOptions = compileDescription.my_Archive_CompileOptions; - my_Link_CompileOptions = compileDescription.my_Link_CompileOptions; - my_All_CompileOptions = compileDescription.my_All_CompileOptions; - } - - /** - * Compares 2 compile descriptors - * - * @param other - * @return true if the 2 are equal else false - */ - public boolean equals(CompileDescription other) { - return myWarningLevel.getEnvValue().equals(other.myWarningLevel.getEnvValue()) - && myDebugLevel.getEnvValue().equals(other.myDebugLevel.getEnvValue()) + } + + public Map getEnvVarsVersion() { + Map ret = new HashMap<>(); + ret.put(SLOEBER_ADDITIONAL_COMPILE_OPTIONS, this.my_C_andCPP_CompileOptions); + ret.put(SLOEBER_ADDITIONAL_CPP_COMPILE_OPTIONS, this.my_CPP_CompileOptions); + ret.put(SLOEBER_ADDITIONAL_C_COMPILE_OPTIONS, this.my_C_CompileOptions); + ret.put(SLOEBER_ASSEMBLY_COMPILE_OPTIONS, this.my_Assembly_CompileOptions); + ret.put(SLOEBER_ARCHIVE_COMPILE_OPTIONS, this.my_Archive_CompileOptions); + ret.put(SLOEBER_LINK_COMPILE_OPTIONS, this.my_Link_CompileOptions); + ret.put(SLOEBER_ALL_COMPILE_OPTIONS, this.my_All_CompileOptions); + ret.put(SLOEBER_WARNING_LEVEL, myWarningLevel.name()); + ret.put(SLOEBER_WARNING_LEVEL_CUSTOM, myWarningLevel.myCustomWarningLevel); + ret.put(SLOEBER_DEBUG_LEVEL, myDebugLevel.name()); + ret.put(SLOEBER_DEBUG_LEVEL_CUSTOM, myDebugLevel.myCustomDebugLevel); + ret.put(SLOEBER_SIZE_TYPE, mySizeCommand.name()); + ret.put(SLOEBER_SIZE_CUSTOM, mySizeCommand.myCustomSizeCommand); + + return ret; + } + + public CompileDescription(TxtFile configFile, String prefix) { + + KeyValueTree tree = configFile.getData(); + KeyValueTree section = tree.getChild(prefix); + + my_C_andCPP_CompileOptions = section.getValue(SLOEBER_ADDITIONAL_COMPILE_OPTIONS); + my_CPP_CompileOptions = section.getValue(SLOEBER_ADDITIONAL_CPP_COMPILE_OPTIONS); + my_C_CompileOptions = section.getValue(SLOEBER_ADDITIONAL_C_COMPILE_OPTIONS); + my_Assembly_CompileOptions = section.getValue(SLOEBER_ASSEMBLY_COMPILE_OPTIONS); + my_Archive_CompileOptions = section.getValue(SLOEBER_ARCHIVE_COMPILE_OPTIONS); + my_Link_CompileOptions = section.getValue(SLOEBER_LINK_COMPILE_OPTIONS); + my_All_CompileOptions = section.getValue(SLOEBER_ALL_COMPILE_OPTIONS); + + try { + myWarningLevel = WarningLevels.valueOf(section.getValue(SLOEBER_WARNING_LEVEL)); + myWarningLevel.setCustomWarningLevel(section.getValue(SLOEBER_WARNING_LEVEL_CUSTOM)); + } catch (@SuppressWarnings("unused") Exception e) { + // ignore as this will be default + } + try { + myDebugLevel = DebugLevels.valueOf(section.getValue(SLOEBER_DEBUG_LEVEL)); + myDebugLevel.setCustomDebugLevel(section.getValue(SLOEBER_DEBUG_LEVEL_CUSTOM)); + } catch (@SuppressWarnings("unused") Exception e) { + // ignore as this will be default + } + try { + mySizeCommand = SizeCommands.valueOf(section.getValue(SLOEBER_SIZE_TYPE)); + mySizeCommand.setCustomSizeCommand(section.getValue(SLOEBER_SIZE_CUSTOM)); + } catch (@SuppressWarnings("unused") Exception e) { + // ignore as this will be default + } + + } + + public CompileDescription() { + // no need to do anything + // this will create default compile options + // note that the Parallel build option is implemented at the ui level + // therefore this is not set here but in the ui before project creation + } + + public CompileDescription(CompileDescription compileDescription) { + myWarningLevel = compileDescription.myWarningLevel; + myDebugLevel = compileDescription.myDebugLevel; + mySizeCommand = compileDescription.mySizeCommand; + myEnableParallelBuild = compileDescription.myEnableParallelBuild; + my_CPP_CompileOptions = compileDescription.my_CPP_CompileOptions; + my_C_CompileOptions = compileDescription.my_C_CompileOptions; + my_C_andCPP_CompileOptions = compileDescription.my_C_andCPP_CompileOptions; + my_Assembly_CompileOptions = compileDescription.my_Assembly_CompileOptions; + my_Archive_CompileOptions = compileDescription.my_Archive_CompileOptions; + my_Link_CompileOptions = compileDescription.my_Link_CompileOptions; + my_All_CompileOptions = compileDescription.my_All_CompileOptions; + } + + /** + * Compares 2 compile descriptors + * + * @param other + * @return true if the 2 are equal else false + */ + public boolean equals(CompileDescription other) { + return myWarningLevel.getEnvValue().equals(other.myWarningLevel.getEnvValue()) + && myDebugLevel.getEnvValue().equals(other.myDebugLevel.getEnvValue()) && mySizeCommand.getEnvValue().equals(other.mySizeCommand.getEnvValue()) && equalCompileOptions(other); - } - - /** - * Compares the compile options of 2 compile descriptors - * - * @param other - * @return true if the 2 have equal compile options else false - */ - private boolean equalCompileOptions(CompileDescription other) { - return (my_CPP_CompileOptions.equals(other.my_CPP_CompileOptions)) - && (my_C_CompileOptions.equals(other.my_C_CompileOptions)) - && (my_C_andCPP_CompileOptions.equals(other.my_C_andCPP_CompileOptions)) - && (my_Assembly_CompileOptions.equals(other.my_Assembly_CompileOptions)) - && (my_Archive_CompileOptions.equals(other.my_Archive_CompileOptions)) - && (my_Link_CompileOptions.equals(other.my_Link_CompileOptions)) - && (my_All_CompileOptions.equals(other.my_All_CompileOptions)); - } + } + + /** + * Compares the compile options of 2 compile descriptors + * + * @param other + * @return true if the 2 have equal compile options else false + */ + private boolean equalCompileOptions(CompileDescription other) { + return (my_CPP_CompileOptions.equals(other.my_CPP_CompileOptions)) + && (my_C_CompileOptions.equals(other.my_C_CompileOptions)) + && (my_C_andCPP_CompileOptions.equals(other.my_C_andCPP_CompileOptions)) + && (my_Assembly_CompileOptions.equals(other.my_Assembly_CompileOptions)) + && (my_Archive_CompileOptions.equals(other.my_Archive_CompileOptions)) + && (my_Link_CompileOptions.equals(other.my_Link_CompileOptions)) + && (my_All_CompileOptions.equals(other.my_All_CompileOptions)); + } } diff --git a/io.sloeber.core/src/io/sloeber/core/api/Const.java b/io.sloeber.core/src/io/sloeber/core/api/Const.java index 02adcf2a..c9525833 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/Const.java +++ b/io.sloeber.core/src/io/sloeber/core/api/Const.java @@ -202,6 +202,7 @@ public class Const extends AutoBuildConstants { public static final String RELEASE = "Release"; + public static final String ARDUINO_SIZE = "arduino-size.cmd"; public static final short PLOTTER_START_DATA = (short) 0xCDAB;// This is the diff --git a/io.sloeber.core/src/io/sloeber/core/builder/SloeberBuilderExtension.java b/io.sloeber.core/src/io/sloeber/core/builder/SloeberBuilderExtension.java index 2b39e3a5..34b23a4a 100644 --- a/io.sloeber.core/src/io/sloeber/core/builder/SloeberBuilderExtension.java +++ b/io.sloeber.core/src/io/sloeber/core/builder/SloeberBuilderExtension.java @@ -133,7 +133,7 @@ private static void generateArduinoSizeCommandFile(IAutoBuildConfigurationDescri if(!isWindows) { return; } - IFile sizeCommandIFile = autoBuildConfData.getBuildFolder().getFile("arduino-size.bat"); + IFile sizeCommandIFile = autoBuildConfData.getBuildFolder().getFile(ARDUINO_SIZE ); SloeberConfiguration confDesc = SloeberConfiguration.getFromAutoBuildConfDesc(autoBuildConfData); File sizeCommandFile = sizeCommandIFile.getLocation().toFile(); From 5cb34821313db0838a4119cce460f522d567626a Mon Sep 17 00:00:00 2001 From: jan Date: Tue, 18 Feb 2025 22:41:21 +0100 Subject: [PATCH 101/115] Use script to start size calculation #1714 Use the same way it is done in windows. --- .../src/io/sloeber/core/api/CompileDescription.java | 5 +---- .../src/io/sloeber/core/builder/SloeberBuilderExtension.java | 3 --- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/core/api/CompileDescription.java b/io.sloeber.core/src/io/sloeber/core/api/CompileDescription.java index 9c66bf0d..83a9c697 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/CompileDescription.java +++ b/io.sloeber.core/src/io/sloeber/core/api/CompileDescription.java @@ -221,10 +221,7 @@ public String getCustomSizeCommand() { public String getEnvValue() { switch (this) { case ARDUINO_WAY: - if (isWindows) { - return Common.makeEnvironmentVar(ENV_KEY_BUILD_PATH) + SLACH + ARDUINO_SIZE; - } - return "${sloeber.size_command.awk}"; //$NON-NLS-1$ + return Common.makeEnvironmentVar(ENV_KEY_BUILD_PATH) + SLACH + ARDUINO_SIZE; case AVR_ALTERNATIVE: return "${sloeber.size_command.avr}"; //$NON-NLS-1$ case RAW_RESULT: diff --git a/io.sloeber.core/src/io/sloeber/core/builder/SloeberBuilderExtension.java b/io.sloeber.core/src/io/sloeber/core/builder/SloeberBuilderExtension.java index 34b23a4a..835b4e0f 100644 --- a/io.sloeber.core/src/io/sloeber/core/builder/SloeberBuilderExtension.java +++ b/io.sloeber.core/src/io/sloeber/core/builder/SloeberBuilderExtension.java @@ -130,9 +130,6 @@ public SloeberBuilderExtension() { @SuppressWarnings("nls") private static void generateArduinoSizeCommandFile(IAutoBuildConfigurationDescription autoBuildConfData) { - if(!isWindows) { - return; - } IFile sizeCommandIFile = autoBuildConfData.getBuildFolder().getFile(ARDUINO_SIZE ); SloeberConfiguration confDesc = SloeberConfiguration.getFromAutoBuildConfDesc(autoBuildConfData); From 62355789965ad41c3a8160e65c7b17c496d1dba3 Mon Sep 17 00:00:00 2001 From: jan Date: Fri, 7 Mar 2025 00:13:17 +0100 Subject: [PATCH 102/115] Welcome page not found in compiled sloeber (V5) --- io.sloeber.core/build.properties | 3 ++- io.sloeber.product/introContent.xml | 8 -------- 2 files changed, 2 insertions(+), 9 deletions(-) delete mode 100644 io.sloeber.product/introContent.xml diff --git a/io.sloeber.core/build.properties b/io.sloeber.core/build.properties index 44cc6f62..8f7dd00f 100644 --- a/io.sloeber.core/build.properties +++ b/io.sloeber.core/build.properties @@ -13,6 +13,7 @@ bin.includes = .,\ icons/,\ launch/,\ lib/jssc-2.9.4.jar,\ - lib/jmdns-3.5.7.jar + lib/jmdns-3.5.7.jar,\ + introContent.xml jars.compile.order = . src.includes = icons/ diff --git a/io.sloeber.product/introContent.xml b/io.sloeber.product/introContent.xml deleted file mode 100644 index d379c164..00000000 --- a/io.sloeber.product/introContent.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file From d08b0d05d3a35c501b26cb8d28b5fc2617f4df5f Mon Sep 17 00:00:00 2001 From: jan Date: Fri, 7 Mar 2025 00:16:33 +0100 Subject: [PATCH 103/115] Add icons to build #1711 --- io.sloeber.autoBuild.ui/build.properties | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/io.sloeber.autoBuild.ui/build.properties b/io.sloeber.autoBuild.ui/build.properties index 0f364769..b89543b2 100644 --- a/io.sloeber.autoBuild.ui/build.properties +++ b/io.sloeber.autoBuild.ui/build.properties @@ -5,5 +5,6 @@ bin.includes = .,\ plugin.properties,\ META-INF/,\ OSGI-INF/,\ - OSGI-INF/l10n/bundle.properties + OSGI-INF/l10n/bundle.properties,\ + icons/ jars.compile.order = . From 1ac2cf50d609a610bab1e1d8775044f7abb8ce00 Mon Sep 17 00:00:00 2001 From: jan Date: Sat, 29 Mar 2025 00:33:40 +0100 Subject: [PATCH 104/115] convert to Sloeber project should convert size command. --- .../src/io/sloeber/core/Messages.java | 4 -- .../sloeber/core/api/CompileDescription.java | 44 ++++++++++++++++--- .../io/sloeber/core/api/SloeberProject.java | 2 + .../src/io/sloeber/core/messages.properties | 4 -- 4 files changed, 40 insertions(+), 14 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/core/Messages.java b/io.sloeber.core/src/io/sloeber/core/Messages.java index dd2af674..c3d9dce5 100644 --- a/io.sloeber.core/src/io/sloeber/core/Messages.java +++ b/io.sloeber.core/src/io/sloeber/core/Messages.java @@ -109,10 +109,6 @@ public class Messages extends NLS { public static String CompileDescription_CustomDebugLevel; public static String CompileDescription_OptimizedForDebug; public static String CompileDescription_OptimizedForRelease; - public static String CompileDescription_SizeArduinoWay; - public static String CompileDescription_SizeAVRAlternative; - public static String CompileDescription_SizeCustom; - public static String CompileDescription_SizeRawResult; public static String CompileDescription_WarningsAll; public static String CompileDescription_WarningsCustom; public static String CompileDescription_WarningsDefault; diff --git a/io.sloeber.core/src/io/sloeber/core/api/CompileDescription.java b/io.sloeber.core/src/io/sloeber/core/api/CompileDescription.java index 83a9c697..449d3605 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/CompileDescription.java +++ b/io.sloeber.core/src/io/sloeber/core/api/CompileDescription.java @@ -166,23 +166,55 @@ public enum SizeCommands { ARDUINO_WAY, AVR_ALTERNATIVE, RAW_RESULT, CUSTOM; + + + + @Override public String toString() { - switch (this) { + return toString (this); + } + + public static String toString(SizeCommands value) { + + switch (value) { case ARDUINO_WAY: - return Messages.CompileDescription_SizeArduinoWay; + return "Arduino Way"; //$NON-NLS-1$ case AVR_ALTERNATIVE: - return Messages.CompileDescription_SizeAVRAlternative; + return "AVR Alternative"; //$NON-NLS-1$ case RAW_RESULT: - return Messages.CompileDescription_SizeRawResult; + return "Raw result"; //$NON-NLS-1$ case CUSTOM: - return Messages.CompileDescription_SizeCustom; + return "Custom"; //$NON-NLS-1$ default: break; } - return super.toString(); + try { + return value.toString(); + } catch (@SuppressWarnings("unused") Exception e) { + // ignore exception + } + return "Arduino Way"; //$NON-NLS-1$ } + public static SizeCommands valueOf(String name, SizeCommands defaultValue) { + if (name.equals(toString(ARDUINO_WAY))) { + return ARDUINO_WAY; + } + if (name.equals(toString(AVR_ALTERNATIVE))) { + return AVR_ALTERNATIVE; + } + if (name.equals(toString(RAW_RESULT))) { + return RAW_RESULT; + } + if (name.equals(toString(CUSTOM))) { + return CUSTOM; + } + return defaultValue; + } + + + private String myCustomSizeCommand = EMPTY; /** diff --git a/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java b/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java index 23c661a3..29e3305a 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java +++ b/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java @@ -49,6 +49,7 @@ import io.sloeber.autoBuild.schema.api.IProjectType; import io.sloeber.core.Activator; import io.sloeber.core.Messages; +import io.sloeber.core.api.CompileDescription.SizeCommands; import io.sloeber.core.internal.ArduinoHardwareLibrary; import io.sloeber.core.internal.ArduinoPrivateLibraryVersion; import io.sloeber.core.internal.SloeberConfiguration; @@ -306,6 +307,7 @@ private static CompileDescription getCompileDescription(KeyValueTree oldConfig) ret.set_CPP_CompileOptions(extraConfig.getValue("cpp.compile")); //$NON-NLS-1$ ret.set_Link_CompileOptions(extraConfig.getValue("link")); //$NON-NLS-1$ ret.set_C_andCPP_CompileOptions(extraConfig.getValue("compile")); //$NON-NLS-1$ + ret.setSizeCommand(SizeCommands.valueOf(extraConfig.getValue("compile.sloeber.size.type"),SizeCommands.ARDUINO_WAY)); //$NON-NLS-1$ // ret.setSizeCommand(extraConfig.getValue("compile")); //$NON-NLS-1$ diff --git a/io.sloeber.core/src/io/sloeber/core/messages.properties b/io.sloeber.core/src/io/sloeber/core/messages.properties index 4fda4b0d..0f3c98cf 100644 --- a/io.sloeber.core/src/io/sloeber/core/messages.properties +++ b/io.sloeber.core/src/io/sloeber/core/messages.properties @@ -82,10 +82,6 @@ sizeReportData=Global variables use "arduino_data" bytes ("int(arduino_data/maxi CompileDescription_CustomDebugLevel=Custom debug level CompileDescription_OptimizedForDebug=Optimized for debug CompileDescription_OptimizedForRelease=Optimized for release -CompileDescription_SizeArduinoWay=Arduino Way -CompileDescription_SizeAVRAlternative=AVR Alternative -CompileDescription_SizeCustom=Custom -CompileDescription_SizeRawResult=Raw result CompileDescription_WarningsAll=All CompileDescription_WarningsCustom=Custom CompileDescription_WarningsDefault=Default From 033a034769778594eb4df8e205b65efb32e68fd5 Mon Sep 17 00:00:00 2001 From: jan Date: Sat, 29 Mar 2025 01:00:44 +0100 Subject: [PATCH 105/115] https://github.com/eclipse-ecf/ecf/issues/165 messed up sloeber Actually eclipse messed up but it is not the first time; won't be the last neither. --- io.sloeber.product/sloeber.target | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io.sloeber.product/sloeber.target b/io.sloeber.product/sloeber.target index 3d24d985..3591998a 100644 --- a/io.sloeber.product/sloeber.target +++ b/io.sloeber.product/sloeber.target @@ -22,7 +22,6 @@ - @@ -65,6 +64,7 @@ + From 5ca9283298a1fef911208a94bffbcfe390bf91f5 Mon Sep 17 00:00:00 2001 From: jan Date: Sat, 29 Mar 2025 17:37:10 +0100 Subject: [PATCH 106/115] Move to java 21 --- io.sloeber.autoBuild.test/.classpath | 18 ++++++---- .../.settings/org.eclipse.jdt.core.prefs | 9 +++++ .../META-INF/MANIFEST.MF | 2 +- io.sloeber.autoBuild.ui/.classpath | 2 +- .../.settings/org.eclipse.jdt.core.prefs | 6 ++-- io.sloeber.autoBuild.ui/META-INF/MANIFEST.MF | 2 +- io.sloeber.autoBuild/.classpath | 2 +- .../.settings/org.eclipse.jdt.core.prefs | 6 ++-- io.sloeber.autoBuild/META-INF/MANIFEST.MF | 2 +- io.sloeber.core.nl1/.classpath | 7 ++++ .../.settings/org.eclipse.jdt.core.prefs | 36 ++++++++++++++++--- io.sloeber.core.nl1/META-INF/MANIFEST.MF | 2 +- io.sloeber.core/.classpath | 2 +- .../.settings/org.eclipse.jdt.core.prefs | 6 ++-- io.sloeber.core/META-INF/MANIFEST.MF | 2 +- io.sloeber.product.sdk/.classpath | 2 +- .../.settings/org.eclipse.jdt.core.prefs | 9 +++++ io.sloeber.product.sdk/META-INF/MANIFEST.MF | 2 +- io.sloeber.product/.classpath | 2 +- .../.settings/org.eclipse.jdt.core.prefs | 6 ++-- io.sloeber.product/META-INF/MANIFEST.MF | 2 +- io.sloeber.tests/.classpath | 3 +- .../.settings/org.eclipse.jdt.core.prefs | 6 ++-- io.sloeber.tests/META-INF/MANIFEST.MF | 2 +- io.sloeber.ui.nl1/META-INF/MANIFEST.MF | 2 +- io.sloeber.ui/.classpath | 2 +- .../.settings/org.eclipse.jdt.core.prefs | 36 ++++++++++++++++--- io.sloeber.ui/META-INF/MANIFEST.MF | 2 +- 28 files changed, 133 insertions(+), 47 deletions(-) create mode 100644 io.sloeber.autoBuild.test/.settings/org.eclipse.jdt.core.prefs create mode 100644 io.sloeber.core.nl1/.classpath create mode 100644 io.sloeber.product.sdk/.settings/org.eclipse.jdt.core.prefs diff --git a/io.sloeber.autoBuild.test/.classpath b/io.sloeber.autoBuild.test/.classpath index 64ed6b37..1aa9eca9 100644 --- a/io.sloeber.autoBuild.test/.classpath +++ b/io.sloeber.autoBuild.test/.classpath @@ -1,7 +1,11 @@ - - - - - - - + + + + + + + + + + + diff --git a/io.sloeber.autoBuild.test/.settings/org.eclipse.jdt.core.prefs b/io.sloeber.autoBuild.test/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..23fa13b1 --- /dev/null +++ b/io.sloeber.autoBuild.test/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,9 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=21 +org.eclipse.jdt.core.compiler.compliance=21 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=21 diff --git a/io.sloeber.autoBuild.test/META-INF/MANIFEST.MF b/io.sloeber.autoBuild.test/META-INF/MANIFEST.MF index d289e44e..4f2c8325 100644 --- a/io.sloeber.autoBuild.test/META-INF/MANIFEST.MF +++ b/io.sloeber.autoBuild.test/META-INF/MANIFEST.MF @@ -21,4 +21,4 @@ Require-Bundle: io.sloeber.autoBuild, junit-platform-engine;bundle-version="1.10.2", junit-platform-suite-api;bundle-version="1.10.2" Automatic-Module-Name: io.sloeber.autoBuild.test -Bundle-RequiredExecutionEnvironment: JavaSE-17 +Bundle-RequiredExecutionEnvironment: JavaSE-21 diff --git a/io.sloeber.autoBuild.ui/.classpath b/io.sloeber.autoBuild.ui/.classpath index 3628e336..35386b94 100644 --- a/io.sloeber.autoBuild.ui/.classpath +++ b/io.sloeber.autoBuild.ui/.classpath @@ -1,6 +1,6 @@ - + diff --git a/io.sloeber.autoBuild.ui/.settings/org.eclipse.jdt.core.prefs b/io.sloeber.autoBuild.ui/.settings/org.eclipse.jdt.core.prefs index 62ef3488..23fa13b1 100644 --- a/io.sloeber.autoBuild.ui/.settings/org.eclipse.jdt.core.prefs +++ b/io.sloeber.autoBuild.ui/.settings/org.eclipse.jdt.core.prefs @@ -1,9 +1,9 @@ eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 -org.eclipse.jdt.core.compiler.compliance=17 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=21 +org.eclipse.jdt.core.compiler.compliance=21 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning org.eclipse.jdt.core.compiler.release=enabled -org.eclipse.jdt.core.compiler.source=17 +org.eclipse.jdt.core.compiler.source=21 diff --git a/io.sloeber.autoBuild.ui/META-INF/MANIFEST.MF b/io.sloeber.autoBuild.ui/META-INF/MANIFEST.MF index 00fec236..60e7d1c6 100644 --- a/io.sloeber.autoBuild.ui/META-INF/MANIFEST.MF +++ b/io.sloeber.autoBuild.ui/META-INF/MANIFEST.MF @@ -17,7 +17,7 @@ Require-Bundle: org.eclipse.tools.templates.ui, org.eclipse.core.filesystem, io.sloeber.autoBuild;bundle-version="4.4.1", org.eclipse.tools.templates.core;bundle-version="2.0.0" -Bundle-RequiredExecutionEnvironment: JavaSE-17 +Bundle-RequiredExecutionEnvironment: JavaSE-21 Bundle-Vendor: Sloeber.io Bundle-ActivationPolicy: lazy Bundle-Activator: io.sloeber.autoBuild.ui.internal.Activator diff --git a/io.sloeber.autoBuild/.classpath b/io.sloeber.autoBuild/.classpath index 065ac06e..e54fbc93 100644 --- a/io.sloeber.autoBuild/.classpath +++ b/io.sloeber.autoBuild/.classpath @@ -2,6 +2,6 @@ - + diff --git a/io.sloeber.autoBuild/.settings/org.eclipse.jdt.core.prefs b/io.sloeber.autoBuild/.settings/org.eclipse.jdt.core.prefs index 62ef3488..23fa13b1 100644 --- a/io.sloeber.autoBuild/.settings/org.eclipse.jdt.core.prefs +++ b/io.sloeber.autoBuild/.settings/org.eclipse.jdt.core.prefs @@ -1,9 +1,9 @@ eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 -org.eclipse.jdt.core.compiler.compliance=17 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=21 +org.eclipse.jdt.core.compiler.compliance=21 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning org.eclipse.jdt.core.compiler.release=enabled -org.eclipse.jdt.core.compiler.source=17 +org.eclipse.jdt.core.compiler.source=21 diff --git a/io.sloeber.autoBuild/META-INF/MANIFEST.MF b/io.sloeber.autoBuild/META-INF/MANIFEST.MF index 3b583249..341912e9 100644 --- a/io.sloeber.autoBuild/META-INF/MANIFEST.MF +++ b/io.sloeber.autoBuild/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: Sloeber auto Build Bundle-SymbolicName: io.sloeber.autoBuild;singleton:=true Bundle-Version: 5.0.0.qualifier -Bundle-RequiredExecutionEnvironment: JavaSE-17 +Bundle-RequiredExecutionEnvironment: JavaSE-21 Bundle-ActivationPolicy: lazy Bundle-ClassPath: . Require-Bundle: org.eclipse.cdt.core;bundle-version="7.0.0", diff --git a/io.sloeber.core.nl1/.classpath b/io.sloeber.core.nl1/.classpath new file mode 100644 index 00000000..06514e04 --- /dev/null +++ b/io.sloeber.core.nl1/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/io.sloeber.core.nl1/.settings/org.eclipse.jdt.core.prefs b/io.sloeber.core.nl1/.settings/org.eclipse.jdt.core.prefs index 27705a03..50226351 100644 --- a/io.sloeber.core.nl1/.settings/org.eclipse.jdt.core.prefs +++ b/io.sloeber.core.nl1/.settings/org.eclipse.jdt.core.prefs @@ -1,9 +1,18 @@ eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=21 +org.eclipse.jdt.core.compiler.compliance=21 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=21 org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 org.eclipse.jdt.core.formatter.align_type_members_on_columns=false org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns=false org.eclipse.jdt.core.formatter.align_with_spaces=false +org.eclipse.jdt.core.formatter.alignment_for_additive_operator=16 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 @@ -11,21 +20,24 @@ org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_c org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 org.eclipse.jdt.core.formatter.alignment_for_assignment=0 -org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator=16 org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 org.eclipse.jdt.core.formatter.alignment_for_compact_loops=16 org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16 org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0 +org.eclipse.jdt.core.formatter.alignment_for_logical_operator=16 org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 org.eclipse.jdt.core.formatter.alignment_for_module_statements=16 org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator=16 org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0 org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_string_concatenation=16 org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 @@ -117,11 +129,12 @@ org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_additive_operator=insert org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator=insert org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=do not insert org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert @@ -152,6 +165,8 @@ org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_after_logical_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator=insert org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert @@ -176,13 +191,17 @@ org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_relational_operator=insert org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert +org.eclipse.jdt.core.formatter.insert_space_after_shift_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation=insert org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_additive_operator=insert org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator=insert org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert @@ -230,6 +249,8 @@ org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do no org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_before_logical_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator=insert org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert @@ -266,9 +287,12 @@ org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not inser org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_relational_operator=insert org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_shift_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation=insert org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert @@ -307,9 +331,13 @@ org.eclipse.jdt.core.formatter.tabulation.char=tab org.eclipse.jdt.core.formatter.tabulation.size=4 org.eclipse.jdt.core.formatter.use_on_off_tags=false org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_additive_operator=true org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false -org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true +org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator=true org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true +org.eclipse.jdt.core.formatter.wrap_before_logical_operator=true +org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator=true org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_before_string_concatenation=true org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter diff --git a/io.sloeber.core.nl1/META-INF/MANIFEST.MF b/io.sloeber.core.nl1/META-INF/MANIFEST.MF index 8aad1286..b8205fdd 100644 --- a/io.sloeber.core.nl1/META-INF/MANIFEST.MF +++ b/io.sloeber.core.nl1/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Sloeber core language pack Bundle-SymbolicName: io.sloeber.core.nl1;singleton:=true -Bundle-RequiredExecutionEnvironment: JavaSE-17 +Bundle-RequiredExecutionEnvironment: JavaSE-21 Bundle-Version: 5.0.0.qualifier Fragment-Host: io.sloeber.core Bundle-Vendor: Sloeber.io diff --git a/io.sloeber.core/.classpath b/io.sloeber.core/.classpath index bf277454..478470af 100644 --- a/io.sloeber.core/.classpath +++ b/io.sloeber.core/.classpath @@ -14,6 +14,6 @@ - + diff --git a/io.sloeber.core/.settings/org.eclipse.jdt.core.prefs b/io.sloeber.core/.settings/org.eclipse.jdt.core.prefs index 96d2364f..24e17346 100644 --- a/io.sloeber.core/.settings/org.eclipse.jdt.core.prefs +++ b/io.sloeber.core/.settings/org.eclipse.jdt.core.prefs @@ -10,11 +10,11 @@ org.eclipse.jdt.core.codeComplete.staticFieldSuffixes= org.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes= org.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes= org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 -org.eclipse.jdt.core.compiler.compliance=17 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=21 +org.eclipse.jdt.core.compiler.compliance=21 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning org.eclipse.jdt.core.compiler.release=enabled -org.eclipse.jdt.core.compiler.source=17 +org.eclipse.jdt.core.compiler.source=21 diff --git a/io.sloeber.core/META-INF/MANIFEST.MF b/io.sloeber.core/META-INF/MANIFEST.MF index 88e86763..c2dc81a5 100644 --- a/io.sloeber.core/META-INF/MANIFEST.MF +++ b/io.sloeber.core/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: Arduino eclipse plugin Bundle-SymbolicName: io.sloeber.core;singleton:=true Bundle-Version: 5.0.0.qualifier -Bundle-RequiredExecutionEnvironment: JavaSE-17 +Bundle-RequiredExecutionEnvironment: JavaSE-21 Bundle-Vendor: Sloeber.io Bundle-ActivationPolicy: lazy Bundle-Activator: io.sloeber.core.Activator diff --git a/io.sloeber.product.sdk/.classpath b/io.sloeber.product.sdk/.classpath index 3f74547a..7eb7ec57 100644 --- a/io.sloeber.product.sdk/.classpath +++ b/io.sloeber.product.sdk/.classpath @@ -1,6 +1,6 @@ - + diff --git a/io.sloeber.product.sdk/.settings/org.eclipse.jdt.core.prefs b/io.sloeber.product.sdk/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..23fa13b1 --- /dev/null +++ b/io.sloeber.product.sdk/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,9 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=21 +org.eclipse.jdt.core.compiler.compliance=21 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=21 diff --git a/io.sloeber.product.sdk/META-INF/MANIFEST.MF b/io.sloeber.product.sdk/META-INF/MANIFEST.MF index bc897649..442109a8 100644 --- a/io.sloeber.product.sdk/META-INF/MANIFEST.MF +++ b/io.sloeber.product.sdk/META-INF/MANIFEST.MF @@ -3,6 +3,6 @@ Bundle-ManifestVersion: 2 Bundle-Name: Sloeber SDK Bundle-SymbolicName: io.sloeber.product.sdk;singleton:=true Bundle-Version: 5.0.0.qualifier -Bundle-RequiredExecutionEnvironment: JavaSE-17 +Bundle-RequiredExecutionEnvironment: JavaSE-21 Bundle-Vendor: baeyens.it Automatic-Module-Name: io.sloeber.product.sdk diff --git a/io.sloeber.product/.classpath b/io.sloeber.product/.classpath index 1da84188..87aa5a7b 100644 --- a/io.sloeber.product/.classpath +++ b/io.sloeber.product/.classpath @@ -1,7 +1,7 @@ - + diff --git a/io.sloeber.product/.settings/org.eclipse.jdt.core.prefs b/io.sloeber.product/.settings/org.eclipse.jdt.core.prefs index 62ef3488..23fa13b1 100644 --- a/io.sloeber.product/.settings/org.eclipse.jdt.core.prefs +++ b/io.sloeber.product/.settings/org.eclipse.jdt.core.prefs @@ -1,9 +1,9 @@ eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 -org.eclipse.jdt.core.compiler.compliance=17 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=21 +org.eclipse.jdt.core.compiler.compliance=21 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning org.eclipse.jdt.core.compiler.release=enabled -org.eclipse.jdt.core.compiler.source=17 +org.eclipse.jdt.core.compiler.source=21 diff --git a/io.sloeber.product/META-INF/MANIFEST.MF b/io.sloeber.product/META-INF/MANIFEST.MF index 9b1cb6b5..94624001 100644 --- a/io.sloeber.product/META-INF/MANIFEST.MF +++ b/io.sloeber.product/META-INF/MANIFEST.MF @@ -4,6 +4,6 @@ Bundle-Name: Sloeber product Bundle-SymbolicName: io.sloeber.product;singleton:=true Bundle-Version: 5.0.0.qualifier Require-Bundle: io.sloeber.ui;bundle-version="5.0.0" -Bundle-RequiredExecutionEnvironment: JavaSE-17 +Bundle-RequiredExecutionEnvironment: JavaSE-21 Bundle-Vendor: baeyens.it Automatic-Module-Name: io.sloeber.product diff --git a/io.sloeber.tests/.classpath b/io.sloeber.tests/.classpath index b672bb97..5ac61f02 100644 --- a/io.sloeber.tests/.classpath +++ b/io.sloeber.tests/.classpath @@ -1,6 +1,6 @@ - + @@ -11,6 +11,7 @@ + diff --git a/io.sloeber.tests/.settings/org.eclipse.jdt.core.prefs b/io.sloeber.tests/.settings/org.eclipse.jdt.core.prefs index f78f7f77..96641ff8 100644 --- a/io.sloeber.tests/.settings/org.eclipse.jdt.core.prefs +++ b/io.sloeber.tests/.settings/org.eclipse.jdt.core.prefs @@ -1,10 +1,10 @@ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 -org.eclipse.jdt.core.compiler.compliance=17 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=21 +org.eclipse.jdt.core.compiler.compliance=21 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning org.eclipse.jdt.core.compiler.release=enabled -org.eclipse.jdt.core.compiler.source=17 +org.eclipse.jdt.core.compiler.source=21 diff --git a/io.sloeber.tests/META-INF/MANIFEST.MF b/io.sloeber.tests/META-INF/MANIFEST.MF index f6cbd958..27aee41b 100644 --- a/io.sloeber.tests/META-INF/MANIFEST.MF +++ b/io.sloeber.tests/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: Sloeber Tests Bundle-SymbolicName: io.sloeber.tests;singleton:=true Bundle-Version: 5.0.0.qualifier -Bundle-RequiredExecutionEnvironment: JavaSE-17 +Bundle-RequiredExecutionEnvironment: JavaSE-21 Bundle-Vendor: Sloeber.io Require-Bundle: org.eclipse.core.runtime, org.eclipse.core.resources, diff --git a/io.sloeber.ui.nl1/META-INF/MANIFEST.MF b/io.sloeber.ui.nl1/META-INF/MANIFEST.MF index a7b2ac39..9e7c4ed0 100644 --- a/io.sloeber.ui.nl1/META-INF/MANIFEST.MF +++ b/io.sloeber.ui.nl1/META-INF/MANIFEST.MF @@ -4,5 +4,5 @@ Bundle-Name: Sloeber ui language pack Bundle-SymbolicName: io.sloeber.ui.nl1;singleton:=true Bundle-Version: 5.0.0.qualifier Fragment-Host: io.sloeber.ui;bundle-version="5.0.0" -Bundle-RequiredExecutionEnvironment: JavaSE-17 +Bundle-RequiredExecutionEnvironment: JavaSE-21 Bundle-Vendor: baeyens.it diff --git a/io.sloeber.ui/.classpath b/io.sloeber.ui/.classpath index 390c4779..de9ab79c 100644 --- a/io.sloeber.ui/.classpath +++ b/io.sloeber.ui/.classpath @@ -1,7 +1,7 @@ - + diff --git a/io.sloeber.ui/.settings/org.eclipse.jdt.core.prefs b/io.sloeber.ui/.settings/org.eclipse.jdt.core.prefs index 27705a03..50226351 100644 --- a/io.sloeber.ui/.settings/org.eclipse.jdt.core.prefs +++ b/io.sloeber.ui/.settings/org.eclipse.jdt.core.prefs @@ -1,9 +1,18 @@ eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=21 +org.eclipse.jdt.core.compiler.compliance=21 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=21 org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 org.eclipse.jdt.core.formatter.align_type_members_on_columns=false org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns=false org.eclipse.jdt.core.formatter.align_with_spaces=false +org.eclipse.jdt.core.formatter.alignment_for_additive_operator=16 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 @@ -11,21 +20,24 @@ org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_c org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 org.eclipse.jdt.core.formatter.alignment_for_assignment=0 -org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator=16 org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 org.eclipse.jdt.core.formatter.alignment_for_compact_loops=16 org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16 org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0 +org.eclipse.jdt.core.formatter.alignment_for_logical_operator=16 org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 org.eclipse.jdt.core.formatter.alignment_for_module_statements=16 org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator=16 org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0 org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_string_concatenation=16 org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 @@ -117,11 +129,12 @@ org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_additive_operator=insert org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator=insert org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=do not insert org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert @@ -152,6 +165,8 @@ org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_after_logical_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator=insert org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert @@ -176,13 +191,17 @@ org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_relational_operator=insert org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert +org.eclipse.jdt.core.formatter.insert_space_after_shift_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation=insert org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_additive_operator=insert org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator=insert org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert @@ -230,6 +249,8 @@ org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do no org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_before_logical_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator=insert org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert @@ -266,9 +287,12 @@ org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not inser org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_relational_operator=insert org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_shift_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation=insert org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert @@ -307,9 +331,13 @@ org.eclipse.jdt.core.formatter.tabulation.char=tab org.eclipse.jdt.core.formatter.tabulation.size=4 org.eclipse.jdt.core.formatter.use_on_off_tags=false org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_additive_operator=true org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false -org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true +org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator=true org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true +org.eclipse.jdt.core.formatter.wrap_before_logical_operator=true +org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator=true org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_before_string_concatenation=true org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter diff --git a/io.sloeber.ui/META-INF/MANIFEST.MF b/io.sloeber.ui/META-INF/MANIFEST.MF index 80889069..ea6296b8 100644 --- a/io.sloeber.ui/META-INF/MANIFEST.MF +++ b/io.sloeber.ui/META-INF/MANIFEST.MF @@ -18,7 +18,7 @@ Require-Bundle: org.eclipse.ui, org.eclipse.jface, io.sloeber.autoBuild.ui, org.eclipse.swt -Bundle-RequiredExecutionEnvironment: JavaSE-17 +Bundle-RequiredExecutionEnvironment: JavaSE-21 Bundle-ActivationPolicy: lazy Bundle-Vendor: Sloeber.io Export-Package: io.sloeber.ui.monitor;x-friends:="io.sloeber.tests", From 2726e0b4d908c907d9c1e7acd9a903d5d62c6fe1 Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 30 Mar 2025 01:12:10 +0100 Subject: [PATCH 107/115] fix the build --- io.sloeber.product/sloeber.product | 7 ++----- io.sloeber.product/sloeber.target | 13 +++++++------ 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/io.sloeber.product/sloeber.product b/io.sloeber.product/sloeber.product index 0f46a223..25c5632d 100644 --- a/io.sloeber.product/sloeber.product +++ b/io.sloeber.product/sloeber.product @@ -22,8 +22,8 @@ https://github.com/sloeber/arduino-eclipse-plugin/graphs/contributors - -perspective (io.sloeber.product.perspective) ---launcher.defaultAction openFile + -perspective (io.sloeber.product.perspective) +--launcher.defaultAction openFile --launcher.appendVmargs -Dorg.eclipse.update.reconcile=false @@ -219,10 +219,8 @@ United States, other countries, or both. - - @@ -248,7 +246,6 @@ United States, other countries, or both. - diff --git a/io.sloeber.product/sloeber.target b/io.sloeber.product/sloeber.target index 3591998a..08d2e6cc 100644 --- a/io.sloeber.product/sloeber.target +++ b/io.sloeber.product/sloeber.target @@ -2,7 +2,7 @@ - + @@ -37,18 +37,20 @@ + + - + - + - + @@ -62,9 +64,8 @@ - + - From c1c2dd0caeece9e82c8b77f18d14c467ad108621 Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 30 Mar 2025 23:12:02 +0200 Subject: [PATCH 108/115] fix the build; sdk is part of the build --- io.sloeber.product.sdk/sdk.product | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/io.sloeber.product.sdk/sdk.product b/io.sloeber.product.sdk/sdk.product index d5acd282..b7f45cc4 100644 --- a/io.sloeber.product.sdk/sdk.product +++ b/io.sloeber.product.sdk/sdk.product @@ -25,10 +25,10 @@ https://github.com/sloeber/arduino-eclipse-plugin/graphs/contributors --launcher.defaultAction openFile --launcher.appendVmargs - -Dorg.eclipse.update.reconcile=false --Dlog4j.configuration=log4j/log4j.xml --Xms256m --Xmx2048m + -Dorg.eclipse.update.reconcile=false +-Dlog4j.configuration=log4j/log4j.xml +-Xms256m +-Xmx2048m --add-modules=ALL-SYSTEM -XstartOnFirstThread -Dorg.eclipse.swt.internal.carbon.smallFonts @@ -209,7 +209,6 @@ United States, other countries, or both. - @@ -239,7 +238,6 @@ United States, other countries, or both. - @@ -250,7 +248,6 @@ United States, other countries, or both. - From 4f4c6a554e0e8285febe82931e34e935551634bb Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 1 Jun 2025 01:36:39 +0200 Subject: [PATCH 109/115] refactor ArduinoPrivateLibrary to ArduinoPrivateHardwareLibraryVersion --- .../src/io/sloeber/arduinoFramework/api/LibraryManager.java | 4 ++-- io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java | 4 ++-- ...rsion.java => ArduinoPrivateHardwareLibraryVersion.java} | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) rename io.sloeber.core/src/io/sloeber/core/internal/{ArduinoPrivateLibraryVersion.java => ArduinoPrivateHardwareLibraryVersion.java} (92%) diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java index bac7c518..469485c7 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java @@ -38,7 +38,7 @@ import io.sloeber.core.common.InstancePreferences; import io.sloeber.core.core.DefaultInstallHandler; import io.sloeber.core.internal.ArduinoHardwareLibrary; -import io.sloeber.core.internal.ArduinoPrivateLibraryVersion; +import io.sloeber.core.internal.ArduinoPrivateHardwareLibraryVersion; import io.sloeber.core.internal.Example; import io.sloeber.core.tools.FileModifiers; import io.sloeber.core.tools.PackageManager; @@ -409,7 +409,7 @@ private static Map getLibrariesFromFolder(IPath IArduinoLibraryVersion retVersion = new ArduinoHardwareLibrary(ipath); ret.put(retVersion.getFQN().toPortableString(), retVersion); } else { - IArduinoLibraryVersion retVersion = new ArduinoPrivateLibraryVersion(ipath); + IArduinoLibraryVersion retVersion = new ArduinoPrivateHardwareLibraryVersion(ipath); ret.put(retVersion.getFQN().toPortableString(), retVersion); } diff --git a/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java b/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java index 29e3305a..5033918e 100644 --- a/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java +++ b/io.sloeber.core/src/io/sloeber/core/api/SloeberProject.java @@ -51,7 +51,7 @@ import io.sloeber.core.Messages; import io.sloeber.core.api.CompileDescription.SizeCommands; import io.sloeber.core.internal.ArduinoHardwareLibrary; -import io.sloeber.core.internal.ArduinoPrivateLibraryVersion; +import io.sloeber.core.internal.ArduinoPrivateHardwareLibraryVersion; import io.sloeber.core.internal.SloeberConfiguration; import io.sloeber.core.listeners.IndexerController; import io.sloeber.core.natures.SloeberNature; @@ -241,7 +241,7 @@ public void run(IProgressMonitor internalMonitor) throws CoreException { continue; } - IPath privateLibFQN = ArduinoPrivateLibraryVersion.calculateFQN(curLibName); + IPath privateLibFQN = ArduinoPrivateHardwareLibraryVersion.calculateFQN(curLibName); foundLib = availableLibs.get(privateLibFQN.toString()); if (foundLib != null) { toInstallLibs.add(foundLib); diff --git a/io.sloeber.core/src/io/sloeber/core/internal/ArduinoPrivateLibraryVersion.java b/io.sloeber.core/src/io/sloeber/core/internal/ArduinoPrivateHardwareLibraryVersion.java similarity index 92% rename from io.sloeber.core/src/io/sloeber/core/internal/ArduinoPrivateLibraryVersion.java rename to io.sloeber.core/src/io/sloeber/core/internal/ArduinoPrivateHardwareLibraryVersion.java index 0c649e65..dc7a4099 100644 --- a/io.sloeber.core/src/io/sloeber/core/internal/ArduinoPrivateLibraryVersion.java +++ b/io.sloeber.core/src/io/sloeber/core/internal/ArduinoPrivateHardwareLibraryVersion.java @@ -12,18 +12,18 @@ import io.sloeber.core.api.VersionNumber; import io.sloeber.core.common.InstancePreferences; -public class ArduinoPrivateLibraryVersion implements IArduinoLibraryVersion { +public class ArduinoPrivateHardwareLibraryVersion implements IArduinoLibraryVersion { private IPath myInstallPath; private String myName; private IPath myFQN; - public ArduinoPrivateLibraryVersion(IPath installPath) { + public ArduinoPrivateHardwareLibraryVersion(IPath installPath) { myInstallPath = installPath; myName = myInstallPath.lastSegment(); myFQN= calculateFQN(getName()); } - public ArduinoPrivateLibraryVersion(String curSaveString) { + public ArduinoPrivateHardwareLibraryVersion(String curSaveString) { String[] parts=curSaveString.split(SEMI_COLON); myName=parts[parts.length-1]; myFQN= calculateFQN(getName()); From d49247048d54f67cf4d65c98a1f0dd19fdfb3041 Mon Sep 17 00:00:00 2001 From: jan Date: Sat, 7 Jun 2025 01:23:28 +0200 Subject: [PATCH 110/115] Making getPrivateLibraryPaths public --- .../src/io/sloeber/arduinoFramework/api/LibraryManager.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java index 469485c7..b313537e 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java @@ -87,6 +87,10 @@ public static String getPrivateLibraryPathsString() { return InstancePreferences.getPrivateLibraryPathsString(); } + public static String[] getPrivateLibraryPaths() { + return InstancePreferences.getPrivateLibraryPaths(); + } + public static void setPrivateLibraryPaths(String[] libraryPaths) { InstancePreferences.setPrivateLibraryPaths(libraryPaths); From 36b35e8b9550526afb7ed1755a6d0a4382006c17 Mon Sep 17 00:00:00 2001 From: jan Date: Sat, 7 Jun 2025 02:51:28 +0200 Subject: [PATCH 111/115] add getLibraryVersionFromFQN --- .../arduinoFramework/api/LibraryManager.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java index b313537e..d45b96e0 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java @@ -475,6 +475,30 @@ public static IArduinoLibraryVersion getLibraryVersionFromLocation(IFolder libFo return getLibrariesPrivate().get(libFolder.getName()); } + public static IArduinoLibraryVersion getLibraryVersionFromFQN(String FQNLibName, BoardDescription boardDescriptor) { + String[] fqnParts = FQNLibName.split(SLACH); + if (fqnParts.length < 3) { + return null; + } + if (!SLOEBER_LIBRARY_FQN.equals(fqnParts[0])) { + // this is not a library + return null; + } + if (MANAGED.equals(fqnParts[1])) { + if (BOARD.equals(fqnParts[2])) { + if (boardDescriptor == null) { + return null; + } + return getLibrariesHarware(boardDescriptor).get(FQNLibName); + } + return getLibrariesdManaged().get(FQNLibName); + } + if (PRIVATE.equals(fqnParts[1])) { + return getLibrariesPrivate().get(FQNLibName); + } + return null; + } + /** * Remove a lib based on the name of the lib * From c4fc97b66bfe227c24a174c06724d3f1675a0c29 Mon Sep 17 00:00:00 2001 From: jan Date: Sat, 7 Jun 2025 03:26:36 +0200 Subject: [PATCH 112/115] Add test for #1723 --- .../src/io/sloeber/core/BuildTests.java | 99 ++++++++++++++++++- 1 file changed, 98 insertions(+), 1 deletion(-) diff --git a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java index ba0f0754..e261db16 100644 --- a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java +++ b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java @@ -2,7 +2,7 @@ - +import static io.sloeber.core.api.Const.*; import java.io.File; import java.util.Arrays; import java.util.Collection; @@ -15,6 +15,8 @@ import java.util.TreeMap; import java.util.stream.Stream; import java.net.URI; +import java.nio.file.Files; +import java.nio.file.StandardOpenOption; import org.apache.commons.io.FileUtils; @@ -25,8 +27,10 @@ import org.eclipse.cdt.core.settings.model.ICProjectDescription; import org.eclipse.cdt.internal.core.pdom.indexer.IndexerPreferences; import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; @@ -801,4 +805,97 @@ public void NightlyBoardPatron(String name, MCUBoard boardID, Example example, C } + + /** + * Use private lib from the workspace. + * Close project + * open project + * Is the lib still there (as does project still build) + * + * @throws Exception + */ + @Test + public void issue1723() throws Exception { + final String projectName = "private_lib"; + final String privateLibFolderName = "an_private_lib"; + final String privateLibName = "a_private_lib"; + final String libHeaderContent=("int aFunction();")+System.lineSeparator(); + String libCodeContent=("#include \""+privateLibName+".h\"")+System.lineSeparator(); + libCodeContent=libCodeContent+("int aFunction(){")+System.lineSeparator(); + libCodeContent=libCodeContent+("}")+System.lineSeparator(); + String libRefContent=("#include \""+privateLibName+".h\"")+System.lineSeparator(); + libRefContent=libRefContent+("int aRefFunction(){")+System.lineSeparator(); + libRefContent=libRefContent+("aFunction();")+System.lineSeparator(); + libRefContent=libRefContent+("}")+System.lineSeparator(); + + + //create a basic arduino project + BoardDescription unoBoardid = Arduino.uno().getBoardDescriptor(); + IProject theTestProject = null; + + IPath templateFolder = Shared.getTemplateFolder("CreateAndCompileTest"); + CodeDescription codeDescriptor = CodeDescription.createCustomTemplate(templateFolder); + theTestProject = SloeberProject.createArduinoProject(projectName, null, unoBoardid, codeDescriptor, + new CompileDescription(), new NullProgressMonitor()); + Shared.waitForIndexer(theTestProject); + + //create a private library project + final IWorkspace workspace = ResourcesPlugin.getWorkspace(); + IWorkspaceRoot root = workspace.getRoot(); + IProject theLibProject= root.getProject("PrivateLibs"); + theLibProject.create(new NullProgressMonitor()); + theLibProject.open(new NullProgressMonitor()); + IFolder libFolder = theLibProject.getFolder(privateLibFolderName); + libFolder.create(true, true, new NullProgressMonitor()); + IFile libHeaderFile=libFolder.getFile(privateLibName+".h"); + IFile libSourceFile=libFolder.getFile(privateLibName+".cpp"); + + Files.write(libHeaderFile.getLocation().toPath(), libHeaderContent.getBytes(), StandardOpenOption.TRUNCATE_EXISTING, + StandardOpenOption.CREATE); + Files.write(libSourceFile.getLocation().toPath(), libCodeContent.getBytes(), StandardOpenOption.TRUNCATE_EXISTING, + StandardOpenOption.CREATE); + + + //build project (should work) + theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, new NullProgressMonitor()); + assertNull(Shared.hasBuildErrors(theTestProject),"Created Project does not build."); + + + //Add code to project that uses private lib + IFolder srcFolder = theTestProject.getFolder("src"); + IFile referingFile=srcFolder.getFile("privateLibUser.cpp"); + Files.write(referingFile.getLocation().toPath(), libRefContent.getBytes(), StandardOpenOption.TRUNCATE_EXISTING, + StandardOpenOption.CREATE); + + //build project (should fail) + theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, new NullProgressMonitor()); + assertNotNull(Shared.hasBuildErrors(theTestProject),"Lib should be missing; build should fail."); + + + //add private libs project to the sloeber preferences private libs + List privateLibList= new LinkedList<>(); + privateLibList.add(theLibProject.getLocation().toOSString()); + privateLibList.addAll( Arrays.asList( LibraryManager.getPrivateLibraryPaths())); + LibraryManager.setPrivateLibraryPaths(privateLibList.toArray(new String[privateLibList.size()])); + + + + //add the private lib to the project + IArduinoLibraryVersion privateArduinoLib=LibraryManager.getLibraryVersionFromFQN(SLOEBER_LIBRARY_FQN+SLACH+PRIVATE+SLACH+libFolder.getName(), null); + Collection myPrivateLibs =new LinkedList<>(); + myPrivateLibs.add(privateArduinoLib); + + ISloeberConfiguration sloeberConf=ISloeberConfiguration.getActiveConfig(theTestProject, true); + sloeberConf.addLibraries(myPrivateLibs); + + //build project (should work) + theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, new NullProgressMonitor()); + assertNull(Shared.hasBuildErrors(theTestProject),"lib added build should succeed"); + + //There should be 1 lib in the project + Map usedLibs=sloeberConf.getUsedLibraries(); + assertTrue(usedLibs.size()==0,"Private Lib not found"); + + + } } From 86684d7782e09519225af6c144d465fa4dff450e Mon Sep 17 00:00:00 2001 From: jan Date: Sat, 7 Jun 2025 23:58:56 +0200 Subject: [PATCH 113/115] improve the regression test --- io.sloeber.tests/src/io/sloeber/core/BuildTests.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java index e261db16..1e75e3d3 100644 --- a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java +++ b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java @@ -892,9 +892,17 @@ public void issue1723() throws Exception { theTestProject.build(IncrementalProjectBuilder.FULL_BUILD, new NullProgressMonitor()); assertNull(Shared.hasBuildErrors(theTestProject),"lib added build should succeed"); + //open and close the project to clear the cache + theTestProject.close(null); + // just wait a while + Thread.sleep(1000); + + theTestProject.open(new NullProgressMonitor()); + //There should be 1 lib in the project + sloeberConf=ISloeberConfiguration.getActiveConfig(theTestProject, true); Map usedLibs=sloeberConf.getUsedLibraries(); - assertTrue(usedLibs.size()==0,"Private Lib not found"); + assertTrue(usedLibs.size()==1,"Private Lib not found"); } From ea28dd87b1a2e76eea0c1814ad84d4768615587e Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 8 Jun 2025 00:48:24 +0200 Subject: [PATCH 114/115] Use different assert to get a better error message --- io.sloeber.tests/src/io/sloeber/core/BuildTests.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java index 1e75e3d3..7f022d57 100644 --- a/io.sloeber.tests/src/io/sloeber/core/BuildTests.java +++ b/io.sloeber.tests/src/io/sloeber/core/BuildTests.java @@ -902,8 +902,7 @@ public void issue1723() throws Exception { //There should be 1 lib in the project sloeberConf=ISloeberConfiguration.getActiveConfig(theTestProject, true); Map usedLibs=sloeberConf.getUsedLibraries(); - assertTrue(usedLibs.size()==1,"Private Lib not found"); - + assertEquals(1,usedLibs.size(),"Private Lib not found"); } } From f0788b1c542e002153d3080b3a19f8e3bbcdc88f Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 8 Jun 2025 01:35:05 +0200 Subject: [PATCH 115/115] Fix for #1723 --- .../arduinoFramework/api/LibraryManager.java | 70 +++++++++++-------- .../core/internal/SloeberConfiguration.java | 2 +- 2 files changed, 42 insertions(+), 30 deletions(-) diff --git a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java index d45b96e0..455cffb2 100644 --- a/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java +++ b/io.sloeber.core/src/io/sloeber/arduinoFramework/api/LibraryManager.java @@ -339,6 +339,17 @@ public static IStatus updateLibraries(Set toUnInstallLib return status; } + /** + * A convenience (and downward compatibility method of + * getLibrariesAll(BoardDescription boardDescriptor, true) { + * + * @param confDesc can be null + * @return A map of FQN IArduinoLibraryVersion + */ + public static TreeMap getLibrariesAll(BoardDescription boardDescriptor) { + return getLibrariesAll( boardDescriptor, true); + } + /** * Given a sloeber configuration provide all the libraries that can be used by * this sketch This boils down to all libraries maintained by the Library @@ -346,36 +357,41 @@ public static IStatus updateLibraries(Set toUnInstallLib * provided by the personal libraries * * @param confDesc can be null - * @return + * @return if keyIsFQN is true: A map of FQN IArduinoLibraryVersion + * if keyIsFQN is false: A map of location IArduinoLibraryVersion */ - public static TreeMap getLibrariesAll(BoardDescription boardDescriptor) { + public static TreeMap getLibrariesAll(BoardDescription boardDescriptor, boolean keyIsFQN) { TreeMap libraries = new TreeMap<>(); - libraries.putAll(getLibrariesdManaged()); - libraries.putAll(getLibrariesPrivate()); + libraries.putAll(getLibrariesdManaged(keyIsFQN)); + libraries.putAll(getLibrariesPrivate(keyIsFQN)); if (boardDescriptor != null) { - libraries.putAll(getLibrariesHarware(boardDescriptor)); + libraries.putAll(getLibrariesHarware(boardDescriptor,keyIsFQN)); } return libraries; } - private static Map getLibrariesdManaged() { + private static Map getLibrariesdManaged(boolean keyIsFQN) { Map ret = new HashMap<>(); for (IArduinoLibraryIndex libindex : libraryIndices) { for (IArduinoLibrary curLib : libindex.getLibraries()) { IArduinoLibraryVersion instVersion = curLib.getInstalledVersion(); if (instVersion != null) { - ret.put(instVersion.getFQN().toPortableString(), instVersion); + if (keyIsFQN) { + ret.put(instVersion.getFQN().toPortableString(), instVersion); + } else { + ret.put(instVersion.getInstallPath().toPortableString(), instVersion); + } } } } return ret; } - private static Map getLibrariesPrivate() { + private static Map getLibrariesPrivate(boolean keyIsFQN) { Map ret = new HashMap<>(); String privateLibPaths[] = InstancePreferences.getPrivateLibraryPaths(); for (String curLibPath : privateLibPaths) { - ret.putAll(getLibrariesFromFolder(new Path(curLibPath), 2, false,true)); + ret.putAll(getLibrariesFromFolder(new Path(curLibPath), 2, false,true,keyIsFQN)); } return ret; @@ -385,12 +401,13 @@ private static Map getLibrariesPrivate() { * for a given folder return all subfolders * * @param ipath the folder you want the subfolders off + * @param keyIsFQN * @return The subfolders of the ipath folder. May contain empty values. This * method returns a key value pair of key equals foldername and value * equals full path. */ private static Map getLibrariesFromFolder(IPath ipath, int depth, - boolean isHardwareLib,boolean isPrivate) { + boolean isHardwareLib,boolean isPrivate, boolean keyIsFQN) { if (ConfigurationPreferences.getInstallationPathLibraries().isPrefixOf(ipath)) { System.err.println("The method findAllPrivateLibs should not be called on Library manager installed libs"); //$NON-NLS-1$ } @@ -409,13 +426,9 @@ private static Map getLibrariesFromFolder(IPath } String fileExt = (new Path(curChild)).getFileExtension(); if (LIBRARY_INDICATION_FILES.contains(curChild) || CODE_EXTENSIONS.contains(fileExt)) { - if (isHardwareLib) { - IArduinoLibraryVersion retVersion = new ArduinoHardwareLibrary(ipath); - ret.put(retVersion.getFQN().toPortableString(), retVersion); - } else { - IArduinoLibraryVersion retVersion = new ArduinoPrivateHardwareLibraryVersion(ipath); - ret.put(retVersion.getFQN().toPortableString(), retVersion); - } + IArduinoLibraryVersion retVersion = isHardwareLib?new ArduinoHardwareLibrary(ipath):new ArduinoPrivateHardwareLibraryVersion(ipath); + String key=keyIsFQN?retVersion.getFQN().toPortableString():retVersion.getInstallPath().toPortableString(); + ret.put(key, retVersion); return ret; } @@ -429,7 +442,7 @@ private static Map getLibrariesFromFolder(IPath IPath LibPath = ipath.append(curFolder); File LibPathFile = LibPath.toFile(); if (LibPathFile.isDirectory() && !LibPathFile.isHidden()) { - ret.putAll(getLibrariesFromFolder(LibPath, depth - 1, isHardwareLib,isPrivate)); + ret.putAll(getLibrariesFromFolder(LibPath, depth - 1, isHardwareLib,isPrivate,keyIsFQN)); } } return ret; @@ -439,21 +452,22 @@ private static Map getLibrariesFromFolder(IPath * Searches all the hardware dependent libraries of a project. If this is a * board referencing a core then the libraries of the referenced core are added * as well + * @param keyIsFQN * * @param project the project to find all hardware libraries for * @return all the library folder names. May contain empty values. */ - public static Map getLibrariesHarware(BoardDescription boardDescriptor) { + public static Map getLibrariesHarware(BoardDescription boardDescriptor, boolean keyIsFQN) { Map ret = new HashMap<>(); // first add the referenced IPath libPath = boardDescriptor.getReferencedCoreLibraryPath(); if (libPath != null) { - ret.putAll(getLibrariesFromFolder(libPath, 1, true,boardDescriptor.isPrivate())); + ret.putAll(getLibrariesFromFolder(libPath, 1, true,boardDescriptor.isPrivate(),keyIsFQN)); } // then add the referencing libPath = boardDescriptor.getReferencingLibraryPath(); if (libPath != null) { - ret.putAll(getLibrariesFromFolder(libPath, 1, true,boardDescriptor.isPrivate())); + ret.putAll(getLibrariesFromFolder(libPath, 1, true,boardDescriptor.isPrivate(),keyIsFQN)); } return ret; } @@ -462,17 +476,15 @@ public static IArduinoLibraryVersion getLibraryVersionFromLocation(IFolder libFo if (boardDescriptor != null) { IPath libPath=boardDescriptor.getReferencedCoreLibraryPath(); if(libPath!=null && libPath.isPrefixOf(libFolder.getLocation())) { - String FQNLibName=ArduinoHardwareLibrary.calculateFQN(libFolder.getName()).toString(); - return getLibrariesHarware(boardDescriptor).get(FQNLibName); + return getLibrariesHarware(boardDescriptor,false).get(libFolder.getLocation().toPortableString()); } } if(ConfigurationPreferences.getInstallationPathLibraries().isPrefixOf(libFolder.getLocation())) { - String FQNLibName= ArduinoLibraryVersion.calculateFQN(libFolder.getName()).toString(); - return getLibrariesdManaged().get(FQNLibName); + return getLibrariesdManaged(false).get(libFolder.getLocation().toPortableString()); } - return getLibrariesPrivate().get(libFolder.getName()); + return getLibrariesPrivate(false).get(libFolder.getLocation().toPortableString()); } public static IArduinoLibraryVersion getLibraryVersionFromFQN(String FQNLibName, BoardDescription boardDescriptor) { @@ -489,12 +501,12 @@ public static IArduinoLibraryVersion getLibraryVersionFromFQN(String FQNLibName, if (boardDescriptor == null) { return null; } - return getLibrariesHarware(boardDescriptor).get(FQNLibName); + return getLibrariesHarware(boardDescriptor,true).get(FQNLibName); } - return getLibrariesdManaged().get(FQNLibName); + return getLibrariesdManaged(true).get(FQNLibName); } if (PRIVATE.equals(fqnParts[1])) { - return getLibrariesPrivate().get(FQNLibName); + return getLibrariesPrivate(true).get(FQNLibName); } return null; } diff --git a/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java b/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java index c883f3c1..8e76dc6e 100644 --- a/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java +++ b/io.sloeber.core/src/io/sloeber/core/internal/SloeberConfiguration.java @@ -611,7 +611,7 @@ private void upDateHardwareLibraries() { } } if (!hardwareLibsFQN.isEmpty()) { - Map boardLibs = LibraryManager.getLibrariesHarware(boardDesc); + Map boardLibs = LibraryManager.getLibrariesHarware(boardDesc,true); for (IPath curReplaceLibFQN : hardwareLibsFQN) { IArduinoLibraryVersion newLib = boardLibs.get(curReplaceLibFQN.toPortableString()); if (newLib != null) {