429 lines
15 KiB
JavaScript
429 lines
15 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/*
|
|
*
|
|
* Copyright 2014 Canonical Ltd.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing,
|
|
* software distributed under the License is distributed on an
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
* KIND, either express or implied. See the License for the
|
|
* specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
*/
|
|
var assert = require('assert');
|
|
var colors = require('colors');
|
|
var fs = require('fs');
|
|
var os = require('os');
|
|
var Q = require('q');
|
|
var path = require('path');
|
|
var shell = require('shelljs');
|
|
|
|
var ConfigParser = require('./config_parser');
|
|
var Constants = require('./constants');
|
|
var Utils = require('./utils');
|
|
var Manifest = require('./manifest');
|
|
var logger = require('./logger');
|
|
|
|
var PLATFORMS = Constants.PLATFORM_TYPES;
|
|
|
|
module.exports.build = function(rootDir, target, nobuild, architecture, framework, debug) {
|
|
var ubuntuDir = path.join(rootDir, 'platforms', 'ubuntu');
|
|
var campoDir = path.join(ubuntuDir, 'build');
|
|
|
|
if (!architecture)
|
|
architecture = Constants.DEFAULT_ARCH;
|
|
if (!framework)
|
|
framework = Constants.DEFAULT_FRAMEWORK;
|
|
assert.ok(fs.existsSync(ubuntuDir));
|
|
assert.ok(fs.existsSync(campoDir));
|
|
|
|
if (target === PLATFORMS.PHONE)
|
|
return buildClickPackage(campoDir, ubuntuDir, nobuild, architecture, framework, debug);
|
|
if (target === PLATFORMS.DESKTOP)
|
|
return buildNative(campoDir, ubuntuDir, nobuild, debug);
|
|
if (target === PLATFORMS.ALL) {
|
|
return buildClickPackage(campoDir, ubuntuDir, nobuild, architecture, framework, debug).then(function () {
|
|
return buildNative(campoDir, ubuntuDir, nobuild);
|
|
});
|
|
}
|
|
};
|
|
|
|
function buildClickPackage(campoDir, ubuntuDir, nobuild, architecture, framework, debug) {
|
|
logger.info('Building Phone Application...');
|
|
|
|
assert.ok(architecture && architecture.match(/^[a-z0-9_]+$/));
|
|
|
|
Manifest.generate(path.join(ubuntuDir, 'config.xml'), ubuntuDir);
|
|
|
|
var archDir = path.join(ubuntuDir, framework, architecture);
|
|
var prefixDir = path.join(archDir, 'prefix');
|
|
|
|
if (!fs.existsSync(archDir))
|
|
shell.mkdir('-p', archDir);
|
|
|
|
if (nobuild && fs.existsSync(path.join(prefixDir, 'cordova-ubuntu'))) {
|
|
return Q();
|
|
}
|
|
|
|
return checkChrootEnv(ubuntuDir, architecture, framework).then(function() {
|
|
shell.rm('-rf', path.join(archDir, 'build'));
|
|
|
|
shell.rm('-rf', prefixDir);
|
|
shell.mkdir(path.join(archDir, 'build'));
|
|
shell.mkdir(prefixDir);
|
|
|
|
Utils.pushd(path.join(archDir, 'build'));
|
|
|
|
var buildType = '"Debug"';
|
|
if (!debug)
|
|
buildType = '"Release"';
|
|
|
|
var cmakeCmd = 'click chroot -a ' + architecture + ' -f ' + framework + ' run cmake ' + campoDir
|
|
+ ' -DCMAKE_INSTALL_PREFIX="' + prefixDir + '"' + ' -DCMAKE_BUILD_TYPE=' + buildType +' -DUBUNTU_TOUCH="1"';
|
|
|
|
var deps = additionalBuildDependencies(ubuntuDir).join(' ').replace(/ARCH/g, architecture);
|
|
if (deps.length)
|
|
cmakeCmd += ' -DADDITIONAL_DEPENDECIES="' + deps + '"';
|
|
return Utils.execAsync(cmakeCmd);
|
|
}).then(function () {
|
|
if (architecture != "i386" && framework != "ubuntu-sdk-15.04")
|
|
Utils.execSync('find . -name AutomocInfo.cmake | xargs sed -i \'s;AM_QT_MOC_EXECUTABLE .*;AM_QT_MOC_EXECUTABLE "/usr/lib/\'$(dpkg-architecture -qDEB_BUILD_MULTIARCH)\'/qt5/bin/moc");\'');
|
|
return Utils.execAsync('click chroot -a ' + architecture + ' -f ' + framework + ' run make -j ' + cpuCount());
|
|
}).then(function () {
|
|
return Utils.execAsync('click chroot -a ' + architecture + ' -f ' + framework + ' run make install');
|
|
}).then(function () {
|
|
Utils.cp(path.join(ubuntuDir, 'www', '*'), path.join(prefixDir, 'www'));
|
|
Utils.cp(path.join(ubuntuDir, 'qml', '*'), path.join(prefixDir, 'qml'));
|
|
Utils.cp(path.join(ubuntuDir, 'cordova.desktop'), prefixDir);
|
|
Utils.cp(path.join(ubuntuDir, 'config.xml'), prefixDir);
|
|
|
|
var content = JSON.parse(fs.readFileSync(path.join(ubuntuDir, 'manifest.json'), {encoding: "utf8"}));
|
|
content.architecture = architecture;
|
|
content.framework = framework;
|
|
fs.writeFileSync(path.join(prefixDir, 'manifest.json'), JSON.stringify(content));
|
|
|
|
content = JSON.parse(fs.readFileSync(path.join(ubuntuDir, 'apparmor.json'), {encoding: "utf8"}));
|
|
if (framework === "ubuntu-sdk-14.10") {
|
|
content.policy_version = 1.2;
|
|
} else {
|
|
content.policy_version = 1.3;
|
|
}
|
|
content.policy_groups.push('webview');
|
|
fs.writeFileSync(path.join(prefixDir, 'apparmor.json'), JSON.stringify(content));
|
|
|
|
Utils.pushd(prefixDir);
|
|
|
|
return Utils.execAsync('click build .');
|
|
}).then(function () {
|
|
checkClickPackage(prefixDir);
|
|
Utils.popd();
|
|
|
|
Utils.popd();
|
|
});
|
|
}
|
|
|
|
function buildNative(campoDir, ubuntuDir, nobuild, debug) {
|
|
logger.info('Building...');
|
|
|
|
var nativeDir = path.join(ubuntuDir, 'native');
|
|
var prefixDir = path.join(nativeDir, 'prefix');
|
|
|
|
if (nobuild && fs.existsSync(path.join(prefixDir, 'cordova-ubuntu'))) {
|
|
return Q();
|
|
}
|
|
|
|
return checkEnv(ubuntuDir).then(function() {
|
|
Manifest.generate(path.join(ubuntuDir, 'config.xml'), ubuntuDir);
|
|
|
|
shell.rm('-rf', prefixDir);
|
|
|
|
shell.mkdir('-p', path.join(nativeDir, 'build'));
|
|
shell.mkdir(prefixDir);
|
|
|
|
Utils.pushd(path.join(nativeDir, 'build'));
|
|
|
|
var buildType = '"Debug"';
|
|
if (!debug)
|
|
buildType = '"Release"';
|
|
|
|
var cmakeCmd = 'cmake ' + campoDir + ' -DCMAKE_INSTALL_PREFIX="' + prefixDir + '"'
|
|
+ ' -DCMAKE_BUILD_TYPE=' + buildType;
|
|
|
|
var deps = additionalBuildDependencies(ubuntuDir).join(' ').replace(/ARCH/g, '');
|
|
if (deps.length)
|
|
cmakeCmd += ' -DADDITIONAL_DEPENDECIES="' + deps + '"';
|
|
|
|
var debDir;
|
|
return Utils.execAsync(cmakeCmd).then(function () {
|
|
return Utils.execAsync('make -j ' + cpuCount() + '; make install');
|
|
}).then(function () {
|
|
Utils.cp(path.join(ubuntuDir, 'config.xml'), prefixDir);
|
|
Utils.cp(path.join(ubuntuDir, 'www', '*'), path.join(prefixDir, 'www'));
|
|
Utils.cp(path.join(ubuntuDir, 'qml', '*'), path.join(prefixDir, 'qml'));
|
|
|
|
Utils.popd();
|
|
|
|
var manifest = JSON.parse(fs.readFileSync(path.join(ubuntuDir, 'manifest.json'), {encoding: "utf8"}));
|
|
|
|
assert(manifest.name.length);
|
|
assert(manifest.name.indexOf(' ') == -1);
|
|
|
|
debDir = path.join(nativeDir, manifest.name);
|
|
|
|
shell.rm('-rf', debDir);
|
|
|
|
shell.mkdir('-p', path.join(debDir, 'debian'));
|
|
Utils.cp(path.join(campoDir, '*'), debDir);
|
|
|
|
Utils.cp(path.join(ubuntuDir, 'config.xml'), path.join(debDir, 'debian'));
|
|
Utils.cp(path.join(ubuntuDir, 'www', '*'), path.join(debDir, 'www'));
|
|
Utils.cp(path.join(ubuntuDir, 'qml', '*'), path.join(debDir, 'qml'));
|
|
|
|
var destDir = path.join('/opt', manifest.name);
|
|
|
|
var icon = fs.readFileSync(path.join(ubuntuDir, 'cordova.desktop'), {encoding: "utf8"}).match(/^Icon=(.+)$/m);
|
|
icon = icon ? '/opt/' + manifest.name + '/' + icon[1] : 'qmlscene';
|
|
|
|
var maintainerEmail = manifest.maintainer.match('<.+>$');
|
|
maintainerEmail = maintainerEmail?maintainerEmail[0]:'';
|
|
var maintainerName = manifest.maintainer.replace(maintainerEmail, '').trim();
|
|
maintainerEmail = maintainerEmail.replace('<', '').replace('>', '');
|
|
|
|
var props = { PACKAGE_NAME: manifest.name,
|
|
PACKAGE_TITLE: manifest.title,
|
|
PACKAGE_VERSION: manifest.version,
|
|
MAINTAINER_NAME: maintainerName,
|
|
MAINTAINER_EMAIL: maintainerEmail,
|
|
PACKAGE_DESCRIPTION: manifest.description,
|
|
PACKAGE_ICON: icon };
|
|
var templateDir = path.join(campoDir, 'bin', 'templates', 'project', 'misc');
|
|
var templates = [{source: path.join(templateDir, 'changelog'), dest: path.join(debDir, 'debian', 'changelog')},
|
|
{source: path.join(templateDir, 'compat'), dest: path.join(debDir, 'debian', 'compat')},
|
|
{source: path.join(templateDir, 'control'), dest: path.join(debDir, 'debian', 'control')},
|
|
{source: path.join(templateDir, 'rules'), dest: path.join(debDir, 'debian', 'rules')},
|
|
{source: path.join(templateDir, 'cordova.desktop'), dest: path.join(debDir, 'debian', 'cordova.desktop')},
|
|
{source: path.join(templateDir, 'install'), dest: path.join(debDir, 'debian', manifest.name + '.install')}];
|
|
for (var i = 0; i < templates.length; i++) {
|
|
fillTemplate(templates[i].source, templates[i].dest, props);
|
|
}
|
|
logger.warn('Note: to build a debian package, run: ');
|
|
logger.warn(' cd ' + debDir + '; ' + 'debuild');
|
|
});
|
|
});
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* Generates a file from a template source, injecting the values contained in the
|
|
* provided js object.
|
|
*/
|
|
function fillTemplate(source, dest, obj) {
|
|
var content = fs.readFileSync(source, {encoding: "utf8"});
|
|
for (var prop in obj) {
|
|
content = content.replace(new RegExp('{' + prop + '}', 'g'), obj[prop]);
|
|
}
|
|
|
|
fs.writeFileSync(dest, content);
|
|
}
|
|
|
|
function additionalBuildDependencies(ubuntuDir) {
|
|
var config = new ConfigParser(path.join(ubuntuDir, 'config.xml'));
|
|
|
|
var pkgConfig = [];
|
|
config.etree.getroot().findall('./feature/deps/pkgConfig').forEach(function (element) {
|
|
var list = JSON.parse(element.text);
|
|
pkgConfig = pkgConfig.concat(list);
|
|
});
|
|
|
|
return pkgConfig;
|
|
}
|
|
|
|
function checkClickPackage(prefixDir) {
|
|
Utils.pushd(prefixDir);
|
|
|
|
if (fs.existsSync('/usr/bin/click-run-checks')) {
|
|
var cmd = '/usr/bin/click-run-checks *.click';
|
|
logger.debug(cmd);
|
|
var output = shell.exec(cmd, { silent: true }).output;
|
|
var json = '[', b = 0;
|
|
for (var i = 0; i < output.length; i++) {
|
|
if (output[i] == '{') {
|
|
if (json.length > 1 && !b)
|
|
json += ', ';
|
|
b++;
|
|
} else if (output[i] == '}') {
|
|
b--;
|
|
if (b == 0)
|
|
json += '} ';
|
|
}
|
|
if (b > 0) {
|
|
json += output[i];
|
|
}
|
|
}
|
|
json += ']';
|
|
var out = JSON.parse(json);
|
|
for (var i = 0; i < out.length; i++) {
|
|
if (out[i].warn) {
|
|
for (var m in out[i].warn) {
|
|
if (out[i].warn[m].text) {
|
|
logger.warn(out[i].warn[m].text);
|
|
if (out[i].warn[m].link)
|
|
logger.warn(out[i].warn[m].link);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (var i = 0; i < out.length; i++) {
|
|
if (out[i].error) {
|
|
for (var m in out[i].error) {
|
|
if (out[i].error[m].text) {
|
|
logger.warn(out[i].error[m].text);
|
|
if (out[i].error[m].link)
|
|
logger.warn(out[i].error[m].link);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Utils.popd();
|
|
}
|
|
|
|
function cpuCount() {
|
|
return os.cpus().length;
|
|
}
|
|
|
|
function checkEnv(ubuntuDir) {
|
|
var deps = [
|
|
'click',
|
|
'cmake',
|
|
'libicu-dev',
|
|
'pkg-config',
|
|
'devscripts',
|
|
'qtbase5-dev',
|
|
'qtchooser',
|
|
'qtdeclarative5-dev',
|
|
'qtfeedback5-dev',
|
|
'qtlocation5-dev',
|
|
'qtmultimedia5-dev',
|
|
'qtpim5-dev',
|
|
'libqt5sensors5-dev',
|
|
'qtsystems5-dev'
|
|
];
|
|
|
|
deps = deps.concat(additionalDependencies(ubuntuDir));
|
|
deps = deps.join(' ');
|
|
deps = deps.replace(/:ARCH/g, '');
|
|
|
|
if (!deps.length)
|
|
return;
|
|
|
|
var cmd = "dpkg-query -Wf'${db:Status-abbrev}' " + deps;
|
|
var installCmd = "sudo apt-get install " + deps;
|
|
|
|
var res = shell.exec(cmd);
|
|
|
|
if (res.code !== 0 || res.output.match(/[^i ]/)) {
|
|
logger.error("Error: missing packages " + deps);
|
|
|
|
var deferred = Q.defer();
|
|
|
|
var readline = require('readline');
|
|
var rl = readline.createInterface(process.stdin, process.stdout);
|
|
rl.setPrompt('Install missing packages? (Yn)> ');
|
|
rl.prompt();
|
|
|
|
rl.on('line', function(line) {
|
|
rl.close();
|
|
if (line !== 'Y' && line !== 'y') {
|
|
deferred.reject(new Error());
|
|
return;
|
|
}
|
|
|
|
Utils.execAsync(installCmd).then(function() {
|
|
deferred.resolve();
|
|
}).catch(function (error) {
|
|
deferred.reject(new Error());
|
|
}).done();
|
|
});
|
|
|
|
return deferred.promise;
|
|
}
|
|
return Q();
|
|
}
|
|
|
|
function checkChrootEnv(ubuntuDir, architecture, framework) {
|
|
var deps = "cmake libicu-dev:ARCH pkg-config qtbase5-dev:ARCH qtchooser qtdeclarative5-dev:ARCH qtfeedback5-dev:ARCH qtlocation5-dev:ARCH qtmultimedia5-dev:ARCH qtpim5-dev:ARCH libqt5sensors5-dev:ARCH qtsystems5-dev:ARCH ";
|
|
deps += additionalDependencies(ubuntuDir).join(' ');
|
|
deps = deps.replace(/ARCH/g, architecture);
|
|
|
|
var chrootCreateCmd = "sudo click chroot -a " + architecture + " -f " + framework + " create";
|
|
var chrootInstallCmd = "sudo click chroot -a " + architecture + " -f " + framework + " install " + deps;
|
|
|
|
var cmd = "click chroot -a " + architecture + " -f " + framework + " run echo 1";
|
|
var res = shell.exec(cmd);
|
|
|
|
if (res.code !== 0) {
|
|
logger.error("\nError: missing " + architecture + " chroot");
|
|
logger.error("run:\n" + chrootCreateCmd);
|
|
|
|
var deferred = Q.defer();
|
|
deferred.reject(new Error());
|
|
|
|
return deferred.promise;
|
|
}
|
|
|
|
cmd = "click chroot -a " + architecture + " -f " + framework + " run dpkg-query -Wf'${db:Status-abbrev}' " + deps;
|
|
|
|
res = shell.exec(cmd);
|
|
if (res.code !== 0 || res.output.match(/[^i ]/)) {
|
|
logger.error("\nError: missing dependency inside " + architecture + " chroot");
|
|
logger.error("run:\n" + chrootInstallCmd);
|
|
|
|
var deferred = Q.defer();
|
|
|
|
var readline = require('readline');
|
|
var rl = readline.createInterface(process.stdin, process.stdout);
|
|
rl.setPrompt('Do you want install it now? (Yn)> ');
|
|
rl.prompt();
|
|
|
|
rl.on('line', function(line) {
|
|
rl.close();
|
|
if (line !== 'Y' && line !== 'y') {
|
|
deferred.reject(new Error());
|
|
return;
|
|
}
|
|
|
|
Utils.execAsync(chrootInstallCmd).then(function() {
|
|
deferred.resolve();
|
|
}).catch(function (error) {
|
|
deferred.reject(new Error());
|
|
}).done();
|
|
});
|
|
|
|
return deferred.promise;
|
|
}
|
|
return Q();
|
|
}
|
|
|
|
function additionalDependencies(ubuntuDir) {
|
|
var config = new ConfigParser(path.join(ubuntuDir, 'config.xml'));
|
|
|
|
var deb = [];
|
|
config.etree.getroot().findall('./feature/deps/deb').forEach(function (element) {
|
|
var list = JSON.parse(element.text);
|
|
deb = deb.concat(list);
|
|
});
|
|
|
|
return deb;
|
|
}
|