Skip to content

Commit

Permalink
Merge pull request atom#1618 from atom/cz-build-symbols
Browse files Browse the repository at this point in the history
Provide breakpad symbols of native modules
  • Loading branch information
zcbenz committed Mar 1, 2014
2 parents 58f4e5b + 1b7dc2f commit 7fe3caf
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 38 deletions.
6 changes: 4 additions & 2 deletions build/Gruntfile.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ module.exports = (grunt) ->
tmpDir = os.tmpdir()
installRoot = process.env.ProgramFiles
buildDir = grunt.option('build-dir') ? path.join(tmpDir, 'atom-build')
symbolsDir = path.join(buildDir, 'Atom.breakpad.syms')
shellAppDir = path.join(buildDir, appName)
contentsDir = shellAppDir
appDir = path.join(shellAppDir, 'resources', 'app')
Expand All @@ -50,6 +51,7 @@ module.exports = (grunt) ->
tmpDir = '/tmp'
installRoot = '/Applications'
buildDir = grunt.option('build-dir') ? path.join(tmpDir, 'atom-build')
symbolsDir = path.join(buildDir, 'Atom.breakpad.syms')
shellAppDir = path.join(buildDir, appName)
contentsDir = path.join(shellAppDir, 'Contents')
appDir = path.join(contentsDir, 'Resources', 'app')
Expand Down Expand Up @@ -123,7 +125,7 @@ module.exports = (grunt) ->
grunt.initConfig
pkg: grunt.file.readJSON('package.json')

atom: {appDir, appName, buildDir, contentsDir, installDir, shellAppDir}
atom: {appDir, appName, symbolsDir, buildDir, contentsDir, installDir, shellAppDir}

coffee: coffeeConfig

Expand Down Expand Up @@ -223,6 +225,6 @@ module.exports = (grunt) ->
grunt.registerTask('compile', ['coffee', 'prebuild-less', 'cson', 'peg'])
grunt.registerTask('lint', ['coffeelint', 'csslint', 'lesslint'])
grunt.registerTask('test', ['shell:kill-atom', 'run-specs'])
grunt.registerTask('ci', ['output-disk-space', 'download-atom-shell', 'build', 'set-version', 'check-licenses', 'lint', 'test', 'codesign', 'publish-build'])
grunt.registerTask('ci', ['output-disk-space', 'download-atom-shell', 'build', 'dump-symbols', 'set-version', 'check-licenses', 'lint', 'test', 'codesign', 'publish-build'])
grunt.registerTask('docs', ['markdown:guides', 'build-docs'])
grunt.registerTask('default', ['download-atom-shell', 'build', 'set-version', 'install'])
1 change: 1 addition & 0 deletions build/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"harmony-collections": "~0.3.8",
"json-front-matter": "~0.1.3",
"legal-eagle": "~0.3.0",
"minidump": "0.4.x",
"rcedit": "~0.1.2",
"request": "~2.27.0",
"rimraf": "~2.2.2",
Expand Down
39 changes: 39 additions & 0 deletions build/tasks/dump-symbols-task.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
async = require 'async'
fs = require 'fs-plus'
path = require 'path'
minidump = require 'minidump'

module.exports = (grunt) ->
{mkdir, rm} = require('./task-helpers')(grunt)

dumpSymbolTo = (binaryPath, targetDirectory, callback) ->
minidump.dumpSymbol binaryPath, (error, content) ->
return callback(error) if error?

moduleLine = /MODULE [^ ]+ [^ ]+ ([0-9A-F]+) (.*)\n/.exec(content)
if moduleLine.length isnt 3
return callback("Invalid output when dumping symbol for #{binaryPath}")

filename = moduleLine[2]
targetPathDirname = path.join(targetDirectory, filename, moduleLine[1])
mkdir targetPathDirname

targetPath = path.join(targetPathDirname, "#{filename}.sym")
fs.writeFile(targetPath, content, callback)

grunt.registerTask 'dump-symbols', 'Dump symbols for native modules', ->
done = @async()

symbolsDir = grunt.config.get('atom.symbolsDir')
rm symbolsDir
mkdir symbolsDir

tasks = []
onFile = (binaryPath) ->
if /.*\.node$/.test(binaryPath)
tasks.push(dumpSymbolTo.bind(this, binaryPath, symbolsDir))
onDirectory = ->
true
fs.traverseTreeSync 'node_modules', onFile, onDirectory

async.parallel tasks, done
94 changes: 58 additions & 36 deletions build/tasks/publish-build-task.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ child_process = require 'child_process'
path = require 'path'

_ = require 'underscore-plus'
async = require 'async'
fs = require 'fs-plus'
GitHub = require 'github-releases'
request = require 'request'

grunt = null
maxReleases = 10
assetName = 'atom-mac.zip'
assetPath = "/tmp/atom-build/#{assetName}"
assets = [
{assetName: 'atom-mac.zip', sourceName: 'Atom.app'}
{assetName: 'atom-mac-symbols.zip', sourceName: 'Atom.breakpad.syms'}
]
commitSha = process.env.JANKY_SHA1
token = process.env.ATOM_ACCESS_TOKEN
defaultHeaders =
Expand All @@ -18,39 +21,48 @@ defaultHeaders =

module.exports = (gruntObject) ->
grunt = gruntObject

grunt.registerTask 'publish-build', 'Publish the built app', ->
return unless process.platform is 'darwin'
return if process.env.JANKY_SHA1 and process.env.JANKY_BRANCH isnt 'master'

done = @async()
buildDir = grunt.config.get('atom.buildDir')

createBuildRelease (error, release) ->
return done(error) if error?
zipApp (error) ->

zipApps buildDir, assets, (error) ->
return done(error) if error?
uploadAsset release, (error) ->
uploadAssets release, buildDir, assets, (error) ->
return done(error) if error?
publishRelease release, (error) ->
return done(error) if error?
getAtomDraftRelease (error, release) ->
return done(error) if error?
deleteExistingAsset release, (error) ->
assetNames = (asset.assetName for asset in assets)
deleteExistingAssets release, assetNames, (error) ->
return done(error) if error?
uploadAsset(release, done)
uploadAssets(release, buildDir, assets, done)

logError = (message, error, details) ->
grunt.log.error(message)
grunt.log.error(error.message ? error) if error?
grunt.log.error(details) if details

zipApp = (callback) ->
fs.removeSync(assetPath)
zipApps = (buildDir, assets, callback) ->
zip = (directory, sourceName, assetName, callback) ->
options = {cwd: directory, maxBuffer: Infinity}
child_process.exec "zip -r --symlinks #{assetName} #{sourceName}", options, (error, stdout, stderr) ->
if error?
logError("Zipping #{sourceName} failed", error, stderr)
callback(error)

options = {cwd: path.dirname(assetPath), maxBuffer: Infinity}
child_process.exec "zip -r --symlinks #{assetName} Atom.app", options, (error, stdout, stderr) ->
if error?
logError('Zipping Atom.app failed', error, stderr)
callback(error)
tasks = []
for {assetName, sourceName} in assets
fs.removeSync(path.join(buildDir, assetName))
tasks.push(zip.bind(this, buildDir, sourceName, assetName))
async.parallel(tasks, callback)

getRelease = (callback) ->
options =
Expand Down Expand Up @@ -93,10 +105,12 @@ deleteRelease = (release) ->
if error? or response.statusCode isnt 204
logError('Deleting release failed', error, body)

deleteExistingAsset = (release, callback) ->
for asset in release.assets when asset.name is assetName
deleteExistingAssets = (release, assetNames, callback) ->
[callback, assetNames] = [assetNames, callback] if not callback?

deleteAsset = (url, callback) ->
options =
uri: asset.url
uri: url
method: 'DELETE'
headers: defaultHeaders
request options, (error, response, body='') ->
Expand All @@ -106,9 +120,10 @@ deleteExistingAsset = (release, callback) ->
else
callback()

return

callback()
tasks = []
for asset in release.assets when not assetNames? or asset.name in assetNames
tasks.push(deleteAsset.bind(this, asset.url))
async.parallel(tasks, callback)

createBuildRelease = (callback) ->
getRelease (error, release) ->
Expand All @@ -117,7 +132,7 @@ createBuildRelease = (callback) ->
return

if release?
deleteExistingAsset release, (error) ->
deleteExistingAssets release, (error) ->
callback(error, release)
return

Expand All @@ -139,23 +154,30 @@ createBuildRelease = (callback) ->
else
callback(null, release)

uploadAsset = (release, callback) ->
options =
uri: release.upload_url.replace(/\{.*$/, "?name=#{assetName}")
method: 'POST'
headers: _.extend({
'Content-Type': 'application/zip'
'Content-Length': fs.getSizeSync(assetPath)
}, defaultHeaders)

assetRequest = request options, (error, response, body='') ->
if error? or response.statusCode >= 400
logError('Upload release asset failed', error, body)
callback(error ? new Error(response.statusCode))
else
callback(null, release)
uploadAssets = (release, buildDir, assets, callback) ->
upload = (release, assetName, assetPath, callback) ->
options =
uri: release.upload_url.replace(/\{.*$/, "?name=#{assetName}")
method: 'POST'
headers: _.extend({
'Content-Type': 'application/zip'
'Content-Length': fs.getSizeSync(assetPath)
}, defaultHeaders)

assetRequest = request options, (error, response, body='') ->
if error? or response.statusCode >= 400
logError("Upload release asset #{assetName} failed", error, body)
callback(error ? new Error(response.statusCode))
else
callback(null, release)

fs.createReadStream(assetPath).pipe(assetRequest)

fs.createReadStream(assetPath).pipe(assetRequest)
tasks = []
for {assetName, sourceName} in assets
assetPath = path.join(buildDir, assetName)
tasks.push(upload.bind(this, release, assetName, assetPath))
async.parallel(tasks, callback)

publishRelease = (release, callback) ->
options =
Expand Down

0 comments on commit 7fe3caf

Please sign in to comment.