Skip to content

Commit

Permalink
Use a fully static seshat build
Browse files Browse the repository at this point in the history
  • Loading branch information
MatMaul authored and Mathieu Velten committed Apr 21, 2023
1 parent ed8870d commit f835f0b
Show file tree
Hide file tree
Showing 8 changed files with 25 additions and 441 deletions.
16 changes: 5 additions & 11 deletions .github/workflows/build_linux.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -150,17 +150,11 @@ jobs:
LIBS=$(readelf -d dist/**/*.node | grep NEEDED)
echo "$LIBS"
if [ "$SQLCIPHER_STATIC" == "1" ]; then
if grep -q "libsqlcipher.so.0" <<< "$LIBS" ; then
exit 2
fi
else
if grep -q "libcrypto.so.1.1" <<< "$LIBS" ; then
exit 3
fi
if ! grep -q "libsqlcipher.so.0" <<< "$LIBS" ; then
exit 4
fi
if grep -q "libsqlcipher.so.0" <<< "$LIBS" ; then
exit 2
fi
if grep -q "libcrypto.so.1.1" <<< "$LIBS" ; then
exit 3
fi
env:
ARCH: ${{ steps.config.outputs.arch }}
Expand Down
284 changes: 13 additions & 271 deletions hak/matrix-seshat/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,101 +22,22 @@ import fsExtra from "fs-extra";
import HakEnv from "../../scripts/hak/hakEnv";
import { DependencyInfo } from "../../scripts/hak/dep";

type WinConfiguration =
| "VC-WIN32"
| "VC-WIN64A"
| "VC-WIN64-ARM"
| "VC-WIN64-CLANGASM-ARM"
| "VC-CLANG-WIN64-CLANGASM-ARM"
| "VC-WIN32-HYBRIDCRT"
| "VC-WIN64A-HYBRIDCRT";

export default async function (hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
if (hakEnv.isWin()) {
await buildOpenSslWin(hakEnv, moduleInfo);
await buildSqlCipherWin(hakEnv, moduleInfo);
} else if (hakEnv.wantsStaticSqlCipherUnix()) {
await buildSqlCipherUnix(hakEnv, moduleInfo);
}
await buildMatrixSeshat(hakEnv, moduleInfo);
}

async function buildOpenSslWin(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
const version = moduleInfo.cfg.dependencies.openssl;
const openSslDir = path.join(moduleInfo.moduleTargetDotHakDir, `openssl-${version}`);
const env = hakEnv.makeGypEnv();

let openSslArch: WinConfiguration;
switch (hakEnv.getTargetArch()) {
case "x64":
openSslArch = "VC-WIN64A";
break;
case "ia32":
openSslArch = "VC-WIN32";
break;
case "arm64":
openSslArch = "VC-WIN64-ARM";
break;
if (!hakEnv.isHost()) {
env.CARGO_BUILD_TARGET = hakEnv.getTargetId();
}

console.log("Building openssl in " + openSslDir);
console.log("Running yarn install");
await new Promise<void>((resolve, reject) => {
const proc = childProcess.spawn(
"perl",
[
"Configure",
"--prefix=" + moduleInfo.depPrefix,
// sqlcipher only uses about a tiny part of openssl. We link statically
// so will only pull in the symbols we use, but we may as well turn off
// as much as possible to save on build time.
"no-afalgeng",
"no-capieng",
"no-cms",
"no-ct",
"no-deprecated",
"no-dgram",
"no-dso",
"no-ec",
"no-ec2m",
"no-gost",
"no-nextprotoneg",
"no-ocsp",
"no-sock",
"no-srp",
"no-srtp",
"no-tests",
"no-ssl",
"no-tls",
"no-dtls",
"no-shared",
"no-aria",
"no-camellia",
"no-cast",
"no-chacha",
"no-cmac",
"no-des",
"no-dh",
"no-dsa",
"no-ecdh",
"no-ecdsa",
"no-idea",
"no-md4",
"no-mdc2",
"no-ocb",
"no-poly1305",
"no-rc2",
"no-rc4",
"no-rmd160",
"no-scrypt",
"no-seed",
"no-siphash",
"no-sm2",
"no-sm3",
"no-sm4",
"no-whirlpool",
openSslArch,
],
"yarn" + (hakEnv.isWin() ? ".cmd" : ""),
["install"],
{
cwd: openSslDir,
cwd: moduleInfo.moduleBuildDir,
env,
shell: true,
stdio: "inherit",
},
);
Expand All @@ -125,194 +46,15 @@ async function buildOpenSslWin(hakEnv: HakEnv, moduleInfo: DependencyInfo): Prom
});
});

await new Promise<void>((resolve, reject) => {
const proc = childProcess.spawn("nmake", ["build_libs"], {
cwd: openSslDir,
stdio: "inherit",
});
proc.on("exit", (code) => {
code ? reject(code) : resolve();
});
});

await new Promise<void>((resolve, reject) => {
const proc = childProcess.spawn("nmake", ["install_dev"], {
cwd: openSslDir,
stdio: "inherit",
});
proc.on("exit", (code) => {
code ? reject(code) : resolve();
});
});
}

async function buildSqlCipherWin(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
const version = moduleInfo.cfg.dependencies.sqlcipher;
const sqlCipherDir = path.join(moduleInfo.moduleTargetDotHakDir, `sqlcipher-${version}`);
const buildDir = path.join(sqlCipherDir, "bld");

await mkdirp(buildDir);

await new Promise<void>((resolve, reject) => {
const proc = childProcess.spawn("nmake", ["/f", path.join("..", "Makefile.msc"), "libsqlite3.lib", "TOP=.."], {
cwd: buildDir,
stdio: "inherit",
env: Object.assign({}, process.env, {
CCOPTS: "-DSQLITE_HAS_CODEC -I" + path.join(moduleInfo.depPrefix, "include"),
LTLIBPATHS: "/LIBPATH:" + path.join(moduleInfo.depPrefix, "lib"),
LTLIBS: "libcrypto.lib",
}),
});
proc.on("exit", (code) => {
code ? reject(code) : resolve();
});
});

await fsExtra.copy(path.join(buildDir, "libsqlite3.lib"), path.join(moduleInfo.depPrefix, "lib", "sqlcipher.lib"));

await fsExtra.copy(path.join(buildDir, "sqlite3.h"), path.join(moduleInfo.depPrefix, "include", "sqlcipher.h"));
}

async function buildSqlCipherUnix(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
const version = moduleInfo.cfg.dependencies.sqlcipher;
const sqlCipherDir = path.join(moduleInfo.moduleTargetDotHakDir, `sqlcipher-${version}`);

const args = [
"--prefix=" + moduleInfo.depPrefix + "",
"--enable-tempstore=yes",
"--enable-shared=no",
"--enable-tcl=no",
];

if (hakEnv.isMac()) {
args.push("--with-crypto-lib=commoncrypto");
}

if (hakEnv.wantsStaticSqlCipherUnix()) {
args.push("--enable-tcl=no");

if (hakEnv.isLinux()) {
args.push("--with-pic=yes");
}
}

if (!hakEnv.isHost()) {
// In the nonsense world of `configure`, it is assumed you are building
// a compiler like `gcc`, so the `host` option actually means the target
// the build output runs on.
args.push(`--host=${hakEnv.getTargetId()}`);
}

const cflags = ["-DSQLITE_HAS_CODEC"];

// If the caller has specified CFLAGS then we shouldn't specify target
// as their compiler may be incompatible (gcc)
if (!hakEnv.isHost() && !process.env.CFLAGS) {
// `clang` uses more logical option naming.
cflags.push(`--target=${hakEnv.getTargetId()}`);
}

if (process.env.CFLAGS) cflags.unshift(process.env.CFLAGS);
args.push(`CFLAGS=${cflags.join(" ")}`);

const ldflags: string[] = [];

if (hakEnv.isMac()) {
ldflags.push("-framework Security");
ldflags.push("-framework Foundation");
}

if (ldflags.length) {
if (process.env.LDFLAGS) ldflags.unshift(process.env.LDFLAGS);
args.push(`LDFLAGS=${ldflags.join(" ")}`);
}

await new Promise<void>((resolve, reject) => {
const proc = childProcess.spawn(path.join(sqlCipherDir, "configure"), args, {
cwd: sqlCipherDir,
stdio: "inherit",
});
proc.on("exit", (code) => {
code ? reject(code) : resolve();
});
});

await new Promise<void>((resolve, reject) => {
const proc = childProcess.spawn("make", [], {
cwd: sqlCipherDir,
stdio: "inherit",
});
proc.on("exit", (code) => {
code ? reject(code) : resolve();
});
});

await new Promise<void>((resolve, reject) => {
const proc = childProcess.spawn("make", ["install"], {
cwd: sqlCipherDir,
stdio: "inherit",
});
proc.on("exit", (code) => {
code ? reject(code) : resolve();
});
});
}

async function buildMatrixSeshat(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
// seshat now uses n-api so we shouldn't need to specify a node version to
// build against, but it does seems to still need something in here, so leaving
// it for now: we should confirm how much of this it still actually needs.
const env = hakEnv.makeGypEnv();

if (!hakEnv.isLinux() || hakEnv.wantsStaticSqlCipherUnix()) {
Object.assign(env, {
SQLCIPHER_STATIC: 1,
SQLCIPHER_LIB_DIR: path.join(moduleInfo.depPrefix, "lib"),
SQLCIPHER_INCLUDE_DIR: path.join(moduleInfo.depPrefix, "include"),
});
}

if (hakEnv.isLinux() && hakEnv.wantsStaticSqlCipherUnix()) {
// Ensure Element uses the statically-linked seshat build, and prevent other applications
// from attempting to use this one. Detailed explanation:
//
// RUSTFLAGS
// An environment variable containing a list of arguments to pass to rustc.
// -Clink-arg=VALUE
// A rustc argument to pass a single argument to the linker.
// -Wl,
// gcc syntax to pass an argument (from gcc) to the linker (ld).
// -Bsymbolic:
// Prefer local/statically linked symbols over those in the environment.
// Prevent overriding native libraries by LD_PRELOAD etc.
// --exclude-libs ALL
// Prevent symbols from being exported by any archive libraries.
// Reduces output filesize and prevents being dynamically linked against.
env.RUSTFLAGS = "-Clink-arg=-Wl,-Bsymbolic -Clink-arg=-Wl,--exclude-libs,ALL";
}

if (hakEnv.isWin()) {
env.RUSTFLAGS = "-Ctarget-feature=+crt-static -Clink-args=libcrypto.lib";
// Note that in general, you can specify targets in Rust without having to have
// the matching toolchain, however for this, cargo gets confused when building
// the build scripts since they run on the host, but vcvarsall.bat sets the c
// compiler in the path to be the one for the target, so we just use the matching
// toolchain for the target architecture which makes everything happy.
env.RUSTUP_TOOLCHAIN = `stable-${hakEnv.getTargetId()}`;
}

if (!hakEnv.isHost()) {
env.CARGO_BUILD_TARGET = hakEnv.getTargetId();
}

console.log("Running neon with env", env);
console.log("Running yarn build");
await new Promise<void>((resolve, reject) => {
const proc = childProcess.spawn(
path.join(moduleInfo.nodeModuleBinDir, "neon" + (hakEnv.isWin() ? ".cmd" : "")),
["build", "--release"],
"yarn" + (hakEnv.isWin() ? ".cmd" : ""),
["run", "build-bundled"],
{
cwd: moduleInfo.moduleBuildDir,
env,
shell: true,
stdio: "inherit",
},
);
Expand Down
17 changes: 0 additions & 17 deletions hak/matrix-seshat/check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,6 @@ import HakEnv from "../../scripts/hak/hakEnv";
import { DependencyInfo } from "../../scripts/hak/dep";

export default async function (hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
if (hakEnv.wantsStaticSqlCipher()) {
// of course tcl doesn't have a --version
await new Promise<void>((resolve, reject) => {
const proc = childProcess.spawn("tclsh", [], {
stdio: ["pipe", "ignore", "ignore"],
});
proc.on("exit", (code) => {
if (code !== 0) {
reject("Can't find tclsh - have you installed TCL?");
} else {
resolve();
}
});
proc.stdin.end();
});
}

const tools = [
["rustc", "--version"],
["python", "--version"], // node-gyp uses python for reasons beyond comprehension
Expand Down
Loading

0 comments on commit f835f0b

Please sign in to comment.