Skip to content

Commit

Permalink
perf: use fcopy
Browse files Browse the repository at this point in the history
  • Loading branch information
develar committed Jun 8, 2017
1 parent 9ef77b9 commit d9a8015
Show file tree
Hide file tree
Showing 22 changed files with 18,517 additions and 16,713 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 docs/Options.md
Expand Up @@ -258,6 +258,7 @@ Configuration Options
* <a name="NsisOptions-artifactName"></a>`artifactName` String - The [artifact file name pattern](https://github.com/electron-userland/electron-builder/wiki/Options#artifact-file-name-pattern). Defaults to `${productName} Setup ${version}.${ext}`.
* <a name="NsisOptions-unicode"></a>`unicode` = `true` Boolean - Whether to create [Unicode installer](http://nsis.sourceforge.net/Docs/Chapter1.html#intro-unicode).
* <a name="NsisOptions-deleteAppDataOnUninstall"></a>`deleteAppDataOnUninstall` = `false` Boolean - *one-click installer only.* Whether to delete app data on uninstall.
* <a name="NsisOptions-packElevateHelper"></a>`packElevateHelper` = `true` Boolean - Whether to pack the elevate executable (required for electron-updater if per-machine installer used or can be used in the future). Ignored if `perMachine` is set to `true`.
* <a name="Config-nsisWeb"></a>`nsisWeb`<a name="NsisWebOptions"></a> - Web Installer specific options.
* <a name="NsisWebOptions-appPackageUrl"></a>`appPackageUrl` String - The application package download URL. Optional — by default computed using publish configuration.

Expand Down
18 changes: 16 additions & 2 deletions docs/api/electron-builder-util.md
Expand Up @@ -83,7 +83,8 @@
* [.FileCopier](#FileCopier)
* [`.copy(src, dest, stat)`](#module_electron-builder-util/out/fs.FileCopier+copy) ⇒ <code>Promise&lt;void&gt;</code>
* [`.copyDir(src, destination, filter, transformer, isUseHardLink)`](#module_electron-builder-util/out/fs.copyDir) ⇒ <code>Promise&lt;any&gt;</code>
* [`.copyFile(src, dest, stats, isUseHardLink)`](#module_electron-builder-util/out/fs.copyFile) ⇒ <code>Promise&lt;any&gt;</code>
* [`.copyFile(src, dest, isEnsureDir)`](#module_electron-builder-util/out/fs.copyFile) ⇒ <code>Promise&lt;any&gt;</code>
* [`.copyOrLinkFile(src, dest, stats, isUseHardLink)`](#module_electron-builder-util/out/fs.copyOrLinkFile) ⇒ <code>Promise&lt;any&gt;</code>
* [`.exists(file)`](#module_electron-builder-util/out/fs.exists) ⇒ <code>Promise&lt;Boolean&gt;</code>
* [`.statOrNull(file)`](#module_electron-builder-util/out/fs.statOrNull) ⇒ <code>Promise&lt; \| module:fs.Stats&gt;</code>
* [`.unlinkIfExists(file)`](#module_electron-builder-util/out/fs.unlinkIfExists) ⇒ <code>Promise&lt;String \| void&gt;</code>
Expand Down Expand Up @@ -122,10 +123,23 @@ Hard links is used if supported and allowed.

<a name="module_electron-builder-util/out/fs.copyFile"></a>

### `electron-builder-util/out/fs.copyFile(src, dest, stats, isUseHardLink)` ⇒ <code>Promise&lt;any&gt;</code>
### `electron-builder-util/out/fs.copyFile(src, dest, isEnsureDir)` ⇒ <code>Promise&lt;any&gt;</code>
**Kind**: method of [<code>electron-builder-util/out/fs</code>](#module_electron-builder-util/out/fs)

| Param | Type |
| --- | --- |
| src | <code>String</code> |
| dest | <code>String</code> |
| isEnsureDir | |

<a name="module_electron-builder-util/out/fs.copyOrLinkFile"></a>

### `electron-builder-util/out/fs.copyOrLinkFile(src, dest, stats, isUseHardLink)` ⇒ <code>Promise&lt;any&gt;</code>
Hard links is used if supported and allowed.
File permission is fixed — allow execute for all if owner can, allow read for all if owner can.

ensureDir is not called, dest parent dir must exists

**Kind**: method of [<code>electron-builder-util/out/fs</code>](#module_electron-builder-util/out/fs)

| Param | Type |
Expand Down
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -40,6 +40,7 @@
"electron-download-tf": "4.3.1",
"electron-is-dev": "^0.1.2",
"electron-osx-sign": "0.4.6",
"fcopy-pre-bundled": "^0.1.2",
"fs-extra-p": "^4.3.0",
"hosted-git-info": "^2.4.2",
"ini": "^1.3.4",
Expand Down
Expand Up @@ -3,7 +3,7 @@ import { debug, exec, execWine, prepareArgs, spawn } from "electron-builder-util
import { copyFile, walk } from "electron-builder-util/out/fs"
import { log } from "electron-builder-util/out/log"
import { WinPackager } from "electron-builder/out/winPackager"
import { copy, createWriteStream, ensureDir, remove, stat, unlink } from "fs-extra-p"
import { createWriteStream, ensureDir, remove, stat, unlink } from "fs-extra-p"
import * as path from "path"

const archiver = require("archiver")
Expand Down Expand Up @@ -50,7 +50,7 @@ export interface SquirrelOptions {
export async function buildInstaller(options: SquirrelOptions, outputDirectory: string, setupExe: string, packager: WinPackager, appOutDir: string) {
const appUpdate = await packager.getTempFile("Update.exe")
await BluebirdPromise.all([
copy(path.join(options.vendorPath, "Update.exe"), appUpdate)
copyFile(path.join(options.vendorPath, "Update.exe"), appUpdate)
.then(() => packager.sign(appUpdate)),
BluebirdPromise.all([remove(`${outputDirectory.replace(/\\/g, "/")}/*-full.nupkg`), remove(path.join(outputDirectory, "RELEASES"))])
.then(() => ensureDir(outputDirectory))
Expand Down Expand Up @@ -79,7 +79,7 @@ export async function buildInstaller(options: SquirrelOptions, outputDirectory:

await BluebirdPromise.all<any>([
pack(options, appOutDir, appUpdate, nupkgPath, version, packager),
copy(path.join(options.vendorPath, "Setup.exe"), setupPath),
copyFile(path.join(options.vendorPath, "Setup.exe"), setupPath),
])

embeddedArchive.file(nupkgPath, {name: packageName})
Expand Down Expand Up @@ -233,7 +233,7 @@ async function encodedZip(archive: any, dir: string, prefix: string, vendorPath:
// createExecutableStubForExe
if (file.endsWith(".exe") && !file.includes("squirrel.exe")) {
const tempFile = await packager.getTempFile("stub.exe")
await copyFile(path.join(vendorPath, "StubExecutable.exe"), tempFile, null, false)
await copyFile(path.join(vendorPath, "StubExecutable.exe"), tempFile)
await execWine(path.join(vendorPath, "WriteZipToSetup.exe"), ["--copy-stub-resources", file, tempFile])
await packager.sign(tempFile)

Expand Down
1 change: 1 addition & 0 deletions packages/electron-builder-util/package.json
Expand Up @@ -12,6 +12,7 @@
],
"dependencies": {
"fs-extra-p": "^4.3.0",
"fcopy-pre-bundled": "^0.1.2",
"is-ci": "^1.0.10",
"stat-mode": "^0.2.2",
"bluebird-lst": "^1.0.2",
Expand Down
29 changes: 13 additions & 16 deletions packages/electron-builder-util/src/fs.ts
@@ -1,5 +1,6 @@
import BluebirdPromise from "bluebird-lst"
import { access, createReadStream, createWriteStream, link, lstat, mkdirs, readdir, readlink, stat, Stats, symlink, unlink, writeFile } from "fs-extra-p"
import fcopy from "fcopy-pre-bundled"
import { access, ensureDir, link, lstat, readdir, readlink, stat, Stats, symlink, unlink, writeFile } from "fs-extra-p"
import isCi from "is-ci"
import * as path from "path"
import Mode from "stat-mode"
Expand Down Expand Up @@ -109,11 +110,17 @@ export async function walk(initialDirPath: string, filter?: Filter | null, consu

const _isUseHardLink = process.platform != "win32" && process.env.USE_HARD_LINKS !== "false" && (isCi || process.env.USE_HARD_LINKS === "true")

export function copyFile(src: string, dest: string, isEnsureDir = true) {
return (isEnsureDir ? ensureDir(path.dirname(dest)) : BluebirdPromise.resolve()).then(() => copyOrLinkFile(src, dest, null, false))
}

/**
* Hard links is used if supported and allowed.
* File permission is fixed — allow execute for all if owner can, allow read for all if owner can.
*
* ensureDir is not called, dest parent dir must exists
*/
export function copyFile(src: string, dest: string, stats?: Stats | null, isUseHardLink = _isUseHardLink): Promise<any> {
export function copyOrLinkFile(src: string, dest: string, stats?: Stats | null, isUseHardLink = _isUseHardLink): Promise<any> {
if (stats != null) {
const originalModeNumber = stats.mode
const mode = new Mode(stats)
Expand Down Expand Up @@ -146,17 +153,7 @@ export function copyFile(src: string, dest: string, stats?: Stats | null, isUseH
}

return new BluebirdPromise(function (resolve, reject) {
const readStream = createReadStream(src)
const writeStream = createWriteStream(dest, stats == null ? undefined : {mode: stats.mode})

readStream.on("error", reject)
writeStream.on("error", reject)

writeStream.on("open", function () {
readStream.pipe(writeStream)
})

writeStream.once("close", resolve)
fcopy(src, dest, stats == null ? undefined : {mode: stats.mode}, error => error == null ? resolve() : reject(error))
})
}

Expand All @@ -181,7 +178,7 @@ export class FileCopier {
}
}
}
await copyFile(src, dest, stat, (!this.isUseHardLink || this.isUseHardLinkFunction == null) ? this.isUseHardLink : this.isUseHardLinkFunction(dest))
await copyOrLinkFile(src, dest, stat, (!this.isUseHardLink || this.isUseHardLinkFunction == null) ? this.isUseHardLink : this.isUseHardLinkFunction(dest))
}
catch (e) {
// files are copied concurrently, so, we must not check here currentIsUseHardLink — our code can be executed after that other handler will set currentIsUseHardLink to false
Expand All @@ -192,7 +189,7 @@ export class FileCopier {
this.isUseHardLink = false
}

await copyFile(src, dest, stat, false)
await copyOrLinkFile(src, dest, stat, false)
}
else {
throw e
Expand All @@ -219,7 +216,7 @@ export function copyDir(src: string, destination: string, filter?: Filter, trans
}

if (!createdSourceDirs.has(parent)) {
await mkdirs(parent.replace(src, destination))
await ensureDir(parent.replace(src, destination))
createdSourceDirs.add(parent)
}

Expand Down
3 changes: 2 additions & 1 deletion packages/electron-builder-util/tsconfig.json
Expand Up @@ -18,6 +18,7 @@
"../../typings/debug.d.ts",
"../../typings/node-emoji.d.ts",
"../../typings/pretty-ms.d.ts",
"../../typings/ansi-escapes.d.ts"
"../../typings/ansi-escapes.d.ts",
"../../typings/fcopy-pre-bundled.d.ts"
]
}
6 changes: 3 additions & 3 deletions packages/electron-builder/src/codeSign.ts
@@ -1,11 +1,11 @@
import BluebirdPromise from "bluebird-lst"
import { randomBytes } from "crypto"
import { exec, getCacheDirectory, getTempName, isEmptyOrSpaces } from "electron-builder-util"
import { statOrNull } from "electron-builder-util/out/fs"
import { copyFile, statOrNull } from "electron-builder-util/out/fs"
import { httpExecutor } from "electron-builder-util/out/nodeHttpExecutor"
import { all, executeFinally } from "electron-builder-util/out/promise"
import { TmpDir } from "electron-builder-util/out/tmp"
import { copy, deleteFile, outputFile, rename } from "fs-extra-p"
import { deleteFile, outputFile, rename } from "fs-extra-p"
import isCi from "is-ci"
import { homedir } from "os"
import * as path from "path"
Expand Down Expand Up @@ -73,7 +73,7 @@ async function createCustomCertKeychain() {
const keychainPath = path.join(getCacheDirectory(), "electron-builder-root-certs.keychain")
const results = await BluebirdPromise.all<string>([
exec("security", ["list-keychains"]),
copy(path.join(__dirname, "..", "certs", "root_certs.keychain"), tmpKeychainPath)
copyFile(path.join(__dirname, "..", "certs", "root_certs.keychain"), tmpKeychainPath)
.then(() => rename(tmpKeychainPath, keychainPath)),
])
const list = results[0]
Expand Down
6 changes: 3 additions & 3 deletions packages/electron-builder/src/fileMatcher.ts
@@ -1,6 +1,6 @@
import BluebirdPromise from "bluebird-lst"
import { asArray, debug } from "electron-builder-util"
import { copyDir, copyFile, Filter, statOrNull } from "electron-builder-util/out/fs"
import { copyDir, copyOrLinkFile, Filter, statOrNull } from "electron-builder-util/out/fs"
import { warn } from "electron-builder-util/out/log"
import { mkdirs } from "fs-extra-p"
import { Minimatch } from "minimatch"
Expand Down Expand Up @@ -183,11 +183,11 @@ export function copyFiles(patterns: Array<FileMatcher> | null): Promise<any> {
const toStat = await statOrNull(pattern.to)
// https://github.com/electron-userland/electron-builder/issues/1245
if (toStat != null && toStat.isDirectory()) {
return await copyFile(pattern.from, path.join(pattern.to, path.basename(pattern.from)), fromStat)
return await copyOrLinkFile(pattern.from, path.join(pattern.to, path.basename(pattern.from)), fromStat)
}

await mkdirs(path.dirname(pattern.to))
return await copyFile(pattern.from, pattern.to, fromStat)
return await copyOrLinkFile(pattern.from, pattern.to, fromStat)
}

if (pattern.isEmpty() || pattern.containsOnlyIgnore()) {
Expand Down
8 changes: 4 additions & 4 deletions packages/electron-builder/src/packager/mac.ts
@@ -1,9 +1,9 @@
import { AsarIntegrity } from "asar-integrity"
import BluebirdPromise from "bluebird-lst"
import { asArray, getPlatformIconFileName, use } from "electron-builder-util"
import { copyFile, unlinkIfExists } from "electron-builder-util/out/fs"
import { copyFile, copyOrLinkFile, unlinkIfExists } from "electron-builder-util/out/fs"
import { warn } from "electron-builder-util/out/log"
import { copy, readFile, rename, unlink, utimes, writeFile } from "fs-extra-p"
import { readFile, rename, unlink, utimes, writeFile } from "fs-extra-p"
import * as path from "path"
import { build as buildPlist, parse as parsePlist } from "plist"
import { normalizeExt, PlatformPackager } from "../platformPackager"
Expand Down Expand Up @@ -116,7 +116,7 @@ export async function createApp(packager: PlatformPackager<any>, appOutDir: stri
let iconFile = appPlist.CFBundleIconFile
if (customIcon != null) {
iconFile = path.basename(customIcon)
await copyFile(customIcon, path.join(resourcesPath, iconFile))
await copyOrLinkFile(customIcon, path.join(resourcesPath, iconFile))
}

const result = <any>{
Expand Down Expand Up @@ -152,7 +152,7 @@ export async function createApp(packager: PlatformPackager<any>, appOutDir: stri

if (icon != null) {
promises.push(unlink(path.join(resourcesPath, oldIcon)))
promises.push(copy(icon, path.join(resourcesPath, appPlist.CFBundleIconFile)))
promises.push(copyFile(icon, path.join(resourcesPath, appPlist.CFBundleIconFile)))
}

await BluebirdPromise.all(promises)
Expand Down
8 changes: 4 additions & 4 deletions packages/electron-builder/src/targets/appx.ts
@@ -1,8 +1,8 @@
import BluebirdPromise from "bluebird-lst"
import { Arch, getArchSuffix, Target } from "electron-builder-core"
import { spawn, use } from "electron-builder-util"
import { copyDir } from "electron-builder-util/out/fs"
import { copy, emptyDir, readFile, writeFile } from "fs-extra-p"
import { copyDir, copyFile } from "electron-builder-util/out/fs"
import { emptyDir, readFile, writeFile } from "fs-extra-p"
import { release } from "os"
import * as path from "path"
import sanitizeFileName from "sanitize-filename"
Expand Down Expand Up @@ -55,9 +55,9 @@ export default class AppXTarget extends Target {
BluebirdPromise.map(["44x44", "50x50", "150x150", "310x150"], size => {
const target = path.join(preAppx, "assets", `${safeName}.${size}.png`)
if (resourceList.includes(`${size}.png`)) {
return copy(path.join(packager.buildResourcesDir, `${size}.png`), target)
return copyFile(path.join(packager.buildResourcesDir, `${size}.png`), target)
}
return copy(path.join(vendorPath, "appxAssets", `SampleAppx.${size}.png`), target)
return copyFile(path.join(vendorPath, "appxAssets", `SampleAppx.${size}.png`), target)
}),
copyDir(appOutDir, path.join(preAppx, "app")),
this.writeManifest(templatePath, preAppx, safeName, arch, publisher)
Expand Down
8 changes: 4 additions & 4 deletions packages/electron-builder/src/targets/dmg.ts
Expand Up @@ -2,10 +2,10 @@ import BluebirdPromise from "bluebird-lst"
import { Arch, Target } from "electron-builder-core"
import { debug, exec, isEmptyOrSpaces, spawn, use } from "electron-builder-util"
import { deepAssign } from "electron-builder-util/out/deepAssign"
import { exists, statOrNull } from "electron-builder-util/out/fs"
import { copyFile, exists, statOrNull } from "electron-builder-util/out/fs"
import { log, warn } from "electron-builder-util/out/log"
import { executeFinally } from "electron-builder-util/out/promise"
import { copy, outputFile, readFile, remove, unlink } from "fs-extra-p"
import { outputFile, readFile, remove, unlink } from "fs-extra-p"
import * as path from "path"
import sanitizeFileName from "sanitize-filename"
import { DmgOptions, MacOptions } from "../options/macOptions"
Expand Down Expand Up @@ -33,7 +33,7 @@ export class DmgTarget extends Target {
const backgroundDir = path.join(tempDir, ".background")
const backgroundFilename = specification.background == null ? null : path.basename(specification.background)
if (backgroundFilename != null) {
await copy(path.resolve(packager.info.projectDir, specification.background!), path.join(backgroundDir, backgroundFilename))
await copyFile(path.resolve(packager.info.projectDir, specification.background!), path.join(backgroundDir, backgroundFilename))
}

let preallocatedSize = 32 * 1024
Expand Down Expand Up @@ -98,7 +98,7 @@ export class DmgTarget extends Target {
}
else {
const volumeIcon = `${volumePath}/.VolumeIcon.icns`
promises.push(copy((await packager.getResource(specification.icon))!, volumeIcon))
promises.push(copyFile((await packager.getResource(specification.icon))!, volumeIcon))
env.volumeIcon = volumeIcon
}

Expand Down
2 changes: 1 addition & 1 deletion packages/electron-builder/src/targets/nsis.ts
Expand Up @@ -111,7 +111,7 @@ export class NsisTarget extends Target {
}

if (isPackElevateHelper !== false) {
await copyFile(path.join(await nsisPathPromise, "elevate.exe"), path.join(appOutDir, "resources", "elevate.exe"), null, false)
await copyFile(path.join(await nsisPathPromise, "elevate.exe"), path.join(appOutDir, "resources", "elevate.exe"), false)
}

const packager = this.packager
Expand Down
5 changes: 3 additions & 2 deletions packages/electron-builder/src/targets/snap.ts
@@ -1,7 +1,8 @@
import { Arch, Target, toLinuxArchString } from "electron-builder-core"
import { replaceDefault, spawn } from "electron-builder-util"
import { copyFile } from "electron-builder-util/out/fs"
import { log } from "electron-builder-util/out/log"
import { copy, emptyDir, outputFile } from "fs-extra-p"
import { emptyDir, outputFile } from "fs-extra-p"
import { safeDump } from "js-yaml"
import { homedir } from "os"
import * as path from "path"
Expand Down Expand Up @@ -45,7 +46,7 @@ export default class SnapTarget extends Target {
await this.helper.icons
if (this.helper.maxIconPath != null) {
snap.icon = "snap/gui/icon.png"
await copy(this.helper.maxIconPath, path.join(snapDir, "gui", "icon.png"))
await copyFile(this.helper.maxIconPath, path.join(snapDir, "gui", "icon.png"))
}

const desktopFile = await this.helper.computeDesktopEntry(this.options, `${packager.executableName}`, path.join(snapDir, "gui", `${snap.name}.desktop`), {
Expand Down
3 changes: 1 addition & 2 deletions packages/electron-builder/src/yarn.ts
Expand Up @@ -55,8 +55,7 @@ function installDependencies(appDir: string, frameworkInfo: DesktopFrameworkInfo
let execPath = process.env.npm_execpath || process.env.NPM_CLI_JS
const execArgs = ["install", "--production"]

const isYarn = isYarnPath(execPath)
if (!isYarn) {
if (!isYarnPath(execPath)) {
if (process.env.NPM_NO_BIN_LINKS === "true") {
execArgs.push("--no-bin-links")
}
Expand Down
4 changes: 2 additions & 2 deletions test/src/ArtifactPublisherTest.ts
@@ -1,12 +1,12 @@
import { HttpError } from "electron-builder-http"
import { CancellationToken } from "electron-builder-http/out/CancellationToken"
import { S3Options } from "electron-builder-http/out/publishOptions"
import { copyFile } from "electron-builder-util/out/fs"
import { TmpDir } from "electron-builder-util/out/tmp"
import { createPublisher } from "electron-builder/out/publish/PublishManager"
import { PublishContext } from "electron-publish"
import { BintrayPublisher } from "electron-publish/out/BintrayPublisher"
import { GitHubPublisher } from "electron-publish/out/gitHubPublisher"
import { copy } from "fs-extra-p"
import isCi from "is-ci"
import { join } from "path"

Expand Down Expand Up @@ -82,7 +82,7 @@ test("Bintray upload", async () => {

const tmpDir = new TmpDir()
const artifactPath = await tmpDir.getTempFile(`icon-${version}.icns`)
await copy(iconPath, artifactPath)
await copyFile(iconPath, artifactPath)

//noinspection SpellCheckingInspection
const publisher = new BintrayPublisher(publishContext, {provider: "bintray", owner: "actperepo", package: "test", repo: "generic", token: "5df2cadec86dff91392e4c419540785813c3db15"}, version)
Expand Down

0 comments on commit d9a8015

Please sign in to comment.