Skip to content

Commit 7d5b747

Browse files
committed
feat: use self-contained fpm on Linux — don't need to install ruby anymore
1 parent 7ac6ca2 commit 7d5b747

File tree

13 files changed

+533
-279
lines changed

13 files changed

+533
-279
lines changed

.idea/dictionaries/develar.xml

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ cache:
2020
- node_modules
2121
- test/testApp/node_modules
2222
- $HOME/.electron
23+
- $HOME/.cache/fpm
2324

2425
addons:
2526
apt:

docs/Multi Platform Build.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,16 @@ brew install gnu-tar libicns graphicsmagick
2828
## Linux
2929
To build app in distributable format for Linux:
3030
```
31-
sudo apt-get install ruby ruby-dev gcc make icnsutils graphicsmagick xz-utils
32-
gem install fpm
31+
sudo apt-get install icnsutils graphicsmagick xz-utils
3332
```
3433

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

3837
```
39-
sudo add-apt-repository ppa:ubuntu-wine/ppa
38+
sudo add-apt-repository ppa:ubuntu-wine/ppa -y
4039
sudo apt-get update
41-
sudo apt-get install wine1.8
40+
sudo apt-get install wine1.8 -y
4241
```
4342

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

5352
To build app in 32 bit from a machine with 64 bit:

package.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
"bugs": "https://github.com/electron-userland/electron-builder/issues",
5656
"homepage": "https://github.com/electron-userland/electron-builder",
5757
"dependencies": {
58-
"7zip-bin": "^0.0.4",
58+
"7zip-bin": "^0.3.0",
5959
"asar": "^0.11.0",
6060
"bluebird": "^3.3.5",
6161
"chalk": "^1.1.3",
@@ -106,7 +106,7 @@
106106
"ts-babel": "^0.8.6",
107107
"tsconfig-glob": "^0.4.3",
108108
"tslint": "^3.9.0-dev.0",
109-
"typescript": "1.9.0-dev.20160507",
109+
"typescript": "1.9.0-dev.20160509",
110110
"whitespace": "^2.0.0"
111111
},
112112
"babel": {
@@ -123,5 +123,8 @@
123123
"test/out/*"
124124
]
125125
},
126-
"typings": "./out/electron-builder.d.ts"
126+
"typings": "./out/electron-builder.d.ts",
127+
"publishConfig": {
128+
"tag": "next"
129+
}
127130
}

src/fpmDownload.ts

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { statOrNull, spawn, debug } from "./util"
2-
import { emptyDir, move, remove } from "fs-extra-p"
1+
import { statOrNull, spawn, debug, debug7z } from "./util"
2+
import { readdir, mkdirs, move, remove } from "fs-extra-p"
33
import { download } from "./httpRequest"
44
import { path7za } from "7zip-bin"
55
import * as path from "path"
6-
import { tmpdir } from "os"
6+
import { homedir } from "os"
77
import { Promise as BluebirdPromise } from "bluebird"
88

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

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

28-
promise = <BluebirdPromise<string>>doDownloadFpm(version)
28+
promise = <BluebirdPromise<string>>doDownloadFpm(version, osAndArch)
2929
versionToPromise.set(version, promise)
3030
return promise
3131
}
3232

33-
async function doDownloadFpm(version: string): Promise<string> {
34-
const dirName = `fpm-${version}-osx`
33+
async function doDownloadFpm(version: string, osAndArch: string): Promise<string> {
34+
const dirName = `fpm-${version}-${osAndArch}`
3535
const url = `https://github.com/develar/fpm-self-contained/releases/download/v${version}/${dirName}.7z`
36-
const cache = path.join(__dirname, "..", "node_modules", ".fpm")
37-
const fpmDir = path.join(cache, dirName)
36+
37+
// 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
38+
// * don't need to find node_modules
39+
// * don't pollute user project dir (important in case of 1-package.json project structure)
40+
// * simplify/speed-up tests (don't download fpm for each test project)
41+
const cacheDir = path.join(homedir(), ".cache", "fpm")
42+
const fpmDir = path.join(cacheDir, dirName)
3843

3944
const stat = await statOrNull(fpmDir)
4045
if (stat != null && stat.isDirectory()) {
46+
debug(`Found existing fpm ${fpmDir}`)
4147
return path.join(fpmDir, "fpm")
4248
}
4349

4450
// the only version currently supported (i.e. all clients are consumed the same version
45-
await emptyDir(cache)
51+
await emptyDir(cacheDir, dirName)
4652

47-
const archiveName = path.join(tmpdir(), getTempName("fpm-download") + ".7z")
53+
// 7z cannot be extracted from the input stream, temp file is required
54+
const tempName = getTempName()
55+
const archiveName = path.join(cacheDir, tempName + ".7z")
56+
debug(`Download fpm from ${url} to ${archiveName}`)
4857
await download(url, archiveName)
49-
const tempUnpackDir = path.join(cache, getTempName())
50-
await spawn(path7za, ["x", archiveName, "-o" + tempUnpackDir, "-bb" + (debug.enabled ? "3" : "0"), "-bd"], {
51-
cwd: cache,
52-
stdio: ["ignore", "inherit", "inherit"],
58+
const tempUnpackDir = path.join(cacheDir, tempName)
59+
const args = ["x", archiveName, "-o" + tempName, "-bd"]
60+
if (debug7z.enabled) {
61+
args.push("-bb3")
62+
}
63+
else if (!debug.enabled) {
64+
args.push("-bb0")
65+
}
66+
await spawn(path7za, args, {
67+
cwd: cacheDir,
68+
stdio: ["ignore", debug.enabled ? "inherit" : "ignore", "inherit"],
5369
})
5470

55-
await move(path.join(tempUnpackDir, dirName), fpmDir, {clobber: true})
56-
await BluebirdPromise.all([remove(tempUnpackDir), remove(archiveName)])
71+
await BluebirdPromise.all([move(path.join(tempUnpackDir, dirName), fpmDir, {clobber: true}), remove(archiveName)])
72+
await remove(tempUnpackDir)
5773

74+
debug(`fpm downloaded to ${fpmDir}`)
5875
return path.join(fpmDir, "fpm")
76+
}
77+
78+
// prefix to not delete dir or archived dir (.7z)
79+
async function emptyDir(dir: string, excludeNamePrefix: string) {
80+
let items: string[] | null = null
81+
try {
82+
items = await readdir(dir)
83+
}
84+
catch (e) {
85+
await mkdirs(dir)
86+
return
87+
}
88+
89+
items = items!
90+
.filter(it => !it.startsWith(excludeNamePrefix))
91+
.map(it => path.join(dir, it))
92+
93+
await BluebirdPromise.map(items, remove)
5994
}

src/linuxPackager.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ export class LinuxPackager extends PlatformPackager<LinuxBuildOptions> {
3737
this.packageFiles = this.computePackageFiles(tempDir)
3838
this.scriptFiles = this.createScripts(tempDir)
3939

40-
if (process.platform === "darwin" && process.env.USE_SYSTEM_FPM !== "true") {
41-
this.fpmPath = downloadFpm("1.5.0-1")
40+
if (process.platform !== "win32" && process.env.USE_SYSTEM_FPM !== "true") {
41+
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"}`)
4242
}
4343
else {
4444
this.fpmPath = BluebirdPromise.resolve("fpm")

src/osxPackager.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { PlatformPackager, BuildInfo, normalizeTargets } from "./platformPackage
22
import { Platform, OsXBuildOptions, MasBuildOptions } from "./metadata"
33
import * as path from "path"
44
import { Promise as BluebirdPromise } from "bluebird"
5-
import { log, debug, spawn, statOrNull } from "./util"
5+
import { log, debug, debug7z, spawn, statOrNull } from "./util"
66
import { createKeychain, deleteKeychain, CodeSigningInfo, generateKeychainName } from "./codeSign"
77
import { path7za } from "7zip-bin"
88
import deepAssign = require("deep-assign")
@@ -221,7 +221,14 @@ export default class OsXPackager extends PlatformPackager<OsXBuildOptions> {
221221
}
222222

223223
private archiveApp(outDir: string, format: string, classifier: string): Promise<string> {
224-
const args = ["a", "-bb" + (debug.enabled ? "3" : "0"), "-bd"]
224+
const args = ["a", "-bd"]
225+
if (debug7z.enabled) {
226+
args.push("-bb3")
227+
}
228+
else if (!debug.enabled) {
229+
args.push("-bb0")
230+
}
231+
225232
const compression = this.devMetadata.build.compression
226233
const storeOnly = compression === "store"
227234
if (format === "zip" || storeOnly) {
@@ -230,7 +237,12 @@ export default class OsXPackager extends PlatformPackager<OsXBuildOptions> {
230237
if (compression === "maximum") {
231238
// http://superuser.com/a/742034
232239
//noinspection SpellCheckingInspection
233-
args.push("-mfb=258", "-mpass=15")
240+
if (format === "zip") {
241+
args.push("-mfb=258", "-mpass=15")
242+
}
243+
else if (format === "7z") {
244+
args.push("-m0=lzma2", "-mx=9", "-mfb=64", "-md=32m", "-ms=on")
245+
}
234246
}
235247

236248
// we use app name here - see https://github.com/electron-userland/electron-builder/pull/204

src/util.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { execFile, spawn as _spawn } from "child_process"
1+
import { execFile, spawn as _spawn, ChildProcess, SpawnOptions } from "child_process"
22
import { Promise as BluebirdPromise } from "bluebird"
33
import readPackageJsonAsync = require("read-package-json")
44
import * as os from "os"
@@ -14,6 +14,7 @@ const __awaiter = require("./awaiter")
1414
export const log = console.log
1515

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

1819
export function warn(message: string) {
1920
console.warn(yellow(message))
@@ -66,20 +67,15 @@ export interface ExecOptions extends BaseExecOptions {
6667
killSignal?: string
6768
}
6869

69-
export interface SpawnOptions extends BaseExecOptions {
70-
custom?: any
71-
detached?: boolean
72-
}
73-
7470
export function exec(file: string, args?: Array<string> | null, options?: ExecOptions): BluebirdPromise<Buffer[]> {
7571
if (debug.enabled) {
7672
debug(`Executing ${file} ${args == null ? "" : args.join(" ")}`)
7773
}
7874

7975
return new BluebirdPromise<Buffer[]>((resolve, reject) => {
80-
execFile(file, args, options, function (error, stdout, stderr) {
76+
execFile(file, <any>args, options, function (error, stdout, stderr) {
8177
if (error == null) {
82-
resolve([stdout, stderr])
78+
resolve(<any>[stdout, stderr])
8379
}
8480
else {
8581
if (stdout.length !== 0) {
@@ -96,14 +92,19 @@ export function exec(file: string, args?: Array<string> | null, options?: ExecOp
9692
})
9793
}
9894

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

104101
return new BluebirdPromise<any>((resolve, reject) => {
105-
const p = _spawn(command, args, options)
102+
const p = _spawn(command, notNullArgs, options)
103+
p.on("error", reject)
106104
p.on("close", (code: number) => code === 0 ? resolve() : reject(new Error(command + " exited with code " + code)))
105+
if (processConsumer != null) {
106+
processConsumer(p, reject)
107+
}
107108
})
108109
}
109110

test/install-linux-dependencies.sh

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
gem install --no-rdoc --no-ri fpm
2-
31
wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
42

53
sudo dpkg --add-architecture i386

test/src/helpers/packTester.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export async function assertPack(fixtureName: string, packagerOptions: PackagerO
3838
const customTmpDir = process.env.TEST_APP_TMP_DIR
3939
if (useTempDir) {
4040
// non-osx test uses the same dir as osx test, but we cannot share node_modules (because tests executed in parallel)
41-
const dir = customTmpDir == null ? path.join(tmpdir(), `${tmpDirPrefix}${fixtureName}-${tmpDirCounter++}}`) : path.resolve(customTmpDir)
41+
const dir = customTmpDir == null ? path.join(tmpdir(), `${tmpDirPrefix}${fixtureName}-${tmpDirCounter++}`) : path.resolve(customTmpDir)
4242
if (customTmpDir != null) {
4343
console.log("Custom temp dir used: %s", customTmpDir)
4444
}

0 commit comments

Comments
 (0)