From d6fa375cd5f4771923ee3ecc78f606c33ff2f8a3 Mon Sep 17 00:00:00 2001 From: Vincent Weevers Date: Sun, 29 Aug 2021 09:05:31 +0200 Subject: [PATCH] Support multi-arch prebuilds for M1 (Apple silicon) Ref https://github.com/prebuild/prebuildify/issues/52 --- index.js | 38 +++++++++++++++++++++++++++++++++++++- test.js | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 619bbfd..d993fdb 100644 --- a/index.js +++ b/index.js @@ -59,8 +59,13 @@ load.path = function (dir) { throw new Error('No native build was found for ' + target + '\n loaded from: ' + dir + '\n') function resolve (dir) { + // Find matching "prebuilds/-" directory + var tuples = readdirSync(path.join(dir, 'prebuilds')).map(parseTuple) + var tuple = tuples.filter(matchTuple(platform, arch)).sort(compareTuples)[0] + if (!tuple) return + // Find most specific flavor first - var prebuilds = path.join(dir, 'prebuilds', platform + '-' + arch) + var prebuilds = path.join(dir, 'prebuilds', tuple.name) var parsed = readdirSync(prebuilds).map(parseTags) var candidates = parsed.filter(matchTags(runtime, abi)) var winner = candidates.sort(compareTags(runtime))[0] @@ -85,6 +90,34 @@ function matchBuild (name) { return /\.node$/.test(name) } +function parseTuple (name) { + // Example: darwin-x64_arm64 + var arr = name.split('-') + if (arr.length !== 2) return + + var platform = arr[0] + var architectures = arr[1].split('_') + + if (!platform) return + if (!architectures.length) return + if (!architectures.every(Boolean)) return + + return { name, platform, architectures } +} + +function matchTuple (platform, arch) { + return function (tuple) { + if (tuple == null) return false + if (tuple.platform !== platform) return false + return tuple.architectures.includes(arch) + } +} + +function compareTuples (a, b) { + // Prefer single-arch prebuilds over multi-arch + return a.architectures.length - b.architectures.length +} + function parseTags (file) { var arr = file.split('.') var extension = arr.pop() @@ -164,3 +197,6 @@ function isAlpine (platform) { load.parseTags = parseTags load.matchTags = matchTags load.compareTags = compareTags +load.parseTuple = parseTuple +load.matchTuple = matchTuple +load.compareTuples = compareTuples diff --git a/test.js b/test.js index 073c66e..e566567 100644 --- a/test.js +++ b/test.js @@ -5,6 +5,9 @@ var shuffle = require('array-shuffle') var parseTags = require('.').parseTags var matchTags = require('.').matchTags var compareTags = require('.').compareTags +var parseTuple = require('.').parseTuple +var matchTuple = require('.').matchTuple +var compareTuples = require('.').compareTuples test('parse tags', function (t) { t.is(parseTags('ignored'), undefined) @@ -119,6 +122,53 @@ test('match and sort tags', function (t) { t.end() }) +test('parse tuples', function (t) { + t.same(parseTuple('linux-arm64'), { + name: 'linux-arm64', + platform: 'linux', + architectures: ['arm64'] + }) + + t.same(parseTuple('darwin-x64_arm64'), { + name: 'darwin-x64_arm64', + platform: 'darwin', + architectures: ['x64', 'arm64'] + }) + + // Should skip invalid tuples + t.is(parseTuple(''), undefined) + t.is(parseTuple('linux-'), undefined) + t.is(parseTuple('-arm64'), undefined) + t.is(parseTuple('linux-arm64_'), undefined) + t.is(parseTuple('linux-arm64__x64'), undefined) + t.is(parseTuple('linux-_arm64'), undefined) + + t.end() +}) + +test('sort tuples', function (t) { + var tuples = ['darwin-arm64_x64_ia32', 'darwin-x64', 'darwin-x64_arm64'] + var sorted = tuples.map(parseTuple).sort(compareTuples).map(getTupleName) + + t.same(sorted, ['darwin-x64', 'darwin-x64_arm64', 'darwin-arm64_x64_ia32']) + t.end() +}) + +test('match tuples', function (t) { + var tuples = ['linux-arm64', 'darwin-x64_arm64', 'darwin-x64'].map(parseTuple) + + t.is(tuples.filter(matchTuple('darwin', 'x64')).sort(compareTuples)[0].name, 'darwin-x64') + t.is(tuples.filter(matchTuple('darwin', 'arm64')).sort(compareTuples)[0].name, 'darwin-x64_arm64') + t.is(tuples.filter(matchTuple('linux', 'arm64')).sort(compareTuples)[0].name, 'linux-arm64') + t.is(tuples.some(matchTuple('linux', 'other')), false) + t.is(tuples.some(matchTuple('other', 'arm64')), false) + t.end() +}) + +function getTupleName (tuple) { + return tuple.name +} + function getFile (tags) { return tags.file }