Skip to content

Commit

Permalink
feat(install): unpack native libraries
Browse files Browse the repository at this point in the history
- Add functions to unzip native libraries.
  • Loading branch information
skjsjhb committed Jan 16, 2025
1 parent b4ae086 commit 857e60b
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 12 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"multiline-ts": "^4.0.1",
"nanoid": "^5.0.9",
"node-sqlite3-wasm": "^0.8.30",
"node-stream-zip": "^1.15.0",
"p-queue": "^8.0.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
Expand Down
9 changes: 9 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions src/main/container/spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,13 @@ export interface Container {
*/
library(name: string): string;

/**
* Gets the path to the native library.
* @param libName Library name.
* @param nativeName Library native artifact name.
*/
nativeLibrary(libName: string, nativeName: string): string;

/**
* Gets the path to the profile document.
* @param id Profile ID.
Expand Down
6 changes: 6 additions & 0 deletions src/main/container/static.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ export class StaticContainer implements Container {
return this.resolve("libraries", new MavenName(name).toPath());
}

nativeLibrary(libName: string, nativeName: string): string {
const n = new MavenName(libName);
n.classifier = nativeName;
return this.resolve("libraries", n.toPath());
}

nativesRoot(id: string): string {
return this.resolve(".natives", id);
}
Expand Down
51 changes: 51 additions & 0 deletions src/main/install/vanilla/natives-lint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { Container } from "@/main/container/spec";
import { nativeLib } from "@/main/profile/native-lib";
import { filterRules } from "@/main/profile/rules";
import type { VersionProfile } from "@/main/profile/version-profile";
import { unwrapESM } from "@/main/util/module";
import fs from "fs-extra";
import type streamZip from "node-stream-zip";
import path from "path";

let zl: typeof streamZip;

async function getLibrary(): Promise<typeof streamZip> {
if (!zl) {
zl = (await unwrapESM(import("node-stream-zip"))).default;
}
return zl;
}

async function unpackOne(lib: string, out: string, exclude?: string[]): Promise<void> {
const z = await getLibrary();
const filter = (p: string) => !(p.endsWith("/") || exclude?.find(e => p.startsWith(e)));
const f = new z.async({ file: lib });

try {
const files = Object.values(await f.entries()).filter(ent => !ent.isDirectory && filter(ent.name));

await Promise.all(files.map(async ent => {
const t = path.join(out, ent.name);
console.debug(`Extracting native artifact: ${lib} (${ent.name}) -> ${t}`);

await fs.ensureDir(path.dirname(t));
await f.extract(ent, t);
}));
} catch (e) {
throw e;
} finally {
await f.close();
}
}

async function unpack(profile: VersionProfile, container: Container, features: Set<string>): Promise<void> {
const nativesRoot = container.nativesRoot(profile.id);
const sources = profile.libraries.filter(l => filterRules(l.rules, features) && nativeLib.isNative(l));

await Promise.all(sources.map(async s => {
const name = nativeLib.getArtifactName(s);
await unpackOne(container.nativeLibrary(s.name, name), nativesRoot, s.extract?.exclude);
}));
}

export const nativesLint = { unpack, unpackOne };
15 changes: 3 additions & 12 deletions src/main/profile/native-lib.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,20 @@
import { MavenName } from "@/main/profile/maven-name";
import type { Library } from "@/main/profile/version-profile";
import { getOSBits, getOSName } from "@/main/sys/os";

/**
* Check whether the given library is a native library (which requires unpacking).
*/
export function isNativeLibrary(l: Library): boolean {
function isNative(l: Library): boolean {
return l.natives !== undefined && getOSName() in l.natives;
}

/**
* Gets the name of the native artifact.
*/
export function getNativeArtifactName(l: Library): string {
function getArtifactName(l: Library): string {
const index = l.natives?.[getOSName()];
if (!index) throw `Library ${l.name} has no native artifact for ${getOSName()}`;
return index.replaceAll("${arch}", getOSBits());
}

/**
* Gets the path to the native artifact (generated by the library name and the classifier).
*/
export function getNativeArtifactPath(l: Library): string {
const nativeName = getNativeArtifactName(l);
const name = new MavenName(l.name);
name.classifier = nativeName;
return name.toPath();
}
export const nativeLib = { isNative, getArtifactName };

0 comments on commit 857e60b

Please sign in to comment.