feat(cli): add support for building binaries
This commit is contained in:
parent
1a2198e13c
commit
20b1edc53b
8 changed files with 167 additions and 93 deletions
|
@ -7,6 +7,7 @@ members = [
|
||||||
"./crates/sys",
|
"./crates/sys",
|
||||||
"./examples/napi",
|
"./examples/napi",
|
||||||
"./examples/napi-compat-mode",
|
"./examples/napi-compat-mode",
|
||||||
|
"./examples/binary",
|
||||||
"./bench",
|
"./bench",
|
||||||
"./memory-testing",
|
"./memory-testing",
|
||||||
]
|
]
|
||||||
|
|
110
cli/src/build.ts
110
cli/src/build.ts
|
@ -100,6 +100,10 @@ export class BuildCommand extends Command {
|
||||||
description: `Bypass to ${chalk.green('cargo build --features')}`,
|
description: `Bypass to ${chalk.green('cargo build --features')}`,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
bin?: string = Option.String('--bin', {
|
||||||
|
description: `Bypass to ${chalk.green('cargo build --bin')}`,
|
||||||
|
})
|
||||||
|
|
||||||
dts?: string = Option.String('--dts', 'index.d.ts', {
|
dts?: string = Option.String('--dts', 'index.d.ts', {
|
||||||
description: `The filename and path of ${chalk.green(
|
description: `The filename and path of ${chalk.green(
|
||||||
'.d.ts',
|
'.d.ts',
|
||||||
|
@ -180,12 +184,54 @@ export class BuildCommand extends Command {
|
||||||
const cwd = this.cargoCwd
|
const cwd = this.cargoCwd
|
||||||
? join(process.cwd(), this.cargoCwd)
|
? join(process.cwd(), this.cargoCwd)
|
||||||
: process.cwd()
|
: process.cwd()
|
||||||
|
|
||||||
|
let tomlContentString: string
|
||||||
|
let tomlContent: any
|
||||||
|
try {
|
||||||
|
debug('Start read toml')
|
||||||
|
tomlContentString = await readFileAsync(join(cwd, 'Cargo.toml'), 'utf-8')
|
||||||
|
} catch {
|
||||||
|
throw new TypeError(`Could not find Cargo.toml in ${cwd}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
debug('Start parse toml')
|
||||||
|
tomlContent = toml.parse(tomlContentString)
|
||||||
|
} catch {
|
||||||
|
throw new TypeError('Could not parse the Cargo.toml')
|
||||||
|
}
|
||||||
|
|
||||||
|
let cargoPackageName: string
|
||||||
|
if (tomlContent.package?.name) {
|
||||||
|
cargoPackageName = tomlContent.package.name
|
||||||
|
} else {
|
||||||
|
throw new TypeError('No package.name field in Cargo.toml')
|
||||||
|
}
|
||||||
|
|
||||||
|
const cargoMetadata = JSON.parse(
|
||||||
|
execSync('cargo metadata --format-version 1', {
|
||||||
|
stdio: 'pipe',
|
||||||
|
}).toString('utf8'),
|
||||||
|
)
|
||||||
|
const packages = cargoMetadata.packages
|
||||||
|
const cargoPackage = packages.find(
|
||||||
|
(p: { name: string }) => p.name === cargoPackageName,
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
!this.bin &&
|
||||||
|
cargoPackage?.targets?.length === 1 &&
|
||||||
|
cargoPackage?.targets[0].kind.length === 1 &&
|
||||||
|
cargoPackage?.targets[0].kind[0] === 'bin'
|
||||||
|
) {
|
||||||
|
this.bin = cargoPackageName
|
||||||
|
}
|
||||||
const releaseFlag = this.isRelease ? `--release` : ''
|
const releaseFlag = this.isRelease ? `--release` : ''
|
||||||
|
|
||||||
const targetFlag = this.targetTripleDir
|
const targetFlag = this.targetTripleDir
|
||||||
? `--target ${this.targetTripleDir}`
|
? `--target ${this.targetTripleDir}`
|
||||||
: ''
|
: ''
|
||||||
const featuresFlag = this.features ? `--features ${this.features}` : ''
|
const featuresFlag = this.features ? `--features ${this.features}` : ''
|
||||||
|
const binFlag = this.bin ? `--bin ${this.bin}` : ''
|
||||||
const triple = this.targetTripleDir
|
const triple = this.targetTripleDir
|
||||||
? parseTriple(this.targetTripleDir)
|
? parseTriple(this.targetTripleDir)
|
||||||
: getDefaultTargetTriple(
|
: getDefaultTargetTriple(
|
||||||
|
@ -199,6 +245,7 @@ export class BuildCommand extends Command {
|
||||||
releaseFlag,
|
releaseFlag,
|
||||||
targetFlag,
|
targetFlag,
|
||||||
featuresFlag,
|
featuresFlag,
|
||||||
|
binFlag,
|
||||||
pFlag,
|
pFlag,
|
||||||
this.cargoFlags,
|
this.cargoFlags,
|
||||||
]
|
]
|
||||||
|
@ -224,7 +271,7 @@ export class BuildCommand extends Command {
|
||||||
const rustflags = process.env.RUSTFLAGS
|
const rustflags = process.env.RUSTFLAGS
|
||||||
? process.env.RUSTFLAGS.split(' ')
|
? process.env.RUSTFLAGS.split(' ')
|
||||||
: []
|
: []
|
||||||
if (triple.raw.includes('musl')) {
|
if (triple.raw.includes('musl') && !this.bin) {
|
||||||
if (!rustflags.includes('target-feature=-crt-static')) {
|
if (!rustflags.includes('target-feature=-crt-static')) {
|
||||||
rustflags.push('-C target-feature=-crt-static')
|
rustflags.push('-C target-feature=-crt-static')
|
||||||
}
|
}
|
||||||
|
@ -317,34 +364,15 @@ export class BuildCommand extends Command {
|
||||||
cwd,
|
cwd,
|
||||||
})
|
})
|
||||||
const { binaryName, packageName } = getNapiConfig(this.configFileName)
|
const { binaryName, packageName } = getNapiConfig(this.configFileName)
|
||||||
let dylibName = this.cargoName
|
let cargoArtifactName = this.cargoName
|
||||||
if (!dylibName) {
|
if (!cargoArtifactName) {
|
||||||
let tomlContentString: string
|
if (this.bin) {
|
||||||
let tomlContent: any
|
cargoArtifactName = cargoPackageName
|
||||||
try {
|
|
||||||
debug('Start read toml')
|
|
||||||
tomlContentString = await readFileAsync(
|
|
||||||
join(cwd, 'Cargo.toml'),
|
|
||||||
'utf-8',
|
|
||||||
)
|
|
||||||
} catch {
|
|
||||||
throw new TypeError(`Could not find Cargo.toml in ${cwd}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
debug('Start parse toml')
|
|
||||||
tomlContent = toml.parse(tomlContentString)
|
|
||||||
} catch {
|
|
||||||
throw new TypeError('Could not parse the Cargo.toml')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tomlContent.package?.name) {
|
|
||||||
dylibName = tomlContent.package.name.replace(/-/g, '_')
|
|
||||||
} else {
|
} else {
|
||||||
throw new TypeError('No package.name field in Cargo.toml')
|
cargoArtifactName = cargoPackageName.replace(/-/g, '_')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tomlContent.lib?.['crate-type']?.includes?.('cdylib')) {
|
if (!this.bin && !tomlContent.lib?.['crate-type']?.includes?.('cdylib')) {
|
||||||
throw new TypeError(
|
throw new TypeError(
|
||||||
`Missing ${chalk.green('crate-type = ["cdylib"]')} in ${chalk.green(
|
`Missing ${chalk.green('crate-type = ["cdylib"]')} in ${chalk.green(
|
||||||
'[lib]',
|
'[lib]',
|
||||||
|
@ -353,18 +381,23 @@ export class BuildCommand extends Command {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug(`Dylib name: ${chalk.greenBright(dylibName)}`)
|
if (this.bin) {
|
||||||
|
debug(`Binary name: ${chalk.greenBright(cargoArtifactName)}`)
|
||||||
|
} else {
|
||||||
|
debug(`Dylib name: ${chalk.greenBright(cargoArtifactName)}`)
|
||||||
|
}
|
||||||
|
|
||||||
const platform = triple.platform
|
const platform = triple.platform
|
||||||
let libExt
|
let libExt = ''
|
||||||
|
|
||||||
debug(`Platform: ${chalk.greenBright(platform)}`)
|
debug(`Platform: ${chalk.greenBright(platform)}`)
|
||||||
|
|
||||||
// Platform based massaging for build commands
|
// Platform based massaging for build commands
|
||||||
|
if (!this.bin) {
|
||||||
switch (platform) {
|
switch (platform) {
|
||||||
case 'darwin':
|
case 'darwin':
|
||||||
libExt = '.dylib'
|
libExt = '.dylib'
|
||||||
dylibName = `lib${dylibName}`
|
cargoArtifactName = `lib${cargoArtifactName}`
|
||||||
break
|
break
|
||||||
case 'win32':
|
case 'win32':
|
||||||
libExt = '.dll'
|
libExt = '.dll'
|
||||||
|
@ -374,7 +407,7 @@ export class BuildCommand extends Command {
|
||||||
case 'openbsd':
|
case 'openbsd':
|
||||||
case 'android':
|
case 'android':
|
||||||
case 'sunos':
|
case 'sunos':
|
||||||
dylibName = `lib${dylibName}`
|
cargoArtifactName = `lib${cargoArtifactName}`
|
||||||
libExt = '.so'
|
libExt = '.so'
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
|
@ -382,6 +415,7 @@ export class BuildCommand extends Command {
|
||||||
'Operating system not currently supported or recognized by the build script',
|
'Operating system not currently supported or recognized by the build script',
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const targetRootDir = await findUp(cwd)
|
const targetRootDir = await findUp(cwd)
|
||||||
|
|
||||||
|
@ -399,7 +433,9 @@ export class BuildCommand extends Command {
|
||||||
: ''
|
: ''
|
||||||
|
|
||||||
debug(`Platform name: ${platformName || chalk.green('[Empty]')}`)
|
debug(`Platform name: ${platformName || chalk.green('[Empty]')}`)
|
||||||
const distFileName = `${binaryName}${platformName}.node`
|
const distFileName = this.bin
|
||||||
|
? cargoArtifactName!
|
||||||
|
: `${binaryName}${platformName}.node`
|
||||||
|
|
||||||
const distModulePath = join(this.destDir ?? '.', distFileName)
|
const distModulePath = join(this.destDir ?? '.', distFileName)
|
||||||
|
|
||||||
|
@ -419,7 +455,7 @@ export class BuildCommand extends Command {
|
||||||
targetRootDir,
|
targetRootDir,
|
||||||
'target',
|
'target',
|
||||||
targetDir,
|
targetDir,
|
||||||
`${dylibName}${libExt}`,
|
`${cargoArtifactName}${libExt}`,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (existsSync(distModulePath)) {
|
if (existsSync(distModulePath)) {
|
||||||
|
@ -430,16 +466,13 @@ export class BuildCommand extends Command {
|
||||||
debug(`Write binary content to [${chalk.yellowBright(distModulePath)}]`)
|
debug(`Write binary content to [${chalk.yellowBright(distModulePath)}]`)
|
||||||
await copyFileAsync(sourcePath, distModulePath)
|
await copyFileAsync(sourcePath, distModulePath)
|
||||||
|
|
||||||
|
if (!this.bin) {
|
||||||
const dtsFilePath = join(
|
const dtsFilePath = join(
|
||||||
process.cwd(),
|
process.cwd(),
|
||||||
this.destDir ?? '.',
|
this.destDir ?? '.',
|
||||||
this.dts ?? 'index.d.ts',
|
this.dts ?? 'index.d.ts',
|
||||||
)
|
)
|
||||||
|
|
||||||
const idents = await processIntermediateTypeFile(
|
|
||||||
intermediateTypeFile,
|
|
||||||
dtsFilePath,
|
|
||||||
)
|
|
||||||
if (this.pipe) {
|
if (this.pipe) {
|
||||||
const pipeCommand = `${this.pipe} ${dtsFilePath}`
|
const pipeCommand = `${this.pipe} ${dtsFilePath}`
|
||||||
console.info(`Run ${chalk.green(pipeCommand)}`)
|
console.info(`Run ${chalk.green(pipeCommand)}`)
|
||||||
|
@ -458,6 +491,10 @@ export class BuildCommand extends Command {
|
||||||
this.appendPlatformToFilename
|
this.appendPlatformToFilename
|
||||||
? join(process.cwd(), this.jsBinding)
|
? join(process.cwd(), this.jsBinding)
|
||||||
: null
|
: null
|
||||||
|
const idents = await processIntermediateTypeFile(
|
||||||
|
intermediateTypeFile,
|
||||||
|
dtsFilePath,
|
||||||
|
)
|
||||||
await writeJsBinding(
|
await writeJsBinding(
|
||||||
binaryName,
|
binaryName,
|
||||||
this.jsPackageName ?? packageName,
|
this.jsPackageName ?? packageName,
|
||||||
|
@ -478,6 +515,7 @@ export class BuildCommand extends Command {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function findUp(dir = process.cwd()): Promise<string | null> {
|
async function findUp(dir = process.cwd()): Promise<string | null> {
|
||||||
const dist = join(dir, 'target')
|
const dist = join(dir, 'target')
|
||||||
|
|
2
examples/binary/.gitignore
vendored
Normal file
2
examples/binary/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
*.node
|
||||||
|
napi-examples-binary
|
7
examples/binary/Cargo.toml
Normal file
7
examples/binary/Cargo.toml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
[package]
|
||||||
|
edition = "2021"
|
||||||
|
name = "napi-examples-binary"
|
||||||
|
publish = false
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[dependencies]
|
14
examples/binary/package.json
Normal file
14
examples/binary/package.json
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"name": "binary",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"bin": "napi-examples-binary",
|
||||||
|
"scripts": {
|
||||||
|
"build": "node ../../cli/scripts/index.js build --js false",
|
||||||
|
"build-aarch64": "node ../../cli/scripts/index.js build --js false --target aarch64-unknown-linux-gnu",
|
||||||
|
"build-armv7": "node ../../cli/scripts/index.js build --js false --target armv7-unknown-linux-gnueabihf",
|
||||||
|
"build-i686": "node ../../cli/scripts/index.js build --js false --target i686-pc-windows-msvc",
|
||||||
|
"build-i686-release": "node ../../cli/scripts/index.js build --js false --release --target i686-pc-windows-msvc",
|
||||||
|
"build-release": "node ../../cli/scripts/index.js build --js false --release"
|
||||||
|
}
|
||||||
|
}
|
3
examples/binary/src/main.rs
Normal file
3
examples/binary/src/main.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
fn main() {
|
||||||
|
println!("Hello World!");
|
||||||
|
}
|
|
@ -8,7 +8,8 @@
|
||||||
"triples",
|
"triples",
|
||||||
"memory-testing",
|
"memory-testing",
|
||||||
"examples/napi",
|
"examples/napi",
|
||||||
"examples/napi-compat-mode"
|
"examples/napi-compat-mode",
|
||||||
|
"examples/binary"
|
||||||
],
|
],
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
|
@ -2217,6 +2217,14 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"binary@workspace:examples/binary":
|
||||||
|
version: 0.0.0-use.local
|
||||||
|
resolution: "binary@workspace:examples/binary"
|
||||||
|
bin:
|
||||||
|
binary: napi-examples-binary
|
||||||
|
languageName: unknown
|
||||||
|
linkType: soft
|
||||||
|
|
||||||
"bl@npm:^4.0.3, bl@npm:^4.1.0":
|
"bl@npm:^4.0.3, bl@npm:^4.1.0":
|
||||||
version: 4.1.0
|
version: 4.1.0
|
||||||
resolution: "bl@npm:4.1.0"
|
resolution: "bl@npm:4.1.0"
|
||||||
|
|
Loading…
Reference in a new issue