2009-05-31 15:53:33 +00:00
|
|
|
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
|
|
|
|
|
|
|
/*
|
|
|
|
Part of the Processing project - http://processing.org
|
|
|
|
|
|
|
|
Copyright (c) 2004-08 Ben Fry and Casey Reas
|
|
|
|
Copyright (c) 2001-04 Massachusetts Institute of Technology
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software Foundation,
|
|
|
|
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
package processing.app.debug;
|
|
|
|
|
2011-10-05 03:03:19 +09:00
|
|
|
import static processing.app.I18n._;
|
2009-05-31 15:53:33 +00:00
|
|
|
|
2015-04-10 17:44:13 +02:00
|
|
|
import java.io.*;
|
2015-06-18 12:22:55 +02:00
|
|
|
import java.util.*;
|
2009-05-31 15:53:33 +00:00
|
|
|
|
2015-05-25 16:32:33 +02:00
|
|
|
import cc.arduino.MyStreamPumper;
|
2014-08-25 13:10:53 +02:00
|
|
|
import cc.arduino.packages.BoardPort;
|
|
|
|
import cc.arduino.packages.Uploader;
|
|
|
|
import cc.arduino.packages.UploaderFactory;
|
|
|
|
|
2015-06-18 12:22:55 +02:00
|
|
|
import cc.arduino.packages.uploaders.MergeSketchWithBooloader;
|
2015-05-21 16:47:50 +02:00
|
|
|
import org.apache.commons.compress.utils.IOUtils;
|
2015-05-25 16:32:33 +02:00
|
|
|
import org.apache.commons.exec.*;
|
2014-08-21 12:23:42 +02:00
|
|
|
import processing.app.BaseNoGui;
|
2011-12-28 20:00:07 +01:00
|
|
|
import processing.app.I18n;
|
2014-04-06 00:29:20 +02:00
|
|
|
import processing.app.PreferencesData;
|
2011-12-28 20:00:07 +01:00
|
|
|
import processing.app.SketchCode;
|
2014-01-30 15:50:09 +01:00
|
|
|
import processing.app.SketchData;
|
2015-01-08 13:45:17 +01:00
|
|
|
import processing.app.helpers.*;
|
2012-12-04 16:46:58 +01:00
|
|
|
import processing.app.helpers.filefilters.OnlyDirs;
|
2014-01-30 15:50:09 +01:00
|
|
|
import processing.app.packages.LibraryList;
|
|
|
|
import processing.app.preproc.PdePreprocessor;
|
2014-08-19 11:53:44 +02:00
|
|
|
import processing.app.legacy.PApplet;
|
2014-10-17 00:05:36 +02:00
|
|
|
import processing.app.packages.LegacyUserLibrary;
|
|
|
|
import processing.app.packages.UserLibrary;
|
2015-04-24 12:03:41 +02:00
|
|
|
import processing.app.tools.DoubleQuotedArgumentsOnWindowsCommandLine;
|
2009-05-31 15:53:33 +00:00
|
|
|
|
2009-06-01 08:32:11 +00:00
|
|
|
public class Compiler implements MessageConsumer {
|
2009-05-31 15:53:33 +00:00
|
|
|
|
2014-08-22 15:22:12 +02:00
|
|
|
/**
|
|
|
|
* File inside the build directory that contains the build options
|
|
|
|
* used for the last build.
|
|
|
|
*/
|
|
|
|
static final public String BUILD_PREFS_FILE = "buildprefs.txt";
|
|
|
|
|
2014-01-30 15:50:09 +01:00
|
|
|
private SketchData sketch;
|
|
|
|
private PreferencesMap prefs;
|
|
|
|
private boolean verbose;
|
2015-01-23 08:30:46 +05:30
|
|
|
private boolean saveHex;
|
2009-06-01 08:32:11 +00:00
|
|
|
|
2011-12-31 15:07:59 +01:00
|
|
|
private List<File> objectFiles;
|
2009-05-31 15:53:33 +00:00
|
|
|
|
2012-06-26 00:51:35 +02:00
|
|
|
private boolean sketchIsCompiled;
|
2013-02-13 19:10:30 +01:00
|
|
|
|
2011-12-31 15:07:59 +01:00
|
|
|
private RunnerException exception;
|
2013-10-29 14:40:49 +01:00
|
|
|
|
2014-01-30 15:50:09 +01:00
|
|
|
/**
|
|
|
|
* Listener interface for progress update on the GUI
|
|
|
|
*/
|
|
|
|
public interface ProgressListener {
|
2015-06-18 12:22:55 +02:00
|
|
|
void progress(int percent);
|
2014-01-30 15:50:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private ProgressListener progressListener;
|
|
|
|
|
2015-01-23 08:30:46 +05:30
|
|
|
static public String build(SketchData data, String buildPath, File tempBuildFolder, ProgressListener progListener, boolean verbose, boolean save) throws RunnerException, PreferencesMapException {
|
2014-08-22 17:43:41 +02:00
|
|
|
if (SketchData.checkSketchFile(data.getPrimaryFile()) == null)
|
2014-08-22 16:24:51 +02:00
|
|
|
BaseNoGui.showError(_("Bad file selected"),
|
2015-01-14 18:05:00 +01:00
|
|
|
_("Bad sketch primary file or bad sketch directory structure"), null);
|
2014-08-22 16:24:51 +02:00
|
|
|
|
2014-08-22 15:22:12 +02:00
|
|
|
String primaryClassName = data.getName() + ".cpp";
|
|
|
|
Compiler compiler = new Compiler(data, buildPath, primaryClassName);
|
|
|
|
File buildPrefsFile = new File(buildPath, BUILD_PREFS_FILE);
|
|
|
|
String newBuildPrefs = compiler.buildPrefsString();
|
|
|
|
|
|
|
|
// Do a forced cleanup (throw everything away) if the previous
|
|
|
|
// build settings do not match the previous ones
|
|
|
|
boolean prefsChanged = compiler.buildPreferencesChanged(buildPrefsFile, newBuildPrefs);
|
|
|
|
compiler.cleanup(prefsChanged, tempBuildFolder);
|
|
|
|
|
|
|
|
if (prefsChanged) {
|
2015-05-21 16:47:50 +02:00
|
|
|
PrintWriter out = null;
|
2014-08-22 15:22:12 +02:00
|
|
|
try {
|
2015-05-21 16:47:50 +02:00
|
|
|
out = new PrintWriter(buildPrefsFile);
|
2014-08-22 15:22:12 +02:00
|
|
|
out.print(newBuildPrefs);
|
|
|
|
} catch (IOException e) {
|
|
|
|
System.err.println(_("Could not write build preferences file"));
|
2015-05-21 16:47:50 +02:00
|
|
|
} finally {
|
|
|
|
IOUtils.closeQuietly(out);
|
2014-08-22 15:22:12 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
compiler.setProgressListener(progListener);
|
|
|
|
|
|
|
|
// compile the program. errors will happen as a RunnerException
|
|
|
|
// that will bubble up to whomever called build().
|
2015-03-30 10:24:18 -07:00
|
|
|
try {
|
2015-01-23 08:30:46 +05:30
|
|
|
if (compiler.compile(verbose, save)) {
|
2015-03-30 10:24:18 -07:00
|
|
|
compiler.size(compiler.getBuildPreferences());
|
|
|
|
return primaryClassName;
|
|
|
|
}
|
|
|
|
} catch (RunnerException e) {
|
|
|
|
// when the compile fails, take this opportunity to show
|
|
|
|
// any helpful info possible before throwing the exception
|
|
|
|
compiler.adviseDuplicateLibraries();
|
|
|
|
throw e;
|
2014-08-22 15:22:12 +02:00
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2014-09-19 16:51:47 +02:00
|
|
|
static public Uploader getUploaderByPreferences(boolean noUploadPort) {
|
2014-08-25 13:10:53 +02:00
|
|
|
TargetPlatform target = BaseNoGui.getTargetPlatform();
|
|
|
|
String board = PreferencesData.get("board");
|
|
|
|
|
2015-06-01 09:55:12 +02:00
|
|
|
BoardPort boardPort = null;
|
|
|
|
if (!noUploadPort) {
|
|
|
|
boardPort = BaseNoGui.getDiscoveryManager().find(PreferencesData.get("serial.port"));
|
2014-09-19 16:51:47 +02:00
|
|
|
}
|
2015-06-01 09:55:12 +02:00
|
|
|
|
|
|
|
return new UploaderFactory().newUploader(target.getBoards().get(board), boardPort, noUploadPort);
|
2014-08-25 13:10:53 +02:00
|
|
|
}
|
|
|
|
|
2014-09-19 16:51:47 +02:00
|
|
|
static public boolean upload(SketchData data, Uploader uploader, String buildPath, String suggestedClassName, boolean usingProgrammer, boolean noUploadPort, List<String> warningsAccumulator) throws Exception {
|
2014-08-25 13:10:53 +02:00
|
|
|
|
|
|
|
if (uploader == null)
|
2014-09-19 16:51:47 +02:00
|
|
|
uploader = getUploaderByPreferences(noUploadPort);
|
2014-08-25 13:10:53 +02:00
|
|
|
|
|
|
|
boolean success = false;
|
|
|
|
|
|
|
|
if (uploader.requiresAuthorization() && !PreferencesData.has(uploader.getAuthorizationKey())) {
|
|
|
|
BaseNoGui.showError(_("Authorization required"),
|
2015-06-12 08:41:36 +02:00
|
|
|
_("No authorization data found"), null);
|
2014-08-25 13:10:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
boolean useNewWarningsAccumulator = false;
|
|
|
|
if (warningsAccumulator == null) {
|
|
|
|
warningsAccumulator = new LinkedList<String>();
|
|
|
|
useNewWarningsAccumulator = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
success = uploader.uploadUsingPreferences(data.getFolder(), buildPath, suggestedClassName, usingProgrammer, warningsAccumulator);
|
|
|
|
} finally {
|
|
|
|
if (uploader.requiresAuthorization() && !success) {
|
|
|
|
PreferencesData.remove(uploader.getAuthorizationKey());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (useNewWarningsAccumulator) {
|
|
|
|
for (String warning : warningsAccumulator) {
|
|
|
|
System.out.print(_("Warning"));
|
|
|
|
System.out.print(": ");
|
|
|
|
System.out.println(warning);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
2015-06-18 12:22:55 +02:00
|
|
|
static public File findCompiledSketch(PreferencesMap prefs) throws PreferencesMapException {
|
|
|
|
List<String> paths = Arrays.asList("{build.path}/{build.project_name}.hex", "{build.path}/{build.project_name}.bin");
|
|
|
|
Optional<File> sketch = paths.stream().
|
|
|
|
map(path -> StringReplacer.replaceFromMapping(path, prefs)).
|
|
|
|
map(File::new).
|
|
|
|
filter(File::exists).
|
|
|
|
findFirst();
|
|
|
|
return sketch.orElseThrow(() -> new IllegalStateException(_("No compiled sketch found")));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-05-31 15:53:33 +00:00
|
|
|
/**
|
2013-10-29 14:40:49 +01:00
|
|
|
* Create a new Compiler
|
2011-12-28 20:00:07 +01:00
|
|
|
* @param _sketch Sketch object to be compiled.
|
|
|
|
* @param _buildPath Where the temporary files live and will be built from.
|
|
|
|
* @param _primaryClassName the name of the combined sketch file w/ extension
|
2009-05-31 15:53:33 +00:00
|
|
|
*/
|
2014-01-30 15:50:09 +01:00
|
|
|
public Compiler(SketchData _sketch, String _buildPath, String _primaryClassName)
|
2011-12-31 14:32:48 +01:00
|
|
|
throws RunnerException {
|
2011-12-28 20:00:07 +01:00
|
|
|
sketch = _sketch;
|
2013-10-29 14:40:49 +01:00
|
|
|
prefs = createBuildPreferences(_buildPath, _primaryClassName);
|
2014-01-30 15:50:09 +01:00
|
|
|
|
2015-03-06 16:17:56 -06:00
|
|
|
// provide access to the source tree
|
|
|
|
prefs.put("build.source.path", _sketch.getFolder().getAbsolutePath());
|
|
|
|
|
2014-01-30 15:50:09 +01:00
|
|
|
// Start with an empty progress listener
|
|
|
|
progressListener = new ProgressListener() {
|
|
|
|
@Override
|
|
|
|
public void progress(int percent) {
|
|
|
|
}
|
|
|
|
};
|
2013-10-29 14:40:49 +01:00
|
|
|
}
|
|
|
|
|
2014-08-22 15:22:12 +02:00
|
|
|
/**
|
|
|
|
* Check if the build preferences used on the previous build in
|
|
|
|
* buildPath match the ones given.
|
|
|
|
*/
|
|
|
|
protected boolean buildPreferencesChanged(File buildPrefsFile, String newBuildPrefs) {
|
|
|
|
// No previous build, so no match
|
|
|
|
if (!buildPrefsFile.exists())
|
|
|
|
return true;
|
|
|
|
|
|
|
|
String previousPrefs;
|
|
|
|
try {
|
|
|
|
previousPrefs = FileUtils.readFileToString(buildPrefsFile);
|
|
|
|
} catch (IOException e) {
|
|
|
|
System.err.println(_("Could not read prevous build preferences file, rebuilding all"));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!previousPrefs.equals(newBuildPrefs)) {
|
|
|
|
System.out.println(_("Build options changed, rebuilding all"));
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the build preferences of the given compiler as a string.
|
|
|
|
* Only includes build-specific preferences, to make sure unrelated
|
|
|
|
* preferences don't cause a rebuild (in particular preferences that
|
|
|
|
* change on every start, like last.ide.xxx.daterun). */
|
|
|
|
protected String buildPrefsString() {
|
|
|
|
PreferencesMap buildPrefs = getBuildPreferences();
|
|
|
|
String res = "";
|
|
|
|
SortedSet<String> treeSet = new TreeSet<String>(buildPrefs.keySet());
|
|
|
|
for (String k : treeSet) {
|
|
|
|
if (k.startsWith("build.") || k.startsWith("compiler.") || k.startsWith("recipes."))
|
|
|
|
res += k + " = " + buildPrefs.get(k) + "\n";
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void setProgressListener(ProgressListener _progressListener) {
|
2014-09-10 18:48:34 +02:00
|
|
|
progressListener = (_progressListener == null ?
|
|
|
|
new ProgressListener() {
|
|
|
|
@Override
|
|
|
|
public void progress(int percent) {
|
|
|
|
}
|
|
|
|
} : _progressListener);
|
2014-01-30 15:50:09 +01:00
|
|
|
}
|
|
|
|
|
2014-08-22 15:22:12 +02:00
|
|
|
/**
|
|
|
|
* Cleanup temporary files used during a build/run.
|
|
|
|
*/
|
|
|
|
protected void cleanup(boolean force, File tempBuildFolder) {
|
|
|
|
// if the java runtime is holding onto any files in the build dir, we
|
|
|
|
// won't be able to delete them, so we need to force a gc here
|
|
|
|
System.gc();
|
|
|
|
|
|
|
|
if (force) {
|
|
|
|
// delete the entire directory and all contents
|
|
|
|
// when we know something changed and all objects
|
|
|
|
// need to be recompiled, or if the board does not
|
|
|
|
// use setting build.dependency
|
|
|
|
//Base.removeDir(tempBuildFolder);
|
|
|
|
|
|
|
|
// note that we can't remove the builddir itself, otherwise
|
|
|
|
// the next time we start up, internal runs using Runner won't
|
|
|
|
// work because the build dir won't exist at startup, so the classloader
|
|
|
|
// will ignore the fact that that dir is in the CLASSPATH in run.sh
|
2014-08-22 18:12:34 +02:00
|
|
|
BaseNoGui.removeDescendants(tempBuildFolder);
|
2014-08-22 15:22:12 +02:00
|
|
|
} else {
|
|
|
|
// delete only stale source files, from the previously
|
|
|
|
// compiled sketch. This allows multiple windows to be
|
|
|
|
// used. Keep everything else, which might be reusable
|
|
|
|
if (tempBuildFolder.exists()) {
|
|
|
|
String files[] = tempBuildFolder.list();
|
2015-05-20 16:32:27 +02:00
|
|
|
if (files != null) {
|
|
|
|
for (String file : files) {
|
|
|
|
if (file.endsWith(".c") || file.endsWith(".cpp") || file.endsWith(".s")) {
|
|
|
|
File deleteMe = new File(tempBuildFolder, file);
|
|
|
|
if (!deleteMe.delete()) {
|
|
|
|
System.err.println("Could not delete " + deleteMe);
|
|
|
|
}
|
2014-08-22 15:22:12 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a fresh applet folder (needed before preproc is run below)
|
|
|
|
//tempBuildFolder.mkdirs();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void size(PreferencesMap prefs) throws RunnerException {
|
|
|
|
String maxTextSizeString = prefs.get("upload.maximum_size");
|
|
|
|
String maxDataSizeString = prefs.get("upload.maximum_data_size");
|
|
|
|
if (maxTextSizeString == null)
|
|
|
|
return;
|
|
|
|
long maxTextSize = Integer.parseInt(maxTextSizeString);
|
|
|
|
long maxDataSize = -1;
|
|
|
|
if (maxDataSizeString != null)
|
|
|
|
maxDataSize = Integer.parseInt(maxDataSizeString);
|
|
|
|
Sizer sizer = new Sizer(prefs);
|
|
|
|
long[] sizes;
|
|
|
|
try {
|
|
|
|
sizes = sizer.computeSize();
|
|
|
|
} catch (RunnerException e) {
|
|
|
|
System.err.println(I18n.format(_("Couldn't determine program size: {0}"),
|
|
|
|
e.getMessage()));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
long textSize = sizes[0];
|
|
|
|
long dataSize = sizes[1];
|
|
|
|
System.out.println();
|
|
|
|
System.out.println(I18n
|
|
|
|
.format(_("Sketch uses {0} bytes ({2}%%) of program storage space. Maximum is {1} bytes."),
|
|
|
|
textSize, maxTextSize, textSize * 100 / maxTextSize));
|
|
|
|
if (dataSize >= 0) {
|
|
|
|
if (maxDataSize > 0) {
|
|
|
|
System.out
|
|
|
|
.println(I18n
|
2015-04-13 12:05:00 +02:00
|
|
|
.format(
|
|
|
|
_("Global variables use {0} bytes ({2}%%) of dynamic memory, leaving {3} bytes for local variables. Maximum is {1} bytes."),
|
|
|
|
dataSize, maxDataSize, dataSize * 100 / maxDataSize,
|
|
|
|
maxDataSize - dataSize));
|
2014-08-22 15:22:12 +02:00
|
|
|
} else {
|
|
|
|
System.out.println(I18n
|
|
|
|
.format(_("Global variables use {0} bytes of dynamic memory."), dataSize));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (textSize > maxTextSize)
|
|
|
|
throw new RunnerException(
|
|
|
|
_("Sketch too big; see http://www.arduino.cc/en/Guide/Troubleshooting#size for tips on reducing it."));
|
|
|
|
|
|
|
|
if (maxDataSize > 0 && dataSize > maxDataSize)
|
|
|
|
throw new RunnerException(
|
|
|
|
_("Not enough memory; see http://www.arduino.cc/en/Guide/Troubleshooting#size for tips on reducing your footprint."));
|
|
|
|
|
|
|
|
int warnDataPercentage = Integer.parseInt(prefs.get("build.warn_data_percentage"));
|
|
|
|
if (maxDataSize > 0 && dataSize > maxDataSize*warnDataPercentage/100)
|
|
|
|
System.err.println(_("Low memory available, stability problems may occur."));
|
|
|
|
}
|
|
|
|
|
2013-10-29 14:40:49 +01:00
|
|
|
/**
|
|
|
|
* Compile sketch.
|
2015-01-08 13:45:17 +01:00
|
|
|
* @param _verbose
|
2013-10-29 14:40:49 +01:00
|
|
|
*
|
|
|
|
* @return true if successful.
|
|
|
|
* @throws RunnerException Only if there's a problem. Only then.
|
|
|
|
*/
|
2015-01-23 08:30:46 +05:30
|
|
|
public boolean compile(boolean _verbose, boolean _save) throws RunnerException, PreferencesMapException {
|
2014-01-30 15:50:09 +01:00
|
|
|
preprocess(prefs.get("build.path"));
|
|
|
|
|
2014-04-06 00:29:20 +02:00
|
|
|
verbose = _verbose || PreferencesData.getBoolean("build.verbose");
|
2015-01-23 08:30:46 +05:30
|
|
|
saveHex = _save;
|
2012-06-26 00:51:35 +02:00
|
|
|
sketchIsCompiled = false;
|
2015-03-06 16:16:04 -06:00
|
|
|
|
|
|
|
// Hook runs at Start of Compilation
|
2015-03-07 10:43:45 -06:00
|
|
|
runActions("hooks.prebuild", prefs);
|
2015-03-06 16:16:04 -06:00
|
|
|
|
2011-06-22 21:20:45 -06:00
|
|
|
objectFiles = new ArrayList<File>();
|
2009-06-01 08:32:11 +00:00
|
|
|
|
2012-02-01 14:34:29 +01:00
|
|
|
// 0. include paths for core + all libraries
|
2014-01-30 15:50:09 +01:00
|
|
|
progressListener.progress(20);
|
2013-12-24 20:08:22 +01:00
|
|
|
List<File> includeFolders = new ArrayList<File>();
|
|
|
|
includeFolders.add(prefs.getFile("build.core.path"));
|
|
|
|
if (prefs.getFile("build.variant.path") != null)
|
|
|
|
includeFolders.add(prefs.getFile("build.variant.path"));
|
2014-10-17 00:05:36 +02:00
|
|
|
for (UserLibrary lib : importedLibraries) {
|
|
|
|
if (verbose) {
|
|
|
|
String legacy = "";
|
|
|
|
if (lib instanceof LegacyUserLibrary)
|
|
|
|
legacy = "(legacy)";
|
2013-10-01 15:03:43 +02:00
|
|
|
System.out.println(I18n
|
|
|
|
.format(_("Using library {0} in folder: {1} {2}"), lib.getName(),
|
2014-10-17 00:05:36 +02:00
|
|
|
lib.getInstalledFolder(), legacy));
|
|
|
|
}
|
2013-12-24 20:08:22 +01:00
|
|
|
includeFolders.add(lib.getSrcFolder());
|
2013-10-01 15:03:43 +02:00
|
|
|
}
|
|
|
|
if (verbose)
|
|
|
|
System.out.println();
|
2013-12-25 20:16:14 +01:00
|
|
|
|
2013-12-26 01:32:30 +01:00
|
|
|
List<String> archs = new ArrayList<String>();
|
2014-08-21 13:18:54 +02:00
|
|
|
archs.add(BaseNoGui.getTargetPlatform().getId());
|
2013-12-26 01:32:30 +01:00
|
|
|
if (prefs.containsKey("architecture.override_check")) {
|
|
|
|
String[] overrides = prefs.get("architecture.override_check").split(",");
|
|
|
|
archs.addAll(Arrays.asList(overrides));
|
|
|
|
}
|
2014-10-17 00:05:36 +02:00
|
|
|
for (UserLibrary lib : importedLibraries) {
|
2013-12-26 01:32:30 +01:00
|
|
|
if (!lib.supportsArchitecture(archs)) {
|
2013-12-25 20:16:14 +01:00
|
|
|
System.err.println(I18n
|
|
|
|
.format(_("WARNING: library {0} claims to run on {1} "
|
|
|
|
+ "architecture(s) and may be incompatible with your"
|
2013-12-26 01:32:30 +01:00
|
|
|
+ " current board which runs on {2} architecture(s)."), lib
|
|
|
|
.getName(), lib.getArchitectures(), archs));
|
2013-12-25 20:16:14 +01:00
|
|
|
System.err.println();
|
|
|
|
}
|
|
|
|
}
|
Compiler: adding hooks (custom recipes) between the main phases. New hooks are:
hooks.sketch.prebuild, hooks.sketch.postbuild, hooks.libraries.prebuild,
hooks.libraries.postbuild, hooks.core.prebuild, hooks.core.postbuild,
hooks.linking.prelink, hooks.linking.postlink, hooks.objcopy.preobjcopy,
hooks.objcopy.postobjcopy, hooks.savehex.presavehex, hooks.savehex.postsavehex
2015-05-26 15:29:47 +02:00
|
|
|
|
|
|
|
runActions("hooks.sketch.prebuild", prefs);
|
|
|
|
|
2012-02-01 14:34:29 +01:00
|
|
|
// 1. compile the sketch (already in the buildPath)
|
2015-01-23 08:30:46 +05:30
|
|
|
progressListener.progress(20);
|
2013-12-24 20:08:22 +01:00
|
|
|
compileSketch(includeFolders);
|
2012-06-26 00:51:35 +02:00
|
|
|
sketchIsCompiled = true;
|
2012-02-01 14:34:29 +01:00
|
|
|
|
Compiler: adding hooks (custom recipes) between the main phases. New hooks are:
hooks.sketch.prebuild, hooks.sketch.postbuild, hooks.libraries.prebuild,
hooks.libraries.postbuild, hooks.core.prebuild, hooks.core.postbuild,
hooks.linking.prelink, hooks.linking.postlink, hooks.objcopy.preobjcopy,
hooks.objcopy.postobjcopy, hooks.savehex.presavehex, hooks.savehex.postsavehex
2015-05-26 15:29:47 +02:00
|
|
|
runActions("hooks.sketch.postbuild", prefs);
|
|
|
|
|
|
|
|
runActions("hooks.libraries.prebuild", prefs);
|
|
|
|
|
2012-02-01 14:34:29 +01:00
|
|
|
// 2. compile the libraries, outputting .o files to: <buildPath>/<library>/
|
|
|
|
// Doesn't really use configPreferences
|
2015-01-23 08:30:46 +05:30
|
|
|
progressListener.progress(30);
|
2013-12-24 20:08:22 +01:00
|
|
|
compileLibraries(includeFolders);
|
2012-02-01 14:34:29 +01:00
|
|
|
|
Compiler: adding hooks (custom recipes) between the main phases. New hooks are:
hooks.sketch.prebuild, hooks.sketch.postbuild, hooks.libraries.prebuild,
hooks.libraries.postbuild, hooks.core.prebuild, hooks.core.postbuild,
hooks.linking.prelink, hooks.linking.postlink, hooks.objcopy.preobjcopy,
hooks.objcopy.postobjcopy, hooks.savehex.presavehex, hooks.savehex.postsavehex
2015-05-26 15:29:47 +02:00
|
|
|
runActions("hooks.libraries.postbuild", prefs);
|
|
|
|
|
|
|
|
runActions("hooks.core.prebuild", prefs);
|
|
|
|
|
2012-02-01 14:34:29 +01:00
|
|
|
// 3. compile the core, outputting .o files to <buildPath> and then
|
|
|
|
// collecting them into the core.a library file.
|
2015-01-23 08:30:46 +05:30
|
|
|
progressListener.progress(40);
|
2012-02-01 14:34:29 +01:00
|
|
|
compileCore();
|
|
|
|
|
Compiler: adding hooks (custom recipes) between the main phases. New hooks are:
hooks.sketch.prebuild, hooks.sketch.postbuild, hooks.libraries.prebuild,
hooks.libraries.postbuild, hooks.core.prebuild, hooks.core.postbuild,
hooks.linking.prelink, hooks.linking.postlink, hooks.objcopy.preobjcopy,
hooks.objcopy.postobjcopy, hooks.savehex.presavehex, hooks.savehex.postsavehex
2015-05-26 15:29:47 +02:00
|
|
|
runActions("hooks.core.postbuild", prefs);
|
|
|
|
|
|
|
|
runActions("hooks.linking.prelink", prefs);
|
|
|
|
|
2012-02-01 14:34:29 +01:00
|
|
|
// 4. link it all together into the .elf file
|
2015-01-23 08:30:46 +05:30
|
|
|
progressListener.progress(50);
|
2013-12-24 20:08:22 +01:00
|
|
|
compileLink();
|
2012-02-01 14:34:29 +01:00
|
|
|
|
Compiler: adding hooks (custom recipes) between the main phases. New hooks are:
hooks.sketch.prebuild, hooks.sketch.postbuild, hooks.libraries.prebuild,
hooks.libraries.postbuild, hooks.core.prebuild, hooks.core.postbuild,
hooks.linking.prelink, hooks.linking.postlink, hooks.objcopy.preobjcopy,
hooks.objcopy.postobjcopy, hooks.savehex.presavehex, hooks.savehex.postsavehex
2015-05-26 15:29:47 +02:00
|
|
|
runActions("hooks.linking.postlink", prefs);
|
|
|
|
|
|
|
|
runActions("hooks.objcopy.preobjcopy", prefs);
|
|
|
|
|
2015-02-16 14:06:20 +01:00
|
|
|
// 5. run objcopy to generate output files
|
2015-01-23 08:30:46 +05:30
|
|
|
progressListener.progress(60);
|
2015-02-16 14:06:20 +01:00
|
|
|
List<String> objcopyPatterns = new ArrayList<String>();
|
|
|
|
for (String key : prefs.keySet()) {
|
|
|
|
if (key.startsWith("recipe.objcopy.") && key.endsWith(".pattern"))
|
|
|
|
objcopyPatterns.add(key);
|
|
|
|
}
|
|
|
|
Collections.sort(objcopyPatterns);
|
|
|
|
for (String recipe : objcopyPatterns) {
|
|
|
|
runRecipe(recipe);
|
|
|
|
}
|
2012-02-01 14:34:29 +01:00
|
|
|
|
Compiler: adding hooks (custom recipes) between the main phases. New hooks are:
hooks.sketch.prebuild, hooks.sketch.postbuild, hooks.libraries.prebuild,
hooks.libraries.postbuild, hooks.core.prebuild, hooks.core.postbuild,
hooks.linking.prelink, hooks.linking.postlink, hooks.objcopy.preobjcopy,
hooks.objcopy.postobjcopy, hooks.savehex.presavehex, hooks.savehex.postsavehex
2015-05-26 15:29:47 +02:00
|
|
|
runActions("hooks.objcopy.postobjcopy", prefs);
|
|
|
|
|
2015-06-18 12:22:55 +02:00
|
|
|
progressListener.progress(70);
|
|
|
|
try {
|
|
|
|
mergeSketchWithBootloaderIfAppropriate(sketch.getName() + ".cpp", prefs);
|
|
|
|
} catch (IOException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
// ignore
|
|
|
|
}
|
|
|
|
|
2015-01-23 08:30:46 +05:30
|
|
|
// 7. save the hex file
|
|
|
|
if (saveHex) {
|
Compiler: adding hooks (custom recipes) between the main phases. New hooks are:
hooks.sketch.prebuild, hooks.sketch.postbuild, hooks.libraries.prebuild,
hooks.libraries.postbuild, hooks.core.prebuild, hooks.core.postbuild,
hooks.linking.prelink, hooks.linking.postlink, hooks.objcopy.preobjcopy,
hooks.objcopy.postobjcopy, hooks.savehex.presavehex, hooks.savehex.postsavehex
2015-05-26 15:29:47 +02:00
|
|
|
runActions("hooks.savehex.presavehex", prefs);
|
|
|
|
|
2015-01-23 08:30:46 +05:30
|
|
|
progressListener.progress(80);
|
|
|
|
saveHex();
|
Compiler: adding hooks (custom recipes) between the main phases. New hooks are:
hooks.sketch.prebuild, hooks.sketch.postbuild, hooks.libraries.prebuild,
hooks.libraries.postbuild, hooks.core.prebuild, hooks.core.postbuild,
hooks.linking.prelink, hooks.linking.postlink, hooks.objcopy.preobjcopy,
hooks.objcopy.postobjcopy, hooks.savehex.presavehex, hooks.savehex.postsavehex
2015-05-26 15:29:47 +02:00
|
|
|
|
|
|
|
runActions("hooks.savehex.postsavehex", prefs);
|
2015-01-23 08:30:46 +05:30
|
|
|
}
|
|
|
|
|
2014-01-30 15:50:09 +01:00
|
|
|
progressListener.progress(90);
|
2015-03-06 16:16:04 -06:00
|
|
|
|
2015-03-07 10:43:45 -06:00
|
|
|
// Hook runs at End of Compilation
|
|
|
|
runActions("hooks.postbuild", prefs);
|
2015-03-30 10:24:18 -07:00
|
|
|
adviseDuplicateLibraries();
|
2015-03-06 16:16:04 -06:00
|
|
|
|
2012-02-01 14:34:29 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-03-30 10:24:18 -07:00
|
|
|
private void adviseDuplicateLibraries() {
|
|
|
|
for (int i=0; i < importedDuplicateHeaders.size(); i++) {
|
|
|
|
System.out.println(I18n.format(_("Multiple libraries were found for \"{0}\""),
|
|
|
|
importedDuplicateHeaders.get(i)));
|
|
|
|
boolean first = true;
|
|
|
|
for (UserLibrary lib : importedDuplicateLibraries.get(i)) {
|
|
|
|
if (first) {
|
|
|
|
System.out.println(I18n.format(_(" Used: {0}"),
|
|
|
|
lib.getInstalledFolder().getPath()));
|
|
|
|
first = false;
|
|
|
|
} else {
|
|
|
|
System.out.println(I18n.format(_(" Not used: {0}"),
|
|
|
|
lib.getInstalledFolder().getPath()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-01 14:34:29 +01:00
|
|
|
private PreferencesMap createBuildPreferences(String _buildPath,
|
|
|
|
String _primaryClassName)
|
|
|
|
throws RunnerException {
|
2012-05-23 15:05:19 +02:00
|
|
|
|
2014-08-21 13:38:28 +02:00
|
|
|
if (BaseNoGui.getBoardPreferences() == null) {
|
2012-05-23 15:05:19 +02:00
|
|
|
RunnerException re = new RunnerException(
|
|
|
|
_("No board selected; please choose a board from the Tools > Board menu."));
|
|
|
|
re.hideStackTrace();
|
|
|
|
throw re;
|
|
|
|
}
|
|
|
|
|
2013-03-05 20:18:46 +01:00
|
|
|
// Check if the board needs a platform from another package
|
2014-08-21 13:18:54 +02:00
|
|
|
TargetPlatform targetPlatform = BaseNoGui.getTargetPlatform();
|
2013-03-05 20:18:46 +01:00
|
|
|
TargetPlatform corePlatform = null;
|
2014-08-21 13:38:28 +02:00
|
|
|
PreferencesMap boardPreferences = BaseNoGui.getBoardPreferences();
|
2015-02-12 17:27:58 +01:00
|
|
|
String core = boardPreferences.get("build.core", "arduino");
|
2013-03-05 20:18:46 +01:00
|
|
|
if (core.contains(":")) {
|
|
|
|
String[] split = core.split(":");
|
|
|
|
core = split[1];
|
2014-08-21 13:38:28 +02:00
|
|
|
corePlatform = BaseNoGui.getTargetPlatform(split[0], targetPlatform.getId());
|
2013-03-05 20:18:46 +01:00
|
|
|
if (corePlatform == null) {
|
|
|
|
RunnerException re = new RunnerException(I18n
|
|
|
|
.format(_("Selected board depends on '{0}' core (not installed)."),
|
|
|
|
split[0]));
|
|
|
|
re.hideStackTrace();
|
|
|
|
throw re;
|
|
|
|
}
|
|
|
|
}
|
2013-02-13 19:10:30 +01:00
|
|
|
|
2011-12-31 14:32:48 +01:00
|
|
|
// Merge all the global preference configuration in order of priority
|
2012-02-01 14:34:29 +01:00
|
|
|
PreferencesMap p = new PreferencesMap();
|
2014-04-06 00:29:20 +02:00
|
|
|
p.putAll(PreferencesData.getMap());
|
2013-03-05 20:18:46 +01:00
|
|
|
if (corePlatform != null)
|
|
|
|
p.putAll(corePlatform.getPreferences());
|
|
|
|
p.putAll(targetPlatform.getPreferences());
|
2014-08-21 13:38:28 +02:00
|
|
|
p.putAll(BaseNoGui.getBoardPreferences());
|
2012-02-01 14:34:29 +01:00
|
|
|
for (String k : p.keySet()) {
|
|
|
|
if (p.get(k) == null)
|
|
|
|
p.put(k, "");
|
2011-12-31 14:32:48 +01:00
|
|
|
}
|
|
|
|
|
2012-02-01 14:34:29 +01:00
|
|
|
p.put("build.path", _buildPath);
|
|
|
|
p.put("build.project_name", _primaryClassName);
|
2014-01-30 15:50:09 +01:00
|
|
|
p.put("build.arch", targetPlatform.getId().toUpperCase());
|
2012-01-13 20:39:48 +01:00
|
|
|
|
Explicitly define compiler.path in avr/platform.txt
Previously, this relied on an (ugly, avr-specific) magic default for the
compiler.path variable, set by the IDE. This allowed the IDE to fall
back to a system-wide toolchain when no bundled toolchain was found (by
making compiler.path empty).
However,
- this only worked for avr, not sam,
- this worked only for gcc, a system-wide avrdude would break on the
avrdude.conf path in platform.txt, and
This would mean that automatic system-wide fallback didn't work in all
situations, so you'd still have to modify platform.txt (or create
platform.local.txt). Since doing that explictly is the most reliable
way, this commit removes the partial-working ability to do this
automatically.
Note that the code to automatically set compiler.path is still kept
around, in case third-party hardware still relies on this. At some
point, this code should be removed, but for now it just shows a warning
message.
2014-04-04 12:04:57 +02:00
|
|
|
// Platform.txt should define its own compiler.path. For
|
|
|
|
// compatibility with earlier 1.5 versions, we define a (ugly,
|
|
|
|
// avr-specific) default for it, but this should be removed at some
|
|
|
|
// point.
|
|
|
|
if (!p.containsKey("compiler.path")) {
|
|
|
|
System.err.println(_("Third-party platform.txt does not define compiler.path. Please report this to the third-party hardware maintainer."));
|
2014-08-21 12:35:01 +02:00
|
|
|
p.put("compiler.path", BaseNoGui.getAvrBasePath());
|
Explicitly define compiler.path in avr/platform.txt
Previously, this relied on an (ugly, avr-specific) magic default for the
compiler.path variable, set by the IDE. This allowed the IDE to fall
back to a system-wide toolchain when no bundled toolchain was found (by
making compiler.path empty).
However,
- this only worked for avr, not sam,
- this worked only for gcc, a system-wide avrdude would break on the
avrdude.conf path in platform.txt, and
This would mean that automatic system-wide fallback didn't work in all
situations, so you'd still have to modify platform.txt (or create
platform.local.txt). Since doing that explictly is the most reliable
way, this commit removes the partial-working ability to do this
automatically.
Note that the code to automatically set compiler.path is still kept
around, in case third-party hardware still relies on this. At some
point, this code should be removed, but for now it just shows a warning
message.
2014-04-04 12:04:57 +02:00
|
|
|
}
|
2012-01-13 20:39:48 +01:00
|
|
|
|
2015-06-18 12:22:55 +02:00
|
|
|
TargetPlatform referencePlatform = null;
|
|
|
|
if (corePlatform != null) {
|
|
|
|
referencePlatform = corePlatform;
|
|
|
|
} else {
|
|
|
|
referencePlatform = targetPlatform;
|
|
|
|
}
|
|
|
|
|
|
|
|
p.put("build.platform.path", referencePlatform.getFolder().getAbsolutePath());
|
|
|
|
|
2012-01-13 20:39:48 +01:00
|
|
|
// Core folder
|
2015-06-18 12:22:55 +02:00
|
|
|
File coreFolder = new File(referencePlatform.getFolder(), "cores");
|
2012-01-13 20:39:48 +01:00
|
|
|
coreFolder = new File(coreFolder, core);
|
2013-03-05 20:18:46 +01:00
|
|
|
p.put("build.core", core);
|
2012-02-01 14:34:29 +01:00
|
|
|
p.put("build.core.path", coreFolder.getAbsolutePath());
|
2012-01-13 20:39:48 +01:00
|
|
|
|
|
|
|
// System Folder
|
2015-06-18 12:22:55 +02:00
|
|
|
File systemFolder = referencePlatform.getFolder();
|
2012-01-13 20:39:48 +01:00
|
|
|
systemFolder = new File(systemFolder, "system");
|
2012-02-01 14:34:29 +01:00
|
|
|
p.put("build.system.path", systemFolder.getAbsolutePath());
|
2015-06-18 12:22:55 +02:00
|
|
|
|
2012-01-13 20:39:48 +01:00
|
|
|
// Variant Folder
|
2012-02-01 14:34:29 +01:00
|
|
|
String variant = p.get("build.variant");
|
2011-12-31 14:32:48 +01:00
|
|
|
if (variant != null) {
|
2012-01-13 20:39:48 +01:00
|
|
|
TargetPlatform t;
|
2011-12-31 14:32:48 +01:00
|
|
|
if (!variant.contains(":")) {
|
2012-01-13 20:39:48 +01:00
|
|
|
t = targetPlatform;
|
2011-12-31 14:32:48 +01:00
|
|
|
} else {
|
2011-12-31 15:23:54 +01:00
|
|
|
String[] split = variant.split(":", 2);
|
2014-08-21 13:38:28 +02:00
|
|
|
t = BaseNoGui.getTargetPlatform(split[0], targetPlatform.getId());
|
2012-01-13 20:39:48 +01:00
|
|
|
variant = split[1];
|
2011-12-31 14:32:48 +01:00
|
|
|
}
|
2012-01-13 20:39:48 +01:00
|
|
|
File variantFolder = new File(t.getFolder(), "variants");
|
|
|
|
variantFolder = new File(variantFolder, variant);
|
2012-02-01 14:34:29 +01:00
|
|
|
p.put("build.variant.path", variantFolder.getAbsolutePath());
|
2012-01-13 20:39:48 +01:00
|
|
|
} else {
|
2012-02-01 14:34:29 +01:00
|
|
|
p.put("build.variant.path", "");
|
2009-06-13 11:41:16 +00:00
|
|
|
}
|
2012-02-01 14:34:29 +01:00
|
|
|
|
2015-05-05 12:32:49 -07:00
|
|
|
// Build Time
|
|
|
|
Date d = new Date();
|
|
|
|
GregorianCalendar cal = new GregorianCalendar();
|
|
|
|
long current = d.getTime()/1000;
|
|
|
|
long timezone = cal.get(cal.ZONE_OFFSET)/1000;
|
|
|
|
long daylight = cal.get(cal.DST_OFFSET)/1000;
|
|
|
|
p.put("extra.time.utc", Long.toString(current));
|
|
|
|
p.put("extra.time.local", Long.toString(current + timezone + daylight));
|
|
|
|
p.put("extra.time.zone", Long.toString(timezone));
|
|
|
|
p.put("extra.time.dst", Long.toString(daylight));
|
|
|
|
|
2012-02-01 14:34:29 +01:00
|
|
|
return p;
|
2011-12-31 14:32:48 +01:00
|
|
|
}
|
2009-09-24 03:16:00 +00:00
|
|
|
|
2013-12-24 20:08:22 +01:00
|
|
|
private List<File> compileFiles(File outputPath, File sourcePath,
|
|
|
|
boolean recurse, List<File> includeFolders)
|
2015-01-08 13:45:17 +01:00
|
|
|
throws RunnerException, PreferencesMapException {
|
2011-12-31 14:32:48 +01:00
|
|
|
List<File> sSources = findFilesInFolder(sourcePath, "S", recurse);
|
|
|
|
List<File> cSources = findFilesInFolder(sourcePath, "c", recurse);
|
|
|
|
List<File> cppSources = findFilesInFolder(sourcePath, "cpp", recurse);
|
2009-06-13 11:41:16 +00:00
|
|
|
List<File> objectPaths = new ArrayList<File>();
|
2011-12-31 14:32:48 +01:00
|
|
|
|
2009-09-25 19:31:01 +00:00
|
|
|
for (File file : sSources) {
|
2013-12-24 20:08:22 +01:00
|
|
|
File objectFile = new File(outputPath, file.getName() + ".o");
|
|
|
|
objectPaths.add(objectFile);
|
2015-01-08 13:54:20 +01:00
|
|
|
String[] cmd = getCommandCompilerByRecipe(includeFolders, file, objectFile, "recipe.S.o.pattern");
|
2011-12-31 15:07:59 +01:00
|
|
|
execAsynchronously(cmd);
|
2009-09-25 19:31:01 +00:00
|
|
|
}
|
|
|
|
|
2009-06-13 11:41:16 +00:00
|
|
|
for (File file : cSources) {
|
2013-12-24 20:08:22 +01:00
|
|
|
File objectFile = new File(outputPath, file.getName() + ".o");
|
|
|
|
File dependFile = new File(outputPath, file.getName() + ".d");
|
2011-12-31 15:07:59 +01:00
|
|
|
objectPaths.add(objectFile);
|
2013-10-14 11:40:13 +02:00
|
|
|
if (isAlreadyCompiled(file, objectFile, dependFile, prefs))
|
2011-12-31 15:07:59 +01:00
|
|
|
continue;
|
2015-01-08 13:54:20 +01:00
|
|
|
String[] cmd = getCommandCompilerByRecipe(includeFolders, file, objectFile, "recipe.c.o.pattern");
|
2011-12-31 15:07:59 +01:00
|
|
|
execAsynchronously(cmd);
|
2009-09-25 19:05:09 +00:00
|
|
|
}
|
2009-09-25 19:31:01 +00:00
|
|
|
|
2009-06-13 11:41:16 +00:00
|
|
|
for (File file : cppSources) {
|
2013-12-24 20:08:22 +01:00
|
|
|
File objectFile = new File(outputPath, file.getName() + ".o");
|
|
|
|
File dependFile = new File(outputPath, file.getName() + ".d");
|
2011-12-31 15:07:59 +01:00
|
|
|
objectPaths.add(objectFile);
|
2013-10-14 11:40:13 +02:00
|
|
|
if (isAlreadyCompiled(file, objectFile, dependFile, prefs))
|
2011-12-31 15:07:59 +01:00
|
|
|
continue;
|
2015-01-08 13:54:20 +01:00
|
|
|
String[] cmd = getCommandCompilerByRecipe(includeFolders, file, objectFile, "recipe.cpp.o.pattern");
|
2011-12-31 15:07:59 +01:00
|
|
|
execAsynchronously(cmd);
|
2009-09-25 19:05:09 +00:00
|
|
|
}
|
2009-06-13 11:41:16 +00:00
|
|
|
|
|
|
|
return objectPaths;
|
2009-09-25 19:05:09 +00:00
|
|
|
}
|
2009-09-24 03:16:00 +00:00
|
|
|
|
2014-10-31 11:39:36 +01:00
|
|
|
/**
|
|
|
|
* Strip escape sequences used in makefile dependency files (.d)
|
|
|
|
* https://github.com/arduino/Arduino/issues/2255#issuecomment-57645845
|
|
|
|
*
|
2015-01-08 13:45:17 +01:00
|
|
|
* @param line
|
2014-10-31 11:39:36 +01:00
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
protected static String unescapeDepFile(String line) {
|
|
|
|
// Replaces: "\\" -> "\"
|
|
|
|
// Replaces: "\ " -> " "
|
|
|
|
// Replaces: "\#" -> "#"
|
|
|
|
line = line.replaceAll("\\\\([ #\\\\])", "$1");
|
|
|
|
// Replaces: "$$" -> "$"
|
|
|
|
line = line.replace("$$", "$");
|
|
|
|
return line;
|
|
|
|
}
|
|
|
|
|
2013-10-14 11:40:13 +02:00
|
|
|
private boolean isAlreadyCompiled(File src, File obj, File dep, Map<String, String> prefs) {
|
2011-12-16 18:20:22 -05:00
|
|
|
boolean ret=true;
|
2015-05-21 16:47:50 +02:00
|
|
|
BufferedReader reader = null;
|
2011-12-16 18:20:22 -05:00
|
|
|
try {
|
2013-10-14 11:40:13 +02:00
|
|
|
//System.out.println("\n isAlreadyCompiled: begin checks: " + obj.getPath());
|
2011-12-16 18:20:22 -05:00
|
|
|
if (!obj.exists()) return false; // object file (.o) does not exist
|
|
|
|
if (!dep.exists()) return false; // dep file (.d) does not exist
|
|
|
|
long src_modified = src.lastModified();
|
|
|
|
long obj_modified = obj.lastModified();
|
|
|
|
if (src_modified >= obj_modified) return false; // source modified since object compiled
|
|
|
|
if (src_modified >= dep.lastModified()) return false; // src modified since dep compiled
|
2015-05-21 16:47:50 +02:00
|
|
|
reader = new BufferedReader(new FileReader(dep.getPath()));
|
2011-12-16 18:20:22 -05:00
|
|
|
String line;
|
|
|
|
boolean need_obj_parse = true;
|
|
|
|
while ((line = reader.readLine()) != null) {
|
|
|
|
if (line.endsWith("\\")) {
|
|
|
|
line = line.substring(0, line.length() - 1);
|
|
|
|
}
|
|
|
|
line = line.trim();
|
2014-10-31 11:39:36 +01:00
|
|
|
line = unescapeDepFile(line);
|
2011-12-16 18:20:22 -05:00
|
|
|
if (line.length() == 0) continue; // ignore blank lines
|
|
|
|
if (need_obj_parse) {
|
|
|
|
// line is supposed to be the object file - make sure it really is!
|
|
|
|
if (line.endsWith(":")) {
|
|
|
|
line = line.substring(0, line.length() - 1);
|
|
|
|
String objpath = obj.getCanonicalPath();
|
|
|
|
File linefile = new File(line);
|
|
|
|
String linepath = linefile.getCanonicalPath();
|
2013-10-14 11:40:13 +02:00
|
|
|
//System.out.println(" isAlreadyCompiled: obj = " + objpath);
|
|
|
|
//System.out.println(" isAlreadyCompiled: line = " + linepath);
|
2011-12-16 18:20:22 -05:00
|
|
|
if (objpath.compareTo(linepath) == 0) {
|
|
|
|
need_obj_parse = false;
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
ret = false; // object named inside .d file is not the correct file!
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ret = false; // object file supposed to end with ':', but didn't
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// line is a prerequisite file
|
|
|
|
File prereq = new File(line);
|
|
|
|
if (!prereq.exists()) {
|
|
|
|
ret = false; // prerequisite file did not exist
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (prereq.lastModified() >= obj_modified) {
|
|
|
|
ret = false; // prerequisite modified since object was compiled
|
|
|
|
break;
|
|
|
|
}
|
2013-10-14 11:40:13 +02:00
|
|
|
//System.out.println(" isAlreadyCompiled: prerequisite ok");
|
2011-12-16 18:20:22 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
|
|
return false; // any error reading dep file = recompile it
|
2015-05-21 16:47:50 +02:00
|
|
|
} finally {
|
|
|
|
IOUtils.closeQuietly(reader);
|
2011-12-16 18:20:22 -05:00
|
|
|
}
|
2013-10-01 15:03:43 +02:00
|
|
|
if (ret && verbose) {
|
|
|
|
System.out.println(I18n.format(_("Using previously compiled file: {0}"), obj.getPath()));
|
2011-12-16 18:20:22 -05:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
2009-09-24 03:16:00 +00:00
|
|
|
|
2009-06-13 11:41:16 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Either succeeds or throws a RunnerException fit for public consumption.
|
|
|
|
*/
|
2011-06-15 23:04:38 -04:00
|
|
|
private void execAsynchronously(String[] command) throws RunnerException {
|
2011-12-31 14:32:48 +01:00
|
|
|
|
|
|
|
// eliminate any empty array entries
|
|
|
|
List<String> stringList = new ArrayList<String>();
|
|
|
|
for (String string : command) {
|
|
|
|
string = string.trim();
|
2012-06-25 15:30:43 +02:00
|
|
|
if (string.length() != 0)
|
2011-12-31 14:32:48 +01:00
|
|
|
stringList.add(string);
|
|
|
|
}
|
|
|
|
command = stringList.toArray(new String[stringList.size()]);
|
2012-01-13 20:39:48 +01:00
|
|
|
if (command.length == 0)
|
|
|
|
return;
|
2011-12-31 14:32:48 +01:00
|
|
|
|
2013-10-01 15:03:43 +02:00
|
|
|
if (verbose) {
|
2011-12-30 15:46:04 +01:00
|
|
|
for (String c : command)
|
|
|
|
System.out.print(c + " ");
|
2009-06-01 08:32:11 +00:00
|
|
|
System.out.println();
|
2009-09-25 19:05:09 +00:00
|
|
|
}
|
2009-05-31 15:53:33 +00:00
|
|
|
|
2015-04-10 17:44:13 +02:00
|
|
|
DefaultExecutor executor = new DefaultExecutor();
|
2015-05-25 16:32:33 +02:00
|
|
|
executor.setStreamHandler(new PumpStreamHandler() {
|
2015-04-10 17:44:13 +02:00
|
|
|
|
|
|
|
@Override
|
2015-05-25 16:32:33 +02:00
|
|
|
protected Thread createPump(InputStream is, OutputStream os, boolean closeWhenExhausted) {
|
|
|
|
final Thread result = new Thread(new MyStreamPumper(is, Compiler.this));
|
|
|
|
result.setDaemon(true);
|
|
|
|
return result;
|
2015-04-10 17:44:13 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2015-04-24 12:03:41 +02:00
|
|
|
CommandLine commandLine = new DoubleQuotedArgumentsOnWindowsCommandLine(command[0]);
|
2015-04-10 17:44:13 +02:00
|
|
|
for (int i = 1; i < command.length; i++) {
|
2015-04-13 12:05:00 +02:00
|
|
|
commandLine.addArgument(command[i], false);
|
2015-04-10 17:44:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int result;
|
|
|
|
executor.setExitValues(null);
|
2009-06-13 11:41:16 +00:00
|
|
|
try {
|
2015-04-10 17:44:13 +02:00
|
|
|
result = executor.execute(commandLine);
|
2009-06-13 11:41:16 +00:00
|
|
|
} catch (IOException e) {
|
|
|
|
RunnerException re = new RunnerException(e.getMessage());
|
|
|
|
re.hideStackTrace();
|
|
|
|
throw re;
|
2009-09-25 19:05:09 +00:00
|
|
|
}
|
2015-04-10 17:44:13 +02:00
|
|
|
executor.setExitValues(new int[0]);
|
2009-09-24 03:16:00 +00:00
|
|
|
|
2009-06-13 11:41:16 +00:00
|
|
|
// an error was queued up by message(), barf this back to compile(),
|
|
|
|
// which will barf it back to Editor. if you're having trouble
|
|
|
|
// discerning the imagery, consider how cows regurgitate their food
|
|
|
|
// to digest it, and the fact that they have five stomaches.
|
|
|
|
//
|
|
|
|
//System.out.println("throwing up " + exception);
|
2011-12-31 15:07:59 +01:00
|
|
|
if (exception != null)
|
|
|
|
throw exception;
|
2009-05-31 15:53:33 +00:00
|
|
|
|
2011-12-31 15:07:59 +01:00
|
|
|
if (result > 1) {
|
|
|
|
// a failure in the tool (e.g. unable to locate a sub-executable)
|
|
|
|
System.err
|
|
|
|
.println(I18n.format(_("{0} returned {1}"), command[0], result));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result != 0) {
|
|
|
|
RunnerException re = new RunnerException(_("Error compiling."));
|
|
|
|
re.hideStackTrace();
|
|
|
|
throw re;
|
|
|
|
}
|
|
|
|
}
|
2009-05-31 15:53:33 +00:00
|
|
|
|
2009-06-01 08:32:11 +00:00
|
|
|
/**
|
|
|
|
* Part of the MessageConsumer interface, this is called
|
|
|
|
* whenever a piece (usually a line) of error message is spewed
|
|
|
|
* out from the compiler. The errors are parsed for their contents
|
|
|
|
* and line number, which is then reported back to Editor.
|
|
|
|
*/
|
|
|
|
public void message(String s) {
|
2010-05-08 20:06:31 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
// remove the build path so people only see the filename
|
|
|
|
// can't use replaceAll() because the path may have characters in it which
|
|
|
|
// have meaning in a regular expression.
|
|
|
|
if (!verbose) {
|
2012-01-13 20:39:48 +01:00
|
|
|
String buildPath = prefs.get("build.path");
|
2010-05-08 20:06:31 +00:00
|
|
|
while ((i = s.indexOf(buildPath + File.separator)) != -1) {
|
|
|
|
s = s.substring(0, i) + s.substring(i + (buildPath + File.separator).length());
|
2009-06-01 08:32:11 +00:00
|
|
|
}
|
|
|
|
}
|
2015-04-10 17:44:13 +02:00
|
|
|
|
2010-05-08 20:06:31 +00:00
|
|
|
// look for error line, which contains file name, line number,
|
|
|
|
// and at least the first line of the error message
|
2015-04-10 17:44:13 +02:00
|
|
|
String errorFormat = "(.+\\.\\w+):(\\d+)(:\\d+)*:\\s*error:\\s*(.*)\\s*";
|
2010-05-08 20:06:31 +00:00
|
|
|
String[] pieces = PApplet.match(s, errorFormat);
|
|
|
|
|
|
|
|
// if (pieces != null && exception == null) {
|
|
|
|
// exception = sketch.placeException(pieces[3], pieces[1], PApplet.parseInt(pieces[2]) - 1);
|
|
|
|
// if (exception != null) exception.hideStackTrace();
|
|
|
|
// }
|
|
|
|
|
|
|
|
if (pieces != null) {
|
2015-04-10 17:44:13 +02:00
|
|
|
String error = pieces[pieces.length - 1], msg = "";
|
2010-08-28 09:33:37 +00:00
|
|
|
|
2015-04-10 17:44:13 +02:00
|
|
|
if (error.trim().equals("SPI.h: No such file or directory")) {
|
2011-10-05 03:03:19 +09:00
|
|
|
error = _("Please import the SPI library from the Sketch > Import Library menu.");
|
|
|
|
msg = _("\nAs of Arduino 0019, the Ethernet library depends on the SPI library." +
|
|
|
|
"\nYou appear to be using it or another library that depends on the SPI library.\n\n");
|
2011-02-26 13:58:03 -05:00
|
|
|
}
|
|
|
|
|
2015-04-10 17:44:13 +02:00
|
|
|
if (error.trim().equals("'BYTE' was not declared in this scope")) {
|
2011-10-05 03:03:19 +09:00
|
|
|
error = _("The 'BYTE' keyword is no longer supported.");
|
|
|
|
msg = _("\nAs of Arduino 1.0, the 'BYTE' keyword is no longer supported." +
|
|
|
|
"\nPlease use Serial.write() instead.\n\n");
|
2011-09-08 18:10:29 -04:00
|
|
|
}
|
|
|
|
|
2015-04-10 17:44:13 +02:00
|
|
|
if (error.trim().equals("no matching function for call to 'Server::Server(int)'")) {
|
2011-10-05 03:03:19 +09:00
|
|
|
error = _("The Server class has been renamed EthernetServer.");
|
|
|
|
msg = _("\nAs of Arduino 1.0, the Server class in the Ethernet library " +
|
|
|
|
"has been renamed to EthernetServer.\n\n");
|
2011-09-08 18:10:29 -04:00
|
|
|
}
|
|
|
|
|
2015-04-10 17:44:13 +02:00
|
|
|
if (error.trim().equals("no matching function for call to 'Client::Client(byte [4], int)'")) {
|
2011-10-05 03:03:19 +09:00
|
|
|
error = _("The Client class has been renamed EthernetClient.");
|
|
|
|
msg = _("\nAs of Arduino 1.0, the Client class in the Ethernet library " +
|
|
|
|
"has been renamed to EthernetClient.\n\n");
|
2011-09-08 18:10:29 -04:00
|
|
|
}
|
|
|
|
|
2015-04-10 17:44:13 +02:00
|
|
|
if (error.trim().equals("'Udp' was not declared in this scope")) {
|
2011-10-05 03:03:19 +09:00
|
|
|
error = _("The Udp class has been renamed EthernetUdp.");
|
|
|
|
msg = _("\nAs of Arduino 1.0, the Udp class in the Ethernet library " +
|
2012-06-01 09:20:32 -04:00
|
|
|
"has been renamed to EthernetUdp.\n\n");
|
2011-09-08 18:10:29 -04:00
|
|
|
}
|
|
|
|
|
2015-04-10 17:44:13 +02:00
|
|
|
if (error.trim().equals("'class TwoWire' has no member named 'send'")) {
|
2011-10-05 03:03:19 +09:00
|
|
|
error = _("Wire.send() has been renamed Wire.write().");
|
|
|
|
msg = _("\nAs of Arduino 1.0, the Wire.send() function was renamed " +
|
|
|
|
"to Wire.write() for consistency with other libraries.\n\n");
|
2011-02-26 13:58:03 -05:00
|
|
|
}
|
2010-08-28 09:33:37 +00:00
|
|
|
|
2015-04-10 17:44:13 +02:00
|
|
|
if (error.trim().equals("'class TwoWire' has no member named 'receive'")) {
|
2011-10-05 03:03:19 +09:00
|
|
|
error = _("Wire.receive() has been renamed Wire.read().");
|
|
|
|
msg = _("\nAs of Arduino 1.0, the Wire.receive() function was renamed " +
|
|
|
|
"to Wire.read() for consistency with other libraries.\n\n");
|
2011-09-08 18:10:29 -04:00
|
|
|
}
|
|
|
|
|
2015-04-10 17:44:13 +02:00
|
|
|
if (error.trim().equals("'Mouse' was not declared in this scope")) {
|
2012-03-31 14:50:56 -04:00
|
|
|
error = _("'Mouse' only supported on the Arduino Leonardo");
|
|
|
|
//msg = _("\nThe 'Mouse' class is only supported on the Arduino Leonardo.\n\n");
|
|
|
|
}
|
|
|
|
|
2015-04-10 17:44:13 +02:00
|
|
|
if (error.trim().equals("'Keyboard' was not declared in this scope")) {
|
2012-03-31 14:50:56 -04:00
|
|
|
error = _("'Keyboard' only supported on the Arduino Leonardo");
|
|
|
|
//msg = _("\nThe 'Keyboard' class is only supported on the Arduino Leonardo.\n\n");
|
|
|
|
}
|
|
|
|
|
2012-05-27 12:32:13 -04:00
|
|
|
RunnerException e = null;
|
|
|
|
if (!sketchIsCompiled) {
|
|
|
|
// Place errors when compiling the sketch, but never while compiling libraries
|
|
|
|
// or the core. The user's sketch might contain the same filename!
|
2014-01-30 15:50:09 +01:00
|
|
|
e = placeException(error, pieces[1], PApplet.parseInt(pieces[2]) - 1);
|
2012-05-27 12:32:13 -04:00
|
|
|
}
|
2011-09-08 18:10:29 -04:00
|
|
|
|
|
|
|
// replace full file path with the name of the sketch tab (unless we're
|
|
|
|
// in verbose mode, in which case don't modify the compiler output)
|
|
|
|
if (e != null && !verbose) {
|
|
|
|
SketchCode code = sketch.getCode(e.getCodeIndex());
|
2012-05-27 12:32:13 -04:00
|
|
|
String fileName = (code.isExtension("ino") || code.isExtension("pde")) ? code.getPrettyName() : code.getFileName();
|
|
|
|
int lineNum = e.getCodeLine() + 1;
|
2015-04-10 17:44:13 +02:00
|
|
|
s = fileName + ":" + lineNum + ": error: " + error + msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (e != null) {
|
|
|
|
if (exception == null || exception.getMessage().equals(e.getMessage())) {
|
|
|
|
exception = e;
|
|
|
|
exception.hideStackTrace();
|
|
|
|
}
|
2011-09-08 18:10:29 -04:00
|
|
|
}
|
2009-06-01 08:32:11 +00:00
|
|
|
}
|
2010-05-08 20:06:31 +00:00
|
|
|
|
2014-02-12 17:17:33 +01:00
|
|
|
if (s.contains("undefined reference to `SPIClass::begin()'") &&
|
|
|
|
s.contains("libraries/Robot_Control")) {
|
|
|
|
String error = _("Please import the SPI library from the Sketch > Import Library menu.");
|
|
|
|
exception = new RunnerException(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (s.contains("undefined reference to `Wire'") &&
|
|
|
|
s.contains("libraries/Robot_Control")) {
|
|
|
|
String error = _("Please import the Wire library from the Sketch > Import Library menu.");
|
|
|
|
exception = new RunnerException(error);
|
|
|
|
}
|
2014-02-12 15:25:58 +01:00
|
|
|
|
2010-05-08 20:06:31 +00:00
|
|
|
System.err.print(s);
|
2009-06-01 08:32:11 +00:00
|
|
|
}
|
2009-09-24 03:16:00 +00:00
|
|
|
|
2015-01-08 13:54:20 +01:00
|
|
|
private String[] getCommandCompilerByRecipe(List<File> includeFolders, File sourceFile, File objectFile, String recipe) throws PreferencesMapException, RunnerException {
|
2013-12-24 20:08:22 +01:00
|
|
|
String includes = prepareIncludes(includeFolders);
|
2011-12-31 14:32:48 +01:00
|
|
|
PreferencesMap dict = new PreferencesMap(prefs);
|
2014-08-21 12:23:42 +02:00
|
|
|
dict.put("ide_version", "" + BaseNoGui.REVISION);
|
2011-12-31 14:32:48 +01:00
|
|
|
dict.put("includes", includes);
|
2013-12-24 20:08:22 +01:00
|
|
|
dict.put("source_file", sourceFile.getAbsolutePath());
|
|
|
|
dict.put("object_file", objectFile.getAbsolutePath());
|
2009-09-25 19:31:01 +00:00
|
|
|
|
2015-04-13 12:22:37 +02:00
|
|
|
setupWarningFlags(dict);
|
|
|
|
|
2015-01-08 13:54:20 +01:00
|
|
|
String cmd = prefs.getOrExcept(recipe);
|
2011-12-31 14:32:48 +01:00
|
|
|
try {
|
2012-01-13 14:10:34 +01:00
|
|
|
return StringReplacer.formatAndSplit(cmd, dict, true);
|
2011-12-31 14:32:48 +01:00
|
|
|
} catch (Exception e) {
|
|
|
|
throw new RunnerException(e);
|
|
|
|
}
|
2009-05-31 15:53:33 +00:00
|
|
|
}
|
|
|
|
|
2015-04-13 12:22:37 +02:00
|
|
|
private void setupWarningFlags(PreferencesMap dict) {
|
2015-04-24 09:06:30 +02:00
|
|
|
if (dict.containsKey("compiler.warning_level")) {
|
|
|
|
String key = "compiler.warning_flags." + dict.get("compiler.warning_level");
|
2015-04-13 12:22:37 +02:00
|
|
|
dict.put("compiler.warning_flags", dict.get(key));
|
|
|
|
} else {
|
|
|
|
dict.put("compiler.warning_flags", dict.get("compiler.warning_flags.none"));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dict.get("compiler.warning_flags") == null) {
|
|
|
|
dict.remove("compiler.warning_flags");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-31 15:53:33 +00:00
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2011-12-31 14:32:48 +01:00
|
|
|
private void createFolder(File folder) throws RunnerException {
|
|
|
|
if (folder.isDirectory())
|
|
|
|
return;
|
2009-06-13 11:41:16 +00:00
|
|
|
if (!folder.mkdir())
|
|
|
|
throw new RunnerException("Couldn't create: " + folder);
|
|
|
|
}
|
|
|
|
|
2011-12-28 20:00:07 +01:00
|
|
|
static public List<File> findFilesInFolder(File folder, String extension,
|
2011-12-31 14:32:48 +01:00
|
|
|
boolean recurse) {
|
2011-12-28 20:00:07 +01:00
|
|
|
List<File> files = new ArrayList<File>();
|
2011-12-31 14:32:48 +01:00
|
|
|
|
2013-10-14 14:48:41 +02:00
|
|
|
if (FileUtils.isSCCSOrHiddenFile(folder)) {
|
2013-10-14 11:40:13 +02:00
|
|
|
return files;
|
|
|
|
}
|
|
|
|
|
2013-10-14 14:48:41 +02:00
|
|
|
File[] listFiles = folder.listFiles();
|
|
|
|
if (listFiles == null) {
|
2011-12-31 14:32:48 +01:00
|
|
|
return files;
|
2013-10-14 14:48:41 +02:00
|
|
|
}
|
2011-12-31 14:32:48 +01:00
|
|
|
|
2013-10-14 14:48:41 +02:00
|
|
|
for (File file : listFiles) {
|
|
|
|
if (FileUtils.isSCCSOrHiddenFile(file)) {
|
2011-12-31 14:32:48 +01:00
|
|
|
continue; // skip hidden files
|
2013-10-14 14:48:41 +02:00
|
|
|
}
|
2011-12-31 14:32:48 +01:00
|
|
|
|
2009-06-01 15:14:05 +00:00
|
|
|
if (file.getName().endsWith("." + extension))
|
|
|
|
files.add(file);
|
2011-12-31 14:32:48 +01:00
|
|
|
|
2009-06-01 15:14:05 +00:00
|
|
|
if (recurse && file.isDirectory()) {
|
|
|
|
files.addAll(findFilesInFolder(file, extension, true));
|
|
|
|
}
|
|
|
|
}
|
2011-12-31 14:32:48 +01:00
|
|
|
|
2009-06-01 15:14:05 +00:00
|
|
|
return files;
|
|
|
|
}
|
2011-06-06 22:34:12 -04:00
|
|
|
|
2011-12-31 14:32:48 +01:00
|
|
|
// 1. compile the sketch (already in the buildPath)
|
2015-01-08 13:45:17 +01:00
|
|
|
void compileSketch(List<File> includeFolders) throws RunnerException, PreferencesMapException {
|
2013-12-24 20:08:22 +01:00
|
|
|
File buildPath = prefs.getFile("build.path");
|
|
|
|
objectFiles.addAll(compileFiles(buildPath, buildPath, false, includeFolders));
|
2011-12-31 14:32:48 +01:00
|
|
|
}
|
2011-06-22 21:20:45 -06:00
|
|
|
|
2011-12-31 14:32:48 +01:00
|
|
|
// 2. compile the libraries, outputting .o files to:
|
|
|
|
// <buildPath>/<library>/
|
2015-01-08 13:45:17 +01:00
|
|
|
void compileLibraries(List<File> includeFolders) throws RunnerException, PreferencesMapException {
|
2014-10-17 00:05:36 +02:00
|
|
|
for (UserLibrary lib : importedLibraries) {
|
2013-12-24 20:08:22 +01:00
|
|
|
compileLibrary(lib, includeFolders);
|
2011-12-31 14:32:48 +01:00
|
|
|
}
|
2011-06-16 20:36:39 -04:00
|
|
|
}
|
2012-12-04 16:46:58 +01:00
|
|
|
|
2014-10-17 00:05:36 +02:00
|
|
|
private void compileLibrary(UserLibrary lib, List<File> includeFolders)
|
2015-01-08 13:45:17 +01:00
|
|
|
throws RunnerException, PreferencesMapException {
|
2013-12-24 16:07:41 +01:00
|
|
|
File libFolder = lib.getSrcFolder();
|
2015-06-15 15:30:37 +02:00
|
|
|
File librariesFolder = new File(prefs.getFile("build.path"), "libraries");
|
|
|
|
if (!librariesFolder.exists() && !librariesFolder.mkdirs()) {
|
|
|
|
throw new RunnerException("Unable to create folder " + librariesFolder);
|
|
|
|
}
|
|
|
|
|
|
|
|
File libBuildFolder = new File(librariesFolder, lib.getName());
|
2014-10-17 00:05:36 +02:00
|
|
|
|
2013-12-24 16:07:41 +01:00
|
|
|
if (lib.useRecursion()) {
|
|
|
|
// libBuildFolder == {build.path}/LibName
|
|
|
|
// libFolder == {lib.path}/src
|
2013-12-24 20:08:22 +01:00
|
|
|
recursiveCompileFilesInFolder(libBuildFolder, libFolder, includeFolders);
|
2014-10-17 00:05:36 +02:00
|
|
|
|
2013-12-24 16:07:41 +01:00
|
|
|
} else {
|
|
|
|
// libFolder == {lib.path}/
|
|
|
|
// utilityFolder == {lib.path}/utility
|
|
|
|
// libBuildFolder == {build.path}/LibName
|
|
|
|
// utilityBuildFolder == {build.path}/LibName/utility
|
|
|
|
File utilityFolder = new File(libFolder, "utility");
|
|
|
|
File utilityBuildFolder = new File(libBuildFolder, "utility");
|
2014-10-17 00:05:36 +02:00
|
|
|
|
2013-12-24 20:08:22 +01:00
|
|
|
includeFolders.add(utilityFolder);
|
|
|
|
compileFilesInFolder(libBuildFolder, libFolder, includeFolders);
|
|
|
|
compileFilesInFolder(utilityBuildFolder, utilityFolder, includeFolders);
|
2014-10-17 00:05:36 +02:00
|
|
|
|
2013-12-24 16:07:41 +01:00
|
|
|
// other libraries should not see this library's utility/ folder
|
2013-12-24 20:08:22 +01:00
|
|
|
includeFolders.remove(utilityFolder);
|
2012-12-04 16:46:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-08 13:45:17 +01:00
|
|
|
private void recursiveCompileFilesInFolder(File srcBuildFolder, File srcFolder, List<File> includeFolders) throws RunnerException, PreferencesMapException {
|
2013-12-24 20:08:22 +01:00
|
|
|
compileFilesInFolder(srcBuildFolder, srcFolder, includeFolders);
|
2013-12-24 16:07:41 +01:00
|
|
|
for (File subFolder : srcFolder.listFiles(new OnlyDirs())) {
|
|
|
|
File subBuildFolder = new File(srcBuildFolder, subFolder.getName());
|
2013-12-24 20:08:22 +01:00
|
|
|
recursiveCompileFilesInFolder(subBuildFolder, subFolder, includeFolders);
|
2013-12-24 16:07:41 +01:00
|
|
|
}
|
2012-12-04 16:46:58 +01:00
|
|
|
}
|
|
|
|
|
2015-01-08 13:45:17 +01:00
|
|
|
private void compileFilesInFolder(File buildFolder, File srcFolder, List<File> includeFolders) throws RunnerException, PreferencesMapException {
|
2013-12-24 16:07:41 +01:00
|
|
|
createFolder(buildFolder);
|
2013-12-24 20:08:22 +01:00
|
|
|
List<File> objects = compileFiles(buildFolder, srcFolder, false, includeFolders);
|
2013-12-24 16:07:41 +01:00
|
|
|
objectFiles.addAll(objects);
|
2012-12-04 16:46:58 +01:00
|
|
|
}
|
|
|
|
|
2011-12-31 14:32:48 +01:00
|
|
|
// 3. compile the core, outputting .o files to <buildPath> and then
|
|
|
|
// collecting them into the core.a library file.
|
2014-06-25 15:53:45 +02:00
|
|
|
// Also compiles the variant (if it supplies actual source files),
|
|
|
|
// which are included in the link directly (not through core.a)
|
2012-01-13 20:39:48 +01:00
|
|
|
void compileCore()
|
2015-01-08 13:45:17 +01:00
|
|
|
throws RunnerException, PreferencesMapException {
|
2011-12-31 14:32:48 +01:00
|
|
|
|
2013-12-24 20:08:22 +01:00
|
|
|
File coreFolder = prefs.getFile("build.core.path");
|
|
|
|
File variantFolder = prefs.getFile("build.variant.path");
|
2015-06-15 15:30:37 +02:00
|
|
|
File buildFolder = new File(prefs.getFile("build.path"), "core");
|
|
|
|
if (!buildFolder.exists() && !buildFolder.mkdirs()) {
|
|
|
|
throw new RunnerException("Unable to create folder " + buildFolder);
|
|
|
|
}
|
2012-01-13 20:39:48 +01:00
|
|
|
|
2013-12-24 20:08:22 +01:00
|
|
|
List<File> includeFolders = new ArrayList<File>();
|
|
|
|
includeFolders.add(coreFolder); // include core path only
|
|
|
|
if (variantFolder != null)
|
|
|
|
includeFolders.add(variantFolder);
|
2011-12-31 14:32:48 +01:00
|
|
|
|
Don't recompile core.a if none of the .o files changed
Before, core.a would be rebuilt on every build, even when none of the
core .o files changed. Now, the timestamps are checked against the
timestamp on core.a first, skipping the build if nothing changed.
Because this uses the current list of .o files, there is a corner case
when a source file is deleted, but no other source file is modified. In
that case, core.a is not rebuilt, even though it should be. However,
this is such a narrow and unrealistic case, that it should pose a real
problem.
This fixes part of #1991
2014-04-10 15:35:11 +02:00
|
|
|
|
|
|
|
if (variantFolder != null)
|
|
|
|
objectFiles.addAll(compileFiles(buildFolder, variantFolder, true,
|
|
|
|
includeFolders));
|
|
|
|
|
2014-04-10 14:47:36 +02:00
|
|
|
File afile = new File(buildFolder, "core.a");
|
|
|
|
|
2014-06-25 15:53:45 +02:00
|
|
|
List<File> coreObjectFiles = compileFiles(buildFolder, coreFolder, true,
|
|
|
|
includeFolders);
|
2011-12-31 14:32:48 +01:00
|
|
|
|
Don't recompile core.a if none of the .o files changed
Before, core.a would be rebuilt on every build, even when none of the
core .o files changed. Now, the timestamps are checked against the
timestamp on core.a first, skipping the build if nothing changed.
Because this uses the current list of .o files, there is a corner case
when a source file is deleted, but no other source file is modified. In
that case, core.a is not rebuilt, even though it should be. However,
this is such a narrow and unrealistic case, that it should pose a real
problem.
This fixes part of #1991
2014-04-10 15:35:11 +02:00
|
|
|
// See if the .a file is already uptodate
|
|
|
|
if (afile.exists()) {
|
|
|
|
boolean changed = false;
|
|
|
|
for (File file : coreObjectFiles) {
|
|
|
|
if (file.lastModified() > afile.lastModified()) {
|
|
|
|
changed = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2011-12-31 14:32:48 +01:00
|
|
|
|
Don't recompile core.a if none of the .o files changed
Before, core.a would be rebuilt on every build, even when none of the
core .o files changed. Now, the timestamps are checked against the
timestamp on core.a first, skipping the build if nothing changed.
Because this uses the current list of .o files, there is a corner case
when a source file is deleted, but no other source file is modified. In
that case, core.a is not rebuilt, even though it should be. However,
this is such a narrow and unrealistic case, that it should pose a real
problem.
This fixes part of #1991
2014-04-10 15:35:11 +02:00
|
|
|
// If none of the object files is newer than the .a file, don't
|
|
|
|
// bother rebuilding the .a file. There is a small corner case
|
|
|
|
// here: If a source file was removed, but no other source file
|
|
|
|
// was modified, this will not rebuild core.a even when it
|
|
|
|
// should. It's hard to fix and not a realistic case, so it
|
|
|
|
// shouldn't be a problem.
|
|
|
|
if (!changed) {
|
|
|
|
if (verbose)
|
|
|
|
System.out.println(I18n.format(_("Using previously compiled file: {0}"), afile.getPath()));
|
|
|
|
return;
|
2011-12-31 14:32:48 +01:00
|
|
|
}
|
|
|
|
}
|
2014-06-25 15:53:45 +02:00
|
|
|
|
2014-04-10 14:47:36 +02:00
|
|
|
// Delete the .a file, to prevent any previous code from lingering
|
|
|
|
afile.delete();
|
|
|
|
|
2014-04-10 14:53:05 +02:00
|
|
|
try {
|
|
|
|
for (File file : coreObjectFiles) {
|
|
|
|
|
|
|
|
PreferencesMap dict = new PreferencesMap(prefs);
|
2014-08-21 12:23:42 +02:00
|
|
|
dict.put("ide_version", "" + BaseNoGui.REVISION);
|
2014-04-10 14:53:05 +02:00
|
|
|
dict.put("archive_file", afile.getName());
|
|
|
|
dict.put("object_file", file.getAbsolutePath());
|
2015-06-15 15:30:37 +02:00
|
|
|
dict.put("build.path", buildFolder.getAbsolutePath());
|
2014-04-10 14:53:05 +02:00
|
|
|
|
|
|
|
String[] cmdArray;
|
2015-01-08 13:45:17 +01:00
|
|
|
String cmd = prefs.getOrExcept("recipe.ar.pattern");
|
2014-04-10 14:53:05 +02:00
|
|
|
try {
|
|
|
|
cmdArray = StringReplacer.formatAndSplit(cmd, dict, true);
|
|
|
|
} catch (Exception e) {
|
|
|
|
throw new RunnerException(e);
|
|
|
|
}
|
|
|
|
execAsynchronously(cmdArray);
|
2011-12-31 14:32:48 +01:00
|
|
|
}
|
2014-04-10 14:53:05 +02:00
|
|
|
} catch (RunnerException e) {
|
|
|
|
afile.delete();
|
|
|
|
throw e;
|
2011-12-31 14:32:48 +01:00
|
|
|
}
|
|
|
|
}
|
2011-06-13 22:14:28 -04:00
|
|
|
|
2011-12-31 14:32:48 +01:00
|
|
|
// 4. link it all together into the .elf file
|
2013-12-24 20:08:22 +01:00
|
|
|
void compileLink()
|
2015-01-08 13:45:17 +01:00
|
|
|
throws RunnerException, PreferencesMapException {
|
2011-12-31 14:32:48 +01:00
|
|
|
|
|
|
|
// TODO: Make the --relax thing in configuration files.
|
|
|
|
|
|
|
|
// For atmega2560, need --relax linker option to link larger
|
|
|
|
// programs correctly.
|
|
|
|
String optRelax = "";
|
|
|
|
if (prefs.get("build.mcu").equals("atmega2560"))
|
|
|
|
optRelax = ",--relax";
|
|
|
|
|
|
|
|
String objectFileList = "";
|
|
|
|
for (File file : objectFiles)
|
|
|
|
objectFileList += " \"" + file.getAbsolutePath() + '"';
|
|
|
|
objectFileList = objectFileList.substring(1);
|
|
|
|
|
|
|
|
PreferencesMap dict = new PreferencesMap(prefs);
|
2012-10-18 19:34:11 +02:00
|
|
|
String flags = dict.get("compiler.c.elf.flags") + optRelax;
|
|
|
|
dict.put("compiler.c.elf.flags", flags);
|
2015-06-15 15:30:37 +02:00
|
|
|
dict.put("archive_file", new File("core", "core.a").getPath());
|
2011-12-31 14:32:48 +01:00
|
|
|
dict.put("object_files", objectFileList);
|
2014-08-21 12:23:42 +02:00
|
|
|
dict.put("ide_version", "" + BaseNoGui.REVISION);
|
2011-12-31 14:32:48 +01:00
|
|
|
|
2015-04-13 12:22:37 +02:00
|
|
|
setupWarningFlags(dict);
|
|
|
|
|
2011-12-31 14:32:48 +01:00
|
|
|
String[] cmdArray;
|
2015-01-08 13:45:17 +01:00
|
|
|
String cmd = prefs.getOrExcept("recipe.c.combine.pattern");
|
2011-12-31 14:32:48 +01:00
|
|
|
try {
|
2012-01-13 14:10:34 +01:00
|
|
|
cmdArray = StringReplacer.formatAndSplit(cmd, dict, true);
|
2011-12-31 14:32:48 +01:00
|
|
|
} catch (Exception e) {
|
|
|
|
throw new RunnerException(e);
|
|
|
|
}
|
|
|
|
execAsynchronously(cmdArray);
|
|
|
|
}
|
|
|
|
|
2015-03-07 10:43:45 -06:00
|
|
|
void runActions(String recipeClass, PreferencesMap prefs) throws RunnerException, PreferencesMapException {
|
|
|
|
List<String> patterns = new ArrayList<String>();
|
|
|
|
for (String key : prefs.keySet()) {
|
|
|
|
if (key.startsWith("recipe."+recipeClass) && key.endsWith(".pattern"))
|
|
|
|
patterns.add(key);
|
|
|
|
}
|
|
|
|
Collections.sort(patterns);
|
|
|
|
for (String recipe : patterns) {
|
|
|
|
runRecipe(recipe);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-08 13:49:51 +01:00
|
|
|
void runRecipe(String recipe) throws RunnerException, PreferencesMapException {
|
2011-12-31 14:32:48 +01:00
|
|
|
PreferencesMap dict = new PreferencesMap(prefs);
|
2014-08-21 12:23:42 +02:00
|
|
|
dict.put("ide_version", "" + BaseNoGui.REVISION);
|
2015-05-05 12:32:49 -07:00
|
|
|
dict.put("sketch_path", sketch.getFolder().getAbsolutePath());
|
2011-12-31 14:32:48 +01:00
|
|
|
|
|
|
|
String[] cmdArray;
|
2015-01-08 13:49:51 +01:00
|
|
|
String cmd = prefs.getOrExcept(recipe);
|
2011-12-31 14:32:48 +01:00
|
|
|
try {
|
2012-01-13 14:10:34 +01:00
|
|
|
cmdArray = StringReplacer.formatAndSplit(cmd, dict, true);
|
2011-12-31 14:32:48 +01:00
|
|
|
} catch (Exception e) {
|
|
|
|
throw new RunnerException(e);
|
|
|
|
}
|
|
|
|
execAsynchronously(cmdArray);
|
|
|
|
}
|
2015-06-18 12:22:55 +02:00
|
|
|
|
|
|
|
private File mergeSketchWithBootloaderIfAppropriate(String className, PreferencesMap prefs) throws IOException {
|
|
|
|
if (!prefs.containsKey("bootloader.noblink") && !prefs.containsKey("bootloader.file")) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
String buildPath = prefs.get("build.path");
|
|
|
|
File sketch = new File(buildPath, className + ".hex");
|
|
|
|
if (!sketch.exists()) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
String bootloaderNoBlink = prefs.get("bootloader.noblink");
|
|
|
|
if (bootloaderNoBlink == null) {
|
|
|
|
bootloaderNoBlink = prefs.get("bootloader.file");
|
|
|
|
}
|
|
|
|
|
2015-06-22 11:50:46 +02:00
|
|
|
File bootloader = new File(new File(prefs.get("runtime.platform.path"), "bootloaders"), bootloaderNoBlink);
|
2015-06-22 11:11:09 +02:00
|
|
|
if (!bootloader.exists()) {
|
|
|
|
System.err.println(I18n.format(_("Bootloader file specified but missing: {0}"), bootloader));
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
File mergedSketch = new File(buildPath, className + ".with_bootloader.hex");
|
|
|
|
FileUtils.copyFile(sketch, mergedSketch);
|
2015-06-18 12:22:55 +02:00
|
|
|
|
|
|
|
new MergeSketchWithBooloader().merge(mergedSketch, bootloader);
|
|
|
|
|
|
|
|
return mergedSketch;
|
|
|
|
}
|
|
|
|
|
2015-01-23 08:30:46 +05:30
|
|
|
//7. Save the .hex file
|
|
|
|
void saveHex() throws RunnerException {
|
2015-06-26 12:24:15 +02:00
|
|
|
List<String> compiledSketches = new ArrayList<>(prefs.subTree("recipe.output.tmp_file", 1).values());
|
|
|
|
List<String> copyOfCompiledSketches = new ArrayList<>(prefs.subTree("recipe.output.save_file", 1).values());
|
|
|
|
|
|
|
|
if (isExportCompiledSketchSupported(compiledSketches, copyOfCompiledSketches)) {
|
2015-05-18 13:34:29 +02:00
|
|
|
System.err.println(_("Warning: This core does not support exporting sketches. Please consider upgrading it or contacting its author"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-01-23 08:30:46 +05:30
|
|
|
PreferencesMap dict = new PreferencesMap(prefs);
|
|
|
|
dict.put("ide_version", "" + BaseNoGui.REVISION);
|
|
|
|
|
2015-06-26 12:24:15 +02:00
|
|
|
if (!compiledSketches.isEmpty()) {
|
|
|
|
for (int i = 0; i < compiledSketches.size(); i++) {
|
|
|
|
saveHex(compiledSketches.get(i), copyOfCompiledSketches.get(i), prefs);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
try {
|
2015-06-18 12:22:55 +02:00
|
|
|
saveHex(prefs.getOrExcept("recipe.output.tmp_file"), prefs.getOrExcept("recipe.output.save_file"), prefs);
|
2015-06-26 12:24:15 +02:00
|
|
|
} catch (PreferencesMapException e) {
|
|
|
|
throw new RunnerException(e);
|
2015-06-18 12:22:55 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-26 12:24:15 +02:00
|
|
|
private boolean isExportCompiledSketchSupported(List<String> compiledSketches, List<String> copyOfCompiledSketches) {
|
|
|
|
return (compiledSketches.isEmpty() || copyOfCompiledSketches.isEmpty() || copyOfCompiledSketches.size() < compiledSketches.size()) && (!prefs.containsKey("recipe.output.tmp_file") || !prefs.containsKey("recipe.output.save_file"));
|
|
|
|
}
|
|
|
|
|
2015-06-18 12:22:55 +02:00
|
|
|
private void saveHex(String compiledSketch, String copyOfCompiledSketch, PreferencesMap dict) throws RunnerException {
|
|
|
|
try {
|
2015-05-13 10:21:57 +02:00
|
|
|
compiledSketch = StringReplacer.replaceFromMapping(compiledSketch, dict);
|
|
|
|
copyOfCompiledSketch = StringReplacer.replaceFromMapping(copyOfCompiledSketch, dict);
|
2015-01-23 08:30:46 +05:30
|
|
|
|
2015-05-13 10:21:57 +02:00
|
|
|
File compiledSketchFile = new File(prefs.get("build.path"), compiledSketch);
|
|
|
|
File copyOfCompiledSketchFile = new File(sketch.getFolder(), copyOfCompiledSketch);
|
2015-01-23 08:30:46 +05:30
|
|
|
|
2015-05-13 10:21:57 +02:00
|
|
|
FileUtils.copyFile(compiledSketchFile, copyOfCompiledSketchFile);
|
2015-01-23 08:30:46 +05:30
|
|
|
} catch (Exception e) {
|
|
|
|
throw new RunnerException(e);
|
|
|
|
}
|
|
|
|
}
|
2011-12-31 14:32:48 +01:00
|
|
|
|
2013-12-24 20:08:22 +01:00
|
|
|
private static String prepareIncludes(List<File> includeFolders) {
|
2011-12-31 14:32:48 +01:00
|
|
|
String res = "";
|
2013-12-24 20:08:22 +01:00
|
|
|
for (File p : includeFolders)
|
|
|
|
res += " \"-I" + p.getAbsolutePath() + '"';
|
2011-12-31 14:32:48 +01:00
|
|
|
|
|
|
|
// Remove first space
|
|
|
|
return res.substring(1);
|
|
|
|
}
|
2012-02-06 00:51:24 +01:00
|
|
|
|
|
|
|
public PreferencesMap getBuildPreferences() {
|
|
|
|
return prefs;
|
|
|
|
}
|
2014-01-30 15:50:09 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Build all the code for this sketch.
|
|
|
|
*
|
|
|
|
* In an advanced program, the returned class name could be different,
|
|
|
|
* which is why the className is set based on the return value.
|
|
|
|
* A compilation error will burp up a RunnerException.
|
|
|
|
*
|
|
|
|
* Setting purty to 'true' will cause exception line numbers to be incorrect.
|
|
|
|
* Unless you know the code compiles, you should first run the preprocessor
|
|
|
|
* with purty set to false to make sure there are no errors, then once
|
|
|
|
* successful, re-export with purty set to true.
|
|
|
|
*
|
|
|
|
* @param buildPath Location to copy all the .java files
|
|
|
|
* @return null if compilation failed, main class name if not
|
|
|
|
*/
|
|
|
|
public void preprocess(String buildPath) throws RunnerException {
|
|
|
|
preprocess(buildPath, new PdePreprocessor());
|
|
|
|
}
|
|
|
|
|
|
|
|
public void preprocess(String buildPath, PdePreprocessor preprocessor) throws RunnerException {
|
|
|
|
|
|
|
|
// 1. concatenate all .pde files to the 'main' pde
|
|
|
|
// store line number for starting point of each code bit
|
|
|
|
|
|
|
|
StringBuffer bigCode = new StringBuffer();
|
|
|
|
int bigCount = 0;
|
2014-02-01 20:47:00 +01:00
|
|
|
for (SketchCode sc : sketch.getCodes()) {
|
2015-05-25 12:53:51 +02:00
|
|
|
if (sc.isExtension(SketchData.SKETCH_EXTENSIONS)) {
|
2014-01-30 15:50:09 +01:00
|
|
|
sc.setPreprocOffset(bigCount);
|
|
|
|
// These #line directives help the compiler report errors with
|
|
|
|
// correct the filename and line number (issue 281 & 907)
|
|
|
|
bigCode.append("#line 1 \"" + sc.getFileName() + "\"\n");
|
|
|
|
bigCode.append(sc.getProgram());
|
|
|
|
bigCode.append('\n');
|
|
|
|
bigCount += sc.getLineCount();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note that the headerOffset isn't applied until compile and run, because
|
|
|
|
// it only applies to the code after it's been written to the .java file.
|
|
|
|
int headerOffset = 0;
|
|
|
|
try {
|
|
|
|
headerOffset = preprocessor.writePrefix(bigCode.toString());
|
|
|
|
} catch (FileNotFoundException fnfe) {
|
|
|
|
fnfe.printStackTrace();
|
|
|
|
String msg = _("Build folder disappeared or could not be written");
|
|
|
|
throw new RunnerException(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 2. run preproc on that code using the sugg class name
|
|
|
|
// to create a single .java file and write to buildpath
|
|
|
|
|
2015-05-04 15:44:34 +02:00
|
|
|
FileOutputStream outputStream = null;
|
2014-01-30 15:50:09 +01:00
|
|
|
try {
|
|
|
|
// Output file
|
|
|
|
File streamFile = new File(buildPath, sketch.getName() + ".cpp");
|
2015-05-04 15:44:34 +02:00
|
|
|
outputStream = new FileOutputStream(streamFile);
|
2014-01-30 15:50:09 +01:00
|
|
|
preprocessor.write(outputStream);
|
|
|
|
} catch (FileNotFoundException fnfe) {
|
|
|
|
fnfe.printStackTrace();
|
|
|
|
String msg = _("Build folder disappeared or could not be written");
|
|
|
|
throw new RunnerException(msg);
|
|
|
|
} catch (RunnerException pe) {
|
|
|
|
// RunnerExceptions are caught here and re-thrown, so that they don't
|
|
|
|
// get lost in the more general "Exception" handler below.
|
|
|
|
throw pe;
|
|
|
|
|
|
|
|
} catch (Exception ex) {
|
|
|
|
// TODO better method for handling this?
|
|
|
|
System.err.println(I18n.format(_("Uncaught exception type: {0}"), ex.getClass()));
|
|
|
|
ex.printStackTrace();
|
|
|
|
throw new RunnerException(ex.toString());
|
2015-05-04 15:44:34 +02:00
|
|
|
} finally {
|
2015-05-21 16:47:50 +02:00
|
|
|
IOUtils.closeQuietly(outputStream);
|
2014-01-30 15:50:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// grab the imports from the code just preproc'd
|
|
|
|
|
|
|
|
importedLibraries = new LibraryList();
|
2015-03-30 10:24:18 -07:00
|
|
|
importedDuplicateHeaders = new ArrayList<String>();
|
|
|
|
importedDuplicateLibraries = new ArrayList<LibraryList>();
|
2014-01-30 15:50:09 +01:00
|
|
|
for (String item : preprocessor.getExtraImports()) {
|
2015-03-30 10:24:18 -07:00
|
|
|
LibraryList list = BaseNoGui.importToLibraryTable.get(item);
|
|
|
|
if (list != null) {
|
|
|
|
UserLibrary lib = list.peekFirst();
|
|
|
|
if (lib != null && !importedLibraries.contains(lib)) {
|
|
|
|
importedLibraries.add(lib);
|
|
|
|
if (list.size() > 1) {
|
|
|
|
importedDuplicateHeaders.add(item);
|
|
|
|
importedDuplicateLibraries.add(list);
|
|
|
|
}
|
|
|
|
}
|
2014-01-30 15:50:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 3. then loop over the code[] and save each .java file
|
|
|
|
|
2014-02-01 20:47:00 +01:00
|
|
|
for (SketchCode sc : sketch.getCodes()) {
|
2015-05-25 12:53:51 +02:00
|
|
|
if (sc.isExtension(SketchData.OTHER_ALLOWED_EXTENSIONS)) {
|
2014-01-30 15:50:09 +01:00
|
|
|
// no pre-processing services necessary for java files
|
|
|
|
// just write the the contents of 'program' to a .java file
|
|
|
|
// into the build directory. uses byte stream and reader/writer
|
|
|
|
// shtuff so that unicode bunk is properly handled
|
|
|
|
String filename = sc.getFileName(); //code[i].name + ".java";
|
|
|
|
try {
|
2014-08-21 13:43:10 +02:00
|
|
|
BaseNoGui.saveFile(sc.getProgram(), new File(buildPath, filename));
|
2014-01-30 15:50:09 +01:00
|
|
|
} catch (IOException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
throw new RunnerException(I18n.format(_("Problem moving {0} to the build folder"), filename));
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (sc.isExtension("ino") || sc.isExtension("pde")) {
|
|
|
|
// The compiler and runner will need this to have a proper offset
|
|
|
|
sc.addPreprocOffset(headerOffset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* List of library folders.
|
|
|
|
*/
|
|
|
|
private LibraryList importedLibraries;
|
2015-03-30 10:24:18 -07:00
|
|
|
private List<String> importedDuplicateHeaders;
|
|
|
|
private List<LibraryList> importedDuplicateLibraries;
|
2014-01-30 15:50:09 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Map an error from a set of processed .java files back to its location
|
|
|
|
* in the actual sketch.
|
|
|
|
* @param message The error message.
|
|
|
|
* @param dotJavaFilename The .java file where the exception was found.
|
|
|
|
* @param dotJavaLine Line number of the .java file for the exception (0-indexed!)
|
|
|
|
* @return A RunnerException to be sent to the editor, or null if it wasn't
|
|
|
|
* possible to place the exception to the sketch code.
|
|
|
|
*/
|
|
|
|
public RunnerException placeException(String message,
|
|
|
|
String dotJavaFilename,
|
|
|
|
int dotJavaLine) {
|
|
|
|
// Placing errors is simple, because we inserted #line directives
|
|
|
|
// into the preprocessed source. The compiler gives us correct
|
|
|
|
// the file name and line number. :-)
|
2014-02-01 20:47:00 +01:00
|
|
|
for (SketchCode code : sketch.getCodes()) {
|
2014-01-30 15:50:09 +01:00
|
|
|
if (dotJavaFilename.equals(code.getFileName())) {
|
|
|
|
return new RunnerException(message, sketch.indexOfCode(code), dotJavaLine);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2009-05-31 15:53:33 +00:00
|
|
|
}
|