diff --git a/.github/workflows/build_linux.yaml b/.github/workflows/build_linux.yaml index 7f434dd3..a8f54308 100644 --- a/.github/workflows/build_linux.yaml +++ b/.github/workflows/build_linux.yaml @@ -66,14 +66,6 @@ jobs: with: name: webapp - - name: Cache .hak - id: cache - uses: actions/cache@v4 - with: - key: ${{ runner.os }}-${{ github.ref_name }}-${{ inputs.sqlcipher }}-${{ inputs.arch }}-${{ hashFiles('hakHash', 'electronVersion', 'dockerbuild/*') }} - path: | - ./.hak - - uses: actions/setup-node@v4 with: node-version-file: .node-version @@ -103,23 +95,11 @@ jobs: platforms: linux/${{ inputs.arch }} tags: ${{ env.HAK_DOCKER_IMAGE }} - - name: Build Natives - if: steps.cache.outputs.cache-hit != 'true' - run: | - docker run \ - -v ${{ github.workspace }}:/work -w /work \ - -e SQLCIPHER_BUNDLED \ - $HAK_DOCKER_IMAGE \ - yarn build:native - - - name: Fix permissions on .hak - run: sudo chown -R $USER:$USER .hak - - name: Check native libraries in hak dependencies run: | shopt -s globstar - for filename in ./.hak/hakModules/**/*.node; do + for filename in ./node_modules/**/*.node; do ./scripts/glibc-check.sh $filename done diff --git a/.github/workflows/build_macos.yaml b/.github/workflows/build_macos.yaml index 4438dc48..362c29d9 100644 --- a/.github/workflows/build_macos.yaml +++ b/.github/workflows/build_macos.yaml @@ -39,14 +39,6 @@ jobs: with: name: webapp - - name: Cache .hak - id: cache - uses: actions/cache@v4 - with: - key: ${{ runner.os }}-${{ hashFiles('hakHash', 'electronVersion') }} - path: | - ./.hak - - name: Install Rust if: steps.cache.outputs.cache-hit != 'true' run: | @@ -73,7 +65,6 @@ jobs: run: | # Python 3.12 drops distutils which keytar relies on pip3 install setuptools - yarn build:native:universal # We split these because electron-builder gets upset if we set CSC_LINK even to an empty string - name: "[Signed] Build App" diff --git a/.github/workflows/build_windows.yaml b/.github/workflows/build_windows.yaml index cb7c318a..b9984d91 100644 --- a/.github/workflows/build_windows.yaml +++ b/.github/workflows/build_windows.yaml @@ -66,14 +66,6 @@ jobs: with: name: webapp - - name: Cache .hak - id: cache - uses: actions/cache@v4 - with: - key: ${{ runner.os }}-${{ inputs.arch }}-${{ hashFiles('hakHash', 'electronVersion') }} - path: | - ./.hak - # ActiveTCL package on choco is from 2015, # this one is newer but includes more than we need - name: Choco install tclsh @@ -117,12 +109,6 @@ jobs: with: arch: ${{ steps.config.outputs.arch || inputs.arch }} - - name: Build Natives - if: steps.cache.outputs.cache-hit != 'true' - run: | - refreshenv - yarn build:native --target ${{ steps.config.outputs.target }} - - name: Install and configure eSigner CKA run: | Set-StrictMode -Version 'Latest' diff --git a/electron-builder.ts b/electron-builder.ts index 660721d1..2c700ee4 100644 --- a/electron-builder.ts +++ b/electron-builder.ts @@ -93,14 +93,7 @@ const config: Omit, "electronFuses"> & { afterPack: async (context: AfterPackContext) => { await injectAsarIntegrity(context); }, - files: [ - "package.json", - { - from: ".hak/hakModules", - to: "node_modules", - }, - "lib/**", - ], + files: ["package.json", "lib/**"], extraResources: [ { from: "res/img", diff --git a/hak/matrix-seshat/build.ts b/hak/matrix-seshat/build.ts deleted file mode 100644 index b4e46f1f..00000000 --- a/hak/matrix-seshat/build.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* -Copyright 2024 New Vector Ltd. -Copyright 2020, 2021 The Matrix.org Foundation C.I.C. - -SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial -Please see LICENSE files in the repository root for full details. -*/ - -import type HakEnv from "../../scripts/hak/hakEnv.js"; -import type { DependencyInfo } from "../../scripts/hak/dep.js"; - -export default async function (hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise { - const env = hakEnv.makeGypEnv(); - - if (!hakEnv.isHost()) { - env.CARGO_BUILD_TARGET = hakEnv.getTargetId(); - } - - console.log("Running yarn install"); - await hakEnv.spawn("yarn", ["install"], { - cwd: moduleInfo.moduleBuildDir, - env, - shell: true, - }); - - const buildTarget = hakEnv.wantsStaticSqlCipher() ? "build-bundled" : "build"; - - console.log("Running yarn build"); - await hakEnv.spawn("yarn", ["run", buildTarget], { - cwd: moduleInfo.moduleBuildDir, - env, - shell: true, - }); -} diff --git a/hak/matrix-seshat/check.ts b/hak/matrix-seshat/check.ts deleted file mode 100644 index 8e292df8..00000000 --- a/hak/matrix-seshat/check.ts +++ /dev/null @@ -1,55 +0,0 @@ -/* -Copyright 2024 New Vector Ltd. -Copyright 2020, 2021 The Matrix.org Foundation C.I.C. - -SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial -Please see LICENSE files in the repository root for full details. -*/ - -import childProcess from "node:child_process"; -import fsProm from "node:fs/promises"; - -import type HakEnv from "../../scripts/hak/hakEnv.js"; -import type { Tool } from "../../scripts/hak/hakEnv.js"; -import type { DependencyInfo } from "../../scripts/hak/dep.js"; - -export default async function (hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise { - const tools: Tool[] = [ - ["rustc", "--version"], - ["python", "--version"], // node-gyp uses python for reasons beyond comprehension - ]; - if (hakEnv.isWin()) { - tools.push(["perl", "--version"]); // for openssl configure - tools.push(["nasm", "-v"]); // for openssl building - tools.push(["patch", "--version"]); // to patch sqlcipher Makefile.msc - tools.push(["nmake", "/?"]); - } else { - tools.push(["make", "--version"]); - } - await hakEnv.checkTools(tools); - - // Ensure Rust target exists (nb. we avoid depending on rustup) - await new Promise((resolve, reject) => { - const rustc = childProcess.execFile( - "rustc", - ["--target", hakEnv.getTargetId(), "--emit=obj", "-o", "tmp", "-"], - (err, out) => { - if (err) { - reject( - "rustc can't build for target " + - hakEnv.getTargetId() + - ": ensure target is installed via `rustup target add " + - hakEnv.getTargetId() + - "` " + - "or your package manager if not using `rustup`", - ); - } - fsProm.unlink("tmp").then(resolve); - }, - ); - rustc.stdin!.write("fn main() {}"); - rustc.stdout!.pipe(process.stdout); - rustc.stderr!.pipe(process.stderr); - rustc.stdin!.end(); - }); -} diff --git a/hak/matrix-seshat/hak.json b/hak/matrix-seshat/hak.json deleted file mode 100644 index c0c65927..00000000 --- a/hak/matrix-seshat/hak.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "scripts": { - "check": "check.ts", - "build": "build.ts" - }, - "copy": "index.node" -} diff --git a/hak/tsconfig.json b/hak/tsconfig.json deleted file mode 100644 index 9c751cc8..00000000 --- a/hak/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "compilerOptions": { - "moduleResolution": "node", - "esModuleInterop": true, - "target": "es2022", - "sourceMap": false, - "strict": true, - "lib": ["es2022"] - }, - "include": ["../scripts/@types/*.d.ts", "./**/*.ts"] -} diff --git a/package.json b/package.json index ba4ef9bd..6eb0890f 100644 --- a/package.json +++ b/package.json @@ -27,17 +27,14 @@ "asar-webapp": "asar p webapp webapp.asar", "start": "yarn run build:ts && yarn run build:res && electron .", "lint": "yarn lint:types && yarn lint:js && yarn lint:workflows", - "lint:js": "eslint --max-warnings 0 src hak playwright scripts && prettier --check .", - "lint:js-fix": "eslint --fix --max-warnings 0 src hak playwright scripts && prettier --log-level=warn --write .", - "lint:types": "yarn lint:types:src && yarn lint:types:test && yarn lint:types:scripts && yarn lint:types:hak", + "lint:js": "eslint --max-warnings 0 src playwright scripts && prettier --check .", + "lint:js-fix": "eslint --fix --max-warnings 0 src playwright scripts && prettier --log-level=warn --write .", + "lint:types": "yarn lint:types:src && yarn lint:types:test && yarn lint:types:scripts", "lint:types:src": "tsc --noEmit", "lint:types:test": "tsc --noEmit -p playwright/tsconfig.json", "lint:types:scripts": "tsc --noEmit -p scripts/tsconfig.json", - "lint:types:hak": "tsc --noEmit -p hak/tsconfig.json", "lint:workflows": "find .github/workflows -type f \\( -iname '*.yaml' -o -iname '*.yml' \\) | xargs -I {} sh -c 'echo \"Linting {}\"; action-validator \"{}\"'", "lint:knip": "knip", - "build:native": "yarn run hak", - "build:native:universal": "yarn run hak --target x86_64-apple-darwin fetchandbuild && yarn run hak --target aarch64-apple-darwin fetchandbuild && yarn run hak --target x86_64-apple-darwin --target aarch64-apple-darwin copyandlink", "build:32": "yarn run build:ts && yarn run build:res && electron-builder --ia32", "build:64": "yarn run build:ts && yarn run build:res && electron-builder --x64", "build:universal": "yarn run build:ts && yarn run build:res && electron-builder --universal", @@ -45,7 +42,6 @@ "build:ts": "tsc", "build:res": "tsx scripts/copy-res.ts", "docker:setup": "docker build --platform linux/amd64 -t element-desktop-dockerbuild -f dockerbuild/Dockerfile .", - "docker:build:native": "scripts/in-docker.sh yarn run hak", "docker:build": "scripts/in-docker.sh yarn run build", "docker:install": "scripts/in-docker.sh yarn install", "clean": "rimraf webapp.asar dist packages deploys lib", @@ -64,6 +60,7 @@ "electron-store": "^10.0.0", "electron-window-state": "^5.0.3", "keytar-forked": "7.10.0", + "matrix-seshat": "https://gitpkg.vercel.app/matrix-org/seshat/seshat-node?t3chguy/binding-gyp", "minimist": "^1.2.6", "png-to-ico": "^2.1.1", "uuid": "^11.0.0" @@ -112,9 +109,6 @@ "tsx": "^4.19.2", "typescript": "5.7.3" }, - "hakDependencies": { - "matrix-seshat": "^4.0.1" - }, "resolutions": { "@types/node": "18.19.76", "config-file-ts": "0.2.8-rc1" diff --git a/scripts/hak/README.md b/scripts/hak/README.md deleted file mode 100644 index a5681d8b..00000000 --- a/scripts/hak/README.md +++ /dev/null @@ -1,81 +0,0 @@ -# hak - -This tool builds native dependencies for element-desktop. Here follows some very minimal -documentation for it. - -Goals: - -- Must build compiled native node modules in a shippable state - (ie. only dynamically linked against libraries that will be on the - target system, all unnecessary files removed). -- Must be able to build any native module, no matter what build system - it uses (electron-rebuild is supposed to do this job but only works - for modules that use gyp). - -It's also loosely designed to be a general tool and agnostic to what it's -actually building. It's used here to build modules for the electron app -but should work equally well for building modules for normal node. - -# Running - -Hak is invoked with a command and a dependency, eg. `yarn run hak fetch matrix-seshat`. -If no dependencies are given, hak runs the command on all dependencies. - -# Files - -There are a lot of files involved: - -- scripts/hak/... - The tool itself -- hak/[dependency] - Files provided by the app that tell hak how to build each of its native dependencies. - Contains a hak.json file and also some script files, each of which must be referenced in hak.json. -- .hak/ - Files generated by hak in the course of doing its job. Includes the dependency module itself and - any of the native dependency's native dependencies. -- .hak/[dependency]/build - An extracted copy of the dependency's node module used to build it. -- .hak/[dependency]/out - Another extracted copy of the dependency, this one contains only what will be shipped. - -# Workings - -Hak works around native node modules that try to fetch or build their native component in -the npm 'install' phase - modules that do this will typically end up with native components -targeted to the build platform and the node that npm/yarn is using, which is no good for an -electron app. - -It does this by installing it with `--ignore-scripts` and then using `yarn link` to keep the -dependency module separate so yarn doesn't try to run its install / postinstall script -at other points (eg. whenever you `yarn add` a random other dependency). - -This also means that the dependencies cannot be listed in `dependencies` or -`devDependencies` in the project, since this would cause npm / yarn to install them and -try to fetch their native parts. Instead, they are listed in `hakDependencies` which -hak reads to install them for you. - -Hak will _not_ install dependencies for the copy of the module it links into your -project, so if your native module has javascript dependencies that are actually needed at -runtime (and not just to fetch / build the native parts), it won't work. - -Hak will generate a `.yarnrc` in the project directory to set the link directory to its -own in the .hak directory (unless one already exists, in which case this is your problem). - -# Lifecycle - -Hak is divided into lifecycle stages, in order: - -- fetch - Download and extract the source of the dependency -- link - Link the copy of the dependency into your node_modules directory -- build - The Good Stuff. Configure and build any native dependencies, then the module itself. -- copy - Copy the built artifact from the module build directory to the module output directory. - -# hak.json - -The scripts section contains scripts used for lifecycle stages that need them (fetch, fetchDeps, build). -It also contains 'prune' and 'copy' which are globs of files to delete from the output module directory -and copy over from the module build directory to the output module directory, respectively. - -# Shortcomings - -Hak doesn't know about dependencies between lifecycle stages, ie. it doesn't know that you need to -'fetch' and 'fetchDeps' before you can 'build', etc. You get to run each individually, and remember -the right order. - -There is also a _lot_ of duplication in the command execution: we should abstract away -some of the boilerplate required to run commands & so forth. diff --git a/scripts/hak/build.ts b/scripts/hak/build.ts deleted file mode 100644 index ccc680f6..00000000 --- a/scripts/hak/build.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* -Copyright 2024 New Vector Ltd. -Copyright 2020 The Matrix.org Foundation C.I.C. - -SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial -Please see LICENSE files in the repository root for full details. -*/ - -import type { DependencyInfo } from "./dep.js"; -import type HakEnv from "./hakEnv.js"; - -export default async function build(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise { - await moduleInfo.scripts.build(hakEnv, moduleInfo); -} diff --git a/scripts/hak/check.ts b/scripts/hak/check.ts deleted file mode 100644 index 2936091a..00000000 --- a/scripts/hak/check.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* -Copyright 2024 New Vector Ltd. -Copyright 2020 The Matrix.org Foundation C.I.C. - -SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial -Please see LICENSE files in the repository root for full details. -*/ - -import type { DependencyInfo } from "./dep.js"; -import type HakEnv from "./hakEnv.js"; - -export default async function check(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise { - if (moduleInfo.scripts.check) { - await moduleInfo.scripts.check(hakEnv, moduleInfo); - } -} diff --git a/scripts/hak/clean.ts b/scripts/hak/clean.ts deleted file mode 100644 index 30d545e1..00000000 --- a/scripts/hak/clean.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* -Copyright 2024 New Vector Ltd. -Copyright 2020 The Matrix.org Foundation C.I.C. - -SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial -Please see LICENSE files in the repository root for full details. -*/ - -import path from "node:path"; -import { rimraf } from "rimraf"; - -import type { DependencyInfo } from "./dep.js"; -import type HakEnv from "./hakEnv.js"; - -export default async function clean(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise { - await rimraf(moduleInfo.moduleDotHakDir); - await rimraf(path.join(hakEnv.dotHakDir, "links", moduleInfo.name)); - await rimraf(path.join(hakEnv.projectRoot, "node_modules", moduleInfo.name)); -} diff --git a/scripts/hak/copy.ts b/scripts/hak/copy.ts deleted file mode 100644 index 6fda80f9..00000000 --- a/scripts/hak/copy.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* -Copyright 2024 New Vector Ltd. -Copyright 2020, 2021 The Matrix.org Foundation C.I.C. - -SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial -Please see LICENSE files in the repository root for full details. -*/ - -import path from "node:path"; -import fsProm from "node:fs/promises"; -import childProcess from "node:child_process"; -import { rimraf } from "rimraf"; -import { glob } from "glob"; -import { mkdirp } from "mkdirp"; - -import type HakEnv from "./hakEnv.js"; -import type { DependencyInfo } from "./dep.js"; - -export default async function copy(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise { - if (moduleInfo.cfg.prune) { - console.log("Removing " + moduleInfo.cfg.prune + " from " + moduleInfo.moduleOutDir); - // rimraf doesn't have a 'cwd' option: it always uses process.cwd() - // (and if you set glob.cwd it just breaks because it can't find the files) - const oldCwd = process.cwd(); - try { - await mkdirp(moduleInfo.moduleOutDir); - process.chdir(moduleInfo.moduleOutDir); - await rimraf(moduleInfo.cfg.prune); - } finally { - process.chdir(oldCwd); - } - } - - if (moduleInfo.cfg.copy) { - // If there are multiple moduleBuildDirs, singular moduleBuildDir - // is the same as moduleBuildDirs[0], so we're just listing the contents - // of the first one. - const files = await glob(moduleInfo.cfg.copy, { - cwd: moduleInfo.moduleBuildDir, - }); - - if (moduleInfo.moduleBuildDirs.length > 1) { - if (!hakEnv.isMac()) { - console.error( - "You asked me to copy multiple targets but I've only been taught " + "how to do that on macOS.", - ); - throw new Error("Can't copy multiple targets on this platform"); - } - - for (const f of files) { - const components = moduleInfo.moduleBuildDirs.map((dir) => path.join(dir, f)); - const dst = path.join(moduleInfo.moduleOutDir, f); - - await mkdirp(path.dirname(dst)); - await new Promise((resolve, reject) => { - childProcess.execFile("lipo", ["-create", "-output", dst, ...components], (err) => { - if (err) { - reject(err); - } else { - resolve(); - } - }); - }); - } - } else { - console.log("Copying files from " + moduleInfo.moduleBuildDir + " to " + moduleInfo.moduleOutDir); - for (const f of files) { - console.log("\t" + f); - const src = path.join(moduleInfo.moduleBuildDir, f); - const dst = path.join(moduleInfo.moduleOutDir, f); - - await mkdirp(path.dirname(dst)); - await fsProm.copyFile(src, dst); - } - } - } -} diff --git a/scripts/hak/dep.ts b/scripts/hak/dep.ts deleted file mode 100644 index bbb717e1..00000000 --- a/scripts/hak/dep.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* -Copyright 2024 New Vector Ltd. -Copyright 2021 The Matrix.org Foundation C.I.C. - -SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial -Please see LICENSE files in the repository root for full details. -*/ - -import type HakEnv from "./hakEnv.js"; - -export interface DependencyInfo { - name: string; - version: string; - cfg: Record; - moduleHakDir: string; - moduleDotHakDir: string; - moduleTargetDotHakDir: string; - moduleBuildDir: string; - moduleBuildDirs: string[]; - moduleOutDir: string; - nodeModuleBinDir: string; - depPrefix: string; - scripts: Record Promise>; -} diff --git a/scripts/hak/fetch.ts b/scripts/hak/fetch.ts deleted file mode 100644 index dc2dbd66..00000000 --- a/scripts/hak/fetch.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* -Copyright 2024 New Vector Ltd. -Copyright 2020 The Matrix.org Foundation C.I.C. - -SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial -Please see LICENSE files in the repository root for full details. -*/ - -import fsProm from "node:fs/promises"; -import pacote from "pacote"; - -import type HakEnv from "./hakEnv.js"; -import type { DependencyInfo } from "./dep.js"; - -export default async function fetch(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise { - let haveModuleBuildDir; - try { - const stats = await fsProm.stat(moduleInfo.moduleBuildDir); - haveModuleBuildDir = stats.isDirectory(); - } catch { - haveModuleBuildDir = false; - } - - if (haveModuleBuildDir) return; - - console.log("Fetching " + moduleInfo.name + "@" + moduleInfo.version); - - const packumentCache = new Map(); - await pacote.extract(`${moduleInfo.name}@${moduleInfo.version}`, moduleInfo.moduleBuildDir, { - packumentCache, - }); - - console.log("Running yarn install in " + moduleInfo.moduleBuildDir); - await hakEnv.spawn("yarn", ["install", "--ignore-scripts"], { - cwd: moduleInfo.moduleBuildDir, - }); - - // also extract another copy to the output directory at this point - // nb. we do not yarn install in the output copy: we could install in - // production mode to get only runtime dependencies and not devDependencies, - // but usually native modules come with dependencies that are needed for - // building/fetching the native modules (eg. node-pre-gyp) rather than - // actually used at runtime: we do not want to bundle these into our app. - // We therefore just install no dependencies at all, and accept that any - // actual runtime dependencies will have to be added to the main app's - // dependencies. We can't tell what dependencies are real runtime deps - // and which are just used for native module building. - await pacote.extract(`${moduleInfo.name}@${moduleInfo.version}`, moduleInfo.moduleOutDir, { - packumentCache, - }); -} diff --git a/scripts/hak/fetchDeps.ts b/scripts/hak/fetchDeps.ts deleted file mode 100644 index 22dbfd5c..00000000 --- a/scripts/hak/fetchDeps.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* -Copyright 2024 New Vector Ltd. -Copyright 2020 The Matrix.org Foundation C.I.C. - -SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial -Please see LICENSE files in the repository root for full details. -*/ - -import { mkdirp } from "mkdirp"; - -import type { DependencyInfo } from "./dep.js"; -import type HakEnv from "./hakEnv.js"; - -export default async function fetchDeps(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise { - await mkdirp(moduleInfo.moduleDotHakDir); - if (moduleInfo.scripts.fetchDeps) { - await moduleInfo.scripts.fetchDeps(hakEnv, moduleInfo); - } -} diff --git a/scripts/hak/hakEnv.ts b/scripts/hak/hakEnv.ts deleted file mode 100644 index ed2bff60..00000000 --- a/scripts/hak/hakEnv.ts +++ /dev/null @@ -1,130 +0,0 @@ -/* -Copyright 2024 New Vector Ltd. -Copyright 2020, 2021 The Matrix.org Foundation C.I.C. - -SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial -Please see LICENSE files in the repository root for full details. -*/ - -import path from "node:path"; -import os from "node:os"; -import { getElectronVersionFromInstalled } from "app-builder-lib/out/electron/electronVersion.js"; -import childProcess, { type SpawnOptions } from "node:child_process"; - -import { type Arch, type Target, TARGETS, getHost, isHostId, type TargetId } from "./target.js"; - -async function getRuntimeVersion(projectRoot: string): Promise { - const electronVersion = await getElectronVersionFromInstalled(projectRoot); - if (!electronVersion) { - throw new Error("Can't determine Electron version"); - } - return electronVersion; -} - -export type Tool = [cmd: string, ...args: string[]]; - -export default class HakEnv { - public readonly target: Target; - public runtime: string = "electron"; - public runtimeVersion?: string; - public dotHakDir: string; - - public constructor( - public readonly projectRoot: string, - targetId: TargetId | null, - ) { - const target = targetId ? TARGETS[targetId] : getHost(); - - if (!target) { - throw new Error(`Unknown target ${targetId}!`); - } - this.target = target; - this.dotHakDir = path.join(this.projectRoot, ".hak"); - } - - public async init(): Promise { - this.runtimeVersion = await getRuntimeVersion(this.projectRoot); - } - - public getTargetId(): TargetId { - return this.target.id; - } - - public isWin(): boolean { - return this.target.platform === "win32"; - } - - public isMac(): boolean { - return this.target.platform === "darwin"; - } - - public isLinux(): boolean { - return this.target.platform === "linux"; - } - - public isFreeBSD(): boolean { - return this.target.platform === "freebsd"; - } - - public getTargetArch(): Arch { - return this.target.arch; - } - - public isHost(): boolean { - return isHostId(this.target.id); - } - - public makeGypEnv(): Record { - return { - ...process.env, - npm_config_arch: this.target.arch, - npm_config_target_arch: this.target.arch, - npm_config_disturl: "https://electronjs.org/headers", - npm_config_runtime: this.runtime, - npm_config_target: this.runtimeVersion, - npm_config_build_from_source: "true", - npm_config_devdir: path.join(os.homedir(), ".electron-gyp"), - }; - } - - public wantsStaticSqlCipher(): boolean { - return !(this.isLinux() || this.isFreeBSD()) || process.env.SQLCIPHER_BUNDLED == "1"; - } - - public spawn( - cmd: string, - args: string[], - { ignoreWinCmdlet, ...options }: SpawnOptions & { ignoreWinCmdlet?: boolean } = {}, - ): Promise { - return new Promise((resolve, reject) => { - const proc = childProcess.spawn(cmd + (!ignoreWinCmdlet && this.isWin() ? ".cmd" : ""), args, { - stdio: "inherit", - // We need shell mode on Windows to be able to launch `.cmd` executables - // See https://nodejs.org/en/blog/vulnerability/april-2024-security-releases-2 - shell: this.isWin(), - ...options, - }); - proc.on("exit", (code) => { - if (code) { - reject(code); - } else { - resolve(); - } - }); - }); - } - - public async checkTools(tools: Tool[]): Promise { - for (const [tool, ...args] of tools) { - try { - await this.spawn(tool, args, { - ignoreWinCmdlet: true, - stdio: ["ignore"], - shell: false, - }); - } catch { - throw new Error(`Can't find ${tool}`); - } - } - } -} diff --git a/scripts/hak/index.ts b/scripts/hak/index.ts deleted file mode 100644 index 22d35731..00000000 --- a/scripts/hak/index.ts +++ /dev/null @@ -1,147 +0,0 @@ -/* -Copyright 2024 New Vector Ltd. -Copyright 2020, 2021 The Matrix.org Foundation C.I.C. - -SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial -Please see LICENSE files in the repository root for full details. -*/ - -import path, { dirname } from "node:path"; -import { fileURLToPath } from "node:url"; - -import HakEnv from "./hakEnv.js"; -import type { TargetId } from "./target.js"; -import type { DependencyInfo } from "./dep.js"; -import { loadJsonFile } from "../../src/utils.js"; -import packageJson from "../../package.json"; - -// These can only be run on specific modules -const MODULECOMMANDS = ["check", "fetch", "link", "build", "copy", "clean"]; - -// Shortcuts for multiple commands at once (useful for building universal binaries -// because you can run the fetch/fetchDeps/build for each arch and then copy/link once) -const METACOMMANDS: Record = { - fetchandbuild: ["check", "fetch", "build"], - copyandlink: ["copy", "link"], -}; - -// Scripts valid in a hak.json 'scripts' section -const HAKSCRIPTS = ["check", "fetch", "build"]; - -const __dirname = dirname(fileURLToPath(import.meta.url)); - -async function main(): Promise { - const prefix = path.join(__dirname, "..", ".."); - - const targetIds: TargetId[] = []; - // Apply `--target ` option if specified - // Can be specified multiple times for the copy command to bundle - // multiple arches into a single universal output module) - for (;;) { - // eslint-disable-line no-constant-condition - const targetIndex = process.argv.indexOf("--target"); - if (targetIndex === -1) break; - - if (targetIndex + 1 >= process.argv.length) { - console.error("--target option specified without a target"); - process.exit(1); - } - // Extract target ID and remove from args - targetIds.push(process.argv.splice(targetIndex, 2)[1] as TargetId); - } - - const hakEnvs = targetIds.map((tid) => new HakEnv(prefix, tid)); - if (hakEnvs.length == 0) hakEnvs.push(new HakEnv(prefix, null)); - for (const h of hakEnvs) { - await h.init(); - } - const hakEnv = hakEnvs[0]; - - const deps: Record = {}; - - const hakDepsCfg = packageJson.hakDependencies || {}; - - for (const dep in hakDepsCfg) { - const hakJsonPath = path.join(prefix, "hak", dep, "hak.json"); - let hakJson: Record; - try { - hakJson = loadJsonFile(hakJsonPath); - } catch { - console.error("No hak.json found for " + dep + "."); - console.log("Expecting " + hakJsonPath); - process.exit(1); - } - deps[dep] = { - name: dep, - version: hakDepsCfg[dep as keyof typeof hakDepsCfg], - cfg: hakJson, - moduleHakDir: path.join(prefix, "hak", dep), - moduleDotHakDir: path.join(hakEnv.dotHakDir, dep), - moduleTargetDotHakDir: path.join(hakEnv.dotHakDir, dep, hakEnv.getTargetId()), - moduleBuildDir: path.join(hakEnv.dotHakDir, dep, hakEnv.getTargetId(), "build"), - moduleBuildDirs: hakEnvs.map((h) => path.join(h.dotHakDir, dep, h.getTargetId(), "build")), - moduleOutDir: path.join(hakEnv.dotHakDir, "hakModules", dep), - nodeModuleBinDir: path.join(hakEnv.dotHakDir, dep, hakEnv.getTargetId(), "build", "node_modules", ".bin"), - depPrefix: path.join(hakEnv.dotHakDir, dep, hakEnv.getTargetId(), "opt"), - scripts: {}, - }; - - for (const s of HAKSCRIPTS) { - if (hakJson.scripts?.[s]) { - // Shockingly, using path.join and backslashes here doesn't work on Windows - const scriptModule = await import(`../../hak/${dep}/${hakJson.scripts[s]}`); - if (scriptModule.default) { - deps[dep].scripts[s] = scriptModule.default; - } else { - deps[dep].scripts[s] = scriptModule; - } - } - } - } - - let cmds: string[]; - if (process.argv.length < 3) { - cmds = ["check", "fetch", "build", "copy", "link"]; - } else if (METACOMMANDS[process.argv[2]]) { - cmds = METACOMMANDS[process.argv[2]]; - } else { - cmds = [process.argv[2]]; - } - - if (hakEnvs.length > 1 && cmds.some((c) => !["copy", "link"].includes(c))) { - // We allow link here too for convenience because it's completely arch independent - console.error("Multiple targets only supported with the copy command"); - return; - } - - let modules = process.argv.slice(3); - if (modules.length === 0) modules = Object.keys(deps); - - for (const cmd of cmds) { - if (!MODULECOMMANDS.includes(cmd)) { - console.error("Unknown command: " + cmd); - console.log("Commands I know about:"); - for (const cmd of MODULECOMMANDS) { - console.log("\t" + cmd); - } - process.exit(1); - } - - const cmdFunc = (await import("./" + cmd)).default; - - for (const mod of modules) { - const depInfo = deps[mod]; - if (depInfo === undefined) { - console.log("Module " + mod + " not found - is it in hakDependencies " + "in your package.json?"); - process.exit(1); - } - console.log("hak " + cmd + ": " + mod); - await cmdFunc(hakEnv, depInfo); - } - } -} - -main().catch((err) => { - console.error(err); - process.exit(1); -}); diff --git a/scripts/hak/link.ts b/scripts/hak/link.ts deleted file mode 100644 index c2c43ceb..00000000 --- a/scripts/hak/link.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* -Copyright 2024 New Vector Ltd. -Copyright 2020 The Matrix.org Foundation C.I.C. - -SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial -Please see LICENSE files in the repository root for full details. -*/ - -import path from "node:path"; -import os from "node:os"; -import fsProm from "node:fs/promises"; - -import type HakEnv from "./hakEnv.js"; -import { type DependencyInfo } from "./dep.js"; - -export default async function link(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise { - const yarnrc = path.join(hakEnv.projectRoot, ".yarnrc"); - // this is fairly terrible but it's reasonably clunky to either parse a yarnrc - // properly or get yarn to do it, so this will probably suffice for now. - // We just check to see if there is a local .yarnrc at all, and assume that - // if you've put one there yourself, you probably know what you're doing and - // we won't meddle with it. - // Also we do this for each module which is unnecessary, but meh. - try { - await fsProm.stat(yarnrc); - } catch { - await fsProm.writeFile( - yarnrc, - // XXX: 1. This must be absolute, as yarn will resolve link directories - // relative to the closest project root, which means when we run it - // in the dependency project, it will put the link directory in its - // own project folder rather than the main project. - // 2. The parser gets very confused by strings with colons in them - // (ie. Windows absolute paths) but strings in quotes get parsed as - // JSON so need to be valid JSON encoded strings (ie. have the - // backslashes escaped). JSON.stringify will add quotes and escape. - "--link-folder " + JSON.stringify(path.join(hakEnv.dotHakDir, "links")) + os.EOL, - ); - } - - await hakEnv.spawn("yarn", ["link"], { - cwd: moduleInfo.moduleOutDir, - }); - await hakEnv.spawn("yarn", ["link", moduleInfo.name], { - cwd: hakEnv.projectRoot, - }); -} diff --git a/scripts/hak/target.ts b/scripts/hak/target.ts deleted file mode 100644 index 3323e0f5..00000000 --- a/scripts/hak/target.ts +++ /dev/null @@ -1,217 +0,0 @@ -/* -Copyright 2024 New Vector Ltd. -Copyright 2021 The Matrix.org Foundation C.I.C. - -SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial -Please see LICENSE files in the repository root for full details. -*/ - -import { GLIBC, MUSL, familySync as processLibC } from "detect-libc"; - -// We borrow Rust's target naming scheme as a way of expressing all target -// details in a single string. -// See https://doc.rust-lang.org/rustc/platform-support.html. -export type TargetId = - | "aarch64-apple-darwin" - | "x86_64-apple-darwin" - | "universal-apple-darwin" - | "i686-pc-windows-msvc" - | "x86_64-pc-windows-msvc" - | "aarch64-pc-windows-msvc" - | "i686-unknown-freebsd" - | "x86_64-unknown-freebsd" - | "aarch64-unknown-freebsd" - | "i686-unknown-linux-musl" - | "i686-unknown-linux-gnu" - | "x86_64-unknown-linux-musl" - | "x86_64-unknown-linux-gnu" - | "aarch64-unknown-linux-musl" - | "aarch64-unknown-linux-gnu" - | "powerpc64le-unknown-linux-musl" - | "powerpc64le-unknown-linux-gnu"; - -// Values are expected to match those used in `process.platform`. -export type Platform = "darwin" | "freebsd" | "linux" | "win32"; - -// Values are expected to match those used in `process.arch`. -export type Arch = "arm64" | "ia32" | "x64" | "ppc64" | "universal"; - -// Values are expected to match those used by Visual Studio's `vcvarsall.bat`. -// See https://docs.microsoft.com/cpp/build/building-on-the-command-line?view=msvc-160#vcvarsall-syntax -export type VcVarsArch = "amd64" | "arm64" | "x86"; - -export type Target = { - id: TargetId; - platform: Platform; - arch: Arch; -}; - -export type WindowsTarget = Target & { - platform: "win32"; - vcVarsArch: VcVarsArch; -}; - -export type LinuxTarget = Target & { - platform: "linux"; - libC: typeof GLIBC | typeof MUSL; -}; - -export type UniversalTarget = Target & { - arch: "universal"; - subtargets: Target[]; -}; - -const aarch64AppleDarwin: Target = { - id: "aarch64-apple-darwin", - platform: "darwin", - arch: "arm64", -}; - -const x8664AppleDarwin: Target = { - id: "x86_64-apple-darwin", - platform: "darwin", - arch: "x64", -}; - -const universalAppleDarwin: UniversalTarget = { - id: "universal-apple-darwin", - platform: "darwin", - arch: "universal", - subtargets: [aarch64AppleDarwin, x8664AppleDarwin], -}; - -const i686PcWindowsMsvc: WindowsTarget = { - id: "i686-pc-windows-msvc", - platform: "win32", - arch: "ia32", - vcVarsArch: "x86", -}; - -const x8664PcWindowsMsvc: WindowsTarget = { - id: "x86_64-pc-windows-msvc", - platform: "win32", - arch: "x64", - vcVarsArch: "amd64", -}; - -const aarch64WindowsMsvc: WindowsTarget = { - id: "aarch64-pc-windows-msvc", - platform: "win32", - arch: "arm64", - vcVarsArch: "arm64", -}; - -const i686UnknownFreebsd: Target = { - id: "i686-unknown-freebsd", - platform: "freebsd", - arch: "ia32", -}; - -const x8664UnknownFreebsd: Target = { - id: "x86_64-unknown-freebsd", - platform: "freebsd", - arch: "x64", -}; - -const aarch64UnknownFreebsd: Target = { - id: "aarch64-unknown-freebsd", - platform: "freebsd", - arch: "arm64", -}; - -const x8664UnknownLinuxGnu: LinuxTarget = { - id: "x86_64-unknown-linux-gnu", - platform: "linux", - arch: "x64", - libC: GLIBC, -}; - -const x8664UnknownLinuxMusl: LinuxTarget = { - id: "x86_64-unknown-linux-musl", - platform: "linux", - arch: "x64", - libC: MUSL, -}; - -const i686UnknownLinuxGnu: LinuxTarget = { - id: "i686-unknown-linux-gnu", - platform: "linux", - arch: "ia32", - libC: GLIBC, -}; - -const i686UnknownLinuxMusl: LinuxTarget = { - id: "i686-unknown-linux-musl", - platform: "linux", - arch: "ia32", - libC: MUSL, -}; - -const aarch64UnknownLinuxGnu: LinuxTarget = { - id: "aarch64-unknown-linux-gnu", - platform: "linux", - arch: "arm64", - libC: GLIBC, -}; - -const aarch64UnknownLinuxMusl: LinuxTarget = { - id: "aarch64-unknown-linux-musl", - platform: "linux", - arch: "arm64", - libC: MUSL, -}; - -const powerpc64leUnknownLinuxGnu: LinuxTarget = { - id: "powerpc64le-unknown-linux-gnu", - platform: "linux", - arch: "ppc64", - libC: GLIBC, -}; - -const powerpc64leUnknownLinuxMusl: LinuxTarget = { - id: "powerpc64le-unknown-linux-musl", - platform: "linux", - arch: "ppc64", - libC: MUSL, -}; - -export const TARGETS: Record = { - // macOS - "aarch64-apple-darwin": aarch64AppleDarwin, - "x86_64-apple-darwin": x8664AppleDarwin, - "universal-apple-darwin": universalAppleDarwin, - // Windows - "i686-pc-windows-msvc": i686PcWindowsMsvc, - "x86_64-pc-windows-msvc": x8664PcWindowsMsvc, - "aarch64-pc-windows-msvc": aarch64WindowsMsvc, - // FreeBSD - "i686-unknown-freebsd": i686UnknownFreebsd, - "x86_64-unknown-freebsd": x8664UnknownFreebsd, - "aarch64-unknown-freebsd": aarch64UnknownFreebsd, - // Linux - "i686-unknown-linux-musl": i686UnknownLinuxMusl, - "i686-unknown-linux-gnu": i686UnknownLinuxGnu, - "x86_64-unknown-linux-musl": x8664UnknownLinuxMusl, - "x86_64-unknown-linux-gnu": x8664UnknownLinuxGnu, - "aarch64-unknown-linux-musl": aarch64UnknownLinuxMusl, - "aarch64-unknown-linux-gnu": aarch64UnknownLinuxGnu, - "powerpc64le-unknown-linux-musl": powerpc64leUnknownLinuxMusl, - "powerpc64le-unknown-linux-gnu": powerpc64leUnknownLinuxGnu, -}; - -export function getHost(): Target | undefined { - return Object.values(TARGETS).find( - (target) => - target.platform === process.platform && - target.arch === process.arch && - (process.platform !== "linux" || (target as LinuxTarget).libC === processLibC()), - ); -} - -export function isHostId(id: TargetId): boolean { - return getHost()?.id === id; -} - -export function isHost(target: Target): boolean { - return getHost()?.id === target.id; -} diff --git a/scripts/in-docker.sh b/scripts/in-docker.sh index 231ddd88..45a37376 100755 --- a/scripts/in-docker.sh +++ b/scripts/in-docker.sh @@ -17,7 +17,6 @@ docker run --rm -ti \ --env ELECTRON_BUILDER_CACHE="/root/.cache/electron-builder" \ -v ${PWD}:/project \ -v ${PWD}/docker/node_modules:/project/node_modules \ - -v ${PWD}/docker/.hak:/project/.hak \ -v ${PWD}/docker/.gnupg:/root/.gnupg \ -v ~/.cache/electron:/root/.cache/electron \ -v ~/.cache/electron-builder:/root/.cache/electron-builder \ diff --git a/yarn.lock b/yarn.lock index 5febb57d..70305d08 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5369,6 +5369,10 @@ math-intrinsics@^1.1.0: resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== +"matrix-seshat@https://gitpkg.vercel.app/matrix-org/seshat/seshat-node?t3chguy/binding-gyp": + version "4.0.1" + resolved "https://gitpkg.vercel.app/matrix-org/seshat/seshat-node?t3chguy/binding-gyp#96cc77502e880d7eb163caaad75c2cbcb3733c6b" + matrix-web-i18n@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/matrix-web-i18n/-/matrix-web-i18n-3.3.0.tgz#a9f9d87d18ef96f75171883abbf201952cbfbe22"