Skip to content

Commit 65c5b67

Browse files
authored
Merge pull request #474 from malept/add-arm-support
Add Linux ARMv7 support
2 parents bc0085d + 0ef914f commit 65c5b67

File tree

11 files changed

+112
-100
lines changed

11 files changed

+112
-100
lines changed

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
### Added
66

77
* `win32metadata` option (#331, #463)
8+
* `linux` platform, `armv7l` arch support (#106, #474)
89

910
### Changed
1011

12+
* `all` now includes the `linux` platform, `armv7l` arch combination
1113
* Default the `platform` option to the host platform (#464)
1214
* Default the `arch` option to the host arch (#36, #464)
1315
* Default the `prune` option to `true` (#235, #472)

common.js

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,17 @@
33
const asar = require('asar')
44
const child = require('child_process')
55
const debug = require('debug')('electron-packager')
6+
const download = require('electron-download')
67
const fs = require('fs-extra')
78
const ignore = require('./ignore')
89
const minimist = require('minimist')
910
const os = require('os')
1011
const path = require('path')
1112
const sanitize = require('sanitize-filename')
13+
const semver = require('semver')
1214
const series = require('run-series')
1315

14-
const archs = ['ia32', 'x64']
16+
const archs = ['ia32', 'x64', 'armv7l']
1517
const platforms = ['darwin', 'linux', 'mas', 'win32']
1618

1719
function parseCLIArgs (argv) {
@@ -81,6 +83,10 @@ function asarApp (appPath, asarOptions, cb) {
8183
})
8284
}
8385

86+
function isPlatformMac (platform) {
87+
return platform === 'darwin' || platform === 'mas'
88+
}
89+
8490
function sanitizeAppName (name) {
8591
return sanitize(name, {replacement: '-'})
8692
}
@@ -116,28 +122,52 @@ function createAsarOpts (opts) {
116122
return asarOptions
117123
}
118124

125+
function createDownloadOpts (opts, platform, arch) {
126+
let downloadOpts = Object.assign({}, opts.download)
127+
128+
subOptionWarning(downloadOpts, 'download', 'platform', platform)
129+
subOptionWarning(downloadOpts, 'download', 'arch', arch)
130+
subOptionWarning(downloadOpts, 'download', 'version', opts.version)
131+
132+
return downloadOpts
133+
}
134+
119135
module.exports = {
120136
archs: archs,
121137
platforms: platforms,
122138

123139
parseCLIArgs: parseCLIArgs,
124140

125-
isPlatformMac: function isPlatformMac (platform) {
126-
return platform === 'darwin' || platform === 'mas'
127-
},
141+
isPlatformMac: isPlatformMac,
128142

129143
subOptionWarning: subOptionWarning,
130144

131145
createAsarOpts: createAsarOpts,
146+
createDownloadOpts: createDownloadOpts,
147+
createDownloadCombos: function createDownloadCombos (opts, selectedPlatforms, selectedArchs, ignoreFunc) {
148+
let combinations = []
149+
for (let arch of selectedArchs) {
150+
for (let platform of selectedPlatforms) {
151+
// Electron does not have 32-bit releases for Mac OS X, so skip that combination
152+
if (isPlatformMac(platform) && arch === 'ia32') continue
153+
// Electron only has armv7l releases for Linux
154+
if (arch === 'armv7l' && platform !== 'linux') continue
155+
if (typeof ignoreFunc === 'function' && ignoreFunc(platform, arch)) continue
156+
combinations.push(createDownloadOpts(opts, platform, arch))
157+
}
158+
}
132159

133-
createDownloadOpts: function createDownloadOpts (opts, platform, arch) {
134-
let downloadOpts = opts.download || {}
135-
136-
subOptionWarning(downloadOpts, 'download', 'platform', platform)
137-
subOptionWarning(downloadOpts, 'download', 'arch', arch)
138-
subOptionWarning(downloadOpts, 'download', 'version', opts.version)
160+
return combinations
161+
},
139162

140-
return downloadOpts
163+
downloadElectronZip: function downloadElectronZip (downloadOpts, cb) {
164+
// armv7l builds have only been backfilled for Electron >= 1.0.0.
165+
// See: https://github.com/electron/electron/pull/6986
166+
if (downloadOpts.arch === 'armv7l' && semver.lt(downloadOpts.version, '1.0.0')) {
167+
downloadOpts.arch = 'arm'
168+
}
169+
debug(`Downloading Electron with options ${JSON.stringify(downloadOpts)}`)
170+
download(downloadOpts, cb)
141171
},
142172

143173
generateFinalBasename: generateFinalBasename,

docs/api.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ The release version of the application. By default the `version` property in the
6767

6868
*String* (default: the arch of the host computer running Node)
6969

70-
Allowed values: `ia32`, `x64`, `all`
70+
Allowed values: `ia32`, `x64`, `armv7l`, `all`
7171

7272
The target system architecture(s) to build for.
7373
Not required if the [`all`](#all) option is set.

index.js

Lines changed: 9 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
const common = require('./common')
44
const debug = require('debug')('electron-packager')
5-
const download = require('electron-download')
65
const extract = require('extract-zip')
76
const fs = require('fs-extra')
87
const getPackageInfo = require('get-package-info')
@@ -12,43 +11,14 @@ const os = require('os')
1211
const path = require('path')
1312
const resolve = require('resolve')
1413
const series = require('run-series')
15-
16-
var supportedArchs = common.archs.reduce(function (result, arch) {
17-
result[arch] = 1
18-
return result
19-
}, {})
20-
21-
var supportedPlatforms = {
22-
// Maps to module ID for each platform (lazy-required if used)
23-
darwin: './mac',
24-
linux: './linux',
25-
mas: './mac', // map to darwin
26-
win32: './win32'
27-
}
14+
const targets = require('./targets')
2815

2916
function debugHostInfo () {
3017
debug(`Electron Packager ${metadata.version}`)
3118
debug(`Node ${process.version}`)
3219
debug(`Host Operating system: ${process.platform} (${process.arch})`)
3320
}
3421

35-
function validateList (list, supported, name) {
36-
// Validates list of architectures or platforms.
37-
// Returns a normalized array if successful, or an error message string otherwise.
38-
39-
if (!list) return `Must specify ${name}`
40-
if (list === 'all') return Object.keys(supported)
41-
42-
if (!Array.isArray(list)) list = list.split(',')
43-
for (var i = list.length; i--;) {
44-
if (!supported[list[i]]) {
45-
return `Unsupported ${name} ${list[i]}; must be one of: ${Object.keys(supported).join(', ')}`
46-
}
47-
}
48-
49-
return list
50-
}
51-
5222
function getMetadata (opts, dir, cb) {
5323
var props = []
5424
if (!opts.name) props.push(['productName', 'name'])
@@ -121,30 +91,20 @@ function createSeries (opts, archs, platforms) {
12191
})
12292
}
12393

124-
var combinations = []
125-
archs.forEach(function (arch) {
126-
platforms.forEach(function (platform) {
127-
// Electron does not have 32-bit releases for Mac OS X, so skip that combination
128-
if (common.isPlatformMac(platform) && arch === 'ia32') return
129-
combinations.push(common.createDownloadOpts(opts, platform, arch))
130-
})
131-
})
132-
13394
var tasks = []
13495
var useTempDir = opts.tmpdir !== false
13596
if (useTempDir) {
13697
tasks.push(function (cb) {
13798
fs.remove(tempBase, cb)
13899
})
139100
}
140-
return tasks.concat(combinations.map(function (combination) {
101+
return tasks.concat(common.createDownloadCombos(opts, platforms, archs).map(combination => {
141102
var arch = combination.arch
142103
var platform = combination.platform
143104
var version = combination.version
144105

145-
return function (callback) {
146-
debug(`Downloading Electron with options ${JSON.stringify(combination)}`)
147-
download(combination, function (err, zipPath) {
106+
return (callback) => {
107+
common.downloadElectronZip(combination, (err, zipPath) => {
148108
if (err) return callback(err)
149109

150110
function createApp (comboOpts) {
@@ -176,7 +136,7 @@ function createSeries (opts, archs, platforms) {
176136
}
177137
}
178138
], function () {
179-
require(supportedPlatforms[platform]).createApp(comboOpts, buildDir, callback)
139+
require(targets.supportedPlatforms[platform]).createApp(comboOpts, buildDir, callback)
180140
})
181141
}
182142

@@ -229,10 +189,10 @@ module.exports = function packager (opts, cb) {
229189
debugHostInfo()
230190
if (debug.enabled) debug(`Packager Options: ${JSON.stringify(opts)}`)
231191

232-
var archs = validateList(opts.all ? 'all' : opts.arch || process.arch, supportedArchs, 'arch')
233-
var platforms = validateList(opts.all ? 'all' : opts.platform || process.platform, supportedPlatforms, 'platform')
234-
if (!Array.isArray(archs)) return cb(new Error(archs))
235-
if (!Array.isArray(platforms)) return cb(new Error(platforms))
192+
let archs = targets.validateListFromOptions(opts, targets.supportedArchs, 'arch')
193+
let platforms = targets.validateListFromOptions(opts, targets.supportedPlatforms, 'platform')
194+
if (!Array.isArray(archs)) return cb(archs)
195+
if (!Array.isArray(platforms)) return cb(platforms)
236196

237197
debug(`Target Platforms: ${platforms.join(', ')}`)
238198
debug(`Target Architectures: ${archs.join(', ')}`)

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
"rcedit": "^0.7.0",
3030
"resolve": "^1.1.6",
3131
"run-series": "^1.1.1",
32-
"sanitize-filename": "^1.6.0"
32+
"sanitize-filename": "^1.6.0",
33+
"semver": "^5.3.0"
3334
},
3435
"devDependencies": {
3536
"buffer-equal": "^1.0.0",

readme.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ It generates executables/bundles for the following **target** platforms:
3333

3434
* Windows (also known as `win32`, for both 32/64 bit)
3535
* OS X (also known as `darwin`) / [Mac App Store](http://electron.atom.io/docs/v0.36.0/tutorial/mac-app-store-submission-guide/) (also known as `mas`)<sup>*</sup>
36-
* Linux (for both x86/x86_64)
36+
* Linux (for x86, x86_64, and armv7l architectures)
3737

3838
<sup>*</sup> *Note for OS X / MAS target bundles: the `.app` bundle can only be signed when building on a host OS X platform.*
3939

targets.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
'use strict'
2+
3+
const common = require('./common')
4+
5+
module.exports = {
6+
supportedArchs: common.archs.reduce((result, arch) => {
7+
result[arch] = true
8+
return result
9+
}, {}),
10+
// Maps to module filename for each platform (lazy-required if used)
11+
supportedPlatforms: {
12+
darwin: './mac',
13+
linux: './linux',
14+
mas: './mac', // map to darwin
15+
win32: './win32'
16+
},
17+
18+
// Validates list of architectures or platforms.
19+
// Returns a normalized array if successful, or throws an Error.
20+
validateListFromOptions: function validateListFromOptions (opts, supported, name) {
21+
if (opts.all) return Object.keys(supported)
22+
23+
let list = opts[name] || process[name]
24+
if (list === 'all') return Object.keys(supported)
25+
26+
if (!Array.isArray(list)) list = list.split(',')
27+
for (let value of list) {
28+
if (!supported[value]) {
29+
return new Error(`Unsupported ${name}=${value}; must be one of: ${Object.keys(supported).join(', ')}`)
30+
}
31+
}
32+
33+
return list
34+
}
35+
}

test/basic.js

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -446,11 +446,8 @@ test('building for Linux target sanitizes binary name', (t) => {
446446
util.teardown()
447447

448448
util.setup()
449-
test('fails with invalid arch', function (t) {
449+
test('fails with invalid arch', (t) => {
450450
var opts = {
451-
name: 'el0374Test',
452-
dir: path.join(__dirname, 'fixtures', 'el-0374'),
453-
version: '0.37.4',
454451
arch: 'z80',
455452
platform: 'linux'
456453
}
@@ -463,11 +460,8 @@ test('fails with invalid arch', function (t) {
463460
util.teardown()
464461

465462
util.setup()
466-
test('fails with invalid platform', function (t) {
463+
test('fails with invalid platform', (t) => {
467464
var opts = {
468-
name: 'el0374Test',
469-
dir: path.join(__dirname, 'fixtures', 'el-0374'),
470-
version: '0.37.4',
471465
arch: 'ia32',
472466
platform: 'dos'
473467
}

test/multitarget.js

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ function verifyPackageExistence (finalPaths, callback) {
2525

2626
util.setup()
2727
test('all test', function (t) {
28-
t.timeoutAfter(config.timeout * 5) // 4-5 packages will be built during this test
28+
t.timeoutAfter(config.timeout * 7) // 5-7 packages will be built during this test
2929

3030
var opts = {
3131
name: 'basicTest',
@@ -34,13 +34,13 @@ test('all test', function (t) {
3434
all: true
3535
}
3636

37-
var expectedAppCount = 6
37+
var expectedAppCount = 7
3838

3939
waterfall([
4040
function (cb) {
4141
if (process.platform === 'win32') {
4242
isAdmin().then((admin) => {
43-
if (!admin) expectedAppCount = 4
43+
if (!admin) expectedAppCount = 5
4444
cb()
4545
})
4646
} else {
@@ -92,8 +92,9 @@ test('platform=all test (one arch)', function (t) {
9292
util.teardown()
9393

9494
util.setup()
95-
test('arch=all test (one platform)', function (t) {
96-
t.timeoutAfter(config.timeout * 2) // 2 packages will be built during this test
95+
test('arch=all test (one platform)', (t) => {
96+
const LINUX_ARCH_COUNT = 3
97+
t.timeoutAfter(config.timeout * LINUX_ARCH_COUNT)
9798

9899
var opts = {
99100
name: 'basicTest',
@@ -107,10 +108,10 @@ test('arch=all test (one platform)', function (t) {
107108
function (cb) {
108109
packager(opts, cb)
109110
}, function (finalPaths, cb) {
110-
t.equal(finalPaths.length, 2, 'packager call should resolve with expected number of paths')
111+
t.equal(finalPaths.length, LINUX_ARCH_COUNT, 'packager call should resolve with expected number of paths')
111112
verifyPackageExistence(finalPaths, cb)
112113
}, function (exists, cb) {
113-
t.true(exists, 'Packages should be generated for both architectures')
114+
t.true(exists, 'Packages should be generated for all expected architectures')
114115
cb()
115116
}
116117
], function (err) {

0 commit comments

Comments
 (0)