var crypto = require('crypto'); var fs = require('fs'); var fse = require('fs-extra'); var path = require('path'); var child_process = require('child_process'); var co = require('co'); var S = require('string'); var filewalker = require('filewalker'); var dependencies = require('../data/Dependencies'); var Builder = module.exports = function(basePath, sources) { this.avrconfig = { Path:'/usr/share/arduino/hardware/tools/avr/bin', BOARD_TAG: 'uno', Tools: { CC_NAME: 'avr-gcc', CXX_NAME: 'avr-g++', OBJCOPY_NAME: 'avr-objcopy', AR_NAME: 'avr-ar', SIZE_NAME: 'avr-size', NM_NAME: 'avr-nm' } }; this.id = null; this.filename = 'main.ino'; this.lib = { Path: { core: path.join(__dirname, '..', 'libraries/core'), external: path.join(__dirname, '..', 'libraries/external'), thirdparty: path.join(__dirname, '..', 'libraries/thirdparty') }, libs: { core: [], external: [], thirdparty: [] }, dirs: { external: [], thirdparty: [] } }; this.basePath = basePath; this.sources = sources || {}; this.workingPath = ''; }; Builder.prototype.init = function(callback) { var self = this; self.id = crypto.randomBytes(16).toString('hex').substr(0, 8) + Date.now(); self.workingPath = path.join(self.basePath, self.id); fse.mkdirs(self.workingPath, function(err) { if (callback) callback(err); }); }; Builder.prototype.scanLibraries = function(callback) { var self = this; co(function *() { try { // Scanning core libraries self.lib.libs.core = yield function(done) { fs.readdir(self.lib.Path.core, done); }; } catch(e) {} yield function(done) { filewalker(self.lib.Path.thirdparty) .on('dir', function(p) { if(!S(p).contains('/')){ //console.log('dir: %s', p); self.lib.dirs.thirdparty.push(p); } }) .on('file', function(p, s) { if(S(p).contains('.o')){ //console.log('file: %s, %d bytes', p, s.size); self.lib.libs.thirdparty.push(p); } }) .on('error', function(err) { console.error(err); }) .on('done', function() { //console.log('%d dirs, %d files, %d bytes', this.dirs, this.files, this.bytes); done(); }) .walk(); }; yield function(done) { filewalker(self.lib.Path.external) .on('dir', function(p) { if(!S(p).contains('/')){ //console.log('dir: %s', p); self.lib.dirs.external.push(p); } }) .on('file', function(p, s) { if(S(p).contains('.o')){ //console.log('file: %s, %d bytes', p, s.size); self.lib.libs.external.push(p); } }) .on('error', function(err) { console.error(err); }) .on('done', function() { //console.log('%d dirs, %d files, %d bytes', this.dirs, this.files, this.bytes); done(); }) .walk(); }; if (callback) callback(); }); }; Builder.prototype.scanSources = function(callback) { var self = this; var content = ''; var tmplist = []; var filepath = path.join(self.workingPath, self.filename); co(function *() { try { content = yield function(done) { fs.readFile(filepath, done); }; } catch(e) {} var lines = S(content.toString()).lines(); lines.forEach(function(line, index, array) { if(S(line).contains('#include')){ if(!S(line).contains('stdio')){ tmplist.push(S(line).between('#include <', '.h>').s); } } else if (S(line).contains('Serial.begin')) { tmplist.push('SoftwareSerial'); } else{ } }); if (callback) callback(null, tmplist); }); }; Builder.prototype.parseOps = function(liblist, callback) { var self = this; var prelinkOps = []; var current = ''; function checklib(lib) { return S(lib).contains(current+'.o'); } function checkutilitylib(lib, index, array) { if (S(lib).contains(current+'/utility/')){ prelinkOps.push( path.join(self.lib.Path.external, lib) ); } return true; } liblist.forEach(function(lib, index, array) { current = lib; var externaltmp = self.lib.libs.external.findIndex(checklib); var thirdpartytmp = self.lib.libs.thirdparty.findIndex(checklib); if(externaltmp != -1){ prelinkOps.push( path.join(self.lib.Path.external, self.lib.libs.external[externaltmp]) ); if ( lib == 'Ethernet' || lib == 'SD' || lib == 'TFT' || lib == 'Wire' ){ self.lib.libs.external.every(checkutilitylib); } } else if(thirdpartytmp != -1){ prelinkOps.push(path.join(self.lib.Path.thirdparty, self.lib.libs.thirdparty[thirdpartytmp])); } else{ console.log('[parseOps]none.'); } }); if (callback) callback(null, prelinkOps); }; Builder.prototype.parseDeps = function(liblist, prelinkOps, callback) { var self = this; var linkOps = prelinkOps; liblist.forEach(function(lib, index, array) { for (var prop in dependencies) { if (prop == lib){ var checkDeps = dependencies[prop]; if(checkDeps.external){ checkDeps.external.forEach(function(dep, index, array) { linkOps.push( path.join(self.lib.Path.external, dep) ); }); } if(checkDeps.thirdparty){ checkDeps.thirdparty.forEach(function(dep, index, array) { linkOps.push( path.join(self.lib.Path.thirdparty, dep) ); }); } } } }); if (callback) callback(null, linkOps); }; Builder.prototype.generateSources = function(callback) { var self = this; co(function *() { // Generating all source files for (var filename in self.sources) { var filepath = path.join(self.workingPath, self.filename); // Need to be fixed. yield function(done) { fse.outputFile(filepath, self.sources[filename], done); }; } if (callback) callback(); }); }; Builder.prototype.generateCompileOps = function(linkOps, callback) { var self = this; var Ops = { CXX: [], AR: [], CC: [], OBJCOPY_eep: [], OBJCOPY_hex: [] } var preCXX = [path.join(self.avrconfig.Path, self.avrconfig.Tools.CXX_NAME), '-x c++', '-include /usr/share/arduino/hardware/arduino/cores/arduino/Arduino.h', '-MMD -c -mmcu=atmega328p', '-DF_CPU=16000000L', '-DARDUINO=105', '-D__PROG_TYPES_COMPAT__', '-I. -I/usr/share/arduino/hardware/arduino/cores/arduino -I/usr/share/arduino/hardware/arduino/variants/standard' ]; self.lib.dirs.external.forEach(function(lib, index, array) { preCXX.push('-I/usr/share/arduino/libraries/' + lib); }); self.lib.dirs.thirdparty.forEach(function(lib, index, array) { preCXX.push('-I' + path.join(__dirname, '../libraries/thirdparty', lib)); }); preCXX.push('-Wall -ffunction-sections -fdata-sections -Os -fno-exceptions ', path.join(self.workingPath, self.filename), '-o ' + path.join(self.workingPath, self.id + '.o')); Ops.CXX = preCXX.join(' '); var preAR = [path.join(self.avrconfig.Path, self.avrconfig.Tools.AR_NAME), 'rcs', path.join(self.workingPath, 'libcore.a') ]; self.lib.libs.core.forEach(function(lib, index, array) { preAR.push(path.join(__dirname, '../libraries/core', lib)); }); linkOps.forEach(function(lib, index, array) { preAR.push(lib); }); Ops.AR = preAR.join(' '); Ops.CC = [path.join(self.avrconfig.Path, self.avrconfig.Tools.CC_NAME), '-mmcu=atmega328p', '-Wl,--gc-sections -Os', '-o ' + path.join(self.workingPath, self.id + '.elf'), path.join(self.workingPath, self.id + '.o'), path.join(self.workingPath, 'libcore.a'), '-lc -lm' ].join(' '); Ops.OBJCOPY_eep = [path.join(self.avrconfig.Path, self.avrconfig.Tools.OBJCOPY_NAME), '-j .eeprom --set-section-flags=.eeprom="alloc,load"', '--change-section-lma .eeprom=0 -O ihex', path.join(self.workingPath, self.id + '.elf'), path.join(self.workingPath, self.id + '.eep') ].join(' '); Ops.OBJCOPY_hex = [path.join(self.avrconfig.Path, self.avrconfig.Tools.OBJCOPY_NAME), '-O ihex -R .eeprom', path.join(self.workingPath, self.id + '.elf'), path.join(self.workingPath, self.id + '.hex') ].join(' '); if (callback) callback(null, Ops); }; Builder.prototype.compile = function(CompileOps, callback) { var self = this; co(function *() { try { yield function(done) { child_process.exec(CompileOps.CXX, done); }; } catch(e) {} try { yield function(done) { child_process.exec(CompileOps.AR, done); }; } catch(e) {} try { yield function(done) { child_process.exec(CompileOps.CC, done); }; } catch(e) {} try { yield function(done) { child_process.exec(CompileOps.OBJCOPY_eep, done); }; } catch(e) {} child_process.exec(CompileOps.OBJCOPY_hex, function(err, cmdout, cmderr) { if (err) { callback(err); } else if (callback) { return callback(null, path.join(self.workingPath, self.id + '.hex')); } }); }); };