forked from rockybars/atom-shell-packager
/
mac.js
207 lines (175 loc) · 8.27 KB
/
mac.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
var common = require('./common')
var fs = require('fs-extra')
var path = require('path')
var plist = require('plist')
var series = require('run-series')
var sign = require('electron-osx-sign')
function rename (basePath, oldName, newName, cb) {
fs.move(path.join(basePath, oldName), path.join(basePath, newName), cb)
}
function moveHelpers (frameworksPath, appName, callback) {
series([' Helper', ' Helper EH', ' Helper NP'].map(function (suffix) {
return function (cb) {
var executableBasePath = path.join(frameworksPath, `Electron${suffix}.app`, 'Contents', 'MacOS')
rename(executableBasePath, `Electron${suffix}`, appName + suffix, function (err) {
if (err) return cb(err)
rename(frameworksPath, `Electron${suffix}.app`, `${appName}${suffix}.app`, cb)
})
}
}), function (err) {
callback(err)
})
}
function filterCFBundleIdentifier (identifier) {
// Remove special characters and allow only alphanumeric (A-Z,a-z,0-9), hyphen (-), and period (.)
// Apple documentation: https://developer.apple.com/library/mac/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/20001431-102070
return identifier.replace(/ /g, '-').replace(/[^a-zA-Z0-9.-]/g, '')
}
function createSignOpts (properties, platform, app) {
// use default sign opts if osx-sign is true, otherwise clone osx-sign object
var signOpts = properties === true ? {identity: null} : Object.assign({}, properties)
// osx-sign options are handed off to sign module, but
// with a few additions from the main options
// user may think they can pass platform or app, but they will be ignored
common.subOptionWarning(signOpts, 'osx-sign', 'platform', platform)
common.subOptionWarning(signOpts, 'osx-sign', 'app', app)
if (signOpts.binaries) {
console.warn('WARNING: osx-sign.binaries signing will fail. Sign manually, or with electron-osx-sign.')
}
// Take argument osx-sign as signing identity:
// if opts['osx-sign'] is true (bool), fallback to identity=null for
// autodiscovery. Otherwise, provide signing certificate info.
if (signOpts.identity === true) {
signOpts.identity = null
}
return signOpts
}
module.exports = {
createApp: function createApp (opts, templatePath, callback) {
var appRelativePath = path.join('Electron.app', 'Contents', 'Resources', 'app')
common.initializeApp(opts, templatePath, appRelativePath, function buildMacApp (err, tempPath) {
if (err) return callback(err)
var contentsPath = path.join(tempPath, 'Electron.app', 'Contents')
var frameworksPath = path.join(contentsPath, 'Frameworks')
var appPlistFilename = path.join(contentsPath, 'Info.plist')
var helperPlistFilename = path.join(frameworksPath, 'Electron Helper.app', 'Contents', 'Info.plist')
var helperEHPlistFilename = path.join(frameworksPath, 'Electron Helper EH.app', 'Contents', 'Info.plist')
var helperNPPlistFilename = path.join(frameworksPath, 'Electron Helper NP.app', 'Contents', 'Info.plist')
var appPlist = plist.parse(fs.readFileSync(appPlistFilename).toString())
var helperPlist = plist.parse(fs.readFileSync(helperPlistFilename).toString())
var helperEHPlist = plist.parse(fs.readFileSync(helperEHPlistFilename).toString())
var helperNPPlist = plist.parse(fs.readFileSync(helperNPPlistFilename).toString())
// Update plist files
// If an extend-info file was supplied, copy its contents in first
if (opts['extend-info']) {
var extendAppPlist = plist.parse(fs.readFileSync(opts['extend-info']).toString())
for (var key in extendAppPlist) {
appPlist[key] = extendAppPlist[key]
}
}
// Now set fields based on explicit options
var defaultBundleName = 'com.electron.' + opts.name.toLowerCase()
var appBundleIdentifier = filterCFBundleIdentifier(opts['app-bundle-id'] || defaultBundleName)
var helperBundleIdentifier = filterCFBundleIdentifier(opts['helper-bundle-id'] || appBundleIdentifier + '.helper')
var appVersion = opts['app-version']
var buildVersion = opts['build-version']
var appCategoryType = opts['app-category-type']
var humanReadableCopyright = opts['app-copyright']
appPlist.CFBundleDisplayName = opts.name
appPlist.CFBundleIdentifier = appBundleIdentifier
appPlist.CFBundleName = opts.name
helperPlist.CFBundleDisplayName = opts.name + ' Helper'
helperPlist.CFBundleIdentifier = helperBundleIdentifier
appPlist.CFBundleExecutable = opts.name
helperPlist.CFBundleName = opts.name
helperPlist.CFBundleExecutable = opts.name + ' Helper'
helperEHPlist.CFBundleDisplayName = opts.name + ' Helper EH'
helperEHPlist.CFBundleIdentifier = helperBundleIdentifier + '.EH'
helperEHPlist.CFBundleName = opts.name + ' Helper EH'
helperEHPlist.CFBundleExecutable = opts.name + ' Helper EH'
helperNPPlist.CFBundleDisplayName = opts.name + ' Helper NP'
helperNPPlist.CFBundleIdentifier = helperBundleIdentifier + '.NP'
helperNPPlist.CFBundleName = opts.name + ' Helper NP'
helperNPPlist.CFBundleExecutable = opts.name + ' Helper NP'
if (appVersion) {
appPlist.CFBundleShortVersionString = appPlist.CFBundleVersion = '' + appVersion
}
if (buildVersion) {
appPlist.CFBundleVersion = '' + buildVersion
}
if (opts.protocols && opts.protocols.length) {
appPlist.CFBundleURLTypes = opts.protocols.map(function (protocol) {
return {
CFBundleURLName: protocol.name,
CFBundleURLSchemes: [].concat(protocol.schemes)
}
})
}
if (appCategoryType) {
appPlist.LSApplicationCategoryType = appCategoryType
}
if (humanReadableCopyright) {
appPlist.NSHumanReadableCopyright = humanReadableCopyright
}
fs.writeFileSync(appPlistFilename, plist.build(appPlist))
fs.writeFileSync(helperPlistFilename, plist.build(helperPlist))
fs.writeFileSync(helperEHPlistFilename, plist.build(helperEHPlist))
fs.writeFileSync(helperNPPlistFilename, plist.build(helperNPPlist))
var operations = []
// Copy in the icon, if supplied
if (opts.icon) {
operations.push(function (cb) {
common.normalizeExt(opts.icon, '.icns', function (err, icon) {
if (err) {
// Ignore error if icon doesn't exist, in case it's only available for other OS
cb(null)
} else {
fs.copy(icon, path.join(contentsPath, 'Resources', appPlist.CFBundleIconFile), cb)
}
})
})
}
// Copy in any other extras
var extras = opts['extra-resource']
if (extras) {
if (!Array.isArray(extras)) extras = [extras]
extras.forEach(function (val) {
operations.push(function (cb) {
fs.copy(val, path.join(contentsPath, 'Resources', path.basename(val)), cb)
})
})
}
// Rename the Contents/MacOS/Electron binary
operations.push(function (cb) {
rename(path.join(contentsPath, 'MacOS'), 'Electron', appPlist.CFBundleExecutable, cb)
})
// Move Helper apps/executables, then top-level .app
var finalAppPath = path.join(tempPath, `${opts.name}.app`)
operations.push(function (cb) {
moveHelpers(frameworksPath, opts.name, cb)
}, function (cb) {
fs.move(path.dirname(contentsPath), finalAppPath, cb)
})
if ((opts.platform === 'all' || opts.platform === 'mas') && opts['osx-sign'] === undefined) {
console.warn('WARNING: signing is required for mas builds. Provide the osx-sign option, or manually sign the app later.')
}
if (opts['osx-sign']) {
operations.push(function (cb) {
sign(createSignOpts(opts['osx-sign'], opts.platform, finalAppPath), function (err) {
if (err) {
// Although not signed successfully, the application is packed.
console.warn('Code sign failed; please retry manually.', err)
}
cb()
})
})
}
series(operations, function (err) {
if (err) return callback(err)
common.moveApp(opts, tempPath, callback)
})
})
},
createSignOpts: createSignOpts,
filterCFBundleIdentifier: filterCFBundleIdentifier
}