Skip to content

Commit

Permalink
feat: use self-contained fpm on Linux — don't need to install ruby an…
Browse files Browse the repository at this point in the history
…ymore
  • Loading branch information
develar committed May 9, 2016
1 parent 7ac6ca2 commit 7d5b747
Show file tree
Hide file tree
Showing 13 changed files with 533 additions and 279 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.

1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ cache:
- node_modules
- test/testApp/node_modules
- $HOME/.electron
- $HOME/.cache/fpm

addons:
apt:
Expand Down
9 changes: 4 additions & 5 deletions docs/Multi Platform Build.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,16 @@ brew install gnu-tar libicns graphicsmagick
## Linux
To build app in distributable format for Linux:
```
sudo apt-get install ruby ruby-dev gcc make icnsutils graphicsmagick xz-utils
gem install fpm
sudo apt-get install icnsutils graphicsmagick xz-utils
```

To build app in distributable format for Windows on Linux:
* Install Wine (1.8+ is required):

```
sudo add-apt-repository ppa:ubuntu-wine/ppa
sudo add-apt-repository ppa:ubuntu-wine/ppa -y
sudo apt-get update
sudo apt-get install wine1.8
sudo apt-get install wine1.8 -y
```

* Install [Mono](http://www.mono-project.com/docs/getting-started/install/linux/#usage) (4.2+ is required):
Expand All @@ -47,7 +46,7 @@ To build app in distributable format for Windows on Linux:
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
echo "deb http://download.mono-project.com/repo/debian wheezy main" | sudo tee /etc/apt/sources.list.d/mono-xamarin.list
sudo apt-get update
sudo apt-get install mono-complete
sudo apt-get install mono-devel -y
```

To build app in 32 bit from a machine with 64 bit:
Expand Down
9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
"bugs": "https://github.com/electron-userland/electron-builder/issues",
"homepage": "https://github.com/electron-userland/electron-builder",
"dependencies": {
"7zip-bin": "^0.0.4",
"7zip-bin": "^0.3.0",
"asar": "^0.11.0",
"bluebird": "^3.3.5",
"chalk": "^1.1.3",
Expand Down Expand Up @@ -106,7 +106,7 @@
"ts-babel": "^0.8.6",
"tsconfig-glob": "^0.4.3",
"tslint": "^3.9.0-dev.0",
"typescript": "1.9.0-dev.20160507",
"typescript": "1.9.0-dev.20160509",
"whitespace": "^2.0.0"
},
"babel": {
Expand All @@ -123,5 +123,8 @@
"test/out/*"
]
},
"typings": "./out/electron-builder.d.ts"
"typings": "./out/electron-builder.d.ts",
"publishConfig": {
"tag": "next"
}
}
69 changes: 52 additions & 17 deletions src/fpmDownload.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { statOrNull, spawn, debug } from "./util"
import { emptyDir, move, remove } from "fs-extra-p"
import { statOrNull, spawn, debug, debug7z } from "./util"
import { readdir, mkdirs, move, remove } from "fs-extra-p"
import { download } from "./httpRequest"
import { path7za } from "7zip-bin"
import * as path from "path"
import { tmpdir } from "os"
import { homedir } from "os"
import { Promise as BluebirdPromise } from "bluebird"

//noinspection JSUnusedLocalSymbols
Expand All @@ -18,42 +18,77 @@ function getTempName(prefix?: string | n): string {
const versionToPromise = new Map<string, BluebirdPromise<string>>()

// can be called in parallel, all calls for the same version will get the same promise - will be downloaded only once
export function downloadFpm(version: string): Promise<string> {
export function downloadFpm(version: string, osAndArch: string): Promise<string> {
let promise = versionToPromise.get(version)
// if rejected, we will try to download again
if (<any>promise != null && !promise!.isRejected()) {
return promise!
}

promise = <BluebirdPromise<string>>doDownloadFpm(version)
promise = <BluebirdPromise<string>>doDownloadFpm(version, osAndArch)
versionToPromise.set(version, promise)
return promise
}

async function doDownloadFpm(version: string): Promise<string> {
const dirName = `fpm-${version}-osx`
async function doDownloadFpm(version: string, osAndArch: string): Promise<string> {
const dirName = `fpm-${version}-${osAndArch}`
const url = `https://github.com/develar/fpm-self-contained/releases/download/v${version}/${dirName}.7z`
const cache = path.join(__dirname, "..", "node_modules", ".fpm")
const fpmDir = path.join(cache, dirName)

// we cache in the global location - in the home dir, not in the node_modules/.cache (https://www.npmjs.com/package/find-cache-dir) because
// * don't need to find node_modules
// * don't pollute user project dir (important in case of 1-package.json project structure)
// * simplify/speed-up tests (don't download fpm for each test project)
const cacheDir = path.join(homedir(), ".cache", "fpm")
const fpmDir = path.join(cacheDir, dirName)

const stat = await statOrNull(fpmDir)
if (stat != null && stat.isDirectory()) {
debug(`Found existing fpm ${fpmDir}`)
return path.join(fpmDir, "fpm")
}

// the only version currently supported (i.e. all clients are consumed the same version
await emptyDir(cache)
await emptyDir(cacheDir, dirName)

const archiveName = path.join(tmpdir(), getTempName("fpm-download") + ".7z")
// 7z cannot be extracted from the input stream, temp file is required
const tempName = getTempName()
const archiveName = path.join(cacheDir, tempName + ".7z")
debug(`Download fpm from ${url} to ${archiveName}`)
await download(url, archiveName)
const tempUnpackDir = path.join(cache, getTempName())
await spawn(path7za, ["x", archiveName, "-o" + tempUnpackDir, "-bb" + (debug.enabled ? "3" : "0"), "-bd"], {
cwd: cache,
stdio: ["ignore", "inherit", "inherit"],
const tempUnpackDir = path.join(cacheDir, tempName)
const args = ["x", archiveName, "-o" + tempName, "-bd"]
if (debug7z.enabled) {
args.push("-bb3")
}
else if (!debug.enabled) {
args.push("-bb0")
}
await spawn(path7za, args, {
cwd: cacheDir,
stdio: ["ignore", debug.enabled ? "inherit" : "ignore", "inherit"],
})

await move(path.join(tempUnpackDir, dirName), fpmDir, {clobber: true})
await BluebirdPromise.all([remove(tempUnpackDir), remove(archiveName)])
await BluebirdPromise.all([move(path.join(tempUnpackDir, dirName), fpmDir, {clobber: true}), remove(archiveName)])
await remove(tempUnpackDir)

debug(`fpm downloaded to ${fpmDir}`)
return path.join(fpmDir, "fpm")
}

// prefix to not delete dir or archived dir (.7z)
async function emptyDir(dir: string, excludeNamePrefix: string) {
let items: string[] | null = null
try {
items = await readdir(dir)
}
catch (e) {
await mkdirs(dir)
return
}

items = items!
.filter(it => !it.startsWith(excludeNamePrefix))
.map(it => path.join(dir, it))

await BluebirdPromise.map(items, remove)
}
4 changes: 2 additions & 2 deletions src/linuxPackager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ export class LinuxPackager extends PlatformPackager<LinuxBuildOptions> {
this.packageFiles = this.computePackageFiles(tempDir)
this.scriptFiles = this.createScripts(tempDir)

if (process.platform === "darwin" && process.env.USE_SYSTEM_FPM !== "true") {
this.fpmPath = downloadFpm("1.5.0-1")
if (process.platform !== "win32" && process.env.USE_SYSTEM_FPM !== "true") {
this.fpmPath = downloadFpm(process.platform === "darwin" ? "1.5.0-1" : "1.5.0-2.3.1", process.platform === "darwin" ? "osx" : `linux-x86${process.arch === "ia32" ? "" : "_64"}`)
}
else {
this.fpmPath = BluebirdPromise.resolve("fpm")
Expand Down
18 changes: 15 additions & 3 deletions src/osxPackager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { PlatformPackager, BuildInfo, normalizeTargets } from "./platformPackage
import { Platform, OsXBuildOptions, MasBuildOptions } from "./metadata"
import * as path from "path"
import { Promise as BluebirdPromise } from "bluebird"
import { log, debug, spawn, statOrNull } from "./util"
import { log, debug, debug7z, spawn, statOrNull } from "./util"
import { createKeychain, deleteKeychain, CodeSigningInfo, generateKeychainName } from "./codeSign"
import { path7za } from "7zip-bin"
import deepAssign = require("deep-assign")
Expand Down Expand Up @@ -221,7 +221,14 @@ export default class OsXPackager extends PlatformPackager<OsXBuildOptions> {
}

private archiveApp(outDir: string, format: string, classifier: string): Promise<string> {
const args = ["a", "-bb" + (debug.enabled ? "3" : "0"), "-bd"]
const args = ["a", "-bd"]
if (debug7z.enabled) {
args.push("-bb3")
}
else if (!debug.enabled) {
args.push("-bb0")
}

const compression = this.devMetadata.build.compression
const storeOnly = compression === "store"
if (format === "zip" || storeOnly) {
Expand All @@ -230,7 +237,12 @@ export default class OsXPackager extends PlatformPackager<OsXBuildOptions> {
if (compression === "maximum") {
// http://superuser.com/a/742034
//noinspection SpellCheckingInspection
args.push("-mfb=258", "-mpass=15")
if (format === "zip") {
args.push("-mfb=258", "-mpass=15")
}
else if (format === "7z") {
args.push("-m0=lzma2", "-mx=9", "-mfb=64", "-md=32m", "-ms=on")
}
}

// we use app name here - see https://github.com/electron-userland/electron-builder/pull/204
Expand Down
23 changes: 12 additions & 11 deletions src/util.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { execFile, spawn as _spawn } from "child_process"
import { execFile, spawn as _spawn, ChildProcess, SpawnOptions } from "child_process"
import { Promise as BluebirdPromise } from "bluebird"
import readPackageJsonAsync = require("read-package-json")
import * as os from "os"
Expand All @@ -14,6 +14,7 @@ const __awaiter = require("./awaiter")
export const log = console.log

export const debug: Debugger = debugFactory("electron-builder")
export const debug7z: Debugger = debugFactory("electron-builder:7z")

export function warn(message: string) {
console.warn(yellow(message))
Expand Down Expand Up @@ -66,20 +67,15 @@ export interface ExecOptions extends BaseExecOptions {
killSignal?: string
}

export interface SpawnOptions extends BaseExecOptions {
custom?: any
detached?: boolean
}

export function exec(file: string, args?: Array<string> | null, options?: ExecOptions): BluebirdPromise<Buffer[]> {
if (debug.enabled) {
debug(`Executing ${file} ${args == null ? "" : args.join(" ")}`)
}

return new BluebirdPromise<Buffer[]>((resolve, reject) => {
execFile(file, args, options, function (error, stdout, stderr) {
execFile(file, <any>args, options, function (error, stdout, stderr) {
if (error == null) {
resolve([stdout, stderr])
resolve(<any>[stdout, stderr])
}
else {
if (stdout.length !== 0) {
Expand All @@ -96,14 +92,19 @@ export function exec(file: string, args?: Array<string> | null, options?: ExecOp
})
}

export function spawn(command: string, args?: Array<string> | null, options?: SpawnOptions): BluebirdPromise<any> {
export function spawn(command: string, args?: Array<string> | null, options?: SpawnOptions, processConsumer?: (it: ChildProcess, reject: (error: Error) => void) => void): BluebirdPromise<any> {
const notNullArgs = args || []
if (debug.enabled) {
debug(`Spawning ${command} ${args == null ? "" : args.join(" ")}`)
debug(`Spawning ${command} ${notNullArgs.join(" ")}`)
}

return new BluebirdPromise<any>((resolve, reject) => {
const p = _spawn(command, args, options)
const p = _spawn(command, notNullArgs, options)
p.on("error", reject)
p.on("close", (code: number) => code === 0 ? resolve() : reject(new Error(command + " exited with code " + code)))
if (processConsumer != null) {
processConsumer(p, reject)
}
})
}

Expand Down
2 changes: 0 additions & 2 deletions test/install-linux-dependencies.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
gem install --no-rdoc --no-ri fpm

wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | sudo apt-key add -

sudo dpkg --add-architecture i386
Expand Down
2 changes: 1 addition & 1 deletion test/src/helpers/packTester.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export async function assertPack(fixtureName: string, packagerOptions: PackagerO
const customTmpDir = process.env.TEST_APP_TMP_DIR
if (useTempDir) {
// non-osx test uses the same dir as osx test, but we cannot share node_modules (because tests executed in parallel)
const dir = customTmpDir == null ? path.join(tmpdir(), `${tmpDirPrefix}${fixtureName}-${tmpDirCounter++}}`) : path.resolve(customTmpDir)
const dir = customTmpDir == null ? path.join(tmpdir(), `${tmpDirPrefix}${fixtureName}-${tmpDirCounter++}`) : path.resolve(customTmpDir)
if (customTmpDir != null) {
console.log("Custom temp dir used: %s", customTmpDir)
}
Expand Down
Loading

0 comments on commit 7d5b747

Please sign in to comment.