Skip to content

Commit

Permalink
feat: build mas + other targets, osx 7z
Browse files Browse the repository at this point in the history
  • Loading branch information
develar committed Apr 29, 2016
1 parent 955507f commit c46e1f5
Show file tree
Hide file tree
Showing 16 changed files with 209 additions and 197 deletions.
1 change: 1 addition & 0 deletions .idea/dictionaries/develar.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .idea/runConfigurations/BuildTest.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .idea/runConfigurations/osxPackagerTest.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion docs/Options.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ See all [appdmg options](https://www.npmjs.com/package/appdmg#json-specification
| --- | ---
| icon | <a name="OsXBuildOptions-icon"></a>The path to icon, which will be shown when mounted (default: `build/icon.icns`).
| background | <a name="OsXBuildOptions-background"></a>The path to background (default: `build/background.png`). The resolution of this file determines the resolution of the installer window.
| target | <a name="OsXBuildOptions-target"></a>Target package type: list of `default`, `dmg`, `zip`, `mas`.
| target | <a name="OsXBuildOptions-target"></a>Target package type: list of `default`, `dmg`, `zip`, `mas`, `7z`.

<a name="WinBuildOptions"></a>
### `.build.win`
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
"electron-osx-sign-tf": "^0.4.0-beta.0",
"electron-packager-tf": "^7.0.2-beta.0",
"electron-winstaller-fixed": "~2.4.0-beta.0",
"fs-extra-p": "^0.3.0",
"fs-extra-p": "^0.4.0",
"globby": "^4.0.0",
"hosted-git-info": "^2.1.4",
"image-size": "^0.5.0",
Expand Down
2 changes: 1 addition & 1 deletion src/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ export interface OsXBuildOptions extends PlatformSpecificBuildOptions {
readonly background?: string

/*
Target package type: list of `default`, `dmg`, `zip`, `mas`.
Target package type: list of `default`, `dmg`, `zip`, `mas`, `7z`.
*/
readonly target?: Array<string>
}
Expand Down
85 changes: 49 additions & 36 deletions src/osxPackager.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PlatformPackager, BuildInfo } from "./platformPackager"
import { PlatformPackager, BuildInfo, normalizeTargets } from "./platformPackager"
import { Platform, OsXBuildOptions } from "./metadata"
import * as path from "path"
import { Promise as BluebirdPromise } from "bluebird"
Expand All @@ -14,7 +14,7 @@ const __awaiter = require("./awaiter")
export default class OsXPackager extends PlatformPackager<OsXBuildOptions> {
codeSigningInfo: Promise<CodeSigningInfo>

readonly target: Array<string>
readonly targets: Array<string>

constructor(info: BuildInfo, cleanupTasks: Array<() => Promise<any>>) {
super(info)
Expand All @@ -28,41 +28,45 @@ export default class OsXPackager extends PlatformPackager<OsXBuildOptions> {
this.codeSigningInfo = BluebirdPromise.resolve(null)
}

let target = this.customBuildOptions == null ? null : this.customBuildOptions.target
if (target != null) {
target = Array.isArray(target) ? target : [target]
target = target.map(it => it.toLowerCase().trim())
for (let t of target) {
if (t !== "default" && t !== "dmg" && t !== "zip" && t !== "mas") {
throw new Error("Unknown target: " + t)
const targets = normalizeTargets(this.customBuildOptions == null ? null : this.customBuildOptions.target)
if (targets != null) {
for (let target of targets) {
if (target !== "default" && target !== "dmg" && target !== "zip" && target !== "mas" && target !== "7z") {
throw new Error("Unknown target: " + target)
}
}
}
this.target = target == null ? ["default"] : target
this.targets = targets == null ? ["default"] : targets
}

get platform() {
return Platform.OSX
}

protected computeAppOutDir(outDir: string, arch: string): string {
return this.target.includes("mas") ? path.join(outDir, `${this.appName}-mas-${arch}`) : super.computeAppOutDir(outDir, arch)
}
async pack(outDir: string, arch: string, postAsyncTasks: Array<Promise<any>>): Promise<any> {
const packOptions = this.computePackOptions(outDir, arch)
let nonMasPromise: Promise<any> = null
if (this.targets.length > 1 || this.targets[0] !== "mas") {
const appOutDir = this.computeAppOutDir(outDir, arch)
nonMasPromise = this.doPack(packOptions, outDir, appOutDir, arch)
.then(() => this.sign(appOutDir, false))
.then(() => postAsyncTasks.push(this.packageInDistributableFormat(outDir, appOutDir, arch)))
}

async doPack(outDir: string, appOutDir: string, arch: string): Promise<any> {
await super.doPack(outDir, appOutDir, arch)
await this.sign(appOutDir, await this.codeSigningInfo)
}
if (this.targets.includes("mas")) {
// osx-sign - disable warning
const appOutDir = path.join(outDir, `${this.appName}-mas-${arch}`)
await this.doPack(Object.assign({}, packOptions, {platform: "mas", "osx-sign": false}), outDir, appOutDir, arch)
await this.sign(appOutDir, true)
}

protected beforePack(options: any): void {
if (this.target.includes("mas")) {
options.platform = "mas"
if (nonMasPromise != null) {
await nonMasPromise
}
// disable warning
options["osx-sign"] = false
}

private async sign(appOutDir: string, codeSigningInfo: CodeSigningInfo): Promise<any> {
private async sign(appOutDir: string, isMas: boolean): Promise<any> {
let codeSigningInfo = await this.codeSigningInfo
if (codeSigningInfo == null) {
codeSigningInfo = {
name: this.options.sign || process.env.CSC_NAME,
Expand All @@ -77,7 +81,6 @@ export default class OsXPackager extends PlatformPackager<OsXBuildOptions> {

log("Signing app")

const isMas = this.target.includes("mas")
const baseSignOptions: BaseSignOptions = {
app: path.join(appOutDir, this.appName + ".app"),
platform: isMas ? "mas" : "darwin"
Expand Down Expand Up @@ -133,10 +136,10 @@ export default class OsXPackager extends PlatformPackager<OsXBuildOptions> {
}

packageInDistributableFormat(outDir: string, appOutDir: string, arch: string): Promise<any> {
const artifactPath = path.join(appOutDir, `${this.appName}-${this.metadata.version}.dmg`)
const promises: Array<Promise<any>> = []

if (this.target.includes("dmg") || this.target.includes("default")) {
if (this.targets.includes("dmg") || this.targets.includes("default")) {
const artifactPath = path.join(appOutDir, `${this.appName}-${this.metadata.version}.dmg`)
promises.push(new BluebirdPromise<any>(async(resolve, reject) => {
log("Creating DMG")
const dmgOptions = {
Expand Down Expand Up @@ -164,24 +167,34 @@ export default class OsXPackager extends PlatformPackager<OsXBuildOptions> {
.then(() => this.dispatchArtifactCreated(artifactPath, `${this.metadata.name}-${this.metadata.version}.dmg`)))
}

if (this.target.includes("zip") || this.target.includes("default")) {
promises.push(this.zipMacApp(appOutDir)
.then(it => this.dispatchArtifactCreated(it, `${this.metadata.name}-${this.metadata.version}-mac.zip`)))
for (let target of this.targets) {
if (target !== "mas" && target !== "dmg") {
const format = target === "default" ? "zip" : target
log("Creating OS X " + format)
// for default we use mac to be compatible with Squirrel.Mac
const classifier = target === "default" ? "mac" : "osx"
promises.push(this.archiveApp(appOutDir, format, classifier)
.then(it => this.dispatchArtifactCreated(it, `${this.metadata.name}-${this.metadata.version}-${classifier}.${format}`)))
}
}

return BluebirdPromise.all(promises)
}

private zipMacApp(outDir: string): Promise<string> {
log("Creating ZIP for Squirrel.Mac")
// we use app name here - see https://github.com/electron-userland/electron-builder/pull/204
const resultPath = `${this.appName}-${this.metadata.version}-mac.zip`
const args = ["a", "-mm=" + (this.devMetadata.build.compression === "store" ? "Copy" : "Deflate"), "-bb" + (debug.enabled ? "3" : "0"), "-bd"]
if (this.devMetadata.build.compression === "maximum") {
private archiveApp(outDir: string, format: string, classifier: string): Promise<string> {
const args = ["a", "-bb" + (debug.enabled ? "3" : "0"), "-bd"]
const compression = this.devMetadata.build.compression
const storeOnly = compression === "store"
if (format === "zip" || storeOnly) {
args.push("-mm=" + (storeOnly ? "Copy" : "Deflate"))
}
if (compression === "maximum") {
// http://superuser.com/a/742034
//noinspection SpellCheckingInspection
args.push("-mfb=258", "-mpass=15")
}

// we use app name here - see https://github.com/electron-userland/electron-builder/pull/204
const resultPath = `${this.appName}-${this.metadata.version}-${classifier}.${format}`
args.push(resultPath, this.appName + ".app")

return spawn(path7za, args, {
Expand Down
15 changes: 10 additions & 5 deletions src/packager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export class Packager implements BuildInfo {
const appPackageFile = this.projectDir === this.appDir ? devPackageFile : path.join(this.appDir, "package.json")
this.metadata = appPackageFile === devPackageFile ? this.devMetadata : await readPackageJson(appPackageFile)
this.checkMetadata(appPackageFile, devPackageFile, platforms)
checkConflictingOptions(this.devMetadata.build)

this.electronVersion = await getElectronVersion(this.devMetadata, devPackageFile)

Expand All @@ -74,11 +75,7 @@ export class Packager implements BuildInfo {
for (let arch of normalizeArchs(platform, this.options.arch)) {
await this.installAppDependencies(platform, arch)
// electron-packager uses productName in the directory name
const appOutDir = await helper.pack(outDir, arch)
if (this.options.dist) {
distTasks.push(helper.packageInDistributableFormat(outDir, appOutDir, arch))
}
}
await helper.pack(outDir, arch, distTasks)}
}

return await BluebirdPromise.all(distTasks)
Expand Down Expand Up @@ -198,3 +195,11 @@ export function normalizePlatforms(platforms: Array<string | Platform>): Array<P
return platforms.map(it => it instanceof Platform ? it : Platform.fromString(it))
}
}

function checkConflictingOptions(options: any) {
for (let name of ["all", "out", "tmpdir", "version", "platform", "dir", "arch"]) {
if (name in options) {
throw new Error(`Option ${name} is ignored, do not specify it.`)
}
}
}
42 changes: 23 additions & 19 deletions src/platformPackager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { statOrNull, use } from "./util"
import { Packager } from "./packager"
import deepAssign = require("deep-assign")
import { statFile } from "asar"
import ElectronPackagerOptions = ElectronPackager.ElectronPackagerOptions

//noinspection JSUnusedLocalSymbols
const __awaiter = require("./awaiter")
Expand Down Expand Up @@ -106,27 +107,27 @@ export abstract class PlatformPackager<DC extends PlatformSpecificBuildOptions>
})
}

async pack(outDir: string, arch: string): Promise<string> {
pack(outDir: string, arch: string, postAsyncTasks: Array<Promise<any>>): Promise<any> {
const appOutDir = this.computeAppOutDir(outDir, arch)
await this.doPack(outDir, appOutDir, arch)
await this.copyExtraResources(appOutDir, arch)
return appOutDir
return this.doPack(this.computePackOptions(outDir, arch), outDir, appOutDir, arch, postAsyncTasks)
}

protected beforePack(options: any): void {
// to override
protected async doPack(options: ElectronPackagerOptions, outDir: string, appOutDir: string, arch: string, postAsyncTasks: Array<Promise<any>> = null) {
await this.packApp(options, appOutDir)
await this.copyExtraResources(appOutDir, arch)
if (postAsyncTasks != null && this.options.dist) {
postAsyncTasks.push(this.packageInDistributableFormat(outDir, appOutDir, arch))
}
}

protected async doPack(outDir: string, appOutDir: string, arch: string): Promise<any> {
protected computePackOptions(outDir: string, arch: string): ElectronPackagerOptions {
const version = this.metadata.version
let buildVersion = version
const buildNumber = this.computeBuildNumber()
if (buildNumber != null) {
buildVersion += "." + buildNumber
}

checkConflictingOptions(this.devMetadata.build)

const options = deepAssign({
dir: this.info.appDir,
out: outDir,
Expand All @@ -153,8 +154,10 @@ export abstract class PlatformPackager<DC extends PlatformSpecificBuildOptions>
delete options.linux
// this option only for windows-installer
delete options.iconUrl
return options
}

this.beforePack(options)
protected async packApp(options: ElectronPackagerOptions, appOutDir: string): Promise<any> {
await pack(options)
await this.sanityCheckPackage(appOutDir, options.asar)
}
Expand Down Expand Up @@ -186,7 +189,7 @@ export abstract class PlatformPackager<DC extends PlatformSpecificBuildOptions>
return await BluebirdPromise.map(await this.getExtraResources(arch), it => copy(path.join(this.projectDir, it), path.join(resourcesDir, it)))
}

abstract packageInDistributableFormat(outDir: string, appOutDir: string, arch: string): Promise<any>
protected abstract packageInDistributableFormat(outDir: string, appOutDir: string, arch: string): Promise<any>

protected async computePackageUrl(): Promise<string> {
const url = this.metadata.homepage || this.devMetadata.homepage
Expand Down Expand Up @@ -247,17 +250,18 @@ export abstract class PlatformPackager<DC extends PlatformSpecificBuildOptions>
}
}

function checkConflictingOptions(options: any) {
for (let name of ["all", "out", "tmpdir", "version", "platform", "dir", "arch"]) {
if (name in options) {
throw new Error(`Option ${name} is ignored, do not specify it.`)
}
}
}

export interface ArtifactCreated {
readonly file: string
readonly artifactName?: string

readonly platform: Platform
}

export function normalizeTargets(targets: Array<string> | string): Array<string> {
if (targets == null) {
return null
}
else {
return (Array.isArray(targets) ? targets : [targets]).map(it => it.toLowerCase().trim())
}
}
14 changes: 8 additions & 6 deletions src/winPackager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,19 +52,19 @@ export class WinPackager extends PlatformPackager<WinBuildOptions> {
return iconPath
}

async pack(outDir: string, arch: string): Promise<string> {
async pack(outDir: string, arch: string, postAsyncTasks: Array<Promise<any>>): Promise<any> {
// we must check icon before pack because electron-packager uses icon and it leads to cryptic error message "spawn wine ENOENT"
await this.iconPath

if (!this.options.dist) {
return await super.pack(outDir, arch)
return await super.pack(outDir, arch, postAsyncTasks)
}

const appOutDir = this.computeAppOutDir(outDir, arch)
const installerOut = computeDistOut(outDir, arch)
log("Removing %s", installerOut)
await BluebirdPromise.all([
this.doPack(outDir, appOutDir, arch),
this.packApp(this.computePackOptions(outDir, arch), appOutDir),
emptyDir(installerOut)
])

Expand All @@ -80,11 +80,13 @@ export class WinPackager extends PlatformPackager<WinBuildOptions> {
})
}

return appOutDir
if (this.options.dist) {
postAsyncTasks.push(this.packageInDistributableFormat(outDir, appOutDir, arch))
}
}

protected async doPack(outDir: string, appOutDir: string, arch: string) {
await super.doPack(outDir, appOutDir, arch)
protected async packApp(options: any, appOutDir: string) {
await super.packApp(options, appOutDir)

if (process.platform === "darwin" && this.options.cscLink != null && this.options.cscKeyPassword != null) {
const filename = this.appName + ".exe"
Expand Down
5 changes: 4 additions & 1 deletion test/src/BuildTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,10 @@ test("invalid platform", (t) => t.throws(assertPack("test-app-one", {
function allPlatformsAndCurrentArch(dist: boolean = true): PackagerOptions {
return {
platform: getPossiblePlatforms(),
dist: dist
dist: dist,
// speed up tests
cscLink: null,
cscInstallerLink: null,
}
}

Expand Down
Loading

0 comments on commit c46e1f5

Please sign in to comment.