Skip to content

Commit

Permalink
feat: Yarn Support (rebuild)
Browse files Browse the repository at this point in the history
Closes #861
  • Loading branch information
develar committed Nov 9, 2016
1 parent e89934d commit 94cc7be
Show file tree
Hide file tree
Showing 11 changed files with 199 additions and 118 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ install:
- if [[ "$TRAVIS_BRANCH" == "master" && "$TRAVIS_PULL_REQUEST" == "false" && "$AUTO_PUBLISH" != "false" && "$TRAVIS_TAG" == "" && "$PUBLISH_TO_NPM" == "true" ]]; then yarn add @develar/semantic-release@next --dev ; fi

script:
- npm run test
- yarn run test

after_success:
- if [[ "$TRAVIS_BRANCH" == "master" && "$TRAVIS_PULL_REQUEST" == "false" && "$AUTO_PUBLISH" != "false" && "$TRAVIS_TAG" == "" && "$PUBLISH_TO_NPM" == "true" ]]; then npm run semantic-release ; fi
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,15 @@
"archiver": "^1.2.0",
"archiver-utils": "^1.3.0",
"asar-electron-builder": "^0.13.5",
"bluebird-lst-c": "^1.0.3",
"bluebird-lst-c": "^1.0.4",
"chalk": "^1.1.3",
"chromium-pickle-js": "^0.2.0",
"cli-cursor": "^1.0.2",
"cuint": "^0.2.2",
"debug": "^2.2.0",
"electron-download": "2.1.2",
"electron-osx-sign-tf": "~1.1.0",
"fs-extra-p": "^2.0.5",
"fs-extra-p": "^2.0.6",
"hosted-git-info": "^2.1.5",
"ini": "^1.3.4",
"isbinaryfile": "^3.0.1",
Expand Down
2 changes: 1 addition & 1 deletion src/codeSign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ async function _findIdentity(namePrefix: CertType, qualifier?: string | null, ke
export async function findIdentity(certType: CertType, qualifier?: string | null, keychain?: string | null): Promise<string | null> {
let identity = process.env.CSC_NAME || qualifier
if (isEmptyOrSpaces(identity)) {
if (keychain == null && !isCi() && process.env.CSC_IDENTITY_AUTO_DISCOVERY === "false") {
if (keychain == null && !isCi() && (process.env.CSC_IDENTITY_AUTO_DISCOVERY === "false")) {
return null
}
else {
Expand Down
3 changes: 2 additions & 1 deletion src/install-app-deps.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
#! /usr/bin/env node

import { computeDefaultAppDirectory, installDependencies, getElectronVersion, use } from "./util/util"
import { computeDefaultAppDirectory, getElectronVersion, use } from "./util/util"
import { printErrorAndExit } from "./util/promise"
import * as path from "path"
import BluebirdPromise from "bluebird-lst-c"
import { DevMetadata } from "./metadata"
import yargs from "yargs"
import { readPackageJson } from "./util/readPackageJson"
import { installDependencies } from "./yarn"

const args: any = yargs
.option("arch", {
Expand Down
3 changes: 2 additions & 1 deletion src/node-gyp-rebuild.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
#! /usr/bin/env node

import { getElectronVersion, exec, getGypEnv } from "./util/util"
import { getElectronVersion, exec } from "./util/util"
import { printErrorAndExit } from "./util/promise"
import * as path from "path"
import yargs from "yargs"
import { readPackageJson } from "./util/readPackageJson"
import { log } from "./util/log"
import { getGypEnv } from "./yarn"

const args: any = yargs
.option("arch", {
Expand Down
20 changes: 15 additions & 5 deletions src/packager.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as path from "path"
import {
computeDefaultAppDirectory, installDependencies, getElectronVersion, use,
exec, isEmptyOrSpaces, statOrNull, getGypEnv
computeDefaultAppDirectory, getElectronVersion, use, exec, isEmptyOrSpaces, exists,
asArray
} from "./util/util"
import { all, executeFinally } from "./util/promise"
import { EventEmitter } from "events"
Expand All @@ -20,6 +20,7 @@ import { createTargets } from "./targets/targetFactory"
import { readPackageJson } from "./util/readPackageJson"
import { TmpDir } from "./util/tmp"
import { BuildOptions } from "./builder"
import { getGypEnv, installDependencies, rebuild } from "./yarn"

function addHandler(emitter: EventEmitter, event: string, handler: Function) {
emitter.on(event, handler)
Expand Down Expand Up @@ -229,7 +230,7 @@ export class Packager implements BuildInfo {

private async installAppDependencies(platform: Platform, arch: Arch): Promise<any> {
if (this.devMetadata.build.nodeGypRebuild === true) {
log(`Execute node-gyp rebuild for arch ${Arch[arch]}`)
log(`Executing node-gyp rebuild for arch ${Arch[arch]}`)
await exec(process.platform === "win32" ? "node-gyp.cmd" : "node-gyp", ["rebuild"], {
env: getGypEnv(this.electronVersion, Arch[arch]),
})
Expand All @@ -241,11 +242,20 @@ export class Packager implements BuildInfo {
}
else {
const forceBuildFromSource = this.devMetadata.build.npmSkipBuildFromSource !== true
if (platform.nodeName !== process.platform && forceBuildFromSource) {
if (forceBuildFromSource && platform.nodeName !== process.platform) {
log("Skip app dependencies rebuild because platform is different")
}
else {
await installDependencies(this.appDir, this.electronVersion, Arch[arch], forceBuildFromSource, (await statOrNull(path.join(this.appDir, "node_modules"))) == null ? "install" : "rebuild", this.devMetadata.build.npmArgs)
if (await exists(path.join(this.appDir, "node_modules"))) {
const args = asArray(this.devMetadata.build.npmArgs)
if (forceBuildFromSource) {
args.push("--build-from-source")
}
await rebuild(this.appDir, this.electronVersion, Arch[arch], args)
}
else {
await installDependencies(this.appDir, this.electronVersion, Arch[arch], forceBuildFromSource, this.devMetadata.build.npmArgs)
}
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/platformPackager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import EventEmitter = NodeJS.EventEmitter
import BluebirdPromise from "bluebird-lst-c"
import * as path from "path"
import { readdir, remove } from "fs-extra-p"
import { statOrNull, use, unlinkIfExists, isEmptyOrSpaces, asArray, dependencies, debug } from "./util/util"
import { statOrNull, use, unlinkIfExists, isEmptyOrSpaces, asArray, debug } from "./util/util"
import { Packager } from "./packager"
import { AsarOptions } from "asar-electron-builder"
import { archiveApp } from "./targets/archive"
Expand All @@ -18,6 +18,7 @@ import { FileMatchOptions, FileMatcher, FilePattern, deprecatedUserIgnoreFilter
import { BuildOptions } from "./builder"
import { PublishConfiguration, GithubOptions, BintrayOptions, GenericServerOptions } from "./options/publishOptions"
import { getRepositoryInfo } from "./repositoryInfo"
import { dependencies } from "./yarn"

export interface PackagerOptions {
targets?: Map<Platform, Map<Arch, string[]>>
Expand Down
98 changes: 12 additions & 86 deletions src/util/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { execFile, spawn as _spawn, ChildProcess, SpawnOptions } from "child_pro
import BluebirdPromise from "bluebird-lst-c"
import { homedir } from "os"
import * as path from "path"
import { readJson, stat, Stats, unlink } from "fs-extra-p"
import { readJson, stat, Stats, unlink, access } from "fs-extra-p"
import { yellow, red } from "chalk"
import _debug from "debug"
import { warn, task, log } from "./log"
import { warn, log } from "./log"
import { createHash } from "crypto"
import Debugger = debug.Debugger

Expand All @@ -16,59 +16,6 @@ export const debug7z: Debugger = _debug("electron-builder:7z")

const DEFAULT_APP_DIR_NAMES = ["app", "www"]

export function installDependencies(appDir: string, electronVersion: string, arch: string = process.arch, forceBuildFromSource: boolean, command: string = "install", additionalArgs?: any): Promise<any> {
return task(`${(command === "install" ? "Installing" : "Rebuilding")} app dependencies for arch ${arch} to ${appDir}`, spawnNpmProduction(command, appDir, forceBuildFromSource, getGypEnv(electronVersion, arch), additionalArgs))
}

export function getGypEnv(electronVersion: string, arch: string): any {
const gypHome = path.join(homedir(), ".electron-gyp")
return Object.assign({}, process.env, {
npm_config_disturl: "https://atom.io/download/electron",
npm_config_target: electronVersion,
npm_config_runtime: "electron",
npm_config_arch: arch,
HOME: gypHome,
USERPROFILE: gypHome,
})
}

export function spawnNpmProduction(command: string, appDir: string, forceBuildFromSource: boolean, env?: any, additionalArgs?: any): Promise<any> {
let npmExecPath = process.env.npm_execpath || process.env.NPM_CLI_JS
const npmExecArgs = [command, "--production"]

if (npmExecPath == null || !npmExecPath.includes("yarn")) {
if (process.env.NPM_NO_BIN_LINKS === "true") {
npmExecArgs.push("--no-bin-links")
}
npmExecArgs.push("--cache-min", "999999999")
}

if (npmExecPath == null) {
npmExecPath = process.platform === "win32" ? "npm.cmd" : "npm"
}
else {
npmExecArgs.unshift(npmExecPath)
npmExecPath = process.env.npm_node_execpath || process.env.NODE_EXE || "node"
}
if (forceBuildFromSource) {
npmExecArgs.push("--build-from-source")
}

if (additionalArgs) {
if (Array.isArray(additionalArgs)) {
npmExecArgs.push(...additionalArgs)
}
else {
npmExecArgs.push(additionalArgs)
}
}

return spawn(npmExecPath, npmExecArgs, {
cwd: appDir,
env: env || process.env
})
}

export interface BaseExecOptions {
cwd?: string
env?: any
Expand Down Expand Up @@ -238,6 +185,16 @@ export async function statOrNull(file: string): Promise<Stats | null> {
}
}

export async function exists(file: string): Promise<boolean> {
try {
await access(file)
return true
}
catch (e) {
return false
}
}

export async function computeDefaultAppDirectory(projectDir: string, userAppDir: string | null | undefined): Promise<string> {
if (userAppDir != null) {
const absolutePath = path.resolve(projectDir, userAppDir)
Expand Down Expand Up @@ -324,35 +281,4 @@ export function getCacheDirectory(): string {
else {
return path.join(homedir(), ".cache", "electron-builder")
}
}

let readInstalled: any = null
export function dependencies(dir: string, extraneousOnly: boolean, result: Set<string>): Promise<Array<string>> {
if (readInstalled == null) {
readInstalled = BluebirdPromise.promisify(require("read-installed"))
}
return readInstalled(dir)
.then((it: any) => flatDependencies(it, result, new Set(), extraneousOnly))
}

function flatDependencies(data: any, result: Set<string>, seen: Set<string>, extraneousOnly: boolean): void {
const deps = data.dependencies
if (deps == null) {
return
}

for (let d of Object.keys(deps)) {
const dep = deps[d]
if (typeof dep !== "object" || (!extraneousOnly && dep.extraneous) || seen.has(dep)) {
continue
}

if (extraneousOnly === dep.extraneous) {
seen.add(dep)
result.add(dep.path)
}
else {
flatDependencies(dep, result, seen, extraneousOnly)
}
}
}
137 changes: 137 additions & 0 deletions src/yarn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import BluebirdPromise from "bluebird-lst-c"
import * as path from "path"
import { task, log } from "./util/log"
import { homedir } from "os"
import { spawn, exists } from "./util/util"

export function installDependencies(appDir: string, electronVersion: string, arch: string = process.arch, forceBuildFromSource: boolean, additionalArgs?: any): Promise<any> {
return task(`Installing app dependencies for arch ${arch} to ${appDir}`, spawnNpmProduction(appDir, forceBuildFromSource, getGypEnv(electronVersion, arch), additionalArgs))
}

export function getGypEnv(electronVersion: string, arch: string): any {
const gypHome = path.join(homedir(), ".electron-gyp")
return Object.assign({}, process.env, {
npm_config_disturl: "https://atom.io/download/electron",
npm_config_target: electronVersion,
npm_config_runtime: "electron",
npm_config_arch: arch,
HOME: gypHome,
USERPROFILE: gypHome,
})
}

function spawnNpmProduction(appDir: string, forceBuildFromSource: boolean, env?: any, additionalArgs?: any): Promise<any> {
let npmExecPath = process.env.npm_execpath || process.env.NPM_CLI_JS
const npmExecArgs = ["install", "--production"]

const isNotYarn = npmExecPath == null || !npmExecPath.includes("yarn")
if (isNotYarn) {
if (process.env.NPM_NO_BIN_LINKS === "true") {
npmExecArgs.push("--no-bin-links")
}
npmExecArgs.push("--cache-min", "999999999")
}

if (npmExecPath == null) {
npmExecPath = process.platform === "win32" ? "npm.cmd" : "npm"
}
else {
npmExecArgs.unshift(npmExecPath)
npmExecPath = process.env.npm_node_execpath || process.env.NODE_EXE || "node"
}
if (isNotYarn && forceBuildFromSource) {
npmExecArgs.push("--build-from-source")
}

if (additionalArgs) {
if (Array.isArray(additionalArgs)) {
npmExecArgs.push(...additionalArgs)
}
else {
npmExecArgs.push(additionalArgs)
}
}

return spawn(npmExecPath, npmExecArgs, {
cwd: appDir,
env: env || process.env
})
}

let readInstalled: any = null
export function dependencies(dir: string, extraneousOnly: boolean, result: Set<string>): Promise<Array<string>> {
if (readInstalled == null) {
readInstalled = BluebirdPromise.promisify(require("read-installed"))
}
return readInstalled(dir)
.then((it: any) => flatDependencies(it, result, new Set(), extraneousOnly))
}

function flatDependencies(data: any, result: Set<string>, seen: Set<string>, extraneousOnly: boolean): void {
const deps = data.dependencies
if (deps == null) {
return
}

for (let d of Object.keys(deps)) {
const dep = deps[d]
if (typeof dep !== "object" || (!extraneousOnly && dep.extraneous) || seen.has(dep)) {
continue
}

if (extraneousOnly === dep.extraneous) {
seen.add(dep)
result.add(dep.path)
}
else {
flatDependencies(dep, result, seen, extraneousOnly)
}
}
}

export async function rebuild(appDir: string, electronVersion: string, arch: string = process.arch, additionalArgs: Array<string>) {
const deps = new Set<string>()
await dependencies(appDir, false, deps)
const nativeDeps = await BluebirdPromise.filter(deps, it => exists(path.join(it, "binding.gyp")), {concurrency: 8})

if (nativeDeps.length === 0) {
return
}

log(`Rebuilding native app dependencies for arch ${arch} to ${appDir}`)

let execPath = process.env.npm_execpath || process.env.NPM_CLI_JS
const execArgs = ["run", "install", "--"]

if (execPath == null) {
if (process.env.FORCE_YARN === "true") {
execPath = process.platform === "win32" ? "yarn.cmd" : "yarn"
}
else {
execPath = process.platform === "win32" ? "npm.cmd" : "npm"
}
}
else {
execArgs.unshift(execPath)
execPath = process.env.npm_node_execpath || process.env.NODE_EXE || "node"
}

const gypHome = path.join(homedir(), ".electron-gyp")
const env = Object.assign({}, process.env, {
HOME: gypHome,
USERPROFILE: gypHome,
})

execArgs.push("--disturl=https://atom.io/download/electron")
execArgs.push(`--target=${electronVersion}`)
execArgs.push("--runtime=electron")
execArgs.push(`--arch=${arch}`)
execArgs.push(...additionalArgs)

for (let dir of nativeDeps) {
await spawn(execPath, execArgs, {
cwd: dir,
env: env
})
}
}
Loading

0 comments on commit 94cc7be

Please sign in to comment.