2017-12-11 22:37:56 +00:00
|
|
|
Promise = require('bluebird')
|
|
|
|
|
2019-05-01 17:12:36 +00:00
|
|
|
exports.QEMU_VERSION = QEMU_VERSION = 'v4.0.0-balena'
|
2017-12-11 22:37:56 +00:00
|
|
|
exports.QEMU_BIN_NAME = QEMU_BIN_NAME = 'qemu-execve'
|
|
|
|
|
2018-10-18 17:54:26 +00:00
|
|
|
exports.installQemuIfNeeded = Promise.method (emulated, logger, arch) ->
|
2017-12-11 22:37:56 +00:00
|
|
|
return false if not (emulated and platformNeedsQemu())
|
|
|
|
|
2018-11-03 03:55:24 +00:00
|
|
|
hasQemu(arch)
|
2017-12-11 22:37:56 +00:00
|
|
|
.then (present) ->
|
|
|
|
if !present
|
2018-10-18 17:54:26 +00:00
|
|
|
logger.logInfo("Installing qemu for #{arch} emulation...")
|
|
|
|
installQemu(arch)
|
2017-12-11 22:37:56 +00:00
|
|
|
.return(true)
|
|
|
|
|
|
|
|
exports.qemuPathInContext = (context) ->
|
|
|
|
path = require('path')
|
2018-10-19 14:38:50 +00:00
|
|
|
binDir = path.join(context, '.balena')
|
2017-12-11 22:37:56 +00:00
|
|
|
binPath = path.join(binDir, QEMU_BIN_NAME)
|
|
|
|
path.relative(context, binPath)
|
|
|
|
|
2018-11-03 03:55:24 +00:00
|
|
|
exports.copyQemu = (context, arch) ->
|
2017-12-11 22:37:56 +00:00
|
|
|
path = require('path')
|
|
|
|
fs = require('mz/fs')
|
|
|
|
# Create a hidden directory in the build context, containing qemu
|
2018-10-19 14:38:50 +00:00
|
|
|
binDir = path.join(context, '.balena')
|
2017-12-11 22:37:56 +00:00
|
|
|
binPath = path.join(binDir, QEMU_BIN_NAME)
|
|
|
|
|
|
|
|
Promise.resolve(fs.mkdir(binDir))
|
|
|
|
.catch(code: 'EEXIST', ->)
|
|
|
|
.then ->
|
2018-11-03 03:55:24 +00:00
|
|
|
getQemuPath(arch)
|
2017-12-11 22:37:56 +00:00
|
|
|
.then (qemu) ->
|
|
|
|
new Promise (resolve, reject) ->
|
|
|
|
read = fs.createReadStream(qemu)
|
|
|
|
write = fs.createWriteStream(binPath)
|
2018-11-12 15:20:51 +00:00
|
|
|
|
2017-12-11 22:37:56 +00:00
|
|
|
read
|
2018-11-12 15:20:51 +00:00
|
|
|
.on('error', reject)
|
2017-12-11 22:37:56 +00:00
|
|
|
.pipe(write)
|
|
|
|
.on('error', reject)
|
|
|
|
.on('finish', resolve)
|
|
|
|
.then ->
|
|
|
|
fs.chmod(binPath, '755')
|
|
|
|
.then ->
|
|
|
|
path.relative(context, binPath)
|
|
|
|
|
2018-11-03 03:55:24 +00:00
|
|
|
hasQemu = (arch) ->
|
2017-12-11 22:37:56 +00:00
|
|
|
fs = require('mz/fs')
|
|
|
|
|
2018-11-03 03:55:24 +00:00
|
|
|
getQemuPath(arch)
|
2017-12-11 22:37:56 +00:00
|
|
|
.then(fs.stat)
|
|
|
|
.return(true)
|
|
|
|
.catchReturn(false)
|
|
|
|
|
2018-11-03 03:55:24 +00:00
|
|
|
getQemuPath = (arch) ->
|
2018-10-19 14:38:50 +00:00
|
|
|
balena = require('balena-sdk').fromSharedOptions()
|
2017-12-11 22:37:56 +00:00
|
|
|
path = require('path')
|
|
|
|
fs = require('mz/fs')
|
|
|
|
|
2018-10-19 14:38:50 +00:00
|
|
|
balena.settings.get('binDirectory')
|
2017-12-11 22:37:56 +00:00
|
|
|
.then (binDir) ->
|
|
|
|
Promise.resolve(fs.mkdir(binDir))
|
|
|
|
.catch(code: 'EEXIST', ->)
|
|
|
|
.then ->
|
2019-05-01 17:12:36 +00:00
|
|
|
path.join(binDir, "#{QEMU_BIN_NAME}-#{arch}-#{QEMU_VERSION}")
|
2017-12-11 22:37:56 +00:00
|
|
|
|
|
|
|
platformNeedsQemu = ->
|
|
|
|
os = require('os')
|
|
|
|
os.platform() == 'linux'
|
|
|
|
|
2018-10-18 17:54:26 +00:00
|
|
|
installQemu = (arch) ->
|
2017-12-11 22:37:56 +00:00
|
|
|
request = require('request')
|
|
|
|
fs = require('fs')
|
|
|
|
zlib = require('zlib')
|
2018-11-03 03:34:29 +00:00
|
|
|
tar = require('tar-stream')
|
2017-12-11 22:37:56 +00:00
|
|
|
|
2018-11-03 03:55:24 +00:00
|
|
|
getQemuPath(arch)
|
2017-12-11 22:37:56 +00:00
|
|
|
.then (qemuPath) ->
|
|
|
|
new Promise (resolve, reject) ->
|
2018-11-03 03:34:29 +00:00
|
|
|
installStream = fs.createWriteStream(qemuPath)
|
2018-11-12 15:20:51 +00:00
|
|
|
|
|
|
|
qemuArch = balenaArchToQemuArch(arch)
|
2019-05-01 17:12:36 +00:00
|
|
|
downloadArchiveName = "qemu-#{QEMU_VERSION.replace(/^v/, '')}-#{qemuArch}.tar.gz"
|
2018-11-03 03:34:29 +00:00
|
|
|
qemuUrl = "https://github.com/balena-io/qemu/releases/download/#{QEMU_VERSION}/#{downloadArchiveName}"
|
2018-11-12 15:20:51 +00:00
|
|
|
|
2018-11-03 03:34:29 +00:00
|
|
|
extract = tar.extract()
|
2018-11-05 17:37:52 +00:00
|
|
|
extract.on 'entry', (header, stream, next) ->
|
2018-11-03 03:34:29 +00:00
|
|
|
stream.on('end', next)
|
2018-11-12 15:20:51 +00:00
|
|
|
if header.name.includes("qemu-#{qemuArch}-static")
|
2018-11-03 03:34:29 +00:00
|
|
|
stream.pipe(installStream)
|
|
|
|
else
|
|
|
|
stream.resume()
|
2018-11-12 15:20:51 +00:00
|
|
|
|
2018-11-03 03:34:29 +00:00
|
|
|
request(qemuUrl)
|
|
|
|
.on('error', reject)
|
|
|
|
.pipe(zlib.createGunzip())
|
|
|
|
.on('error', reject)
|
|
|
|
.pipe(extract)
|
|
|
|
.on('error', reject)
|
2018-11-05 17:37:52 +00:00
|
|
|
.on 'finish', ->
|
2018-11-03 03:34:29 +00:00
|
|
|
fs.chmodSync(qemuPath, '755')
|
|
|
|
resolve()
|
2018-11-12 15:20:51 +00:00
|
|
|
|
|
|
|
balenaArchToQemuArch = (arch) ->
|
|
|
|
switch arch
|
2018-12-03 13:10:54 +00:00
|
|
|
when 'armv7hf', 'rpi', 'armhf' then 'arm'
|
|
|
|
when 'aarch64' then 'aarch64'
|
|
|
|
else throw new Error("Cannot install emulator for architecture #{arch}")
|