#include "Builder.h"

#include "Project.h"
#include "FileTools.h"
#include "VarProcessor.h"
#include "Settings.h"
#include "LibraryPool.h"

// constructor
BuilderClass::BuilderClass()
{
	_errorStream.Clear();
	_parsedMessages.Clear();
	_isError = false;
}

// destructor
BuilderClass::~BuilderClass()
{
}

// get includes
void BuilderClass::GetIncludes(void)
{
	_includes.Clear();

	// core and variant
	_includes << "-I\"" << _coreRoot << "\"";
	_includes << " -I\"" << _variantRoot << "\"";

	// libraries
	for(int iLib = 0; iLib < _libraries.GetCount(); iLib++)
	{
		Ptr<LibraryInfo> lib = _libraries[iLib];
		_includes << " -I\"" << lib->GetIncludePath() << "\"";
	}
	
	// sketch
	_includes << " -I\"" << _project->GetFolder() << "\"";
}


// create recipes
bool BuilderClass::CreateRecipes(void)
{
//	DUMP(_variables);
	_recipe_c_o					= VarProcessor::SubstVars(_variables.Get("recipe.c.o.pattern"			, ""), _variables);
	_recipe_cpp_o				= VarProcessor::SubstVars(_variables.Get("recipe.cpp.o.pattern"			, ""), _variables);
	_recipe_S_o					= VarProcessor::SubstVars(_variables.Get("recipe.S.o.pattern"			, ""), _variables);
	_recipe_ar					= VarProcessor::SubstVars(_variables.Get("recipe.ar.pattern"			, ""), _variables);
	_recipe_combine				= VarProcessor::SubstVars(_variables.Get("recipe.c.combine.pattern"		, ""), _variables);
	_recipe_objcopy_eep			= VarProcessor::SubstVars(_variables.Get("recipe.objcopy.eep.pattern"	, ""), _variables);
	_recipe_objcopy_hex			= VarProcessor::SubstVars(_variables.Get("recipe.objcopy.hex.pattern"	, ""), _variables);
	_recipe_output_tmp_file		= VarProcessor::SubstVars(_variables.Get("recipe.output.tmp_file"		, ""), _variables);
	_recipe_output_save_file	= VarProcessor::SubstVars(_variables.Get("recipe.output.save_file"		, ""), _variables);

	_recipe_size				= VarProcessor::SubstVars(_variables.Get("recipe.size.pattern"			, ""), _variables);
	_recipe_size_regex			= _variables.Get("recipe.size.regex"			, "");
	_recipe_size_regex_data		= _variables.Get("recipe.size.regex.data"		, "");
	_recipe_size_regex_eeprom	= _variables.Get("recipe.size.regex.eeprom"		, "");

	_recipe_preproc_includes	= VarProcessor::SubstVars(_variables.Get("recipe.preproc.includes"		, ""), _variables);
	_recipe_preproc_macros		= VarProcessor::SubstVars(_variables.Get("recipe.preproc.macros"		, ""), _variables);
	_recipe_elf_lst				= VarProcessor::SubstVars(_variables.Get("recipe.elf.lst.pattern"		, ""), _variables);
	
//	DUMP(_recipe_c_o);
//	DUMP(_recipe_cpp_o);
//	DUMP(_recipe_S_o);
//	DUMP(_recipe_ar);
//	DUMP(_recipe_combine);
//	DUMP(_recipe_objcopy_eep);
//	DUMP(_recipe_objcopy_hex);
//	DUMP(_recipe_output_tmp_file);
//	DUMP(_recipe_output_save_file);
//	DUMP(_recipe_size);
//	DUMP(_recipe_preproc_includes);
//	DUMP(_recipe_preproc_macros);
	
	return true;
}

// compile command
bool BuilderClass::Compile(String const &src, String const &obj)
{
	String ext = GetFileExt(src);
	String cmd;
	if(ext == ".c")
		cmd = _recipe_c_o;
	else if(ext == ".cpp")
		cmd = _recipe_cpp_o;
	else if(ext == ".S")
		cmd = _recipe_S_o;
	else
		return false;
	
	// clear process tools (wipes errors and stray processes)
	_procTools.Clear();
	
	VectorMap<String, String>vars ;
	vars.Add("source_file", src);
	vars.Add("object_file", obj);
	cmd = VarProcessor::SubstVars(cmd, vars);

	if(_compilerVerbosity >= 1)
		_project->ShowBuildResults(Format(t_("Compiling %s\n"), GetFileName(src)));
	if(_compilerVerbosity >= 2)
		_project->ShowBuildResults(Format(t_("Compiler command :\n%s\n"), cmd));
	bool res = _procTools.Exec(cmd);
	if(_compilerVerbosity >= 3)
		_project->ShowBuildResults(Format(t_("Compiler output :\n%s\n"), _procTools.GetOutput()));
	
	String err = _procTools.GetError();
	if(!err.IsEmpty())
		_project->ShowBuildErrors(err);
	_errorStream += err;

	return res;
}

bool BuilderClass::Archive(String const &archive, String const &obj)
{
	// clear process tools (wipes errors and stray processes)
	_procTools.Clear();
	
	VectorMap<String, String>vars ;
	vars.Add("object_file", obj);
	vars.Add("archive_file", GetFileName(archive));
	vars.Add("archive_file_path", archive);
	String cmd = VarProcessor::SubstVars(_recipe_ar, vars);
	
	if(_compilerVerbosity >= 2)
		_project->ShowBuildResults(Format(t_("Archive command :\n%s\n"), cmd));
	bool res = _procTools.Exec(cmd);
	if(_compilerVerbosity >= 3)
		_project->ShowBuildResults(Format(t_("Archiver output :\n%s\n"), _procTools.GetOutput()));
	
	String err = _procTools.GetError();
	if(!err.IsEmpty())
		_project->ShowBuildErrors(err);
	_errorStream += err;
	return res;
}

bool BuilderClass::Archive(String const &archive, Vector<String> const &objects)
{
	// check if we need to run archive
	if(!_fullRebuild && !SourceTools::Older(archive, objects))
		return true;
	
	FileDelete(archive);
	
	String s = Format(t_("Creating archive '%s'"), GetFileName(archive));
	if(_compilerVerbosity >= 1)
	{
		s += " : [ ";
		for(int iObj = 0; iObj < objects.GetCount(); iObj++)
			s += GetFileTitle(objects[iObj]) + " ";
		s += "]";
	}
	_project->ShowBuildResults(s + "\n");

	for(int iObj = 0; iObj < objects.GetCount(); iObj++)
		_isError |= !Archive(archive, objects[iObj]);

	return !_isError;
}

bool BuilderClass::Combine(void)
{
	// clear process tools (wipes errors and stray processes)
	_procTools.Clear();
	
	String s = Format(t_("Linking %s.elf"), _variables.Get("build.project_name"));
	if(_compilerVerbosity >= 1)
	{
		s += " : [ _CORE.ar "; 
		for(int i = 0;  i < _libraries.GetCount();  i++)
			s += "LIB_" + _libraries[i]->GetName() + ".ar ";
		s += "_SKETCH_.ar ]";
	}
	_project->ShowBuildResults(s + "\n");

	// create objects list for linking
	// (all in form of .ar files)
	// core file shall be inserted twice, otherwise linker will complain
	String objects;
	objects << " \"" << AppendFileName(_buildPath, "_CORE_.ar") << "\"";
	objects << " \"" << AppendFileName(_buildPath, "_SKETCH_.ar") << "\"";
	String root = AppendFileName(_buildPath, "_LIB_");
	// @@@ WARNING - DANGEROUS OPTION --allow-multiple-definition NEEDED HERE...
//	objects << " -Wl,--allow-multiple-definition -Wl,--start-group";
	objects << " -Wl,--start-group";
	for(int iLib = 0; iLib < _libraries.GetCount(); iLib++)
	{
		String libFile = root + _libraries[iLib]->GetName() + ".ar";
		if(FileExists(libFile))
			objects << " \""  << libFile << "\"";
	}
	objects << " -Wl,--end-group";
	
	VectorMap<String, String>vars ;
	vars.Add("object_files", objects);
	vars.Add("archive_file", "_CORE_.ar");
	vars.Add("archive_file_path", AppendFileName(_buildPath, "_CORE_.ar"));
	String cmd = VarProcessor::SubstVars(_recipe_combine, vars);
	
	if(_compilerVerbosity >= 2)
		_project->ShowBuildResults(Format(t_("Linker command :\n%s\n"), cmd));
	bool res = _procTools.Exec(cmd);
	if(_compilerVerbosity >= 3)
		_project->ShowBuildResults(Format(t_("Linker output :\n%s\n"), _procTools.GetOutput()));
	String err = _procTools.GetError();
	if(!err.IsEmpty())
		_project->ShowBuildErrors(err);
	_errorStream += err;
	return res;
}

bool BuilderClass::ObjCopy_HEX(void)
{
	// clear process tools (wipes errors and stray processes)
	_procTools.Clear();
	
	String projName = _variables.Get("build.project_name");
	String s = Format(t_("Creating HEX file '%s.hex'"), projName);
	if(_compilerVerbosity >= 1)
		s += " : [ " + projName + ".elf ]";
	_project->ShowBuildResults(s + "\n");

	String cmd = _recipe_objcopy_hex;
	if(_compilerVerbosity >= 2)
		_project->ShowBuildResults(Format(t_("Objcopy command :\n%s\n"), cmd));
	bool res = _procTools.Exec(cmd);
	
	if(_compilerVerbosity >= 3)
		_project->ShowBuildResults(Format(t_("Objcopy output :\n%s\n"), _procTools.GetOutput()));
	String err = _procTools.GetError();
	if(!err.IsEmpty())
		_project->ShowBuildErrors(err);
	_errorStream += err;
	return res;
}

// build core files
bool BuilderClass::BuildCore(void)
{
	_project->ShowBuildResults(t_("Building core files\n"));

	// get platform path core folder
	String sourcePath = _coreRoot;
	
	// get destination path
	String destPath = AppendFileName(_buildPath, "core");
//	RealizeDirectory(destPath);
	
	// get all core sources
	Vector<String> sources = FileTools::RecurseScan(sourcePath, _sourcesPattern);
//DUMP(sources);	
	// append full path to sources and get object files
	Vector<String>objects;
	for(int i = 0; i < sources.GetCount(); i++)
	{
		String s = sources[i];
		String o = AppendFileName(destPath, s) + ".o";
		s = AppendFileName(sourcePath, s);
		sources[i] = s;
		objects.Add(o);
	}

	// compile the object files as needed looking on dependencies
	for(int i = 0; i < sources.GetCount(); i++)
	{
		String const &s = sources[i];
		String const &o = objects[i];
		RealizeDirectory(GetFileFolder(o));
		if(_fullRebuild || _depend.Check(o))
			_isError |= !Compile(s, o);
	}
	
	// create the core archive
	return !_isError | Archive(AppendFileName(_buildPath, "_CORE_.ar"), objects);
}

// _project->ShowBuildResults(Format(t_("Warning - using library '%s' with wrong architecture\n"), res.info.name));


// build a library
bool BuilderClass::BuildLib(Ptr<LibraryInfo> lib)
{
	_project->ShowBuildResults(Format(t_("Building library '%s'\n"), lib->GetName()));

	// gather all source files
	Vector<String> sources = lib->ScanSources(false);
	
	// some libs have just includes...
	if(!sources.GetCount())
		return true;
	
	// get destination path
	String destPath = AppendFileName(_buildPath, "_LIB_" + lib->GetName());
	RealizeDirectory(destPath);
	
	// append full path to sources and get object files
	String libRoot = lib->GetSourcesRoot();
	Vector<String>objects;
	for(int i = 0; i < sources.GetCount(); i++)
	{
		String s = sources[i];
		
		// as some objects can have same name in different subfolders, we can't simply
		// use them as-is. We replace instead all paths delimiters with _ and add to name
		String o = UnixPath(s);
		for(int k = 0; k < o.GetCount(); k++)
			if(o[k] == '/')
				o.Set(k, '_');
		o = AppendFileName(destPath, o) + ".o";
		s = AppendFileName(libRoot, s);
		sources[i] = s;
		objects.Add(o);
	}

	// compile the object files as needed looking on dependencies
	for(int i = 0; i < sources.GetCount(); i++)
	{
		String const &s = sources[i];
		String const &o = objects[i];
		if(_fullRebuild || _depend.Check(o))
			_isError |= !Compile(s, o);
	}
	
	// create the archive
	return !_isError && Archive(AppendFileName(_buildPath, "_LIB_" + lib->GetName() + ".ar"), objects);
}


// build used libraries
bool BuilderClass::BuildLibs(void)
{
	for(int iLib = 0; iLib < _libraries.GetCount(); iLib++)
		_isError |= !BuildLib(_libraries[iLib]);
	return !_isError;
}

// preprocess the sketch
bool BuilderClass::PreprocessSketch(void)
{
	if(!_fullRebuild && !SourceTools::Newer(_project->GetINOPath(), _preprocessedINO))
		return true;

	_project->ShowBuildResults(Format(t_("Preprocess '%s'\n"), GetFileName(_project->GetINOPath())));

	String s = LoadFile(_project->GetINOPath());
	if(s.IsEmpty())
	{
		_project->ShowBuildErrors(t_("PREPROCESS FAILED\n"));
		return false;
	}
	FileOut f(_preprocessedINO);
	if(!f)
	{
		_project->ShowBuildErrors(t_("PREPROCESS FAILED\n"));
		return false;
	}
	f << "#include <Arduino.h>\n";
	f << "#line 1 \"" << UnixPath(_project->GetINOPath()) << "\"\n";
	f << s;
	f.Close();
	return true;
}

// build the sketch
bool BuilderClass::BuildSketch(void)
{
	// preprocess the INO file if needed
	if(!PreprocessSketch())
		return false;
	
	_project->ShowBuildResults(t_("Building sketch files\n"));

	// get destination path
	String destPath = AppendFileName(_buildPath, "_SKETCH_");
	RealizeDirectory(destPath);
	
	// get all sketch sources
	String sourcePath = _project->GetFolder();
	Vector<String> sources = FileTools::RecurseScan(sourcePath, _sourcesPattern);
	
	// append full path to sources and get object files
	Vector<String>objects;
	for(int i = 0; i < sources.GetCount(); i++)
	{
		String s = sources[i];
		String o = AppendFileName(destPath, s) + ".o";
		s = AppendFileName(sourcePath, s);
		sources[i] = s;
		objects.Add(o);
	}
	
	// add preprocessed INO file and object
	sources << _preprocessedINO;
	objects << AppendFileName(destPath, GetFileName(_preprocessedINO)) + ".o";

	// compile the object files as needed looking on dependencies
	for(int i = 0; i < sources.GetCount(); i++)
	{
		String const &s = sources[i];
		String const &o = objects[i];
		if(_fullRebuild || _depend.Check(o))
			_isError |= !Compile(s, o);
	}
	
	// create the core archive
	return !_isError && Archive(AppendFileName(_buildPath, "_SKETCH_.ar"), objects);
}

// calculate sketch size
bool BuilderClass::CalcSize(void)
{
	// clear process tools (wipes errors and stray processes)
	_procTools.Clear();
	
	String projName = _variables.Get("build.project_name");
	String s = Format(t_("Getting size for '%s.hex'"), projName);
	_project->ShowBuildResults(s + "\n");

	String cmd = _recipe_size;
	if(_compilerVerbosity >= 2)
		_project->ShowBuildResults(Format(t_("Size command :\n%s\n"), cmd));
	bool res = _procTools.Exec(cmd);
	
	String out = _procTools.GetOutput();
	String err = _procTools.GetError();

	if(_compilerVerbosity >= 3)
		_project->ShowBuildResults(Format(t_("Size output :\n%s\n"), out));
	if(!err.IsEmpty())
		_project->ShowBuildErrors(err);
	else
	{
		// we shall parse output here, with regex expressions
		RegExp sizeExp;
		StringStream ss(out);

		sizeExp.SetPattern(_recipe_size_regex);
		int codeSize = 0;
		while(!ss.IsEof())
		if(sizeExp.Match(ss.GetLine()))
			codeSize += ScanInt(sizeExp.GetString(0));
		
		ss.Seek(0);
		sizeExp.Clear();
		sizeExp.SetPattern(_recipe_size_regex_data);
		int dataSize = 0;
		while(!ss.IsEof())
		if(sizeExp.Match(ss.GetLine()))
			dataSize += ScanInt(sizeExp.GetString(0));

		ss.Seek(0);
		sizeExp.Clear();
		sizeExp.SetPattern(_recipe_size_regex_eeprom);
		int eepSize = 0;
		while(!ss.IsEof())
		if(sizeExp.Match(ss.GetLine()))
			eepSize += ScanInt(sizeExp.GetString(0));

		_project->ShowBuildResults(t_("Sketch output sizes:\n"));
		_project->ShowBuildResults(Format(t_("  CODE   : %10d bytes\n"), codeSize));
		_project->ShowBuildResults(Format(t_("  DATA   : %10d bytes\n"), dataSize));
		_project->ShowBuildResults(Format(t_("  EEPROM : %10d bytes\n"), eepSize));
	}

	_errorStream += err;
	return res;
}

// parse error messages
// return true if ok, false if found errors
bool BuilderClass::ParseMessages(void)
{
	bool hasErrors = false;
	const char *fatalErrPattern = ": fatal error: ";
	const char *errPattern = ": error: ";
	const char *warnPattern = ": warning: ";

	StringStream ss(_errorStream);
	bool keepLine = false;
	String s;
	while(!ss.IsEof() || keepLine)
	{
		bool warn = false;
		bool err = false;
		int iPattern = -1;
		int iMessage = -1;
		if(!keepLine)
		{
			s = ss.GetLine();
		}
		else
			keepLine = false;
		if( (iPattern = s.Find(fatalErrPattern)) >= 0)
		{
			err = true;
			hasErrors = true;
			iMessage = iPattern + strlen(fatalErrPattern);
		}
		else if( (iPattern = s.Find(errPattern)) >= 0)
		{
			err = true;
			hasErrors = true;
			iMessage = iPattern + strlen(errPattern);
		}
		else if( (iPattern = s.Find(warnPattern)) >= 0)
		{
			warn = true;
			iMessage = iPattern + strlen(warnPattern);
		}
		if(err || warn)
		{
			// got error or message warning, we must construct
			// the reporting struct and fill it
			ErrorLine &el = _parsedMessages.Add();
			el.isError = err;
			int i = iMessage;
			// path:line:column: error: message
			//                          |
			while(s[i] && IsSpace(s[i]))
				i++;
			el.message = s.Mid(i);
			el.file = "";
			el.line = 0;
			el.pos = 0;
			
			// find out file and line of message
			// path:line:column: error: message
			//                 |
			i = iPattern - 1;
			// path:line:column: error: message
			//                |
			while(i > 0 && IsDigit(s[i]))
				i--;
			// path:line:column: error: message
			//          |
			el.pos = ScanInt(s.Mid(i+1));
			i--;
			// path:line:column: error: message
			//         |
			while(i > 0 && IsDigit(s[i]))
				i--;
			// path:line:column: error: message
			//     |
			el.line = ScanInt(s.Mid(i+1));
			el.file = s.Left(i);
			
			// get remaining error lines up to end of file
			// or next error or warning
			if(ss.IsEof())
				break;
			keepLine = true;
			while(!ss.IsEof())
			{
				s = ss.GetLine();
				if(s.Find(fatalErrPattern) >= 0 || s.Find(errPattern) >= 0 || s.Find(warnPattern) >= 0)
					break;
			}
		}
	}
	return !hasErrors;
}

// pre-build stuffs
// (prepare for building)
bool BuilderClass::PrepareBuild(Project *project)
{
	// setup the environment
	if(!Setup(project))
	{
		SetError();
		return false;
	}
	
	// save all project files
	_project->Save();
	
	// clear dependency data
	_depend.Clear();
	
	// clear output from previous run
	_errorStream.Clear();
	_parsedMessages.Clear();
	_isError = false;

	// clear build results in project
	_project->ClearBuildResults();
	_parsedMessages.Clear();
	
	// clear output streams
	_errorStream.Clear();
	
	// get used libraries
	_libraries = _project->FindLibraries();
	
	// append dependend libraries if required
	if(Settings.GetCompilerAutoLibDependencies())
		_libraries = LibraryPool.SolveDependencies(_libraries, _architecture);

	// get and append includes to variables
	GetIncludes();
	VarProcessor::MergeVars(_variables, "includes", _includes);
	
	// create recipes
	if(!CreateRecipes())
	{
		SetError();
		return false;
	}
	
	return true;
}

// build command
bool BuilderClass::Build(Project *project)
{
	if(!PrepareBuild(project))
		return false;
	
	// check optimizations of last build
	// if different from current, force a full rebuild
	String optFile = AppendFileName(_buildPath, "compiler.options");
	String storedOpts;
	String currentOpts = _variables.Get("compiler.optimization_flags");
	if(FileExists(optFile))
		storedOpts = LoadFile(optFile);
	if(storedOpts != currentOpts)
	{
		_fullRebuild = true;
		SaveFile(optFile, currentOpts);
	}

	// preprocessed ino file path
	String inoName = GetFileName(_project->GetINOPath());
	_preprocessedINO = AppendFileName(_buildPath, inoName) + ".cpp";
	
	// build core files
	_isError |= !BuildCore();
	
	// build used libraries
	_isError |= !BuildLibs();
	
	// build sketch
	_isError |= !BuildSketch();
	
	// link
	if(!_isError)
		_isError |= !Combine();
	
	// hex
	if(!_isError)
		_isError |= !ObjCopy_HEX();
	
	// size
	if(!_isError)
		_isError |= !CalcSize();
	
	// parse compiler messages
	_isError |= !ParseMessages();
	
	// show results in project's pane
	project->ShowMessages(_parsedMessages);
	
	return !_isError;
}

// show upload progress callback
bool BuilderClass::showUploadProgressCb(String const &o, String const &e)
{
	if(!o.IsEmpty())
		_project->ShowBuildResults(o);
	if(!e.IsEmpty())
		_project->ShowBuildErrors(e);
	return false;
}

// upload command
bool BuilderClass::Upload(Project *project)
{
	// build first, if needed
	if(!Build(project))
		return false;
	
	// create upload recipes
	String uploadTool = _variables.Get("upload.tool");

	// upload.pattern
	// tools.TOOL.upload.pattern
	// canbe any of above
	_upload_recipe = _variables.Get("upload.pattern", "");
	if(_upload_recipe.IsEmpty())
		_upload_recipe = _variables.Get("tools." + uploadTool + ".upload.pattern", "");
	if(_upload_recipe.IsEmpty())
		return false;

	_upload_network_recipe = _variables.Get("upload.network_pattern", "");
	if(_upload_network_recipe.IsEmpty())
		_upload_network_recipe = _variables.Get("tools." + uploadTool + ".upload.network_pattern", "");
	if(_upload_network_recipe.IsEmpty())
		_upload_network_recipe = _upload_recipe;

	// now we need to set {upload.verbose}, {serial.port} and {network.port}
	String verb = _variables.Get("tools." + uploadTool + ".verbose", "");
	String quiet = _variables.Get("tools." + uploadTool + ".quiet", "");
	if(_uploaderVerbosity < 2)
		_variables.GetAdd("upload.verbose") = quiet;
	else if(_uploaderVerbosity > 2 && _uploaderVerbosity < 4 )
		_variables.GetAdd("upload.verbose") = verb;
	else
		_variables.GetAdd("upload.verbose") = verb + " " + verb;
	
	//	{upload.network.port}
	//	{upload.network.endpoint_upload}
	//	{upload.network.endpoint_sync}
	//	{upload.network.endpoint_reset}
	//	{upload.network.sync_return}

	String serialPort = Settings.GetDevice();
	String networkPort = "";
	String enpointUpload = "";
	String endpointSync = "";
	String endpointReset = "";
	String syncReturn = "";
	
	bool isNetwork = false;
	int idx = serialPort.Find("@");
	if(idx > 0)
	{
		isNetwork = true;
		
		// strip "net:" from port
		serialPort = serialPort.Mid(idx + 1);
		
		// split to get network port
		Vector<String>v = Split(serialPort, ":");
		serialPort = v[0];
		if(v.GetCount() >= 2)
			networkPort = v[1];
	}

	_variables.GetAdd("serial.port") = serialPort;
	_variables.GetAdd("network.port") = networkPort;
	_variables.GetAdd("upload.network.port") = networkPort;

	_variables.GetAdd("upload.network.endpoint_upload") = enpointUpload;
	_variables.GetAdd("upload.network.endpoint_sync") = endpointSync;
	_variables.GetAdd("upload.network.endpoint_reset") = endpointReset;
	_variables.GetAdd("upload.network.sync_return") = syncReturn;
	
	// {config.path} and {upload.verify}
	
	// substitute some shorted variables
	// {cmd.path}
	// {config.path}
	// {upload.verify}
	// {network_cmd}
	String cmd				= _variables.Get("runtime.tools." + uploadTool + ".cmd", "");
	if(cmd.IsEmpty())
		cmd					= _variables.Get("tools." + uploadTool + ".cmd", "");
	String path				= _variables.Get("tools." + uploadTool + ".path", "");
	if(path.IsEmpty())
		path				= _variables.Get("runtime.tools." + uploadTool + ".path", "");
	String cmd_path			= _variables.Get("tools." + uploadTool + ".cmd.path", "");
	String config_path		= _variables.Get("tools." + uploadTool + ".config.path", "");
	String upload_verify	= _variables.Get("tools." + uploadTool + ".upload.verify", "");
	String upload_Noverify	= _variables.Get("tools." + uploadTool + ".upload.params.noverify", "");
	String network_cmd		= _variables.Get("tools." + uploadTool + ".network_cmd", "");
	_variables.GetAdd("cmd")				= cmd;
	_variables.GetAdd("path")				= path;
	_variables.GetAdd("cmd.path")			= cmd_path;
	_variables.GetAdd("config.path")		= config_path;
	_variables.GetAdd("upload.verify")		= upload_verify;
	_variables.GetAdd("network_cmd")		= network_cmd;
	_variables.GetAdd("network.password")	= "";

//DUMP(_variables);	

//DUMP(_upload_recipe);
//DUMP(_upload_network_recipe);

	// substitute variables
	_upload_recipe			= VarProcessor::SubstVars(_upload_recipe, _variables);
	_upload_network_recipe	= VarProcessor::SubstVars(_upload_network_recipe, _variables);

//DUMP(_upload_recipe);
//DUMP(_upload_network_recipe);

	_procTools.Clear();
	if(!isNetwork)
		cmd = _upload_recipe;
	else
		cmd = _upload_network_recipe;
	
	_project->ShowBuildResults(Format(t_("Uploading sketch '%s'\n"), project->GetTitle()));
	if(_uploaderVerbosity >= 1)
		_project->ShowBuildResults(cmd + "\n");

	_procTools.WhenOutErr = THISBACK(showUploadProgressCb);
	bool res = _procTools.Exec(cmd);
	_procTools.WhenOutErr.Clear();
	_project->ShowBuildResults("\n");
	if(!res)
		_project->ShowBuildErrors(t_("Upload FAILED\n"));
	String err = _procTools.GetError();
	if(!err.IsEmpty())
		_project->ShowBuildErrors(err);
	_errorStream += err;
	return res;
}

String  BuilderClass::ShowDisasm(Project *project)
{
	if(!Build(project))
		return "";
	
	if(_recipe_elf_lst.IsEmpty())
	{
		project->ShowBuildErrors(t_("Current package doesn't support disassembly\n"));
		return "";
	}
		
	_procTools.Clear();
	DUMP(_recipe_elf_lst);
	bool res = _procTools.Exec(_recipe_elf_lst);
	if(!res)
	{
		project->ShowBuildErrors(t_("Disassembly FAILED\n"));
		return "";
	}
	
	// get disassembly output
	String dis = _procTools.GetOutput();

	// strip non ascii characters
	String cleaned;
	for(auto c:dis)
		if(c >= ' ' || c == 0x0d || c == 0x0a || c == '\t')
			cleaned.Cat(c);
	
	// store the disassembly on build location
	String projName = _variables.Get("build.project_name");
	String destPath = ForceExt(AppendFileName(_buildPath, projName), ".lst");
	SaveFile(destPath, cleaned);
	
	// return disassembled file path
	return destPath;
}

// build (compile) a single file
// (output discarded, just for speed tests)
bool BuilderClass::CompileFile(Project *project, String path)
{
	// do not try to compile non-source files
	String ext = ToLower(GetFileExt(path));
	if(findarg(ext, ".c", ".cpp", ".cxx", ".ino", ".s") < 0)
		return false;
	
	// prepare build environment
	if(!PrepareBuild(project))
		return false;
	
	// get a temporary folder to 
	String tmpFolder = AppendFileName(GetTempFolder(), "FishIDE.tmp.builds");
	RealizeDirectory(tmpFolder);
	
	// ino file needs preprocessing
	if(ext == ".ino")
	{
		String s = LoadFile(path);
		String inoName = GetFileName(path);
		String preprocessedFile = AppendFileName(tmpFolder, inoName) + ".cpp";
		FileOut f(preprocessedFile);
		if(!f)
			return false;
		f << "#include <Arduino.h>\n";
		f << "#line 1 \"" << UnixPath(_project->GetINOPath()) << "\"\n";
		f << s;
		f.Close();
		path = preprocessedFile;
	}
	
	// create dest file name
	String destPath = AppendFileName(tmpFolder, GetFileName(path)) + ".o";
	
	_isError = !Compile(path, destPath);
	
	// parse compiler messages
	_isError |= !ParseMessages();
	
	// show results in project's pane
	project->ShowMessages(_parsedMessages);
	
	return !_isError;
}


BuilderClass &__GetBuilder(void)
{
	static BuilderClass builder;
	
	return builder;
	
}
