Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: cross-platform prebuild downloads #1153

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type HashTree = { [path: string]: string | HashTree };
type CacheOptions = {
ABI: string;
arch: string;
platform: string;
debug: boolean;
electronVersion: string;
headerURL: string;
Expand Down Expand Up @@ -159,6 +160,7 @@ export async function generateCacheKey(opts: CacheOptions): Promise<string> {
.update(path.basename(opts.modulePath))
.update(opts.ABI)
.update(opts.arch)
.update(opts.platform)
.update(opts.debug ? 'debug' : 'not debug')
.update(opts.headerURL)
.update(opts.electronVersion);
Expand Down
4 changes: 4 additions & 0 deletions src/module-type/node-gyp/node-gyp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ export class NodeGyp extends NativeModule {
}

async rebuildModule(): Promise<void> {
if (this.rebuilder.platform !== process.platform) {
throw new Error("node-gyp does not support cross-compiling native modules from source.");
}

if (this.modulePath.includes(' ')) {
console.error('Attempting to build a module with a space in the path');
console.error('See https://github.com/nodejs/node-gyp/issues/65#issuecomment-368820565 for reasons why this may not work');
Expand Down
12 changes: 11 additions & 1 deletion src/rebuild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ export interface RebuildOptions {
* The version of Electron to build against.
*/
electronVersion: string;
/**
* Override the target platform to something other than the host system platform.
* Note: This only applies to downloading prebuilt binaries. **It is not possible to cross-compile native modules.**
*
* @defaultValue The system {@link https://nodejs.org/api/process.html#processplatform | `process.platform`} value
*/
platform?: NodeJS.Platform;
/**
* Override the target rebuild architecture to something other than the host system architecture.
*
Expand Down Expand Up @@ -132,7 +139,7 @@ export class Rebuilder implements IRebuilder {
public lifecycle: EventEmitter;
public buildPath: string;
public electronVersion: string;
public platform: string = process.platform;
public platform: NodeJS.Platform;
public arch: string;
public force: boolean;
public headerURL: string;
Expand All @@ -151,6 +158,7 @@ export class Rebuilder implements IRebuilder {
this.lifecycle = options.lifecycle;
this.buildPath = options.buildPath;
this.electronVersion = options.electronVersion;
this.platform = options.platform || process.platform;
this.arch = options.arch || process.arch;
this.force = options.force || false;
this.headerURL = options.headerURL || 'https://www.electronjs.org/headers';
Expand Down Expand Up @@ -199,6 +207,7 @@ export class Rebuilder implements IRebuilder {
'rebuilding with args:',
this.buildPath,
this.electronVersion,
this.platform,
this.arch,
extraModules,
this.force,
Expand Down Expand Up @@ -289,6 +298,7 @@ export class Rebuilder implements IRebuilder {
cacheKey = await generateCacheKey({
ABI: this.ABI,
arch: this.arch,
platform: this.platform,
debug: this.debug,
electronVersion: this.electronVersion,
headerURL: this.headerURL,
Expand Down
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export interface IRebuilder {
lifecycle: EventEmitter;
mode: RebuildMode;
msvsVersion?: string;
platform: string;
platform: NodeJS.Platform;
prebuildTagPrefix: string;
buildFromSource: boolean;
useCache: boolean;
Expand Down
27 changes: 26 additions & 1 deletion test/module-type-node-gyp.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { EventEmitter } from 'events';
import { expect } from 'chai';
import chai, { expect } from 'chai';
import chaiAsPromised from 'chai-as-promised';

import { cleanupTestModule, resetTestModule, TEST_MODULE_PATH as testModulePath } from './helpers/module-setup';
import { NodeGyp } from '../lib/module-type/node-gyp/node-gyp';
import { Rebuilder } from '../lib/rebuild';

chai.use(chaiAsPromised);

describe('node-gyp', () => {
describe('buildArgs', () => {

Expand Down Expand Up @@ -54,5 +57,27 @@ describe('node-gyp', () => {
expect(args).to.not.include('--force-process-config');
});
});

context('cross-compilation', async () => {
it('throws error early if platform mismatch', async function () {
let platform: NodeJS.Platform = 'darwin';

// we're verifying platform mismatch error throwing, not `rebuildModule` rebuilding.
if (process.platform === platform) {
platform = 'win32';
}

const rebuilder = new Rebuilder({
buildPath: testModulePath,
electronVersion: '15.3.0',
lifecycle: new EventEmitter(),
platform
});
const nodeGyp = new NodeGyp(rebuilder, testModulePath);

const errorMessage = "node-gyp does not support cross-compiling native modules from source.";
expect(nodeGyp.rebuildModule()).to.eventually.be.rejectedWith(new Error(errorMessage))
})
})
});
});
21 changes: 19 additions & 2 deletions test/module-type-node-pre-gyp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import path from 'path';

import { cleanupTestModule, resetTestModule, TIMEOUT_IN_MILLISECONDS, TEST_MODULE_PATH as testModulePath } from './helpers/module-setup';
import { NodePreGyp } from '../lib/module-type/node-pre-gyp';
import { Rebuilder } from '../lib/rebuild';
import { Rebuilder, RebuilderOptions } from '../lib/rebuild';

chai.use(chaiAsPromised);

describe('node-pre-gyp', function () {
this.timeout(TIMEOUT_IN_MILLISECONDS);

const modulePath = path.join(testModulePath, 'node_modules', 'sqlite3');
const rebuilderArgs = {
const rebuilderArgs: RebuilderOptions = {
buildPath: testModulePath,
electronVersion: '8.0.0',
arch: process.arch,
Expand Down Expand Up @@ -64,4 +64,21 @@ describe('node-pre-gyp', function () {
nodePreGyp = new NodePreGyp(rebuilder, modulePath);
expect(await nodePreGyp.findPrebuiltModule()).to.equal(true);
});

it('should download for target platform', async () => {
let rebuilder = new Rebuilder(rebuilderArgs);
let nodePreGyp = new NodePreGyp(rebuilder, modulePath);
expect(await nodePreGyp.findPrebuiltModule()).to.equal(true);

let alternativePlatform: NodeJS.Platform;
if (process.platform === 'win32') {
alternativePlatform = 'darwin';
} else {
alternativePlatform = 'win32';
}

rebuilder = new Rebuilder({ ...rebuilderArgs, platform: alternativePlatform });
nodePreGyp = new NodePreGyp(rebuilder, modulePath);
expect(await nodePreGyp.findPrebuiltModule()).to.equal(true);
});
});
24 changes: 22 additions & 2 deletions test/module-type-prebuild-install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import path from 'path';

import { cleanupTestModule, resetTestModule, TIMEOUT_IN_MILLISECONDS, TEST_MODULE_PATH as testModulePath } from './helpers/module-setup';
import { PrebuildInstall } from '../lib/module-type/prebuild-install';
import { Rebuilder } from '../lib/rebuild';
import { Rebuilder, RebuilderOptions } from '../lib/rebuild';

chai.use(chaiAsPromised);

describe('prebuild-install', () => {
const modulePath = path.join(testModulePath, 'node_modules', 'farmhash');
const rebuilderArgs = {
const rebuilderArgs: RebuilderOptions = {
buildPath: testModulePath,
electronVersion: '8.0.0',
arch: process.arch,
Expand Down Expand Up @@ -52,5 +52,25 @@ describe('prebuild-install', () => {
const prebuildInstall = new PrebuildInstall(rebuilder, modulePath);
expect(prebuildInstall.findPrebuiltModule()).to.eventually.be.rejectedWith("Native module 'farmhash' requires Node-API but Electron v2.0.0 does not support Node-API");
});

it('should download for target platform', async function () {
if (process.platform === 'darwin' && process.arch === 'arm64') {
this.skip(); // farmhash module has no prebuilt binaries for ARM64
}
let rebuilder = new Rebuilder(rebuilderArgs);
let prebuild = new PrebuildInstall(rebuilder, modulePath);
expect(await prebuild.findPrebuiltModule()).to.equal(true);

let alternativePlatform: NodeJS.Platform;
if (process.platform === 'win32') {
alternativePlatform = 'darwin';
} else {
alternativePlatform = 'win32';
}

rebuilder = new Rebuilder({ ...rebuilderArgs, platform: alternativePlatform });
prebuild = new PrebuildInstall(rebuilder, modulePath);
expect(await prebuild.findPrebuiltModule()).to.equal(true);
});
});
});
20 changes: 20 additions & 0 deletions test/module-type-prebuildify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,24 @@ describe('prebuildify', () => {
});
});
});

describe('cross-platform downloads', async () => {
it('should download for target platform', async () => {
const fixtureDir = path.join(fixtureBaseDir, 'napi');
let rebuilder = createRebuilder();
let prebuildify = new Prebuildify(rebuilder, fixtureDir);
expect(await prebuildify.findPrebuiltModule()).to.equal(true);

let alternativePlatform: NodeJS.Platform;
if (process.platform === 'win32') {
alternativePlatform = 'darwin';
} else {
alternativePlatform = 'win32';
}

rebuilder = createRebuilder({ platform: alternativePlatform });
prebuildify = new Prebuildify(rebuilder, fixtureDir);
expect(await prebuildify.findPrebuiltModule()).to.equal(true);
});
})
});