Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
6 changes: 4 additions & 2 deletions crates/libdatadog-wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,26 +50,28 @@ getrandom = { version = "0.2", features = ["js"] }
# which are not supported by Node.js 18. Keep this list to features used by the
# Rust output and supported by every Node.js version that we test.
wasm-opt = [
"-Oz",
"-O3",
"--enable-mutable-globals",
"--enable-nontrapping-float-to-int",
"--enable-bulk-memory",
"--enable-sign-ext",
"--enable-reference-types",
"--enable-multivalue",
"--enable-simd",
"--converge",
]

[package.metadata.wasm-pack.profile.profiling]
# Keep function names for crate attribution after wasm-opt runs in Linux CI.
wasm-opt = [
"-Oz",
"-O3",
"--enable-mutable-globals",
"--enable-nontrapping-float-to-int",
"--enable-bulk-memory",
"--enable-sign-ext",
"--enable-reference-types",
"--enable-multivalue",
"--enable-simd",
"--converge",
"-g",
]
23 changes: 22 additions & 1 deletion scripts/build-wasm.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ const env = {
...process.env,
}

// Keep the min-size profile while relaxing only LLVM's inlining cap for the compression hot path.
// Rebenchmark this LLVM-internal threshold when updating rust-toolchain.toml.
const libdatadogWasmRustFlags = '-C target-feature=+simd128 -C llvm-args=-inline-threshold=45'

if (isMacOS) {
const homebrewDir = env.HOMEBREW_DIR ?? '/opt/homebrew'
const llvmDir = `${homebrewDir}/opt/llvm/`
Expand Down Expand Up @@ -64,14 +68,31 @@ if (isMacOS) {
function buildWasm (cratePath, outputDirectory, options = {}) {
const { profiling = false, skipOptimization = false } = options
const resolvedOutputDirectory = path.resolve(cratePath, outputDirectory)
const buildEnvironment = { ...env }
if (path.basename(cratePath) === 'libdatadog-wasm') {
if (buildEnvironment.CARGO_ENCODED_RUSTFLAGS === undefined) {
const rustFlagsName = buildEnvironment.RUSTFLAGS === undefined
? 'CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUSTFLAGS'
: 'RUSTFLAGS'
buildEnvironment[rustFlagsName] = [
buildEnvironment[rustFlagsName],
libdatadogWasmRustFlags,
].filter(Boolean).join(' ')
} else {
buildEnvironment.CARGO_ENCODED_RUSTFLAGS = [
buildEnvironment.CARGO_ENCODED_RUSTFLAGS,
...libdatadogWasmRustFlags.split(' '),
].filter(Boolean).join('\x1F')
}
}
fs.rmSync(resolvedOutputDirectory, { force: true, recursive: true })
const args = ['build']
if (profiling) args.push('--profiling')
if (skipOptimization) args.push('--no-opt')
args.push('--target', 'nodejs', cratePath, '--out-dir', resolvedOutputDirectory)
childProcess.execFileSync('wasm-pack', args, {
env: {
...env,
...buildEnvironment,
// Cargo's release profile strips the function names needed for size attribution.
...(profiling && { CARGO_PROFILE_RELEASE_STRIP: 'false' }),
},
Expand Down
61 changes: 60 additions & 1 deletion test/build-wasm.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ const { test } = require('node:test')

const buildScript = path.join(__dirname, '..', 'scripts', 'build-wasm.js')

test('cleans WASM output relative to each crate', () => {
test('builds WASM crates with scoped compiler flags and output paths', () => {
const temporaryRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'build-wasm-'))
const projectRoot = path.join(temporaryRoot, 'nested', 'repository')
const binaryDirectory = path.join(temporaryRoot, 'bin')
const homebrewDirectory = path.join(temporaryRoot, 'homebrew')
const llvmDirectory = path.join(homebrewDirectory, 'opt', 'llvm', 'bin')
const existingRustFlags = '-C debuginfo=1'
const libdatadogWasmRustFlags = '-C target-feature=+simd128 -C llvm-args=-inline-threshold=45'

try {
fs.mkdirSync(projectRoot, { recursive: true })
Expand All @@ -29,6 +31,13 @@ test('cleans WASM output relative to each crate', () => {
fs.mkdirSync(outputDirectory, { recursive: true })
fs.writeFileSync(path.join(outputDirectory, '.gitignore'), '')
fs.writeFileSync(path.join(outputDirectory, 'built'), '')
fs.writeFileSync(
path.join(outputDirectory, 'rustflags'),
process.env.CARGO_ENCODED_RUSTFLAGS ??
process.env.RUSTFLAGS ??
process.env.CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUSTFLAGS ??
'',
)
`)

for (const library of ['library_config', 'pipeline']) {
Expand All @@ -53,6 +62,7 @@ test('cleans WASM output relative to each crate', () => {
cwd: projectRoot,
env: {
...process.env,
CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUSTFLAGS: existingRustFlags,
HOMEBREW_DIR: homebrewDirectory,
PATH: `${binaryDirectory}${path.delimiter}${process.env.PATH}`,
},
Expand All @@ -73,6 +83,55 @@ test('cleans WASM output relative to each crate', () => {
assert(fs.existsSync(path.join(outputDirectory, 'built')))
assert(!fs.existsSync(path.join(outputDirectory, '.gitignore')))
assert(fs.existsSync(path.join(unrelatedDirectory, 'keep')))
assert.strictEqual(
fs.readFileSync(path.join(outputDirectory, 'rustflags'), 'utf8'),
existingRustFlags,
)
}

const crateDirectory = path.join(projectRoot, 'crates', 'libdatadog-wasm')
fs.mkdirSync(crateDirectory, { recursive: true })

const encodedExistingRustFlags = existingRustFlags.replaceAll(' ', '\x1F')
const encodedLibdatadogWasmRustFlags = libdatadogWasmRustFlags.replaceAll(' ', '\x1F')
const rustFlagCases = [
{
environment: { CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUSTFLAGS: existingRustFlags },
expected: `${existingRustFlags} ${libdatadogWasmRustFlags}`,
name: 'target',
},
{
environment: { RUSTFLAGS: existingRustFlags },
expected: `${existingRustFlags} ${libdatadogWasmRustFlags}`,
name: 'standard',
},
{
environment: { CARGO_ENCODED_RUSTFLAGS: encodedExistingRustFlags },
expected: `${encodedExistingRustFlags}\x1F${encodedLibdatadogWasmRustFlags}`,
name: 'encoded',
},
]

for (const { environment, expected, name } of rustFlagCases) {
const outputDirectory = path.join(projectRoot, 'prebuilds', `libdatadog-wasm-${name}`)
execFileSync(process.execPath, [buildScript, crateDirectory, outputDirectory], {
cwd: projectRoot,
env: {
...process.env,
CARGO_ENCODED_RUSTFLAGS: undefined,
CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUSTFLAGS: undefined,
RUSTFLAGS: undefined,
...environment,
HOMEBREW_DIR: homebrewDirectory,
PATH: `${binaryDirectory}${path.delimiter}${process.env.PATH}`,
},
stdio: 'pipe',
})

assert.strictEqual(
fs.readFileSync(path.join(outputDirectory, 'rustflags'), 'utf8'),
expected,
)
}
} finally {
fs.rmSync(temporaryRoot, { force: true, recursive: true })
Expand Down
Loading