package io.sloeber.core.tools;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.envvar.EnvironmentVariable;
import org.eclipse.cdt.core.envvar.IContributedEnvironment;
import org.eclipse.cdt.core.envvar.IEnvironmentVariable;
import org.eclipse.cdt.core.envvar.IEnvironmentVariableManager;
import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsProvider;
import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsProvidersKeeper;
import org.eclipse.cdt.core.parser.util.StringUtil;
import org.eclipse.cdt.core.settings.model.CIncludePathEntry;
import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
import org.eclipse.cdt.core.settings.model.ICFolderDescription;
import org.eclipse.cdt.core.settings.model.ICLanguageSetting;
import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry;
import org.eclipse.cdt.core.settings.model.ICSettingEntry;
import org.eclipse.cdt.managedbuilder.core.IManagedBuildInfo;
import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager;
import org.eclipse.cdt.managedbuilder.language.settings.providers.AbstractBuiltinSpecsDetector;
import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
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.ResourcesPlugin;
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.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.ui.console.ConsolePlugin;
import org.eclipse.ui.console.IConsole;
import org.eclipse.ui.console.IConsoleManager;
import org.eclipse.ui.console.MessageConsole;
import cc.arduino.packages.discoverers.NetworkDiscovery;
import io.sloeber.core.InternalBoardDescriptor;
import io.sloeber.core.api.BoardDescriptor;
import io.sloeber.core.api.CompileOptions;
import io.sloeber.core.api.Defaults;
import io.sloeber.core.common.Common;
import io.sloeber.core.common.ConfigurationPreferences;
import io.sloeber.core.common.Const;
import io.sloeber.core.managers.ArduinoPlatform;
import io.sloeber.core.managers.Manager;
import io.sloeber.core.managers.Tool;
import io.sloeber.core.managers.ToolDependency;
@SuppressWarnings("nls")
/**
* ArduinoHelpers is a static class containing general purpose functions
*
* @author Jan Baeyens
*
*/
public class Helpers extends Common {
private static final String ARDUINO_CORE_FOLDER_NAME = "cores";
private static final String ARDUINO_CORE_BUILD_FOLDER_NAME = "core";
private static final String BUILD_PATH_SYSCALLS_SAM3 = "\"{build.path}/syscalls_sam3.c.o\"";
private static final String BUILD_PATH_ARDUINO_SYSCALLS_SAM3 = "\"{build.path}/" + ARDUINO_CORE_BUILD_FOLDER_NAME
+ "/syscalls_sam3.c.o\"";
private static final String BUILD_PATH_SYSCALLS_MTK = "\"{build.path}/syscalls_mtk.c.o\"";
private static final String BUILD_PATH_ARDUINO_SYSCALLS_MTK = "\"{build.path}/" + ARDUINO_CORE_BUILD_FOLDER_NAME
+ "/syscalls_mtk.c.o\"";
private static final String ENV_KEY_ARCHITECTURE = ERASE_START + "ARCHITECTURE";
private static final String ENV_KEY_BUILD_VARIANT = ERASE_START + "BUILD.VARIANT";
private static final String ENV_KEY_JANTJE_BUILD_VARIANT = ERASE_START + "JANTJE.BUILD_VARIANT";
private static final String ENV_KEY_JANTJE_UPLOAD_TOOL = ERASE_START + "JANTJE.UPLOAD.TOOL";
private static final String ACTION_PROGRAM = "PROGRAM";
private static final String ENV_KEY_BUILD_ARCH = ERASE_START + "BUILD.ARCH";
private static final String ENV_KEY_BUILD_CORE = ERASE_START + "BUILD.CORE";
private static final String ENV_KEY_BUILD_GENERIC_PATH = ERASE_START + "BUILD.GENERIC.PATH";
private static final String ENV_KEY_HARDWARE_PATH = ERASE_START + "RUNTIME.HARDWARE.PATH";
private static final String ENV_KEY_PLATFORM_PATH = ERASE_START + "RUNTIME.PLATFORM.PATH";
private static final String ENV_KEY_COMPILER_PATH = ERASE_START + "COMPILER.PATH";
private static final String ENV_KEY_JANTJE_VARIANT_REFERENCED_PLATFORM = ERASE_START
+ "JANTJE.VARIANT.REFERENCED.PLATFORM";
private static final String ENV_KEY_JANTJE_UPLOAD_REFERENCED_PLATFORM = ERASE_START
+ "JANTJE.UPLOAD.REFERENCED.PLATFORM";
private static final String ENV_KEY_JANTJE_BUILD_CORE = ERASE_START + "JANTJE.BUILD_CORE";
private static final String ENV_KEY_JANTJE_MAKE_LOCATION = ENV_KEY_JANTJE_START + "MAKE_LOCATION";
private static final String TOOL_KEY = "\\$\\{TOOL}";
private static final String FILE_KEY = "\\$\\{FILE}";
private static final String BOARD_KEY = "\\$\\{BOARD}";
/**
* This method is the internal working class that adds the provided include
* path to all configurations and languages.
*
* @param configurationDescription
* The configuration description of the project to add it to
* @param IncludePath
* The path to add to the include folders
* @see addLibraryDependency
* {@link #addLibraryDependency(IProject, IProject)}
*/
private static void addIncludeFolder(ICConfigurationDescription configurationDescription, IPath IncludePath) {
// find all languages
ICFolderDescription folderDescription = configurationDescription.getRootFolderDescription();
ICLanguageSetting[] languageSettings = folderDescription.getLanguageSettings();
// 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.")) {
ICLanguageSettingEntry[] OrgIncludeEntries = lang.getSettingEntries(ICSettingEntry.INCLUDE_PATH);
ICLanguageSettingEntry[] IncludeEntries = new ICLanguageSettingEntry[OrgIncludeEntries.length + 1];
System.arraycopy(OrgIncludeEntries, 0, IncludeEntries, 0, OrgIncludeEntries.length);
IncludeEntries[OrgIncludeEntries.length] = new CIncludePathEntry(IncludePath,
ICSettingEntry.VALUE_WORKSPACE_PATH); // (location.toString());
lang.setSettingEntries(ICSettingEntry.INCLUDE_PATH, IncludeEntries);
}
}
}
}
/**
* Removes include folders that are not valid. This method does not save the
* configurationDescription description
*
* @param configurationDescription
* the configuration that is checked
* @return true is a include path has been removed. False if the include
* path remains unchanged.
*/
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.")) {
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 {
Common.log(new Status(IStatus.WARNING, Const.CORE_PLUGIN_ID,
"Removed invalid include path" + cusPath, 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;
}
public static void addCodeFolder(IProject project, Path toLinkFolder, String LinkName,
ICConfigurationDescription configurationDescriptions[]) throws CoreException {
for (ICConfigurationDescription curConfig : configurationDescriptions) {
Helpers.addCodeFolder(project, toLinkFolder, LinkName, curConfig);
}
}
/**
* 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 target
* the folder name relative to the project
* @param source
* the fully qualified name of the folder to link to
*/
public static void LinkFolderToFolder(IProject project, IPath source, IPath target) {
// create target parent folder and grandparents
IPath ParentFolders = new Path(target.toString()).removeLastSegments(1);
for (int curfolder = ParentFolders.segmentCount() - 1; curfolder >= 0; curfolder--) {
try {
createNewFolder(project, ParentFolders.removeLastSegments(curfolder).toString(), null);
} catch (CoreException e) {// ignore this error as the parent
// folders may have been created yet
}
}
// create the actual link
try {
createNewFolder(project, target.toString(), source);
} catch (CoreException e) {
Common.log(
new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID, Messages.Helpers_Create_folder_failed + target, e));
}
}
/**
* This method creates a link folder in the project and add the folder as a
* source path to the project it also adds the path to the include folder if
* the include path parameter points to a path that contains a subfolder
* named "utility" this subfolder will be added to the include path as well
* <br/>
* Forget about this. Arduino made this all so complicated I don't know
* anymore what needs to be added to what<br/>
* <br/>
*
* note Arduino has these subfolders in the libraries that need to be
* include.<br/>
* <br/>
*
* note that in the current eclipse version, there is no need to add the
* subfolder as a code folder. This may change in the future as it looks
* like a bug to me.<br/>
*
* @param project
* @param Path
* @throws CoreException
*
* @see addLibraryDependency
* {@link #addLibraryDependency(IProject, IProject)}
*/
public static void addCodeFolder(IProject project, IPath toLinkFolder, String LinkName,
ICConfigurationDescription configurationDescription) throws CoreException {
IFolder link = project.getFolder(LinkName);
LinkFolderToFolder(project, toLinkFolder, new Path(LinkName));
// Now the folder has been created we need to make sure the special
// folders are added to the path
addIncludeFolder(configurationDescription, link.getFullPath());
String possibleIncludeFolder = "utility";
File file = toLinkFolder.append(possibleIncludeFolder).toFile();
if (file.exists()) {
addIncludeFolder(configurationDescription, link.getFullPath().append(possibleIncludeFolder));
}
possibleIncludeFolder = "src";
file = toLinkFolder.append(possibleIncludeFolder).toFile();
if (file.exists()) {
addIncludeFolder(configurationDescription, link.getFullPath().append(possibleIncludeFolder));
}
possibleIncludeFolder = "arch";
file = toLinkFolder.append(possibleIncludeFolder).toFile();
if (file.exists()) {
addIncludeFolder(configurationDescription,
link.getFullPath().append(possibleIncludeFolder).append(makeEnvironmentVar(ENV_KEY_ARCHITECTURE)));
}
}
public static void removeCodeFolder(IProject project, String LinkName) throws CoreException {
IFolder link = project.getFolder(LinkName);
if (link.exists()) {
link.delete(true, null);
}
}
/**
* This method creates a link folder in the project and adds the folder as a
* source path to the project it also adds the path to the include folder if
* the include path parameter points to a path that contains a subfolder
* named "utility" this subfolder will be added to the include path as well
* <br/>
* <br/>
*
* note Arduino has these subfolders in the libraries that need to be
* include.<br/>
* <br/>
*
* note that in the current eclipse version, there is no need to add the
* subfolder as a code folder. This may change in the future as it looks
* like a bug to me.<br/>
*
* @param project
* @param Path
* @throws CoreException
*
* @see addLibraryDependency
* {@link #addLibraryDependency(IProject, IProject)}
*/
public static void addCodeFolder(IProject project, Path Path, ICConfigurationDescription configurationDescription)
throws CoreException {
String NiceName = Path.lastSegment();
addCodeFolder(project, Path, NiceName, configurationDescription);
}
/**
* addTheNatures replaces all existing natures by the natures needed for a
* arduino project
*
* @param project
* The project where the natures need to be added to
* @throws CoreException
*/
public static void addTheNatures(IProjectDescription description) throws CoreException {
String[] newnatures = new String[5];
newnatures[0] = "org.eclipse.cdt.core.cnature";
newnatures[1] = "org.eclipse.cdt.core.ccnature";
newnatures[2] = "org.eclipse.cdt.managedbuilder.core.managedBuildNature";
newnatures[3] = "org.eclipse.cdt.managedbuilder.core.ScannerConfigNature";
newnatures[4] = Const.ARDUINO_NATURE_ID;
description.setNatureIds(newnatures);
}
/**
* This method adds the content of a content stream to a file If the file
* already exist the file remains untouched
*
* @param container
* used as a reference to the file
* @param path
* The path to the file relative from the container
* @param contentStream
* The stream to put in the file
* @param monitor
* A monitor to show progress
* @throws CoreException
*/
public static void addFileToProject(IContainer container, Path path, InputStream contentStream,
IProgressMonitor monitor, boolean overwrite) throws CoreException {
final IFile file = container.getFile(path);
file.refreshLocal(IResource.DEPTH_INFINITE, monitor);
if (overwrite && file.exists()) {
file.delete(true, null);
file.refreshLocal(IResource.DEPTH_INFINITE, monitor);
}
if (!file.exists() && (contentStream != null)) {
file.create(contentStream, true, monitor);
}
}
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;
}
/**
* This method adds the Arduino code in a subfolder named Arduino. 2 linked
* subfolders named core and variant link to the real Arduino code note
*
* @param project
* The project to add the arduino code to
* @param configurationDescription
* The configuration description that will contain the change
* @throws CoreException
*/
public static void addArduinoCodeToProject(IProject project, ICConfigurationDescription configurationDescription)
throws CoreException {
String boardVariant = getBuildEnvironmentVariable(configurationDescription, ENV_KEY_BUILD_VARIANT,
new String());
String buildCoreFolder = getBuildEnvironmentVariable(configurationDescription, ENV_KEY_BUILD_CORE,
new String());
String redirectCorePlatform = getBuildEnvironmentVariable(configurationDescription,
ENV_KEY_JANTJE_CORE_REFERENCED_PLATFORM, new String());
IPath corePath = new Path(redirectCorePlatform).append(ARDUINO_CORE_FOLDER_NAME).append(buildCoreFolder);
addCodeFolder(project, corePath, ARDUINO_CODE_FOLDER_NAME + '/' + ARDUINO_CORE_BUILD_FOLDER_NAME,
configurationDescription);
if (boardVariant.isEmpty()) {
// remove the existing link
Helpers.removeCodeFolder(project, ARDUINO_CODE_FOLDER_NAME + "/variant");
} else {
String redirectVariantPath = getBuildEnvironmentVariable(configurationDescription,
ENV_KEY_JANTJE_VARIANT_REFERENCED_PLATFORM, new String());
IPath VariantFile = new Path(redirectVariantPath).append(VARIANTS_FOLDER_NAME).append(boardVariant);
Helpers.addCodeFolder(project, VariantFile, ARDUINO_CODE_FOLDER_NAME + "/variant",
configurationDescription);
}
}
/**
* Creates a new folder resource as a link or local
*
* @param Project
* the project the folder is added to
* @param newFolderName
* 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(IProject Project, String newFolderName, IPath linklocation)
throws CoreException {
final IFolder newFolderHandle = Project.getFolder(newFolderName);
if (linklocation != null) {
URI relativeLinklocation = Project.getPathVariableManager().convertToRelative(URIUtil.toURI(linklocation),
false, null);
newFolderHandle.createLink(relativeLinklocation, IResource.REPLACE | IResource.ALLOW_MISSING_LOCAL, null);
} else {
newFolderHandle.create(0, true, null);
}
}
/**
* Remove all the arduino environment variables.
*
* @param contribEnv
* @param confDesc
*/
private static void removeAllEraseEnvironmentVariables(IContributedEnvironment contribEnv,
ICConfigurationDescription confDesc) {
IEnvironmentVariable[] CurVariables = contribEnv.getVariables(confDesc);
for (int i = (CurVariables.length - 1); i > 0; i--) {
if (CurVariables[i].getName().startsWith(Const.ERASE_START)) {
contribEnv.removeVariable(CurVariables[i].getName(), confDesc);
}
}
}
/**
* Sets the default values. Basically some settings are not set in the
* platform.txt file. Here I set these values. This method should be called
* as first. This way the values in platform.txt and boards.txt will take
* precedence of the default values declared here
*
* @param projectName
*
* @param contribEnv
* @param confDesc
* @param platformFile
* Used to define the hardware as different settings are needed
* for avr and sam
*/
private static void setTheEnvironmentVariablesSetTheDefaults(String projectName, IContributedEnvironment contribEnv,
ICConfigurationDescription confDesc, BoardDescriptor boardDescriptor) {
// Set some default values because the platform.txt does not contain
// them
Path platformPath = new Path(boardDescriptor.getPlatformPath().toString());
String architecture = boardDescriptor.getArchitecture();
// String packagename = boardDescriptor.getPackage();
int numSegmentsToSubtractForHardwarePath = 1;
if (architecture.contains(DOT)) { // in case there is a version in the
// path ignore the version
numSegmentsToSubtractForHardwarePath = 3;
architecture = platformPath.removeLastSegments(2).lastSegment();
// packagename = platformPath.removeLastSegments(4).lastSegment();
}
boardDescriptor.saveConfiguration(confDesc, contribEnv);
setBuildEnvironmentVariable(contribEnv, confDesc, ENV_KEY_ARCHITECTURE, architecture.toUpperCase());
setBuildEnvironmentVariable(contribEnv, confDesc, ENV_KEY_BUILD_ARCH, architecture.toUpperCase());
setBuildEnvironmentVariable(contribEnv, confDesc, ENV_KEY_HARDWARE_PATH,
platformPath.removeLastSegments(numSegmentsToSubtractForHardwarePath).toString());
setBuildEnvironmentVariable(contribEnv, confDesc, ENV_KEY_PLATFORM_PATH, platformPath.toString());
// setBuildEnvironmentVariable(contribEnv, confDesc,
// ENV_KEY_SERIAL_PORT,
// makeEnvironmentVar(Const.ENV_KEY_JANTJE_UPLOAD_PORT));
if (Platform.getOS().equals(Platform.OS_WIN32)) {
setBuildEnvironmentVariable(contribEnv, confDesc, ENV_KEY_JANTJE_MAKE_LOCATION,
ConfigurationPreferences.getMakePath().toString() + '/');
}
// Build Time
Date d = new Date();
GregorianCalendar cal = new GregorianCalendar();
long current = d.getTime() / 1000;
long timezone = cal.get(Calendar.ZONE_OFFSET) / 1000;
long daylight = cal.get(Calendar.DST_OFFSET) / 1000;
// p.put("extra.time.utc", Long.toString(current));
setBuildEnvironmentVariable(contribEnv, confDesc, "A.EXTRA.TIME.UTC", Long.toString(current));
setBuildEnvironmentVariable(contribEnv, confDesc, "A.EXTRA.TIME.LOCAL",
Long.toString(current + timezone + daylight));
setBuildEnvironmentVariable(contribEnv, confDesc, "A.EXTRA.TIME.ZONE", Long.toString(timezone));
setBuildEnvironmentVariable(contribEnv, confDesc, "A.EXTRA.TIME.DTS", Long.toString(daylight));
// End of Teensy specific settings
// some glue to make it work
String pathDelimiter = makeEnvironmentVar("PathDelimiter");
if (Platform.getOS().equals(Platform.OS_WIN32)) {
String systemroot = makeEnvironmentVar("SystemRoot");
setBuildEnvironmentVariable(contribEnv, confDesc, "PATH",
makeEnvironmentVar(ENV_KEY_COMPILER_PATH) + pathDelimiter
+ makeEnvironmentVar(ENV_KEY_BUILD_GENERIC_PATH) + pathDelimiter + systemroot + "\\system32"
+ pathDelimiter + systemroot + pathDelimiter + systemroot + "\\system32\\Wbem"
+ pathDelimiter + makeEnvironmentVar("sloeber_path_extension"));
} else {
setBuildEnvironmentVariable(contribEnv, confDesc, "PATH",
makeEnvironmentVar(ENV_KEY_COMPILER_PATH) + pathDelimiter
+ makeEnvironmentVar(ENV_KEY_BUILD_GENERIC_PATH) + pathDelimiter
+ makeEnvironmentVar("PATH"));
}
// if (firstTime)
String sizeSwitch = getBuildEnvironmentVariable(confDesc, ENV_KEY_JANTJE_SIZE_SWITCH, new String(), false);
if (sizeSwitch.isEmpty()) {
setBuildEnvironmentVariable(contribEnv, confDesc, ENV_KEY_JANTJE_SIZE_SWITCH,
makeEnvironmentVar(get_ENV_KEY_RECIPE(ACTION_SIZE)));
} else {
sizeSwitch.toString();
}
}
private static void setTheEnvironmentVariablesAddAFile(IContributedEnvironment contribEnv,
ICConfigurationDescription confDesc, File envVarFile) {
setTheEnvironmentVariablesAddAFile(ERASE_START, contribEnv, confDesc, envVarFile, true);
}
/**
* This method parses a file with environment variables like the
* platform.txt file for values to be added to the environment variables
*
* @param contribEnv
* @param confDesc
* @param envVarFile
* The file to parse
*/
private static void setTheEnvironmentVariablesAddAFile(String prefix, IContributedEnvironment contribEnv,
ICConfigurationDescription confDesc, File envVarFile, boolean touppercase) {
try (DataInputStream dataInputStream = new DataInputStream(new FileInputStream(envVarFile));
BufferedReader br = new BufferedReader(new InputStreamReader(dataInputStream));) {
String strLine;
// Read File Line By Line
while ((strLine = br.readLine()) != null) {
String realData[] = strLine.split("#");// Ignore
// everything after
// first #
if (realData.length > 0) {
String var[] = realData[0].split("=", 2); // look
// for assignment
if (var.length == 2) {
String value = var[1].trim();
if (value.contains(BUILD_PATH_SYSCALLS_SAM3)) {
value = value.replace(BUILD_PATH_SYSCALLS_SAM3, BUILD_PATH_ARDUINO_SYSCALLS_SAM3);
} else if (value.contains(BUILD_PATH_SYSCALLS_MTK)) {
value = value.replace(BUILD_PATH_SYSCALLS_MTK, BUILD_PATH_ARDUINO_SYSCALLS_MTK);
}
setBuildEnvironmentVariable(contribEnv, confDesc, MakeKeyString(prefix, var[0]),
MakeEnvironmentString(value, prefix, touppercase));
}
}
}
} catch (FileNotFoundException e) {
Common.log(new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID,
Messages.Helpers_Error_parsing + envVarFile.toString() + Messages.Helpers_File_does_not_exists, e));
} catch (IOException e) {
Common.log(new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID,
Messages.Helpers_Error_parsing + envVarFile.toString() + Messages.Helpers_IO_exception, e));
}
}
/**
* This method parses the boards.txt file for values to be added to the
* environment variables First it adds all the variables based on the board
* name [boardID].[key]=[value] results in [key]=[value] (taking in account
* the modifiers) Then it parses for the menu variables
* menu.[menuID].[boardID].[selectionID].[key]=[value] results in
* [key]=[value] (taking in account the modifiers)
*
* @param contribEnv
* @param confDesc
* @param platformFilename
* The file to parse
*/
private static void setTheEnvironmentVariablesAddtheBoardsTxt(IContributedEnvironment contribEnv,
ICConfigurationDescription confDesc, InternalBoardDescriptor boardDescriptor, boolean warn) {
TxtFile boardsFile = boardDescriptor.getTxtFile();
String boardID = boardDescriptor.getBoardID();
Map<String, String> options = boardDescriptor.getOptions();
// Get the boards section and add all entries to the environment
// variables
Map<String, String> boardSectionMap = boardsFile.getSection(boardID);
if (boardSectionMap == null) {
if (warn) {
Common.log(new Status(IStatus.INFO, Const.CORE_PLUGIN_ID,
Messages.Helpers_The_project + confDesc.getProjectDescription().getProject().getName()
+ Messages.Helpers_Invalid_boards_config + confDesc.getName()
+ Messages.Helpers_boards_file + boardsFile.getTxtFile().toString()
+ Messages.Helpers_Boards_id + boardID));
}
return;
}
for (Entry<String, String> currentPair : boardSectionMap.entrySet()) {
// if it is not a menu item add it
if (!currentPair.getKey().startsWith(Messages.Helpers_menu)) {
String keyString = MakeKeyString(currentPair.getKey());
String valueString = MakeEnvironmentString(currentPair.getValue(), Const.ERASE_START, true);
contribEnv.addVariable(new EnvironmentVariable(keyString, valueString), confDesc);
}
}
for (Entry<String, String> currentPair : boardSectionMap.entrySet()) {
// if it is not a menu item add it
if (currentPair.getKey().startsWith(Messages.Helpers_menu)) {
String[] keySplit = currentPair.getKey().split("\\.");
String menuID = keySplit[1];
String menuItemID = keySplit[2];
if (menuItemID.equalsIgnoreCase(options.get(menuID.toUpperCase()))) {
// we also need to skip the name
String StartValue = MENU + DOT + menuID + DOT + menuItemID + DOT; // $NON-NLS-1$
try {
String keyString = MakeKeyString(currentPair.getKey().substring(StartValue.length()));
String valueString = MakeEnvironmentString(currentPair.getValue(), Const.ERASE_START, true);
contribEnv.addVariable(new EnvironmentVariable(keyString, valueString), confDesc);
} catch (StringIndexOutOfBoundsException e) {
// ignore as this is the case when the menu name is
// processed
}
}
}
}
}
private static void addPlatformFileTools(ArduinoPlatform platform, IContributedEnvironment contribEnv,
ICConfigurationDescription confDesc) {
if (platform.getToolsDependencies() != null) {
for (ToolDependency tool : platform.getToolsDependencies()) {
String keyString = MakeKeyString("runtime.tools." + tool.getName() + ".path");
Tool theTool = tool.getTool();
if (theTool == null) {
Common.log(new Status(IStatus.WARNING, Const.CORE_PLUGIN_ID,
"Error adding platformFileTools while processing tool " + tool.getName() + " version "
+ tool.getVersion() + " Installpath is null"));
return;
}
String valueString = new Path(theTool.getInstallPath().toString()).toString();
setBuildEnvironmentVariable(contribEnv, confDesc, keyString, valueString);
keyString = MakeKeyString("runtime.tools." + tool.getName() + tool.getVersion() + ".path");
setBuildEnvironmentVariable(contribEnv, confDesc, keyString, valueString);
keyString = MakeKeyString("runtime.tools." + tool.getName() + '-' + tool.getVersion() + ".path");
setBuildEnvironmentVariable(contribEnv, confDesc, keyString, valueString);
}
}
}
private static void setTheEnvironmentVariablesAddThePlatformInfo(IContributedEnvironment contribEnv,
ICConfigurationDescription confDesc) {
String platformFileName = getBuildEnvironmentVariable(confDesc, Const.ENV_KEY_JANTJE_PLATFORM_FILE,
new String());
String referenceCoredPlatformFileName = getBuildEnvironmentVariable(confDesc,
ENV_KEY_JANTJE_CORE_REFERENCED_PLATFORM, new String());
ArduinoPlatform platform = null;
String curversion = null;
for (ArduinoPlatform curPlatform : Manager.getInstalledPlatforms()) {
addPlatformFileTools(curPlatform, contribEnv, confDesc);
if (curPlatform.isInstalled() && "avr".equalsIgnoreCase(curPlatform.getArchitecture())
&& "arduino".equalsIgnoreCase(curPlatform.getPackage().getMaintainer())) {
if (Version.compare(curPlatform.getVersion(), curversion) > 0) {
curversion = curPlatform.getVersion();
platform = curPlatform;
}
}
}
// add the newest arduino avr platform again for the idiots wanting to
// reference arduino without referencing it
if (platform != null) {
addPlatformFileTools(platform, contribEnv, confDesc);
}
// by adding the referencenced platform after the real platform
platform = Manager.getPlatform(new Path(referenceCoredPlatformFileName).append(PLATFORM_FILE_NAME).toFile());
if (platform != null) {
addPlatformFileTools(platform, contribEnv, confDesc);
}
// and the real platform
platform = Manager.getPlatform(new File(platformFileName));
if (platform != null) {
// skip if this platform has no platform.txt. This is to fix
// problem with arduboy that provide tooldependencies but no
// platform.txt
if (platform.getPlatformFile().exists()) {
addPlatformFileTools(platform, contribEnv, confDesc);
}
}
}
/**
* 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 static void setTheEnvironmentVariables(IProject project, ICConfigurationDescription confDesc,
InternalBoardDescriptor boardsDescriptor) {
// first get all the data we need
IEnvironmentVariableManager envManager = CCorePlugin.getDefault().getBuildEnvironmentManager();
IContributedEnvironment contribEnv = envManager.getContributedEnvironment();
Programmers localProgrammers[] = Programmers.fromBoards(boardsDescriptor);
String boardid = boardsDescriptor.getBoardID();
InternalBoardDescriptor pluginPreProcessingBoardsTxt = new InternalBoardDescriptor(
new TxtFile(ConfigurationPreferences.getPreProcessingBoardsFile()), boardid);
InternalBoardDescriptor pluginPostProcessingBoardsTxt = new InternalBoardDescriptor(
new TxtFile(ConfigurationPreferences.getPostProcessingBoardsFile()), boardid);
File pluginPreProcessingPlatformTxt = ConfigurationPreferences.getPreProcessingPlatformFile();
File pluginPostProcessingPlatformTxt = ConfigurationPreferences.getPostProcessingPlatformFile();
// Now we have all info we can start processing
// first remove all Arduino Variables so there is no memory effect
removeAllEraseEnvironmentVariables(contribEnv, confDesc);
setTheEnvironmentVariablesSetTheDefaults(project.getName(), contribEnv, confDesc, boardsDescriptor);
// add the stuff that comes with the plugin that are marked as pre
setTheEnvironmentVariablesAddAFile(new String(), contribEnv, confDesc, pluginPreProcessingPlatformTxt, false);
setTheEnvironmentVariablesAddtheBoardsTxt(contribEnv, confDesc, pluginPreProcessingBoardsTxt, false);
// Do some magic for the arduino:arduino stuff
setTheEnvironmentVariablesRedirectToOtherVendors(contribEnv, confDesc, boardsDescriptor);
// process the platform file that is referenced in the build.core of the
// boards.txt file
Path coreReferencedPlatform = new Path(
Common.getBuildEnvironmentVariable(confDesc, ENV_KEY_JANTJE_CORE_REFERENCED_PLATFORM, new String()));
Path upLoadreferencedPlatform = new Path(
Common.getBuildEnvironmentVariable(confDesc, ENV_KEY_JANTJE_UPLOAD_REFERENCED_PLATFORM, new String()));
Path variantReferencedPlatform = new Path(
Common.getBuildEnvironmentVariable(confDesc, ENV_KEY_JANTJE_VARIANT_REFERENCED_PLATFORM, new String()));
if (upLoadreferencedPlatform.toFile().exists()) {
setTheEnvironmentVariablesAddAFile(contribEnv, confDesc,
upLoadreferencedPlatform.append(PLATFORM_FILE_NAME).toFile());
}
if (variantReferencedPlatform.toFile().exists()
&& !variantReferencedPlatform.equals(upLoadreferencedPlatform)) {
setTheEnvironmentVariablesAddAFile(contribEnv, confDesc,
variantReferencedPlatform.append(PLATFORM_FILE_NAME).toFile());
}
if (coreReferencedPlatform.toFile().exists() && !coreReferencedPlatform.equals(variantReferencedPlatform)) {
setTheEnvironmentVariablesAddAFile(contribEnv, confDesc,
coreReferencedPlatform.append(PLATFORM_FILE_NAME).toFile());
}
File localPlatfrmFilename = new File(boardsDescriptor.getPlatformFile());
// process the platform file next to the selected boards.txt
if (localPlatfrmFilename.exists()) {
setTheEnvironmentVariablesAddAFile(contribEnv, confDesc, localPlatfrmFilename);
}
setTheEnvironmentVariablesAddThePlatformInfo(contribEnv, confDesc);
// add the boards file
setTheEnvironmentVariablesAddtheBoardsTxt(contribEnv, confDesc, boardsDescriptor, true);
String programmer = contribEnv.getVariable(get_Jantje_KEY_PROTOCOL(ACTION_UPLOAD), confDesc).getValue();
for (Programmers curProgrammer : localProgrammers) {
String programmerID = curProgrammer.getBoardIDFromBoardName(programmer);
if (programmerID != null) {
InternalBoardDescriptor progBoard = new InternalBoardDescriptor(curProgrammer, programmerID);
setTheEnvironmentVariablesAddtheBoardsTxt(contribEnv, confDesc, progBoard, false);
}
}
// add the stuff that comes with the plugin that is marked as post
setTheEnvironmentVariablesAddAFile(contribEnv, confDesc, pluginPostProcessingPlatformTxt);
setTheEnvironmentVariablesAddtheBoardsTxt(contribEnv, confDesc, pluginPostProcessingBoardsTxt, false);
// Do some coded post processing
setTheEnvironmentVariablesPostProcessing(contribEnv, confDesc, boardsDescriptor);
}
/**
* This method is to support the [vendor]:[value] as described in
* https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5-3rd-party-
* Hardware-specification This method parses the boards.txt file for
* myboard.build.core myboard.build.variant currently not supported
* myboard.upload.tool myboard.bootloader.tool
*
* in case myboard.build.core is of type [vendor]:[value]
* PATH_VARIABLE_NAME_ARDUINO_PLATFORM is changed to the correct value in
* case myboard.build.variant is of type [vendor]:[value]
* PATH_VARIABLE_NAME_ARDUINO_PINS is changed to the correct value
*
* this method also sets ENV_KEY_JANTJE_BUILD_CORE and
* ENV_KEY_JANTJE_BUILD_VARIANT to [value] of respectively
* myboard.build.core and myboard.build.variant
*
* This method relies on the post processing to set
* A.BUILD.CORE=${ENV_KEY_JANTJE_BUILD_CORE}
* A.BUILD.VARIANT=${ENV_KEY_JANTJE_BUILD_VARIANT}
*
* @param contribEnv
* @param confDesc
* @param boardsFile
* @param boardID
*/
private static void setTheEnvironmentVariablesRedirectToOtherVendors(IContributedEnvironment contribEnv,
ICConfigurationDescription confDesc, InternalBoardDescriptor boardsDescriptor) {
Map<String, String> boardInfo = boardsDescriptor.getTxtFile().getSection(boardsDescriptor.getBoardID());
if (boardInfo == null) {
return; // there is a problem with the board ID
}
String core = boardInfo.get("build.core");
String variant = boardInfo.get("build.variant");
String upload = boardInfo.get("upload.tool");
if (core != null) {
String valueSplit[] = core.split(COLON);
if (valueSplit.length == 2) {
String refVendor = valueSplit[0];
String actualValue = valueSplit[1];
Common.setBuildEnvironmentVariable(contribEnv, confDesc, ENV_KEY_JANTJE_BUILD_CORE, actualValue);
IPath referencdPlatform = findReferencedPlatform(refVendor, boardsDescriptor.getArchitecture());
if (referencdPlatform == null) {
Common.log(new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID,
Messages.Helpers_tool_reference_missing.replaceAll(TOOL_KEY, core)
.replaceAll(FILE_KEY, boardsDescriptor.getBoardsFile())
.replaceAll(BOARD_KEY, boardsDescriptor.getBoardID())));
} else {
setBuildEnvironmentVariable(contribEnv, confDesc, ENV_KEY_JANTJE_CORE_REFERENCED_PLATFORM,
referencdPlatform.toString());
}
} else {
setBuildEnvironmentVariable(contribEnv, confDesc, ENV_KEY_JANTJE_BUILD_CORE, core);
}
}
if (variant != null) {
String valueSplit[] = variant.split(COLON);
if (valueSplit.length == 2) {
String refVendor = valueSplit[0];
String actualValue = valueSplit[1];
Common.setBuildEnvironmentVariable(contribEnv, confDesc, ENV_KEY_JANTJE_BUILD_VARIANT, actualValue);
IPath referencdPlatform = findReferencedPlatform(refVendor, boardsDescriptor.getArchitecture());
if (referencdPlatform == null) {
Common.log(new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID,
Messages.Helpers_tool_reference_missing.replaceAll(TOOL_KEY, variant)
.replaceAll(FILE_KEY, boardsDescriptor.getBoardsFile())
.replaceAll(BOARD_KEY, boardsDescriptor.getBoardID())));
} else {
Common.setBuildEnvironmentVariable(contribEnv, confDesc, ENV_KEY_JANTJE_VARIANT_REFERENCED_PLATFORM,
referencdPlatform.toString());
}
} else {
setBuildEnvironmentVariable(contribEnv, confDesc, ENV_KEY_JANTJE_BUILD_VARIANT, variant);
}
}
if (upload != null) {
String valueSplit[] = upload.split(COLON);
if (valueSplit.length == 2) {
String refVendor = valueSplit[0];
String actualValue = valueSplit[1];
Common.setBuildEnvironmentVariable(contribEnv, confDesc, ENV_KEY_JANTJE_UPLOAD_TOOL, actualValue);
IPath referencdPlatform = findReferencedPlatform(refVendor, boardsDescriptor.getArchitecture());
if (referencdPlatform == null) {
Common.log(new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID,
Messages.Helpers_tool_reference_missing.replaceAll(TOOL_KEY, upload)
.replaceAll(FILE_KEY, boardsDescriptor.getBoardsFile())
.replaceAll(BOARD_KEY, boardsDescriptor.getBoardID())));
} else {
Common.setBuildEnvironmentVariable(contribEnv, confDesc, ENV_KEY_JANTJE_UPLOAD_TOOL, actualValue);
setBuildEnvironmentVariable(contribEnv, confDesc, ENV_KEY_JANTJE_UPLOAD_REFERENCED_PLATFORM,
referencdPlatform.toString());
}
} else {
setBuildEnvironmentVariable(contribEnv, confDesc, ENV_KEY_JANTJE_UPLOAD_TOOL, upload);
}
}
}
/**
* This method looks for a referenced platform. Ask the boards manager to
* find the latest installed vendor/architecture platform file
*
* If this is not found there is still sme old code that probably can be
* deleted.
*
* @param vendor
* @param architecture
* @return
*/
private static IPath findReferencedPlatform(String vendor, String architecture) {
// ask the boardsmanager for the platform file
IPath ret = Manager.getPlatformInstallPath(vendor, architecture);
return ret;
}
/**
* Following post processing is done
*
* the macro expansion resolves the "file tag" Therefore I split the
* "recipe" patterns in 2 parts (before and after the "file tag") the
* pattern in the toolchain is then ${first part} ${files} ${second part}
*
* The handling of the upload variables is done differently in arduino than
* here. 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}
*
* if a programmer is selected different from default some extra actions are
* done here so no special code is needed to handle programmers
*
* @param contribEnv
* @param confDesc
* @param boardsDescriptor
*/
private static void setTheEnvironmentVariablesPostProcessing(IContributedEnvironment contribEnv,
ICConfigurationDescription confDesc, InternalBoardDescriptor boardsDescriptor) {
CompileOptions compileOptions = new CompileOptions(confDesc);
// a save will overwrite the warning settings set by arduino
compileOptions.save(confDesc);
String actions[] = { ACTION_C_to_O, ACTION_CPP_to_O, ACTION_S_to_O, ACTION_OBJCOPY_to_HEX,
ACTION_OBJCOPY_to_EEP, ACTION_SIZE, ACTION_AR, ACTION_C_COMBINE };
for (String action : actions) {
String recipeKey = get_ENV_KEY_RECIPE(action);
String recipe = getBuildEnvironmentVariable(confDesc, recipeKey, new String(), false);
//
recipe = adaptCompilerCommand(recipe);
setBuildEnvironmentVariable(confDesc, recipeKey, recipe);
String recipeParts[] = recipe.split(
"(\"\\$\\{A.OBJECT_FILE}\")|(\\$\\{A.OBJECT_FILES})|(\"\\$\\{A.SOURCE_FILE}\")|(\"[^\"]*\\$\\{A.ARCHIVE_FILE}\")|(\"[^\"]*\\$\\{A.ARCHIVE_FILE_PATH}\")",
3);
switch (recipeParts.length) {
case 0:
setBuildEnvironmentVariable(contribEnv, confDesc, recipeKey + DOT + '1',
Messages.Helpers_No_command_for + recipeKey);
break;
case 1:
setBuildEnvironmentVariableRecipe(contribEnv, confDesc, recipeKey + DOT + '1', recipeParts[0]);
break;
case 2:
setBuildEnvironmentVariableRecipe(contribEnv, confDesc, recipeKey + DOT + '1', recipeParts[0]);
setBuildEnvironmentVariableRecipe(contribEnv, confDesc, recipeKey + DOT + '2', recipeParts[1]);
break;
case 3:
setBuildEnvironmentVariableRecipe(contribEnv, confDesc, recipeKey + DOT + '1', recipeParts[0]);
setBuildEnvironmentVariableRecipe(contribEnv, confDesc, recipeKey + DOT + '2', recipeParts[1]);
setBuildEnvironmentVariableRecipe(contribEnv, confDesc, recipeKey + DOT + '3', recipeParts[2]);
break;
default:
// this should never happen as the split is limited to 3
}
}
String programmer = contribEnv.getVariable(get_Jantje_KEY_PROTOCOL(ACTION_UPLOAD), confDesc).getValue();
if (programmer.equalsIgnoreCase(Defaults.getDefaultUploadProtocol())) {
IEnvironmentVariable uploadToolVar = contribEnv.getVariable(ENV_KEY_JANTJE_UPLOAD_TOOL, confDesc);
String MComPort = boardsDescriptor.getUploadPort();
if ((uploadToolVar == null) || (MComPort.isEmpty())) {
Common.log(new Status(IStatus.WARNING, Const.CORE_PLUGIN_ID,
"Upload will fail due to missing upload parameters"));
} else {
String uploadTool = uploadToolVar.getValue();
String host = getHostFromComPort(MComPort);
if (host != null) {
String platform = contribEnv.getVariable(Const.ENV_KEY_JANTJE_ARCITECTURE_ID, confDesc).getValue();
setBuildEnvironmentVariable(contribEnv, confDesc, ENV_KEY_NETWORK_PORT,
NetworkDiscovery.getPort(host));
setBuildEnvironmentVariable(contribEnv, confDesc, ENV_KEY_NETWORK_AUTH,
NetworkDiscovery.hasAuth(host) ? TRUE : FALSE);
setBuildEnvironmentVariable(contribEnv, confDesc, ENV_KEY_SERIAL_PORT, host);
try {
String key = ERASE_START + platform.toUpperCase() + DOT + "NETWORK" + DOT
+ ACTION_UPLOAD.toUpperCase() + DOT + ENV_TOOL;
String networkUploadTool = contribEnv.getVariable(key, confDesc).getValue();
if (!networkUploadTool.isEmpty()) {
uploadTool = networkUploadTool;
setBuildEnvironmentVariable(contribEnv, confDesc, get_ENV_KEY_TOOL(UPLOAD_CLASS),
UPLOAD_CLASS_DEFAULT);
setBuildEnvironmentVariable(contribEnv, confDesc, ENV_KEY_RESET_BEFORE_UPLOAD, FALSE);
}
} catch (Exception e) {
// simply ignore
}
}
setBuildEnvironmentVariable(contribEnv, confDesc, get_Jantje_KEY_RECIPE(ACTION_UPLOAD),
makeEnvironmentVar(get_ENV_KEY_RECIPE(uploadTool, ACTION_UPLOAD)));
setBuildEnvironmentVariable(contribEnv, confDesc, get_ENV_KEY_TOOL(ACTION_PROGRAM),
makeEnvironmentVar(get_ENV_KEY_TOOL(ACTION_UPLOAD)));
}
} else {
String uploadTool = new String();
try {
uploadTool = contribEnv.getVariable("A.PROGRAM.TOOL", confDesc).getValue();
} catch (Exception e) {
Common.log(
new Status(IStatus.WARNING, Const.CORE_PLUGIN_ID, Messages.Helpers_ProblemInProgrammerFie, e));
}
setBuildEnvironmentVariable(contribEnv, confDesc, get_Jantje_KEY_RECIPE(ACTION_UPLOAD),
makeEnvironmentVar(get_ENV_KEY_RECIPE(uploadTool, ACTION_PROGRAM)));
setBuildEnvironmentVariable(contribEnv, confDesc, get_ENV_KEY_TOOL(ACTION_PROGRAM), uploadTool);
}
ArrayList<String> objcopyCommand = new ArrayList<>();
// I'm looping through the set of variables to fix some things up
try {
IEnvironmentVariable[] curVariables = contribEnv.getVariables(confDesc);
for (IEnvironmentVariable curVariable : curVariables) {
String name = curVariable.getName();
// Arduino uses the board approach for the tools.
// as I'm not, therefore I mod the tools in the command to be
// FQN
if (name.startsWith("A.TOOLS.")) {
String toolID = curVariable.getName().split("\\.")[2];
String recipe = curVariable.getValue();
int indexOfVar = recipe.indexOf("${A.");
while (indexOfVar != -1) {
int endIndexOfVar = recipe.indexOf('}', indexOfVar);
if (endIndexOfVar != -1) {
String foundSuffix = recipe.substring(indexOfVar + 3, endIndexOfVar);
String foundVar = "A" + foundSuffix;
String replaceVar = "A.TOOLS." + toolID.toUpperCase() + foundSuffix;
if (contribEnv.getVariable(foundVar, confDesc) == null) {// $NON-NLS-1$
recipe = recipe.replaceAll(foundVar, replaceVar);
}
}
indexOfVar = recipe.indexOf("${A.", indexOfVar + 4);
}
setBuildEnvironmentVariable(contribEnv, confDesc, name, recipe);
}
if (name.startsWith("A.RECIPE.OBJCOPY.") && name.endsWith(".PATTERN")
&& !curVariable.getValue().isEmpty()) {
objcopyCommand.add(makeEnvironmentVar(name));
}
}
} catch (Exception e) {
Common.log(new Status(IStatus.WARNING, Const.CORE_PLUGIN_ID, "parsing of upload recipe failed", e));
}
Collections.sort(objcopyCommand);
setBuildEnvironmentVariable(contribEnv, confDesc, "JANTJE.OBJCOPY", StringUtil.join(objcopyCommand, "\n\t"));
// if we have a variant defined in a menu option we need to
// grab the value in ENV_KEY_BUILD_VARIANT and put it in
// ENV_KEY_JANTJE_BUILD_VARIANT
// because ENV_KEY_JANTJE_BUILD_VARIANT is empty
String variant = getBuildEnvironmentVariable(confDesc, ENV_KEY_JANTJE_BUILD_VARIANT, "", true);
if (variant.isEmpty()) {
variant = getBuildEnvironmentVariable(confDesc, ENV_KEY_BUILD_VARIANT, "", true);
setBuildEnvironmentVariable(contribEnv, confDesc, ENV_KEY_JANTJE_BUILD_VARIANT, variant);
}
// link build.core to jantje.build.core
setBuildEnvironmentVariable(contribEnv, confDesc, ENV_KEY_BUILD_CORE,
makeEnvironmentVar(ENV_KEY_JANTJE_BUILD_CORE));
// link build.variant to jantje.build.variant
setBuildEnvironmentVariable(contribEnv, confDesc, ENV_KEY_BUILD_VARIANT,
makeEnvironmentVar(ENV_KEY_JANTJE_BUILD_VARIANT));
}
private static void setBuildEnvironmentVariableRecipe(IContributedEnvironment contribEnv,
ICConfigurationDescription confdesc, String key, String recipe) {
IEnvironmentVariable var = new EnvironmentVariable(key, makePathEnvironmentString(recipe));
contribEnv.addVariable(var, confdesc);
// The expansion is needed because the adaptCompilerCommand can not
// handle
// noon extended environment variables
IEnvironmentVariableManager envManager = CCorePlugin.getDefault().getBuildEnvironmentManager();
var = envManager.getVariable(key, confdesc, true);
var = new EnvironmentVariable(key, adaptCompilerCommand(var.getValue()));
contribEnv.addVariable(var, confdesc);
}
/*
* due to the way arduino and cdt work some conversions are needed her.
* replaceAll(" -MMD ", " ") CDT adds -MMD so we delete them
*
* replaceAll("[^\\\\]\"\"", "" I can't recall what this one is for but it
* removes "" except \""
*
* For the os dependent stuff see
* https://github.com/jantje/arduino-eclipse-plugin/issues/493 in windows
* replace '-DXXX="YYY"' with "-DXXX=\\"YYY\\"" in windows replace '-DXXXX='
* * with -DXXXX=
*
* replaceAll(" ", " ") due to the above replacements there can be multiple
* spaces. this cause(s/d) problems so I re^lace them with 1 space. note
* that -with the current implementation- this means that is you define a
* string to a define and the string has multiple spaces there will only be
* one left. This one has to be the last replacement !!
*
* I also do some stuff for the warning settings
*/
private static String adaptCompilerCommand(String recipe) {
String ret = recipe.replaceAll(" -MMD ", " ");
ret = ret.replaceAll("[^\\\\]\"\"", "");
String replaceString = " '-D$1=\"$2\"'"; // linux and mac
if (Platform.getOS().equals(Platform.OS_WIN32)) {
ret = ret.replaceAll(" '-D(\\S+)='", " -D$1=");
replaceString = " \"-D$1=\\\\\"$2\\\\\"\""; // windows
}
ret = ret.replaceAll(" '?-D(\\S+)=\\\\?\"(.+?)\\\\?\"'?", replaceString);
ret = ret.replaceAll(" '-D(\\S+)='", replaceString);
ret = ret.replaceAll(" ", " ");
return ret;
}
/**
* When parsing boards.txt and platform.txt some processing needs to be done
* to get "acceptable environment variable values" This method does the
* parsing {xx} is replaced with ${XX} if to uppercase is true {xx} is
* replaced with ${xx} if to uppercase is false
*
* @param inputString
* the value string as read from the file
* @return the string to be stored as value for the environment variable
*/
public static String MakeEnvironmentString(String inputString, String keyPrefix, boolean touppercase) {
// String ret = inputString.replaceAll("-o \"\\{object_file}\"",
// "").replaceAll("\"\\{object_file}\"",
// "").replaceAll("\"\\{source_file}\"", "")
// .replaceAll("\\{", "\\${" + ArduinoConst.ENV_KEY_START);
String ret = inputString.replaceAll("\\{(?!\\{)", "\\${" + keyPrefix);
if (!touppercase) {
return ret;
}
StringBuilder sb = new StringBuilder(ret);
String regex = "\\{[^}]*\\}";
Pattern p = Pattern.compile(regex); // Create the pattern.
Matcher matcher = p.matcher(sb); // Create the matcher.
while (matcher.find()) {
String buf = sb.substring(matcher.start(), matcher.end()).toUpperCase();
sb.replace(matcher.start(), matcher.end(), buf);
}
return sb.toString();
}
/**
* When parsing boards.txt and platform.txt some processing needs to be done
* to get "acceptable environment variable keys" This method does the
* parsing some examples on windows "test.windows" becomes "A.TEST"
* "test.linux" becomes "A.TEST.LINUX"
*
* on Linux "test.windows" becomes "A.TEST.WINDOWS" "test.linux" becomes
* "A.TEST"
*
*
* @param inputString
* the key string as read from the file
* @return the string to be used as key for the environment variable
*/
private static String MakeKeyString(String string) {
return MakeKeyString(ERASE_START, string);
}
private static String MakeKeyString(String prefix, String string) {
String osString = "\\.\\.";
if (Platform.getOS().equals(Platform.OS_LINUX)) {
osString = "\\.LINUX";
} else if (Platform.getOS().equals(Platform.OS_WIN32)) {
osString = "\\.WINDOWS";
}
return prefix + string.toUpperCase().replaceAll(osString, new String());
}
/**
* 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 Still then the "indexer
* needs to recheck his includes from the language provider which still is
* not working
*
* @param project
*/
public static void setDirtyFlag(IProject project, ICConfigurationDescription cfgDescription) {
IManagedBuildInfo buildInfo = ManagedBuildManager.getBuildInfo(project);
if (buildInfo == null) {
return; // Project is not a managed build project
}
IFolder buildFolder = project.getFolder(cfgDescription.getName());
if (buildFolder.exists()) {
try {
buildFolder.delete(true, null);
} catch (CoreException e) {
Common.log(new Status(IStatus.ERROR, Const.CORE_PLUGIN_ID,
Messages.Helpers_delete_folder_failed + cfgDescription.getName(), e));
}
}
List<ILanguageSettingsProvider> providers;
if (cfgDescription instanceof ILanguageSettingsProvidersKeeper) {
providers = new ArrayList<>(
((ILanguageSettingsProvidersKeeper) cfgDescription).getLanguageSettingProviders());
for (ILanguageSettingsProvider provider : providers) {
if ((provider instanceof AbstractBuiltinSpecsDetector)) { // basically
// check
// for
// working
// copy
// clear and reset isExecuted flag
((AbstractBuiltinSpecsDetector) provider).clear();
}
}
}
}
/**
* Given a source file calculates the base of the output file. this method
* may not be needed if I can used the eclipse default behavior. However the
* eclipse default behavior is different from the arduino default behavior.
* So I keep it for now and we'll see how it goes The eclipse default
* behavior is (starting from the project folder [configuration]/Source The
* Arduino default behavior is all in 1 location (so no subfolders)
*
* @param Source
* The source file to find the
* @return The base file name for the ouput if Source is "file.cpp" the
* output is "file.cpp"
*/
public static IPath GetOutputName(IPath Source) {
IPath outputName;
if (Source.toString().startsWith(Const.ARDUINO_CODE_FOLDER_NAME)) {
outputName = new Path(Const.ARDUINO_CODE_FOLDER_NAME).append(Source.lastSegment());
} else {
outputName = Source;
}
return outputName;
}
/**
* Converts a name to a tagged environment variable if variableName ="this"
* the output is "${this}"
*
* @param variableName
* @return
*/
private static String makeEnvironmentVar(String variableName) {
return "${" + variableName + '}';
}
/**
* Give the string entered in the com port try to extract a host. If no host
* is found return null yun.local at xxx.yyy.zzz (arduino yun) returns
* yun.local
*
* @param mComPort
* @return
*/
public static String getHostFromComPort(String mComPort) {
String host = mComPort.split(Const.SPACE)[0];
if (host.equals(mComPort))
return null;
return host;
}
/**
* 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(IProject project, IPath source, IPath target) {
File[] a = source.toFile().listFiles();
if (a == null) {
Common.log(new Status(IStatus.INFO, Const.CORE_PLUGIN_ID,
Messages.Helpers_link_folder + source + Messages.Helpers_is_empty, null));
return;
}
for (File f : a) {
if (f.isDirectory()) {
LinkFolderToFolder(project, source.append(f.getName()), target.append(f.getName()));
} else {
final IFile newFileHandle = project.getFile(target.append(f.getName()));
try {
newFileHandle.createLink(source.append(f.getName()),
IResource.REPLACE | IResource.ALLOW_MISSING_LOCAL, null);
} catch (CoreException e) {
e.printStackTrace();
}
}
}
}
}