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: Skip lipo if native module is already universal. Add native module fixtures for lipo tests #126

Draft
wants to merge 18 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 4 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
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['<rootDir>/test/**/*.spec.ts'],
transform: {
'^.+\\.ts?$': [
'ts-jest',
Expand Down
9 changes: 5 additions & 4 deletions src/asar-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,7 @@ export const mergeASARs = async ({
continue;
}

if (
MACHO_UNIVERSAL_MAGIC.has(x64Content.readUInt32LE(0)) &&
MACHO_UNIVERSAL_MAGIC.has(arm64Content.readUInt32LE(0))
) {
if (isUniversalMachO(x64Content) && isUniversalMachO(arm64Content)) {
continue;
}

Expand Down Expand Up @@ -223,3 +220,7 @@ export const mergeASARs = async ({
await Promise.all([fs.remove(x64Dir), fs.remove(arm64Dir)]);
}
};

export const isUniversalMachO = (fileContent: Buffer) => {
return MACHO_UNIVERSAL_MAGIC.has(fileContent.readUInt32LE(0));
};
25 changes: 21 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import { spawn } from '@malept/cross-spawn-promise';
import * as asar from '@electron/asar';
import { spawn } from '@malept/cross-spawn-promise';
import * as dircompare from 'dir-compare';
import * as fs from 'fs-extra';
import { minimatch } from 'minimatch';
import * as os from 'os';
import * as path from 'path';
import * as plist from 'plist';
import * as dircompare from 'dir-compare';

import {
AsarMode,
detectAsarMode,
generateAsarIntegrity,
isUniversalMachO,
mergeASARs,
} from './asar-utils';
import { d } from './debug';
import { AppFile, AppFileType, getAllAppFiles } from './file-utils';
import { AsarMode, detectAsarMode, generateAsarIntegrity, mergeASARs } from './asar-utils';
import { sha } from './sha';
import { d } from './debug';

/**
* Options to pass into the {@link makeUniversalApp} function.
Expand Down Expand Up @@ -161,6 +167,17 @@ export const makeUniversalApp = async (opts: MakeUniversalOpts): Promise<void> =
const first = await fs.realpath(path.resolve(tmpApp, machOFile.relativePath));
const second = await fs.realpath(path.resolve(opts.arm64AppPath, machOFile.relativePath));

// check if both files (same name) are already universal.
// this must occur before checking `sha` as their sha's will be different between builds if being built locally
if (
isUniversalMachO(await fs.readFile(first)) &&
isUniversalMachO(await fs.readFile(second))
) {
d(machOFile.relativePath, `is already universal across builds, skipping lipo`);
knownMergedMachOFiles.add(machOFile.relativePath);
continue;
}

const x64Sha = await sha(path.resolve(opts.x64AppPath, machOFile.relativePath));
const arm64Sha = await sha(path.resolve(opts.arm64AppPath, machOFile.relativePath));
if (x64Sha === arm64Sha) {
Expand Down
281 changes: 281 additions & 0 deletions test/__snapshots__/index.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,212 @@ exports[`makeUniversalApp force packages successfully if \`out\` bundle already
}
`;

exports[`makeUniversalApp no asar mode different app dirs with different macho files (shim and lipo) 1`] = `
{
"files": {
"index.js": {
"integrity": {
"algorithm": "SHA256",
"blockSize": 4194304,
"blocks": [
"f1e14240f7c833900fca84fabc2f0ff27084efdf1c5b228b015515de3f8fa28e",
],
"hash": "f1e14240f7c833900fca84fabc2f0ff27084efdf1c5b228b015515de3f8fa28e",
},
"size": 1063,
},
"package.json": {
"integrity": {
"algorithm": "SHA256",
"blockSize": 4194304,
"blocks": [
"2873266521e41d58d02e7acfbbbdb046edfa04b6ce262b8987de8e8548671fc7",
],
"hash": "2873266521e41d58d02e7acfbbbdb046edfa04b6ce262b8987de8e8548671fc7",
},
"size": 33,
},
},
}
`;

exports[`makeUniversalApp no asar mode different app dirs with different macho files (shim and lipo) 2`] = `
[
"private/var/i-aint-got-no-rhythm.bin",
]
`;

exports[`makeUniversalApp no asar mode different app dirs with different macho files (shim and lipo) 3`] = `
[
"index.js",
"node-mac-permissions.node",
{
"content": "{
"name": "app",
"main": "index.js"
}",
"name": "package.json",
},
{
"content": "hello world",
"name": "private/var/file.txt",
},
"private/var/i-aint-got-no-rhythm.bin",
]
`;

exports[`makeUniversalApp no asar mode different app dirs with different macho files (shim and lipo) 4`] = `
[
"index.js",
"node-mac-permissions.node",
{
"content": "{
"name": "app",
"main": "index.js"
}",
"name": "package.json",
},
{
"content": "hello world",
"name": "private/var/file.txt",
},
"private/var/hello-world.bin",
]
`;

exports[`makeUniversalApp no asar mode different app dirs with different macho files (shim and lipo) 5`] = `
{
"Contents/Info.plist": {},
}
`;

exports[`makeUniversalApp no asar mode different app dirs with universal macho files (shim but don't lipo) 1`] = `
{
"files": {
"index.js": {
"integrity": {
"algorithm": "SHA256",
"blockSize": 4194304,
"blocks": [
"f1e14240f7c833900fca84fabc2f0ff27084efdf1c5b228b015515de3f8fa28e",
],
"hash": "f1e14240f7c833900fca84fabc2f0ff27084efdf1c5b228b015515de3f8fa28e",
},
"size": 1063,
},
"package.json": {
"integrity": {
"algorithm": "SHA256",
"blockSize": 4194304,
"blocks": [
"2873266521e41d58d02e7acfbbbdb046edfa04b6ce262b8987de8e8548671fc7",
],
"hash": "2873266521e41d58d02e7acfbbbdb046edfa04b6ce262b8987de8e8548671fc7",
},
"size": 33,
},
},
}
`;

exports[`makeUniversalApp no asar mode different app dirs with universal macho files (shim but don't lipo) 2`] = `
[
"private/var/i-aint-got-no-rhythm.bin",
]
`;

exports[`makeUniversalApp no asar mode different app dirs with universal macho files (shim but don't lipo) 3`] = `
[
"index.js",
"node-mac-permissions.node",
{
"content": "{
"name": "app",
"main": "index.js"
}",
"name": "package.json",
},
{
"content": "hello world",
"name": "private/var/file.txt",
},
"private/var/i-aint-got-no-rhythm.bin",
]
`;

exports[`makeUniversalApp no asar mode different app dirs with universal macho files (shim but don't lipo) 4`] = `
[
"index.js",
"node-mac-permissions.node",
{
"content": "{
"name": "app",
"main": "index.js"
}",
"name": "package.json",
},
{
"content": "hello world",
"name": "private/var/file.txt",
},
"private/var/hello-world.bin",
]
`;

exports[`makeUniversalApp no asar mode different app dirs with universal macho files (shim but don't lipo) 5`] = `
{
"Contents/Info.plist": {},
}
`;

exports[`makeUniversalApp no asar mode identical app dirs with different macho files (e.g. do not shim, but still lipo) 1`] = `
[
"index.js",
"node-mac-permissions.node",
{
"content": "{
"name": "app",
"main": "index.js"
}",
"name": "package.json",
},
{
"content": "hello world",
"name": "private/var/file.txt",
},
]
`;

exports[`makeUniversalApp no asar mode identical app dirs with different macho files (e.g. do not shim, but still lipo) 2`] = `
{
"Contents/Info.plist": {},
}
`;

exports[`makeUniversalApp no asar mode identical app dirs with universal macho files (e.g., do not shim, just copy x64 dir) 1`] = `
[
"index.js",
"node-mac-permissions.node",
{
"content": "{
"name": "app",
"main": "index.js"
}",
"name": "package.json",
},
{
"content": "hello world",
"name": "private/var/file.txt",
},
]
`;

exports[`makeUniversalApp no asar mode identical app dirs with universal macho files (e.g., do not shim, just copy x64 dir) 2`] = `
{
"Contents/Info.plist": {},
}
`;

exports[`makeUniversalApp no asar mode should correctly merge two identical app folders 1`] = `
[
"index.js",
Expand Down Expand Up @@ -582,3 +788,78 @@ exports[`makeUniversalApp no asar mode should shim two different app folders 5`]
"Contents/Info.plist": {},
}
`;

exports[`makeUniversalApp works for lipo binary resources 1`] = `
{
"files": {
"index.js": {
"integrity": {
"algorithm": "SHA256",
"blockSize": 4194304,
"blocks": [
"0f6311dac07f0876c436ce2be042eb88c96e17eaf140b39627cf720dd87ad5b8",
],
"hash": "0f6311dac07f0876c436ce2be042eb88c96e17eaf140b39627cf720dd87ad5b8",
},
"size": 66,
},
"package.json": {
"integrity": {
"algorithm": "SHA256",
"blockSize": 4194304,
"blocks": [
"d6226276d47adc7aa20e6c46e842e258f5157313074a035051a89612acdd6be3",
],
"hash": "d6226276d47adc7aa20e6c46e842e258f5157313074a035051a89612acdd6be3",
},
"size": 41,
},
"private": {
"files": {
"var": {
"files": {
"app": {
"files": {
"file.txt": {
"link": "private/var/file.txt",
},
},
},
"file.txt": {
"integrity": {
"algorithm": "SHA256",
"blockSize": 4194304,
"blocks": [
"b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9",
],
"hash": "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9",
},
"size": 11,
},
},
},
},
},
"var": {
"link": "private/var",
},
},
}
`;

exports[`makeUniversalApp works for lipo binary resources 2`] = `
[
"node-mac-permissions.node",
]
`;

exports[`makeUniversalApp works for lipo binary resources 3`] = `
{
"Contents/Info.plist": {
"Resources/app.asar": {
"algorithm": "SHA256",
"hash": "7e6af4d00f4cc737eff922e2b386128a269f80887b79a011022f1276bdbe7832",
},
},
}
`;
6 changes: 6 additions & 0 deletions test/fixtures/native/README.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Arch-specific modules generated from `node-mac-permissions` (https://github.com/codebytere/node-mac-permissions) using `electron/rebuild`

Universal module generated with `lipo`
```
lipo ./test/fixtures/native/node-mac-permissions.x64.node ./test/fixtures/native/node-mac-permissions.arm64.node -create -output ./test/fixtures/native/node-mac-permissions.universal.node
```
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading