From f38efe2a91bb266fa79f58ca2f0c1e5a7899e044 Mon Sep 17 00:00:00 2001 From: Doug Schaefer Date: Wed, 12 Aug 2015 14:37:51 -0400 Subject: [PATCH] Scanner info with built-ins working for Arduino. Change-Id: Ifa3ea76c11324e06fb7f735f5eb435f387fbbedc --- .../internal/ArduinoScannerInfoProvider.java | 25 +-- .../core/internal/board/ArduinoPlatform.java | 26 ++- .../build/ArduinoBuildConfiguration.java | 172 +++++++++++++++--- .../core/internal/build/ArduinoBuilder.java | 82 ++++++++- .../templates/board.mk | 3 + 5 files changed, 257 insertions(+), 51 deletions(-) diff --git a/toolchains/arduino/org.eclipse.cdt.arduino.core/src/org/eclipse/cdt/arduino/core/internal/ArduinoScannerInfoProvider.java b/toolchains/arduino/org.eclipse.cdt.arduino.core/src/org/eclipse/cdt/arduino/core/internal/ArduinoScannerInfoProvider.java index 5cdc1a2ae09..12e8555529c 100644 --- a/toolchains/arduino/org.eclipse.cdt.arduino.core/src/org/eclipse/cdt/arduino/core/internal/ArduinoScannerInfoProvider.java +++ b/toolchains/arduino/org.eclipse.cdt.arduino.core/src/org/eclipse/cdt/arduino/core/internal/ArduinoScannerInfoProvider.java @@ -1,13 +1,13 @@ package org.eclipse.cdt.arduino.core.internal; -import java.util.HashMap; -import java.util.Map; - -import org.eclipse.cdt.core.parser.ExtendedScannerInfo; +import org.eclipse.cdt.arduino.core.internal.build.ArduinoBuildConfiguration; import org.eclipse.cdt.core.parser.IScannerInfo; import org.eclipse.cdt.core.parser.IScannerInfoChangeListener; import org.eclipse.cdt.core.parser.IScannerInfoProvider; +import org.eclipse.core.resources.IBuildConfiguration; +import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; /** * Responsible for collecting scanner info on Arduino Projects. @@ -16,22 +16,23 @@ public class ArduinoScannerInfoProvider implements IScannerInfoProvider { @Override public IScannerInfo getScannerInformation(IResource resource) { - Map symbols = new HashMap<>(); - String[] includePath = { "/Users/dschaefer/.arduinocdt/hardware/arduino/avr/1.6.7/cores/arduino" }; - ExtendedScannerInfo scannerInfo = new ExtendedScannerInfo(symbols, includePath); - return scannerInfo; + try { + IProject project = resource.getProject(); + IBuildConfiguration config = project.getActiveBuildConfig(); + ArduinoBuildConfiguration arduinoConfig = config.getAdapter(ArduinoBuildConfiguration.class); + return arduinoConfig.getScannerInfo(resource); + } catch (CoreException e) { + Activator.log(e); + return null; + } } @Override public void subscribe(IResource resource, IScannerInfoChangeListener listener) { - // TODO Auto-generated method stub - } @Override public void unsubscribe(IResource resource, IScannerInfoChangeListener listener) { - // TODO Auto-generated method stub - } } diff --git a/toolchains/arduino/org.eclipse.cdt.arduino.core/src/org/eclipse/cdt/arduino/core/internal/board/ArduinoPlatform.java b/toolchains/arduino/org.eclipse.cdt.arduino.core/src/org/eclipse/cdt/arduino/core/internal/board/ArduinoPlatform.java index ad387787140..6b6d87c3e5f 100644 --- a/toolchains/arduino/org.eclipse.cdt.arduino.core/src/org/eclipse/cdt/arduino/core/internal/board/ArduinoPlatform.java +++ b/toolchains/arduino/org.eclipse.cdt.arduino.core/src/org/eclipse/cdt/arduino/core/internal/board/ArduinoPlatform.java @@ -7,9 +7,11 @@ *******************************************************************************/ package org.eclipse.cdt.arduino.core.internal.board; +import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.io.Reader; +import java.io.StringReader; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; @@ -41,6 +43,7 @@ public class ArduinoPlatform { private transient ArduinoPackage pkg; private transient HierarchicalProperties boardsFile; + private transient Properties platformProperties; void setOwner(ArduinoPackage pkg) { this.pkg = pkg; @@ -125,13 +128,24 @@ public class ArduinoPlatform { } public Properties getPlatformProperties() throws CoreException { - Properties properties = new Properties(); - try (Reader reader = new FileReader(getInstallPath().resolve("platform.txt").toFile())) { //$NON-NLS-1$ - properties.load(reader); - return properties; - } catch (IOException e) { - throw new CoreException(new Status(IStatus.ERROR, Activator.getId(), "Loading platform.txt", e)); + if (platformProperties == null) { + platformProperties = new Properties(); + try (BufferedReader reader = new BufferedReader( + new FileReader(getInstallPath().resolve("platform.txt").toFile()))) { //$NON-NLS-1$ + // There are regex's here and need to preserve the \'s + StringBuffer buffer = new StringBuffer(); + for (String line = reader.readLine(); line != null; line = reader.readLine()) { + buffer.append(line.replace("\\", "\\\\")); //$NON-NLS-1$ //$NON-NLS-2$ + buffer.append('\n'); + } + try (Reader reader1 = new StringReader(buffer.toString())) { + platformProperties.load(reader1); + } + } catch (IOException e) { + throw new CoreException(new Status(IStatus.ERROR, Activator.getId(), "Loading platform.txt", e)); + } } + return platformProperties; } public boolean isInstalled() { diff --git a/toolchains/arduino/org.eclipse.cdt.arduino.core/src/org/eclipse/cdt/arduino/core/internal/build/ArduinoBuildConfiguration.java b/toolchains/arduino/org.eclipse.cdt.arduino.core/src/org/eclipse/cdt/arduino/core/internal/build/ArduinoBuildConfiguration.java index a576ee739ba..6b51fc128ae 100644 --- a/toolchains/arduino/org.eclipse.cdt.arduino.core/src/org/eclipse/cdt/arduino/core/internal/build/ArduinoBuildConfiguration.java +++ b/toolchains/arduino/org.eclipse.cdt.arduino.core/src/org/eclipse/cdt/arduino/core/internal/build/ArduinoBuildConfiguration.java @@ -1,13 +1,19 @@ package org.eclipse.cdt.arduino.core.internal.build; +import java.io.BufferedReader; import java.io.File; import java.io.FilenameFilter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.eclipse.cdt.arduino.core.internal.Activator; import org.eclipse.cdt.arduino.core.internal.ArduinoPreferences; @@ -24,6 +30,8 @@ import org.eclipse.cdt.core.model.ICProject; import org.eclipse.cdt.core.model.IOutputEntry; import org.eclipse.cdt.core.model.IPathEntry; import org.eclipse.cdt.core.model.ISourceRoot; +import org.eclipse.cdt.core.parser.ExtendedScannerInfo; +import org.eclipse.cdt.core.parser.IScannerInfo; import org.eclipse.core.resources.IBuildConfiguration; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; @@ -48,16 +56,30 @@ public class ArduinoBuildConfiguration { private final IBuildConfiguration config; + private Properties properties; + + // Cache for scanner info + private IScannerInfo cScannerInfo; + private IScannerInfo cppScannerInfo; + private ArduinoBuildConfiguration(IBuildConfiguration config) { this.config = config; } + private static Map cache = new HashMap<>(); + public static class Factory implements IAdapterFactory { @SuppressWarnings("unchecked") @Override public T getAdapter(Object adaptableObject, Class adapterType) { if (adapterType.equals(ArduinoBuildConfiguration.class) && adaptableObject instanceof IBuildConfiguration) { - return (T) new ArduinoBuildConfiguration((IBuildConfiguration) adaptableObject); + IBuildConfiguration config = (IBuildConfiguration) adaptableObject; + ArduinoBuildConfiguration arduinoConfig = cache.get(config); + if (arduinoConfig == null) { + arduinoConfig = new ArduinoBuildConfiguration(config); + cache.put(config, arduinoConfig); + } + return (T) arduinoConfig; } return null; } @@ -96,11 +118,28 @@ public class ArduinoBuildConfiguration { return ArduinoBoardManager.instance.getBoard(boardName, platformName, packageName); } + private Properties getProperties() throws CoreException { + if (properties == null) { + ArduinoBoard board = getBoard(); + ArduinoPlatform platform = board.getPlatform(); + properties = board.getBoardProperties(); + properties.putAll(board.getPlatform().getPlatformProperties()); + for (ToolDependency toolDep : platform.getToolsDependencies()) { + properties.putAll(toolDep.getTool().getToolProperties()); + } + } + return properties; + } + public IFolder getBuildFolder() throws CoreException { IProject project = config.getProject(); return project.getFolder("build"); //$NON-NLS-1$ } + public File getBuildDirectory() throws CoreException { + return new File(getBuildFolder().getLocationURI()); + } + public IFile getMakeFile() throws CoreException { IFolder buildFolder = getBuildFolder(); ArduinoBoard board = getBoard(); @@ -153,11 +192,8 @@ public class ArduinoBuildConfiguration { buildModel.put("project_srcs", sourceFiles); //$NON-NLS-1$ // the recipes - Properties properties = board.getBoardProperties(); - properties.putAll(board.getPlatform().getPlatformProperties()); - for (ToolDependency toolDep : platform.getToolsDependencies()) { - properties.putAll(toolDep.getTool().getToolProperties()); - } + Properties properties = new Properties(); + properties.putAll(getProperties()); properties.put("runtime.ide.version", "1.6.7"); //$NON-NLS-1$ //$NON-NLS-2$ properties.put("build.arch", platform.getArchitecture().toUpperCase()); //$NON-NLS-1$ properties.put("build.path", "$(OUTPUT_DIR)"); //$NON-NLS-1$ //$NON-NLS-2$ @@ -185,8 +221,11 @@ public class ArduinoBuildConfiguration { return name.endsWith(".cpp") || name.endsWith(".c"); } }); + String[] platformSource = new String[platformFiles.length]; - for (int i = 0; i < platformSource.length; ++i) { + for (int i = 0; i < platformSource.length; ++i) + + { platformSource[i] = platformFiles[i].getAbsolutePath(); } buildModel.put("platform_srcs", platformSource); //$NON-NLS-1$ @@ -202,6 +241,7 @@ public class ArduinoBuildConfiguration { buildModel.put("recipe_c_combine_pattern", resolveProperty("recipe.c.combine.pattern", properties)); //$NON-NLS-1$ //$NON-NLS-2$ buildModel.put("recipe_objcopy_eep_pattern", resolveProperty("recipe.objcopy.eep.pattern", properties)); //$NON-NLS-1$ //$NON-NLS-2$ buildModel.put("recipe_objcopy_hex_pattern", resolveProperty("recipe.objcopy.hex.pattern", properties)); //$NON-NLS-1$ //$NON-NLS-2$ + buildModel.put("recipe_size_pattern", resolveProperty("recipe.size.pattern", properties)); //$NON-NLS-1$ //$NON-NLS-2$ ArduinoTemplateGenerator templateGen = new ArduinoTemplateGenerator(); templateGen.generateFile(buildModel, "board.mk", makeFile, monitor); //$NON-NLS-1$ @@ -234,17 +274,7 @@ public class ArduinoBuildConfiguration { return res; } - public String[] getBuildCommand() throws CoreException { - return new String[] { "make", "-f", getMakeFile().getName() }; //$NON-NLS-1$ //$NON-NLS-2$ - } - - public String[] getCleanCommand() throws CoreException { - return new String[] { "make", "-f", getMakeFile().getName(), "clean" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - } - - public String[] getEnvironment() throws CoreException { - Map env = new HashMap<>(System.getenv()); - + public void setEnvironment(Map env) throws CoreException { // Arduino home to find platforms and libraries env.put("ARDUINO_HOME", ArduinoPreferences.getArduinoHome().toString()); //$NON-NLS-1$ @@ -284,13 +314,109 @@ public class ArduinoBuildConfiguration { pathKey = "PATH"; //$NON-NLS-1$ } env.put(pathKey, path); + } - // Reformat as a proper env. - List strEnv = new ArrayList<>(env.size()); - for (Map.Entry entry : env.entrySet()) { - strEnv.add(entry.getKey() + "=" + entry.getValue()); //$NON-NLS-1$ + public String[] getBuildCommand() throws CoreException { + return new String[] { "make", "-f", getMakeFile().getName() }; //$NON-NLS-1$ //$NON-NLS-2$ + } + + public String[] getCleanCommand() throws CoreException { + return new String[] { "make", "-f", getMakeFile().getName(), "clean" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + public String[] getSizeCommand() throws CoreException { + return new String[] { "make", "-f", getMakeFile().getName(), "size" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + public String getCodeSizeRegex() throws CoreException { + return (String) getBoard().getPlatform().getPlatformProperties().getProperty("recipe.size.regex"); //$NON-NLS-1$ + } + + public int getMaxCodeSize() throws CoreException { + String sizeStr = (String) getBoard().getBoardProperties().getProperty("upload.maximum_size"); //$NON-NLS-1$ + return sizeStr != null ? Integer.parseInt(sizeStr) : -1; + } + + public String getDataSizeRegex() throws CoreException { + return (String) getBoard().getPlatform().getPlatformProperties().getProperty("recipe.size.regex.data"); //$NON-NLS-1$ + } + + public int getMaxDataSize() throws CoreException { + String sizeStr = (String) getBoard().getBoardProperties().getProperty("upload.maximum_data_size"); //$NON-NLS-1$ + return sizeStr != null ? Integer.parseInt(sizeStr) : -1; + } + + public IScannerInfo getScannerInfo(IResource resource) throws CoreException { + // what language is this resource and pick the right path; + switch (CCorePlugin.getContentType(resource.getProject(), resource.getName()).getId()) { + case CCorePlugin.CONTENT_TYPE_CXXSOURCE: + case CCorePlugin.CONTENT_TYPE_CXXHEADER: + if (cppScannerInfo == null) { + cppScannerInfo = calculateScannerInfo("recipe.cpp.o.pattern", resource); //$NON-NLS-1$ + } + return cppScannerInfo; + default: + if (cScannerInfo == null) { + cScannerInfo = calculateScannerInfo("recipe.c.o.pattern", resource); //$NON-NLS-1$ + } + return cScannerInfo; + } + } + + private IScannerInfo calculateScannerInfo(String recipe, IResource resource) throws CoreException { + try { + ArduinoPlatform platform = getBoard().getPlatform(); + Properties properties = new Properties(); + properties.putAll(getProperties()); + properties.put("runtime.ide.version", "1.6.7"); //$NON-NLS-1$ //$NON-NLS-2$ + properties.put("build.arch", platform.getArchitecture().toUpperCase()); //$NON-NLS-1$ + + Path tmpFile = Files.createTempFile("cdt", ".cpp"); + properties.put("source_file", tmpFile.toString()); //$NON-NLS-1$ + properties.put("object_file", "-"); //$NON-NLS-1$ //$NON-NLS-2$ + + String includes = "-E -P -v -dD"; //$NON-NLS-1$ + for (Path include : platform.getIncludePath()) { + includes += " -I\"" + include.toString() + '"'; //$NON-NLS-1$ + } + properties.put("includes", includes); //$NON-NLS-1$ + + // TODO Windows + String[] command = new String[] { "sh", "-c", resolveProperty(recipe, properties) }; //$NON-NLS-1$ //$NON-NLS-2$ + ProcessBuilder processBuilder = new ProcessBuilder(command).directory(getBuildDirectory()) + .redirectErrorStream(true); + setEnvironment(processBuilder.environment()); + Process process = processBuilder.start(); + + Map symbols = new HashMap<>(); + List includePath = new ArrayList<>(); + Pattern definePattern = Pattern.compile("#define (.*)\\s(.*)"); //$NON-NLS-1$ + boolean inIncludePaths = false; + try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + for (String line = reader.readLine(); line != null; line = reader.readLine()) { + if (inIncludePaths) { + if (line.equals("End of search list.")) { //$NON-NLS-1$ + inIncludePaths = false; + } else { + includePath.add(line.trim()); + } + } else if (line.startsWith("#define ")) { //$NON-NLS-1$ + Matcher matcher = definePattern.matcher(line); + if (matcher.matches()) { + symbols.put(matcher.group(1), matcher.group(2)); + } + } else if (line.equals("#include <...> search starts here:")) { //$NON-NLS-1$ + inIncludePaths = true; + } + } + } + Files.delete(tmpFile); + ExtendedScannerInfo scannerInfo = new ExtendedScannerInfo(symbols, + includePath.toArray(new String[includePath.size()])); + return scannerInfo; + } catch (IOException e) { + throw new CoreException(new Status(IStatus.ERROR, Activator.getId(), "Compiler built-ins", e)); } - return strEnv.toArray(new String[strEnv.size()]); } } diff --git a/toolchains/arduino/org.eclipse.cdt.arduino.core/src/org/eclipse/cdt/arduino/core/internal/build/ArduinoBuilder.java b/toolchains/arduino/org.eclipse.cdt.arduino.core/src/org/eclipse/cdt/arduino/core/internal/build/ArduinoBuilder.java index 30ca80e6915..0b03879a299 100644 --- a/toolchains/arduino/org.eclipse.cdt.arduino.core/src/org/eclipse/cdt/arduino/core/internal/build/ArduinoBuilder.java +++ b/toolchains/arduino/org.eclipse.cdt.arduino.core/src/org/eclipse/cdt/arduino/core/internal/build/ArduinoBuilder.java @@ -7,13 +7,15 @@ *******************************************************************************/ package org.eclipse.cdt.arduino.core.internal.build; -import java.io.File; +import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStreamReader; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.eclipse.cdt.arduino.core.internal.Activator; import org.eclipse.cdt.arduino.core.internal.console.ArduinoConsoleService; -import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IncrementalProjectBuilder; @@ -40,13 +42,18 @@ public class ArduinoBuilder extends IncrementalProjectBuilder { ArduinoBuildConfiguration config = getBuildConfig().getAdapter(ArduinoBuildConfiguration.class); config.generateMakeFile(monitor); - IFolder buildFolder = config.getBuildFolder(); - Process process = Runtime.getRuntime().exec(config.getBuildCommand(), config.getEnvironment(), - new File(buildFolder.getLocationURI())); + ProcessBuilder processBuilder = new ProcessBuilder().command(config.getBuildCommand()) + .directory(config.getBuildDirectory()); + config.setEnvironment(processBuilder.environment()); + Process process = processBuilder.start(); consoleService.monitor(process, null); - buildFolder.refreshLocal(IResource.DEPTH_INFINITE, monitor); + if (process.exitValue() == 0) { + showSizes(config, consoleService); + } + + config.getBuildFolder().refreshLocal(IResource.DEPTH_INFINITE, monitor); } catch (IOException e) { throw new CoreException(new Status(IStatus.ERROR, Activator.getId(), "Build error", e)); } @@ -64,16 +71,71 @@ public class ArduinoBuilder extends IncrementalProjectBuilder { ArduinoBuildConfiguration config = getBuildConfig().getAdapter(ArduinoBuildConfiguration.class); - IFolder buildFolder = config.getBuildFolder(); - Process process = Runtime.getRuntime().exec(config.getCleanCommand(), config.getEnvironment(), - new File(buildFolder.getLocationURI())); + ProcessBuilder processBuilder = new ProcessBuilder().command(config.getCleanCommand()) + .directory(config.getBuildDirectory()); + config.setEnvironment(processBuilder.environment()); + Process process = processBuilder.start(); consoleService.monitor(process, null); - buildFolder.refreshLocal(IResource.DEPTH_INFINITE, monitor); + config.getBuildFolder().refreshLocal(IResource.DEPTH_INFINITE, monitor); } catch (IOException e) { throw new CoreException(new Status(IStatus.ERROR, Activator.getId(), "Build error", e)); } } + private void showSizes(ArduinoBuildConfiguration config, ArduinoConsoleService console) throws CoreException { + try { + int codeSize = -1; + int dataSize = -1; + + String codeSizeRegex = config.getCodeSizeRegex(); + Pattern codeSizePattern = codeSizeRegex != null ? Pattern.compile(codeSizeRegex) : null; + String dataSizeRegex = config.getDataSizeRegex(); + Pattern dataSizePattern = codeSizeRegex != null ? Pattern.compile(dataSizeRegex) : null; + + if (codeSizePattern == null && dataSizePattern == null) { + return; + } + + int maxCodeSize = config.getMaxCodeSize(); + int maxDataSize = config.getMaxDataSize(); + + ProcessBuilder processBuilder = new ProcessBuilder().command(config.getSizeCommand()) + .directory(config.getBuildDirectory()).redirectErrorStream(true); + config.setEnvironment(processBuilder.environment()); + Process process = processBuilder.start(); + try (BufferedReader processOut = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + for (String line = processOut.readLine(); line != null; line = processOut.readLine()) { + if (codeSizePattern != null) { + Matcher matcher = codeSizePattern.matcher(line); + if (matcher.matches()) { + codeSize += Integer.parseInt(matcher.group(1)); + } + } + if (dataSizePattern != null) { + Matcher matcher = dataSizePattern.matcher(line); + if (matcher.matches()) { + dataSize += Integer.parseInt(matcher.group(1)); + } + } + } + } + + console.writeOutput("Program store usage: " + codeSize); + if (maxCodeSize > 0) { + console.writeOutput(" of maximum " + maxCodeSize); + } + console.writeOutput(" bytes\n"); + + console.writeOutput("Initial RAM usage: " + dataSize); + if (maxCodeSize > 0) { + console.writeOutput(" of maximum " + maxDataSize); + } + console.writeOutput(" bytes\n"); + } catch (IOException e) { + throw new CoreException(new Status(IStatus.ERROR, Activator.getId(), "Checking sizes", e)); + } + } + } diff --git a/toolchains/arduino/org.eclipse.cdt.arduino.core/templates/board.mk b/toolchains/arduino/org.eclipse.cdt.arduino.core/templates/board.mk index 1add9209ad1..3d648ec9e89 100644 --- a/toolchains/arduino/org.eclipse.cdt.arduino.core/templates/board.mk +++ b/toolchains/arduino/org.eclipse.cdt.arduino.core/templates/board.mk @@ -44,6 +44,9 @@ $(OUTPUT_DIR)/libc.a: $(PLATFORM_OBJS) clean: $(RMDIR) $(OUTPUT_DIR) +size: + ${recipe_size_pattern} + <#list project_srcs as file> <#assign cpp = file?matches("(.*)\\.cpp")> <#if cpp>