Skip to content

Instantly share code, notes, and snippets.

@begeeben
Last active August 29, 2015 14:26
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save begeeben/358e87242fabe1112b16 to your computer and use it in GitHub Desktop.
Save begeeben/358e87242fabe1112b16 to your computer and use it in GitHub Desktop.
A handly script to make and run Firefox OS Gaia. Watch for file changes and make on the fly.
// Usage: node make_onsave.js b2g tv browser
// node make_onsave.js nightly phone settings
// node make_onsave.js simulator tv tv-deck
// node make_onsave.js b2g tv
'use strict';
var fs = require('fs');
var path = require('path');
var exec = require('child_process').exec;
// Firefox runtimes
// Change these to your local runtime paths
var firefoxRuntimes = {
firefox: '/Applications/FirefoxNightly.app/Contents/MacOS/firefox-bin',
developer: '/Applications/FirefoxDeveloperEdition.app/Contents/MacOS/firefox-bin',
nightly: '/Applications/FirefoxNightly.app/Contents/MacOS/firefox-bin',
mulet: '/Applications/mulet/FirefoxNightly.app/Contents/MacOS/firefox-bin',
b2g: '/Applications/B2G.app/Contents/MacOS/b2g-bin',
simulator: '/Users/asail/mozilla/b2g-tv/B2G.app/Contents/MacOS/b2g-bin'
};
var runtimeTypes = ['firefox', 'developer', 'b2g', 'mulet', 'nightly', 'simulator'];
var deviceTypes = ['phone', 'tv'];
var apps = fs.readdirSync(path.join(__dirname, 'apps')).filter(function(file) {
return fs.statSync(path.join(path.join(__dirname, 'apps'), file)).isDirectory();
});
var tvApps = fs.readdirSync(path.join(__dirname, 'tv_apps')).filter(function(file) {
return fs.statSync(path.join(path.join(__dirname, 'tv_apps'), file)).isDirectory();
});
// Default runtime and device type
var runtime = 'b2g';
var deviceType = 'tv';
var app;
process.argv.forEach(function(val, index, array) {
if (runtimeTypes.indexOf(val) > -1) {
runtime = val;
} else if (deviceTypes.indexOf(val) > -1) {
deviceType = val;
} else if (apps.indexOf(val) > -1 || tvApps.indexOf(val) > -1) {
app = val;
}
});
var makeScript = '';
if (app) {
makeScript = 'APP=' + app + ' ';
}
if (deviceType === 'tv') {
makeScript += 'GAIA_DEVICE_TYPE=' + deviceType + ' DEVICE_DEBUG=1 ';
} else {
makeScript += 'DEVICE_DEBUG=1 ';
}
if (runtime === 'nightly') {
makeScript += 'DESKTOP=0 ';
}
makeScript += 'make';
var runScript = firefoxRuntimes[runtime] + ' -profile ';
if (deviceType === 'tv') {
runScript += '${PWD}/profile -start-debugger-server 6000 -screen 1920x1080';
// runScript += ' -screen 1920x1080 app://browser.gaiamobile.org';
} else {
runScript += path.join(__dirname, 'profile');
// runScript += path.join(__dirname, 'profile-debug') + ' --no-remote app://sms.gaiamobile.org';
}
function makeGaia (callback) {
console.log('makeGaia: ' + makeScript);
var make = exec(makeScript);
make.stdout.on('data', function (data) {
console.log(data);
});
make.stderr.on('data', function (data) {
console.log(data);
});
make.on('close', function (code) {
console.log(code);
if (typeof callback === 'function') {
callback();
}
});
}
function startRuntime (callback) {
console.log('startRuntime: ' + runScript);
var run = exec(runScript);
run.stdout.on('data', function (data) {
console.log(data);
if (data.trim() === 'Opening debugger server on 6000' && typeof callback === 'function') {
// XXX: Run the callback later in case the debugger server hasn't been ready yet.
setTimeout(callback, 6000);
}
});
run.stderr.on('data', function (data) {
console.log(data);
});
run.on('close', function (code) {
console.log(code);
});
}
function watch () {
function onfilechange (event, filename) {
console.log('file changed in source directory');
if (filename && filename.indexOf('.') !== 0) {
console.log('filename provided: ' + filename);
makeGaia(connectToRuntime);
} else {
console.log('filename not provided');
}
}
if (deviceType === 'phone') {
fs.watch(path.join(__dirname, 'apps'), { persistent: true, recursive: true }, onfilechange);
} else {
fs.watch(path.join(__dirname, 'tv_apps'), { persistent: true, recursive: true }, onfilechange);
}
fs.watch(path.join(__dirname, 'shared'), { persistent: true, recursive: true }, onfilechange);
}
makeGaia(function () {
startRuntime(function() {
watch();
// connectToRuntime();
});
});
// Connect to runtime with remote debugging protocol
var net = require('net');
var client;
var webappsActor;
var appActor;
// borrowed from https://github.com/harthur/firefox-client/blob/master/lib/client.js#L117
function sendMessage(message) {
if (!message.to) {
throw new Error("No actor specified in request");
}
if (!client) {
throw new Error("Not connected, connect() before sending requests");
}
var str = JSON.stringify(message);
// message is preceded by byteLength(message):
str = (new Buffer(str).length) + ":" + str;
console.log('sendMessage: ' + str);
client.write(str);
}
function connectToRuntime() {
console.log('connectToRuntime');
// borrowed from https://github.com/harthur/firefox-client/blob/a9caacdf8f138d07195402fe1b7183c7aeb964ec/lib/client.js#L194
function readMessage() {
var sep = str.toString().indexOf(':');
if (sep < 0) {
return false;
}
// beginning of a message is preceded by byteLength(message) + ":"
var count = parseInt(str.slice(0, sep));
if (str.length - (sep + 1) < count) {
console.log('no complete response yet'.grey);
return false;
}
str = str.slice(sep + 1);
var packet = str.slice(0, count);
str = str.slice(count);
var message;
try {
message = JSON.parse(packet.toString());
} catch(e) {
throw new Error('Couldn\'t parse packet from server as JSON ' + e +
', message:\n' + packet);
}
handleMessage(message);
return true;
}
function handleMessage(message) {
console.log('handleMessage: ', message);
if (message.webappsActor) {
webappsActor = message.webappsActor;
sendMessage({
to: webappsActor,
type: 'getAppActor',
manifestURL: 'app://' + app + '.gaiamobile.org/manifest.webapp'
});
}
if (message.actor) {
if (message.actor.url.indexOf(app) > -1) {
appActor = message.actor.actor;
reload();
}
}
}
var str = new Buffer('');
client = net.connect({port: 6000},
function() { //'connect' listener
console.log('connected to server!');
sendMessage({to: 'root', type: 'listTabs'});
});
client.on('data', function(data) {
// console.log(data.toString());
// console.log('\n');
str = Buffer.concat([str, data]);
while(readMessage()) {}
});
client.on('error', function(error) {
console.log(error.toString());
});
client.on('end', function() {
console.log('disconnected from server');
});
}
function reload() {
console.log('reload');
sendMessage({
to: appActor,
type: 'reload',
options: {
'force': true
},
manifestURL: 'app://' + app + '.gaiamobile.org/manifest.webapp'
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment