@dbx-tools/projen 0.6.186 → 0.6.191
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -7
- package/index.ts +2 -2
- package/package.json +4 -4
- package/src/project-js.ts +17 -15
- package/src/project-rs.ts +148 -45
- package/src/release.ts +4 -4
- package/src/tags.ts +1 -0
- package/src/uniffi.ts +0 -37
- package/tasks/bump.ts +1 -1
- package/tasks/publish-uniffi-local.ts +2 -0
- package/tasks/rust.ts +7 -4
- package/tasks/uniffi-release.mjs +25 -5
- package/tasks/uniffi.ts +28 -19
package/README.md
CHANGED
|
@@ -94,19 +94,20 @@ configurable `root` (default `packages/rs`), generates its Cargo manifest, and
|
|
|
94
94
|
derives the crate name and repository from the parent project. A crate containing
|
|
95
95
|
`uniffi::setup_scaffolding!()`
|
|
96
96
|
automatically wires matching public Node and Python binding packages using the
|
|
97
|
-
|
|
97
|
+
`<name>-rs` folder suffix. Node packages are named `@<scope>/<name>-rs`;
|
|
98
|
+
Python distributions are named `<scope>-<name>-rs` and import generated values
|
|
99
|
+
from `<scope>.<name>_rs.bindings`. Binding packages are always dedicated and
|
|
100
|
+
never merge generated code into handwritten Node or Python packages.
|
|
101
|
+
Repository-specific dependencies and features remain
|
|
98
102
|
declarative options in `.projenrc.ts`; generated bindings are built separately
|
|
99
103
|
from projen synthesis. Target-independent Node binding TypeScript is committed
|
|
100
104
|
and remains generated/read-only, while native libraries stay ignored. Node
|
|
101
105
|
facades compile to `lib/` and publish JavaScript entry points that plain Node
|
|
102
106
|
can load from `node_modules`. A complete `bindings.ts` / `_bindings.ts` /
|
|
103
107
|
`_bindings-ffi.ts` triplet is exported directly from the generated package
|
|
104
|
-
barrel. Python keeps `bindings.py` as the generated implementation and
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
package-root export; Python generation refuses to overwrite a non-generated
|
|
108
|
-
package root. Do not create a `nodeExports` binding subpath or a handwritten
|
|
109
|
-
type facade.
|
|
108
|
+
barrel. Python keeps `bindings.py` as the generated implementation and leaves
|
|
109
|
+
`__init__.py` empty. Node generation fails when direct binding names conflict.
|
|
110
|
+
Do not create a `nodeExports` binding subpath or a handwritten type facade.
|
|
110
111
|
|
|
111
112
|
Rust dependencies between binding-enabled workspace crates become Node
|
|
112
113
|
`workspace:*` and Python `internalDependencies` automatically. Python generation
|
|
@@ -158,6 +159,12 @@ release-enabled binary's `releaseExcludeOs` package option to omit it from
|
|
|
158
159
|
incompatible rows through Cargo `--exclude`. Release binary packaging and
|
|
159
160
|
artifact upload are skipped in those rows. UniFFI crates cannot use this option
|
|
160
161
|
because every configured target must produce their artifacts.
|
|
162
|
+
Set a release binary's `cli` package option and the workspace
|
|
163
|
+
`cliRegistryPath` to generate a typed `dbx` command registry from the same
|
|
164
|
+
selected targets. The generated entries carry only the archives that the
|
|
165
|
+
release matrix publishes, so an excluded operating system fails locally before
|
|
166
|
+
any download is attempted. The runtime command uses `@dbx-tools/core`
|
|
167
|
+
`bin.ensure`; the core installer itself contains no product registry.
|
|
161
168
|
Rust rows prepare native npm
|
|
162
169
|
archives and Python wheels; they do not install Bun or UBRN and do not build
|
|
163
170
|
Node facades. A binary-only row therefore installs no language package tool.
|
package/index.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Hand edits are overwritten on the next watch; this file is read-only.
|
|
4
4
|
|
|
5
5
|
export const PACKAGE_IDENTIFIER = "@dbx-tools/projen";
|
|
6
|
-
export const PACKAGE_VERSION = "0.6.
|
|
6
|
+
export const PACKAGE_VERSION = "0.6.190";
|
|
7
7
|
export * as barrels from "./src/barrels.ts";
|
|
8
8
|
export * as bunApp from "./src/bun-app.ts";
|
|
9
9
|
export * as bunWorkflow from "./src/bun-workflow.ts";
|
|
@@ -51,7 +51,7 @@ export type { DBXToolsJavaScriptProject, DBXToolsJavaScriptProjectOptions, DBXTo
|
|
|
51
51
|
export { DBXToolsPythonProject, DBXToolsPythonWorkspace } from "./src/project-py.ts";
|
|
52
52
|
export type { PythonRepositoryOptions, PythonPackageOptions, PythonTrustedPublisherOptions, DBXToolsPythonProjectOptions, PythonReleaseOptions, DBXToolsPythonWorkspaceOptions } from "./src/project-py.ts";
|
|
53
53
|
export { RustReleaseOs, RustReleaseCpu, UNIFFI_RELEASE_TARGETS, DBXToolsRustProject, DBXToolsRustWorkspace } from "./src/project-rs.ts";
|
|
54
|
-
export type { CargoDependencyOptions, CargoDependency, RustPackageOptions, DBXToolsRustWorkspaceOptions, RustReleasePlatform, UniFFIReleaseTarget, RustBindingMapping, RustWorkspaceMapping } from "./src/project-rs.ts";
|
|
54
|
+
export type { CargoDependencyOptions, CargoDependency, RustCliOptions, RustPackageOptions, DBXToolsRustWorkspaceOptions, RustReleasePlatform, UniFFIReleaseTarget, RustBindingMapping, RustWorkspaceMapping, RustReleaseBinaryAssetMapping, RustReleaseBinaryMapping } from "./src/project-rs.ts";
|
|
55
55
|
export { COMPILED_DIR, COMPILED_COMPILER_OPTIONS } from "./src/publish.ts";
|
|
56
56
|
export { DBXToolsRelease } from "./src/release.ts";
|
|
57
57
|
export type { ReleaseStage, ReleaseDocsOptions, DBXToolsReleaseOptions } from "./src/release.ts";
|
package/package.json
CHANGED
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@clack/prompts": "^1.7.0",
|
|
28
|
-
"@dbx-tools/core": "0.6.
|
|
29
|
-
"@dbx-tools/path": "0.6.
|
|
30
|
-
"@dbx-tools/shared-core": "0.6.
|
|
28
|
+
"@dbx-tools/core": "0.6.191",
|
|
29
|
+
"@dbx-tools/path": "0.6.191",
|
|
30
|
+
"@dbx-tools/shared-core": "0.6.191",
|
|
31
31
|
"commander": "^15.0.0",
|
|
32
32
|
"concurrently": "^10.0.3",
|
|
33
33
|
"constructs": "^10.6.0",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
},
|
|
48
48
|
"main": "index.ts",
|
|
49
49
|
"license": "Apache-2.0",
|
|
50
|
-
"version": "0.6.
|
|
50
|
+
"version": "0.6.191",
|
|
51
51
|
"types": "index.ts",
|
|
52
52
|
"type": "module",
|
|
53
53
|
"exports": {
|
package/src/project-js.ts
CHANGED
|
@@ -446,13 +446,13 @@ function defaultProjectOptions(
|
|
|
446
446
|
devDeps: ["@types/node@^24.6.0"],
|
|
447
447
|
...(isRoot
|
|
448
448
|
? {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
449
|
+
prettier: true,
|
|
450
|
+
prettierOptions: {
|
|
451
|
+
settings: PRETTIER_SETTINGS,
|
|
452
|
+
ignoreFile: true,
|
|
453
|
+
ignoreFileOptions: { ignorePatterns: [...ignore.ignorePatterns({ test: false })] },
|
|
454
|
+
},
|
|
455
|
+
}
|
|
456
456
|
: {}),
|
|
457
457
|
...options,
|
|
458
458
|
...copiedGitIgnoreOptions(options),
|
|
@@ -508,10 +508,10 @@ const DEV_DEPS_ROOT: string[] = ["typescript@^5.9.3", `@types/bun@${BUN_VERSION}
|
|
|
508
508
|
/** Options for {@link DBXToolsNodeProject} (the monorepo root). */
|
|
509
509
|
export interface DBXToolsJavaScriptProjectOptions
|
|
510
510
|
extends
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
511
|
+
CommonProjectOptions,
|
|
512
|
+
Partial<javascript.NodeProjectOptions>,
|
|
513
|
+
DBXToolsConfigOptions,
|
|
514
|
+
DBXToolsPNPMWorkspaceOptions {
|
|
515
515
|
/**
|
|
516
516
|
* The npm scope for generated package names (`@<scope>/<seg-...>`). Defaults to
|
|
517
517
|
* the (resolved) project name; a leading `@` is optional.
|
|
@@ -584,7 +584,8 @@ export interface DBXToolsTypeScriptProjectOptions
|
|
|
584
584
|
*/
|
|
585
585
|
export class DBXToolsNodeProject
|
|
586
586
|
extends javascript.NodeProject
|
|
587
|
-
implements DBXToolsJavaScriptProject
|
|
587
|
+
implements DBXToolsJavaScriptProject
|
|
588
|
+
{
|
|
588
589
|
readonly language = "javascript" as const;
|
|
589
590
|
readonly scope: string;
|
|
590
591
|
readonly dbxToolsConfig: DBXToolsConfig;
|
|
@@ -684,8 +685,8 @@ export const ROOT_INSTALL_ONLY_MIXIN = mixin.create(
|
|
|
684
685
|
installDependencies(trigger: unknown): void;
|
|
685
686
|
logInstallTrigger(trigger: unknown): void;
|
|
686
687
|
};
|
|
687
|
-
nodePackage.installDependencies = () => {
|
|
688
|
-
nodePackage.logInstallTrigger = () => {
|
|
688
|
+
nodePackage.installDependencies = () => {};
|
|
689
|
+
nodePackage.logInstallTrigger = () => {};
|
|
689
690
|
},
|
|
690
691
|
);
|
|
691
692
|
|
|
@@ -698,7 +699,8 @@ export const ROOT_INSTALL_ONLY_MIXIN = mixin.create(
|
|
|
698
699
|
*/
|
|
699
700
|
export class DBXToolsTypeScriptProject
|
|
700
701
|
extends typescript.TypeScriptProject
|
|
701
|
-
implements DBXToolsJavaScriptProject
|
|
702
|
+
implements DBXToolsJavaScriptProject
|
|
703
|
+
{
|
|
702
704
|
readonly language = "javascript" as const;
|
|
703
705
|
readonly scope: string;
|
|
704
706
|
readonly dbxToolsConfig: DBXToolsConfig;
|
package/src/project-rs.ts
CHANGED
|
@@ -39,6 +39,13 @@ export interface CargoDependencyOptions {
|
|
|
39
39
|
|
|
40
40
|
export type CargoDependency = string | CargoDependencyOptions;
|
|
41
41
|
|
|
42
|
+
export interface RustCliOptions {
|
|
43
|
+
/** `dbx` subcommand. Defaults to the Rust package directory. */
|
|
44
|
+
readonly command?: string;
|
|
45
|
+
/** Root CLI help text. Defaults to the Rust package description. */
|
|
46
|
+
readonly description?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
42
49
|
export interface RustPackageOptions {
|
|
43
50
|
readonly directory: string;
|
|
44
51
|
readonly description?: string;
|
|
@@ -54,9 +61,9 @@ export interface RustPackageOptions {
|
|
|
54
61
|
readonly defaultFeatures?: readonly string[];
|
|
55
62
|
/** Cargo and release executable name. Defaults to the generated package name. */
|
|
56
63
|
readonly binaryName?: string;
|
|
64
|
+
/** Publish this release binary through the generated `dbx` command registry. */
|
|
65
|
+
readonly cli?: boolean | RustCliOptions;
|
|
57
66
|
readonly bindings?: readonly ("node" | "python")[];
|
|
58
|
-
readonly nodeDependencies?: readonly string[];
|
|
59
|
-
readonly nodeDevDependencies?: readonly string[];
|
|
60
67
|
readonly uniffiConfig?: Readonly<Record<string, unknown>>;
|
|
61
68
|
}
|
|
62
69
|
|
|
@@ -82,6 +89,8 @@ export interface DBXToolsRustWorkspaceOptions {
|
|
|
82
89
|
readonly releaseTargets?: readonly UniFFIReleaseTarget[];
|
|
83
90
|
/** Maintained OS/CPU combinations to release. Defaults to every supported target. */
|
|
84
91
|
readonly releasePlatforms?: readonly RustReleasePlatform[];
|
|
92
|
+
/** Optional generated TypeScript registry for `cli`-enabled release binaries. */
|
|
93
|
+
readonly cliRegistryPath?: string;
|
|
85
94
|
}
|
|
86
95
|
|
|
87
96
|
export enum RustReleaseOs {
|
|
@@ -154,6 +163,14 @@ export const UNIFFI_RELEASE_TARGETS: readonly UniFFIReleaseTarget[] = [
|
|
|
154
163
|
os: RustReleaseOs.WINDOWS,
|
|
155
164
|
cpu: RustReleaseCpu.X64,
|
|
156
165
|
},
|
|
166
|
+
{
|
|
167
|
+
runner: "windows-11-vs2026-arm",
|
|
168
|
+
cargo: "aarch64-pc-windows-msvc",
|
|
169
|
+
node: "win32-arm64-msvc",
|
|
170
|
+
python: "win_arm64",
|
|
171
|
+
os: RustReleaseOs.WINDOWS,
|
|
172
|
+
cpu: RustReleaseCpu.ARM64,
|
|
173
|
+
},
|
|
157
174
|
] as const;
|
|
158
175
|
|
|
159
176
|
const RUST_CACHE_ENV = {
|
|
@@ -230,6 +247,21 @@ function releasePlatformsFromEnvironment(
|
|
|
230
247
|
});
|
|
231
248
|
}
|
|
232
249
|
|
|
250
|
+
function releaseBinaryAssetName(binaryName: string, nodeTarget: string, os: RustReleaseOs): string {
|
|
251
|
+
const extension = os === RustReleaseOs.WINDOWS ? "zip" : "tar.gz";
|
|
252
|
+
return `${binaryName}-${nodeTarget}.${extension}`;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function rustCliRegistrySource(binaries: readonly RustReleaseBinaryMapping[]): string {
|
|
256
|
+
return [
|
|
257
|
+
"// GENERATED by projen - DO NOT EDIT.",
|
|
258
|
+
"// Rust release package options are the source of truth.",
|
|
259
|
+
"",
|
|
260
|
+
`export const RUST_RELEASE_BINARY_COMMANDS = ${JSON.stringify(binaries, null, 2)} as const;`,
|
|
261
|
+
"",
|
|
262
|
+
].join("\n");
|
|
263
|
+
}
|
|
264
|
+
|
|
233
265
|
function uniffiReleaseTaskSource(): string {
|
|
234
266
|
const sourceDirectory = dirname(fileURLToPath(import.meta.url));
|
|
235
267
|
const candidates = [
|
|
@@ -259,6 +291,23 @@ export interface RustWorkspaceMapping {
|
|
|
259
291
|
readonly root: string;
|
|
260
292
|
readonly crates: readonly string[];
|
|
261
293
|
readonly bindings: readonly RustBindingMapping[];
|
|
294
|
+
readonly binaries: readonly RustReleaseBinaryMapping[];
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/** One platform archive published for a Rust CLI binary. */
|
|
298
|
+
export interface RustReleaseBinaryAssetMapping {
|
|
299
|
+
readonly os: RustReleaseOs;
|
|
300
|
+
readonly cpu: RustReleaseCpu;
|
|
301
|
+
readonly name: string;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/** Runtime metadata for one lazily installed Rust CLI binary. */
|
|
305
|
+
export interface RustReleaseBinaryMapping {
|
|
306
|
+
readonly command: string;
|
|
307
|
+
readonly description: string;
|
|
308
|
+
readonly binaryName: string;
|
|
309
|
+
readonly repository: string;
|
|
310
|
+
readonly assets: readonly RustReleaseBinaryAssetMapping[];
|
|
262
311
|
}
|
|
263
312
|
|
|
264
313
|
export function orderRustBindings(bindings: readonly RustBindingMapping[]): RustBindingMapping[] {
|
|
@@ -474,12 +523,13 @@ function rustPackageDependencies(
|
|
|
474
523
|
);
|
|
475
524
|
}
|
|
476
525
|
|
|
477
|
-
/** Generated Rust workspace plus convention-derived UniFFI
|
|
526
|
+
/** Generated Rust workspace plus convention-derived UniFFI binding packages. */
|
|
478
527
|
export class DBXToolsRustWorkspace {
|
|
479
528
|
readonly packages: readonly DBXToolsRustProject[];
|
|
480
529
|
readonly nodePackages: readonly DBXToolsTypeScriptProject[];
|
|
481
530
|
readonly pythonPackages: readonly PythonPackageOptions[];
|
|
482
531
|
readonly bindingMappings: readonly RustBindingMapping[];
|
|
532
|
+
readonly releaseBinaries: readonly RustReleaseBinaryMapping[];
|
|
483
533
|
readonly workspaceMapping: RustWorkspaceMapping;
|
|
484
534
|
|
|
485
535
|
constructor(project: javascript.NodeProject, options: DBXToolsRustWorkspaceOptions) {
|
|
@@ -492,6 +542,7 @@ export class DBXToolsRustWorkspace {
|
|
|
492
542
|
projectRepositoryUrl(project) ??
|
|
493
543
|
coreProject.repositoryUrl(project.outdir) ??
|
|
494
544
|
"";
|
|
545
|
+
const nativeTargets = releaseTargets(options);
|
|
495
546
|
const pythonModulePrefix = options.pythonModulePrefix ?? scope.replaceAll("-", "_");
|
|
496
547
|
const packageOptions = options.packages ?? {};
|
|
497
548
|
this.packages = discoverRustCrates(resolve(project.outdir, root)).map(
|
|
@@ -501,6 +552,56 @@ export class DBXToolsRustWorkspace {
|
|
|
501
552
|
...packageOptions[directory],
|
|
502
553
|
}),
|
|
503
554
|
);
|
|
555
|
+
const commands = new Set<string>();
|
|
556
|
+
this.releaseBinaries = this.packages
|
|
557
|
+
.filter((pkg) => Boolean(pkg.packageOptions.cli))
|
|
558
|
+
.map((pkg) => {
|
|
559
|
+
if (!pkg.packageOptions.release) {
|
|
560
|
+
throw new Error(`${pkg.crateName} must set release when cli is configured`);
|
|
561
|
+
}
|
|
562
|
+
const configured = pkg.packageOptions.cli;
|
|
563
|
+
const command =
|
|
564
|
+
typeof configured === "object" && configured.command
|
|
565
|
+
? configured.command
|
|
566
|
+
: pkg.packageOptions.directory;
|
|
567
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(command)) {
|
|
568
|
+
throw new Error(`Invalid Rust CLI command: ${command}`);
|
|
569
|
+
}
|
|
570
|
+
if (commands.has(command)) {
|
|
571
|
+
throw new Error(`Duplicate Rust CLI command: ${command}`);
|
|
572
|
+
}
|
|
573
|
+
commands.add(command);
|
|
574
|
+
const description =
|
|
575
|
+
(typeof configured === "object" ? configured.description : undefined) ??
|
|
576
|
+
pkg.packageOptions.description ??
|
|
577
|
+
pkg.crateName;
|
|
578
|
+
const excludedOs = new Set(pkg.packageOptions.releaseExcludeOs ?? []);
|
|
579
|
+
const binaryName = pkg.packageOptions.binaryName ?? pkg.crateName;
|
|
580
|
+
return {
|
|
581
|
+
command,
|
|
582
|
+
description,
|
|
583
|
+
binaryName,
|
|
584
|
+
repository,
|
|
585
|
+
assets: nativeTargets
|
|
586
|
+
.filter((target) => !excludedOs.has(target.os))
|
|
587
|
+
.map((target) => ({
|
|
588
|
+
os: target.os,
|
|
589
|
+
cpu: target.cpu,
|
|
590
|
+
name: releaseBinaryAssetName(binaryName, target.node, target.os),
|
|
591
|
+
})),
|
|
592
|
+
};
|
|
593
|
+
});
|
|
594
|
+
if (this.releaseBinaries.length && !repository) {
|
|
595
|
+
throw new Error("A repository URL is required when Rust CLI commands are configured");
|
|
596
|
+
}
|
|
597
|
+
if (this.releaseBinaries.length && !options.cliRegistryPath) {
|
|
598
|
+
throw new Error("cliRegistryPath is required when Rust CLI commands are configured");
|
|
599
|
+
}
|
|
600
|
+
if (options.cliRegistryPath) {
|
|
601
|
+
new TextFile(project, options.cliRegistryPath, {
|
|
602
|
+
lines: rustCliRegistrySource(this.releaseBinaries).trimEnd().split("\n"),
|
|
603
|
+
});
|
|
604
|
+
}
|
|
504
605
|
|
|
505
606
|
const bindings = this.packages.filter((pkg) => pkg.uniffi);
|
|
506
607
|
const packageDependencies = (pkg: DBXToolsRustProject) =>
|
|
@@ -531,7 +632,7 @@ export class DBXToolsRustWorkspace {
|
|
|
531
632
|
this.bindingMappings = orderRustBindings(
|
|
532
633
|
bindings.map((pkg) => {
|
|
533
634
|
const targets = pkg.packageOptions.bindings ?? ["node", "python"];
|
|
534
|
-
const
|
|
635
|
+
const packageDirectory = `${string.toSlug(pkg.packageOptions.directory)}-rs`;
|
|
535
636
|
const dependencies = [
|
|
536
637
|
...new Set([...bindingDependencies(pkg, "node"), ...bindingDependencies(pkg, "python")]),
|
|
537
638
|
].map((dependency) => dependency.crateName);
|
|
@@ -541,15 +642,15 @@ export class DBXToolsRustWorkspace {
|
|
|
541
642
|
rust: `${root}/${pkg.packageOptions.directory}`,
|
|
542
643
|
...(targets.includes("node")
|
|
543
644
|
? {
|
|
544
|
-
node: `${nodeRoot}/${
|
|
545
|
-
nodePackage: `@${scope}/${
|
|
645
|
+
node: `${nodeRoot}/${packageDirectory}`,
|
|
646
|
+
nodePackage: `@${scope}/${packageDirectory}`,
|
|
546
647
|
}
|
|
547
648
|
: {}),
|
|
548
649
|
...(targets.includes("python")
|
|
549
650
|
? {
|
|
550
|
-
python: `${options.pythonRoot ?? "packages/py"}/${
|
|
551
|
-
pythonPackage:
|
|
552
|
-
pythonModule: pythonModuleName(pythonModulePrefix,
|
|
651
|
+
python: `${options.pythonRoot ?? "packages/py"}/${packageDirectory}`,
|
|
652
|
+
pythonPackage: `${scope}-${packageDirectory}`,
|
|
653
|
+
pythonModule: pythonModuleName(pythonModulePrefix, packageDirectory),
|
|
553
654
|
}
|
|
554
655
|
: {}),
|
|
555
656
|
};
|
|
@@ -567,7 +668,10 @@ export class DBXToolsRustWorkspace {
|
|
|
567
668
|
"bindings.python.external_packages": Object.fromEntries(
|
|
568
669
|
dependencies.map((dependency) => [
|
|
569
670
|
dependency.crateName.replaceAll("-", "_"),
|
|
570
|
-
`${pythonModuleName(
|
|
671
|
+
`${pythonModuleName(
|
|
672
|
+
pythonModulePrefix,
|
|
673
|
+
`${string.toSlug(dependency.packageOptions.directory)}-rs`,
|
|
674
|
+
)}.bindings`,
|
|
571
675
|
]),
|
|
572
676
|
),
|
|
573
677
|
})
|
|
@@ -583,6 +687,7 @@ export class DBXToolsRustWorkspace {
|
|
|
583
687
|
root,
|
|
584
688
|
crates: this.packages.map((pkg) => `${root}/${pkg.packageOptions.directory}`),
|
|
585
689
|
bindings: this.bindingMappings,
|
|
690
|
+
binaries: this.releaseBinaries,
|
|
586
691
|
};
|
|
587
692
|
if (dbxToolsProject) {
|
|
588
693
|
dbxToolsProject.dbxToolsConfig.rust = this.workspaceMapping;
|
|
@@ -609,23 +714,25 @@ export class DBXToolsRustWorkspace {
|
|
|
609
714
|
this.pythonPackages = bindings
|
|
610
715
|
.filter((pkg) => (pkg.packageOptions.bindings ?? ["node", "python"]).includes("python"))
|
|
611
716
|
.map((pkg) => {
|
|
612
|
-
const
|
|
717
|
+
const directory = `${string.toSlug(pkg.packageOptions.directory)}-rs`;
|
|
718
|
+
const module = pythonModuleName(pythonModulePrefix, directory);
|
|
719
|
+
const name = `${scope}-${directory}`;
|
|
613
720
|
return {
|
|
614
|
-
directory
|
|
615
|
-
name
|
|
721
|
+
directory,
|
|
722
|
+
name,
|
|
616
723
|
module,
|
|
617
724
|
description: `Python bindings for ${pkg.crateName}`,
|
|
618
725
|
uniffi: true,
|
|
619
726
|
internalDependencies: bindingDependencies(pkg, "python").map(
|
|
620
|
-
(dependency) => dependency.packageOptions.directory
|
|
727
|
+
(dependency) => `${string.toSlug(dependency.packageOptions.directory)}-rs`,
|
|
621
728
|
),
|
|
622
729
|
generatedSources: [
|
|
623
730
|
`src/${module.replaceAll(".", "/")}/bindings.py`,
|
|
624
731
|
`src/${module.replaceAll(".", "/")}/__init__.py`,
|
|
625
732
|
],
|
|
626
733
|
trustedPublisher: {
|
|
627
|
-
environment: `pypi-${
|
|
628
|
-
artifacts: `platform-specific wheels for ${
|
|
734
|
+
environment: `pypi-${name}`,
|
|
735
|
+
artifacts: `platform-specific wheels for ${nativeTargets
|
|
629
736
|
.map((target) => `${target.os}-${target.cpu}`)
|
|
630
737
|
.join(", ")}; all architectures publish to this one PyPI project`,
|
|
631
738
|
},
|
|
@@ -639,7 +746,7 @@ export class DBXToolsRustWorkspace {
|
|
|
639
746
|
for (const binding of bindings.filter((pkg) =>
|
|
640
747
|
(pkg.packageOptions.bindings ?? ["node", "python"]).includes("node"),
|
|
641
748
|
)) {
|
|
642
|
-
const directory = binding.packageOptions.directory
|
|
749
|
+
const directory = `${string.toSlug(binding.packageOptions.directory)}-rs`;
|
|
643
750
|
const memberPath = `${nodeRoot}/${directory}`;
|
|
644
751
|
const found = existing.get(memberPath);
|
|
645
752
|
const existingNode = found instanceof DBXToolsTypeScriptProject ? found : undefined;
|
|
@@ -656,10 +763,7 @@ export class DBXToolsRustWorkspace {
|
|
|
656
763
|
`@${scope}/${directory.toLowerCase().replace(/[^a-z0-9-]+/g, "-")}`,
|
|
657
764
|
);
|
|
658
765
|
node.dbxToolsConfig.uniffi = true;
|
|
659
|
-
|
|
660
|
-
node.package.addField("description", `Node bindings for ${binding.crateName}`);
|
|
661
|
-
}
|
|
662
|
-
const nativeTargets = releaseTargets(options);
|
|
766
|
+
node.package.addField("description", `Node bindings for ${binding.crateName}`);
|
|
663
767
|
if (options.release ?? true) {
|
|
664
768
|
node.package.addField(
|
|
665
769
|
"optionalDependencies",
|
|
@@ -674,16 +778,11 @@ export class DBXToolsRustWorkspace {
|
|
|
674
778
|
node.addDeps("@ubjs/core@0.31.0-5", "@ubjs/node@0.31.0-5");
|
|
675
779
|
node.addDeps(
|
|
676
780
|
...bindingDependencies(binding, "node").map(
|
|
677
|
-
(dependency) =>
|
|
781
|
+
(dependency) =>
|
|
782
|
+
`@${scope}/${string.toSlug(dependency.packageOptions.directory)}-rs@workspace:*`,
|
|
678
783
|
),
|
|
679
784
|
);
|
|
680
|
-
if (binding.packageOptions.nodeDependencies?.length) {
|
|
681
|
-
node.addDeps(...binding.packageOptions.nodeDependencies);
|
|
682
|
-
}
|
|
683
785
|
node.addDevDeps(`uniffi-bindgen-react-native@${UBRN_VERSION}`);
|
|
684
|
-
if (binding.packageOptions.nodeDevDependencies?.length) {
|
|
685
|
-
node.addDevDeps(...binding.packageOptions.nodeDevDependencies);
|
|
686
|
-
}
|
|
687
786
|
nodePackages.push(node);
|
|
688
787
|
}
|
|
689
788
|
this.nodePackages = nodePackages;
|
|
@@ -728,20 +827,9 @@ export class DBXToolsRustWorkspace {
|
|
|
728
827
|
if (this.bindingMappings.some((binding) => binding.node)) {
|
|
729
828
|
project.tasks.tryFind("pre-compile")?.spawn(bindingsTask);
|
|
730
829
|
}
|
|
731
|
-
project.
|
|
732
|
-
description: "Generate and run UniFFI Node and Python example CLIs",
|
|
733
|
-
exec: [
|
|
734
|
-
"bun run rs:bindings",
|
|
735
|
-
...this.bindingMappings.flatMap((binding) => [
|
|
736
|
-
...(binding.node ? [`bun ${binding.node}/test/cli.ts`] : []),
|
|
737
|
-
...(binding.python
|
|
738
|
-
? [`uv run --project ${binding.python} ${binding.python}/test/cli.py`]
|
|
739
|
-
: []),
|
|
740
|
-
]),
|
|
741
|
-
].join(" && "),
|
|
742
|
-
});
|
|
830
|
+
project.removeTask("rs:bindings:demo");
|
|
743
831
|
if (releaseEnabled) {
|
|
744
|
-
this.addReleaseWorkflow(project, options,
|
|
832
|
+
this.addReleaseWorkflow(project, options, nativeTargets);
|
|
745
833
|
}
|
|
746
834
|
}
|
|
747
835
|
|
|
@@ -813,6 +901,7 @@ export class DBXToolsRustWorkspace {
|
|
|
813
901
|
`--python "${binding.python}"`,
|
|
814
902
|
`--node-package "${binding.nodePackage}"`,
|
|
815
903
|
`--python-package "${binding.pythonPackage}"`,
|
|
904
|
+
`--python-module "${binding.pythonModule}"`,
|
|
816
905
|
'--cargo-target "${{ matrix.cargo }}"',
|
|
817
906
|
'--node-triple "${{ matrix.node }}"',
|
|
818
907
|
'--python-tag "${{ matrix.python }}"',
|
|
@@ -829,15 +918,25 @@ export class DBXToolsRustWorkspace {
|
|
|
829
918
|
? `\${{ ${excludedOs.map((os) => `matrix.os != '${os}'`).join(" && ")} }}`
|
|
830
919
|
: undefined;
|
|
831
920
|
const binaryCommands = releaseBinaries.flatMap((pkg) => {
|
|
921
|
+
const windowsAsset = releaseBinaryAssetName(
|
|
922
|
+
pkg.binary,
|
|
923
|
+
"${{ matrix.node }}",
|
|
924
|
+
RustReleaseOs.WINDOWS,
|
|
925
|
+
);
|
|
926
|
+
const unixAsset = releaseBinaryAssetName(
|
|
927
|
+
pkg.binary,
|
|
928
|
+
"${{ matrix.node }}",
|
|
929
|
+
RustReleaseOs.LINUX,
|
|
930
|
+
);
|
|
832
931
|
const commands = [
|
|
833
932
|
`mkdir -p "dist/release/${pkg.crate}/\${{ matrix.node }}/binary/stage"`,
|
|
834
933
|
`SOURCE="target/\${{ matrix.cargo }}/release/${pkg.binary}\${{ matrix.os == 'win32' && '.exe' || '' }}"`,
|
|
835
934
|
`DESTINATION="dist/release/${pkg.crate}/\${{ matrix.node }}/binary/stage/${pkg.binary}\${{ matrix.os == 'win32' && '.exe' || '' }}"`,
|
|
836
935
|
'cp "$SOURCE" "$DESTINATION"',
|
|
837
936
|
'if [ "${{ matrix.os }}" = "win32" ]; then',
|
|
838
|
-
` 7z a "dist/release/${pkg.crate}/\${{ matrix.node }}/binary/${
|
|
937
|
+
` 7z a "dist/release/${pkg.crate}/\${{ matrix.node }}/binary/${windowsAsset}" "$DESTINATION"`,
|
|
839
938
|
"else",
|
|
840
|
-
` tar -C "dist/release/${pkg.crate}/\${{ matrix.node }}/binary/stage" -czf "dist/release/${pkg.crate}/\${{ matrix.node }}/binary/${
|
|
939
|
+
` tar -C "dist/release/${pkg.crate}/\${{ matrix.node }}/binary/stage" -czf "dist/release/${pkg.crate}/\${{ matrix.node }}/binary/${unixAsset}" "${pkg.binary}"`,
|
|
841
940
|
"fi",
|
|
842
941
|
`rm -rf "dist/release/${pkg.crate}/\${{ matrix.node }}/binary/stage"`,
|
|
843
942
|
];
|
|
@@ -869,7 +968,7 @@ export class DBXToolsRustWorkspace {
|
|
|
869
968
|
name: `Upload ${binding.crate} Python wheel`,
|
|
870
969
|
uses: "actions/upload-artifact@v7",
|
|
871
970
|
with: {
|
|
872
|
-
name: `${binding.
|
|
971
|
+
name: `${binding.pythonPackage}--\${{ matrix.python }}--python-wheel`,
|
|
873
972
|
path: `dist/release/${binding.crate}/\${{ matrix.node }}/python/*.whl`,
|
|
874
973
|
"retention-days": 7,
|
|
875
974
|
},
|
|
@@ -942,7 +1041,11 @@ export class DBXToolsRustWorkspace {
|
|
|
942
1041
|
{
|
|
943
1042
|
name: "Install Linux native dependencies",
|
|
944
1043
|
if: "${{ matrix.os == 'linux' }}",
|
|
945
|
-
run:
|
|
1044
|
+
run: [
|
|
1045
|
+
"sudo rm -f /etc/apt/sources.list.d/google-chrome.list",
|
|
1046
|
+
"sudo apt-get update",
|
|
1047
|
+
"sudo apt-get install --yes libdbus-1-dev pkg-config",
|
|
1048
|
+
].join("\n"),
|
|
946
1049
|
},
|
|
947
1050
|
{
|
|
948
1051
|
name: "Build Rust outputs",
|
package/src/release.ts
CHANGED
|
@@ -247,16 +247,16 @@ function addDocsJobs(
|
|
|
247
247
|
{ name: "Configure Pages", uses: "actions/configure-pages@v5" },
|
|
248
248
|
{ name: "Install dependencies", run: "bun install" },
|
|
249
249
|
{ name: "Generate docs from READMEs", run: "bun docs/scripts/sync-readmes.mjs" },
|
|
250
|
-
{
|
|
251
|
-
name: "Check generated titles",
|
|
252
|
-
run: "bun docs/scripts/check-generated-titles.mjs",
|
|
253
|
-
},
|
|
254
250
|
{ name: "Install docs dependencies", run: "bun install --cwd .docs-build/site" },
|
|
255
251
|
bunCacheSaveStep(),
|
|
256
252
|
{
|
|
257
253
|
name: "Generate TypeScript API docs",
|
|
258
254
|
run: "bun docs/scripts/generate-api-docs.mjs",
|
|
259
255
|
},
|
|
256
|
+
{
|
|
257
|
+
name: "Check generated titles",
|
|
258
|
+
run: "bun docs/scripts/check-generated-titles.mjs",
|
|
259
|
+
},
|
|
260
260
|
{ name: "Build docs", run: "bun run --cwd .docs-build/site build" },
|
|
261
261
|
{
|
|
262
262
|
name: "Check generated links",
|
package/src/tags.ts
CHANGED
|
@@ -182,6 +182,7 @@ export const PACKAGE_TAG_MIXINS = {
|
|
|
182
182
|
openapi: create(projectPredicate.hasTag("openapi"), (p) => {
|
|
183
183
|
p.addDeps("openapi-fetch@catalog:");
|
|
184
184
|
applyCompilerOptions(p, { target: "ES2022", lib: [...DOM_LIB], types: [] });
|
|
185
|
+
addPackageFiles(p, "openapi.json");
|
|
185
186
|
}),
|
|
186
187
|
} satisfies Record<string, ConstructsMixin>;
|
|
187
188
|
|
package/src/uniffi.ts
CHANGED
|
@@ -56,40 +56,3 @@ export const addExplicitInterfaceReexports = (
|
|
|
56
56
|
if (exports.length === 0) return facade;
|
|
57
57
|
return `${facade.trimEnd()}\n${exports.join("\n")}\n`;
|
|
58
58
|
};
|
|
59
|
-
|
|
60
|
-
const GENERATED_BINDINGS_HEADER = "GENERATED by UniFFI binding generation";
|
|
61
|
-
|
|
62
|
-
function pythonAllNames(source: string, file: string): string[] {
|
|
63
|
-
const body = /^__all__\s*=\s*\[([\s\S]*?)\]/m.exec(source)?.[1];
|
|
64
|
-
if (body === undefined) {
|
|
65
|
-
throw new Error(`Python exports must define a literal __all__ list: ${file}`);
|
|
66
|
-
}
|
|
67
|
-
return [...body.matchAll(/["']([^"']+)["']/g)].map((match) => match[1]!);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/** Generate one package root from the binding module's explicit exports. */
|
|
71
|
-
export function mergePythonBindingExports(
|
|
72
|
-
initSource: string,
|
|
73
|
-
bindingsSource: string,
|
|
74
|
-
options: { readonly crate: string; readonly file: string },
|
|
75
|
-
): string {
|
|
76
|
-
if (initSource.trim() && !initSource.includes(GENERATED_BINDINGS_HEADER)) {
|
|
77
|
-
throw new Error(`Cannot replace non-generated Python package exports: ${options.file}`);
|
|
78
|
-
}
|
|
79
|
-
const bindingNames = pythonAllNames(bindingsSource, options.file);
|
|
80
|
-
const publicNames = [...bindingNames].sort((left, right) =>
|
|
81
|
-
left < right ? -1 : left > right ? 1 : 0,
|
|
82
|
-
);
|
|
83
|
-
return [
|
|
84
|
-
"# GENERATED by UniFFI binding generation - DO NOT EDIT.",
|
|
85
|
-
`# Regenerated from the ${options.crate} Rust exports.`,
|
|
86
|
-
"# Hand edits are overwritten on the next watch; this file is read-only.",
|
|
87
|
-
"",
|
|
88
|
-
"from .bindings import *",
|
|
89
|
-
"",
|
|
90
|
-
"__all__ = [",
|
|
91
|
-
...publicNames.map((name) => ` ${JSON.stringify(name)},`),
|
|
92
|
-
"]",
|
|
93
|
-
"",
|
|
94
|
-
].join("\n");
|
|
95
|
-
}
|
package/tasks/bump.ts
CHANGED
|
@@ -55,8 +55,8 @@ import { exec, project } from "@dbx-tools/core";
|
|
|
55
55
|
import { log, net } from "@dbx-tools/shared-core";
|
|
56
56
|
import { Command, Option } from "commander";
|
|
57
57
|
import { activePythonIndexes, resolveLocalPypi } from "./python-registry.ts";
|
|
58
|
-
import { readDbxToolsConfig, repoRoot } from "../src/packages.ts";
|
|
59
58
|
import { releasePlatformFilter } from "../src/_release-platform.ts";
|
|
59
|
+
import { readDbxToolsConfig, repoRoot } from "../src/packages.ts";
|
|
60
60
|
import {
|
|
61
61
|
type Semver,
|
|
62
62
|
compareSemver,
|
|
@@ -157,6 +157,8 @@ function buildAndPublish(binding: RustBindingMapping, version: string): void {
|
|
|
157
157
|
includeNode ? binding.nodePackage! : "",
|
|
158
158
|
"--python-package",
|
|
159
159
|
includePython ? binding.pythonPackage! : "",
|
|
160
|
+
"--python-module",
|
|
161
|
+
includePython ? binding.pythonModule! : "",
|
|
160
162
|
"--cargo-target",
|
|
161
163
|
rustHost(),
|
|
162
164
|
"--node-triple",
|
package/tasks/rust.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env -S bun
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
2
3
|
import { existsSync } from "node:fs";
|
|
3
4
|
import { dirname, isAbsolute, resolve, sep } from "node:path";
|
|
4
|
-
import { spawnSync } from "node:child_process";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { log } from "@dbx-tools/shared-core";
|
|
7
7
|
import { readDbxToolsConfig, repoRoot } from "../src/packages.ts";
|
|
@@ -40,7 +40,7 @@ function currentStructure(config: RustWorkspaceMapping): RustWorkspaceMapping {
|
|
|
40
40
|
const binding = recorded.get(rust);
|
|
41
41
|
return binding ? [binding] : [{ crate: "", rust }];
|
|
42
42
|
});
|
|
43
|
-
return { root: config.root, crates, bindings };
|
|
43
|
+
return { root: config.root, crates, bindings, binaries: config.binaries ?? [] };
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
/** Whether discovered crate membership or UniFFI marker membership changed. */
|
|
@@ -70,7 +70,9 @@ function ownerBinding(
|
|
|
70
70
|
function generate(binding: RustBindingMapping): void {
|
|
71
71
|
const targets = [
|
|
72
72
|
...(binding.node ? ["--node", binding.node] : []),
|
|
73
|
-
...(binding.python
|
|
73
|
+
...(binding.python
|
|
74
|
+
? ["--python", binding.python, "--python-module", binding.pythonModule ?? ""]
|
|
75
|
+
: []),
|
|
74
76
|
];
|
|
75
77
|
const result = spawnSync(
|
|
76
78
|
process.execPath,
|
|
@@ -92,8 +94,9 @@ export function affectedRustBindings(
|
|
|
92
94
|
const affected = new Set(changed);
|
|
93
95
|
const ordered = orderRustBindings(bindings);
|
|
94
96
|
for (const binding of ordered) {
|
|
95
|
-
if (binding.dependencies?.some((dependency) => affected.has(dependency)))
|
|
97
|
+
if (binding.dependencies?.some((dependency) => affected.has(dependency))) {
|
|
96
98
|
affected.add(binding.crate);
|
|
99
|
+
}
|
|
97
100
|
}
|
|
98
101
|
return ordered.filter((binding) => affected.has(binding.crate));
|
|
99
102
|
}
|
package/tasks/uniffi-release.mjs
CHANGED
|
@@ -27,6 +27,7 @@ const parsed = parseArgs({
|
|
|
27
27
|
"node-package": { type: "string" },
|
|
28
28
|
"native-package": { type: "string" },
|
|
29
29
|
"python-package": { type: "string" },
|
|
30
|
+
"python-module": { type: "string" },
|
|
30
31
|
"cargo-target": { type: "string" },
|
|
31
32
|
"node-triple": { type: "string" },
|
|
32
33
|
"python-tag": { type: "string" },
|
|
@@ -231,11 +232,28 @@ const writable = (path) => {
|
|
|
231
232
|
if (existsSync(path)) chmodSync(path, statSync(path).mode | 0o200);
|
|
232
233
|
};
|
|
233
234
|
|
|
235
|
+
const cargoMetadata = spawnSync("cargo", ["metadata", "--format-version", "1", "--no-deps"], {
|
|
236
|
+
cwd: root,
|
|
237
|
+
encoding: "utf8",
|
|
238
|
+
});
|
|
239
|
+
if (cargoMetadata.error) {
|
|
240
|
+
throw new Error(`cargo metadata failed: ${cargoMetadata.error.message}`, {
|
|
241
|
+
cause: cargoMetadata.error,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
if (cargoMetadata.status !== 0) {
|
|
245
|
+
throw new Error(`cargo metadata exited with ${cargoMetadata.status}`);
|
|
246
|
+
}
|
|
247
|
+
const cargoTargetRoot = JSON.parse(cargoMetadata.stdout).target_directory;
|
|
248
|
+
if (typeof cargoTargetRoot !== "string" || !cargoTargetRoot) {
|
|
249
|
+
throw new Error("cargo metadata returned no target_directory");
|
|
250
|
+
}
|
|
251
|
+
|
|
234
252
|
const libraryPath = (crate, cargoTarget, os) => {
|
|
235
253
|
const name = crate.replaceAll("-", "_");
|
|
236
254
|
const extension = os === "darwin" ? "dylib" : os === "win32" ? "dll" : "so";
|
|
237
255
|
const prefix = os === "win32" ? "" : "lib";
|
|
238
|
-
return resolve(
|
|
256
|
+
return resolve(cargoTargetRoot, cargoTarget, "release", `${prefix}${name}.${extension}`);
|
|
239
257
|
};
|
|
240
258
|
|
|
241
259
|
const packageNode = ({
|
|
@@ -387,6 +405,7 @@ const packagePython = ({
|
|
|
387
405
|
version,
|
|
388
406
|
cargoTarget,
|
|
389
407
|
os,
|
|
408
|
+
pythonModule,
|
|
390
409
|
}) => {
|
|
391
410
|
const pythonRoot = resolve(output, "python-root");
|
|
392
411
|
cpSync(resolve(root, pythonDirectory), pythonRoot, { recursive: true });
|
|
@@ -407,12 +426,12 @@ const packagePython = ({
|
|
|
407
426
|
}
|
|
408
427
|
writeFileSync(pyproject, metadata);
|
|
409
428
|
|
|
410
|
-
const packageName =
|
|
411
|
-
|
|
429
|
+
const packageName = pythonModule.split(".").at(-1);
|
|
430
|
+
if (!packageName) throw new Error(`Invalid Python module ${pythonModule}`);
|
|
431
|
+
const packageDirectory = resolve(pythonRoot, "src", ...pythonModule.split("."));
|
|
412
432
|
const generatedDirectory = mkdtempSync(join(tmpdir(), `${packageName}-python-`));
|
|
413
433
|
const generator = resolve(
|
|
414
|
-
|
|
415
|
-
"target",
|
|
434
|
+
cargoTargetRoot,
|
|
416
435
|
cargoTarget,
|
|
417
436
|
"release",
|
|
418
437
|
`${crate}-uniffi-bindgen${os === "win32" ? ".exe" : ""}`,
|
|
@@ -506,6 +525,7 @@ const build = () => {
|
|
|
506
525
|
version,
|
|
507
526
|
cargoTarget,
|
|
508
527
|
os,
|
|
528
|
+
pythonModule: required("python-module"),
|
|
509
529
|
});
|
|
510
530
|
}
|
|
511
531
|
};
|
package/tasks/uniffi.ts
CHANGED
|
@@ -21,7 +21,6 @@ import {
|
|
|
21
21
|
addExplicitInterfaceReexports,
|
|
22
22
|
addTypeScriptExtensionsToBindingImports,
|
|
23
23
|
makeDefaultedInterfaceParametersOptional,
|
|
24
|
-
mergePythonBindingExports,
|
|
25
24
|
} from "../src/uniffi.ts";
|
|
26
25
|
|
|
27
26
|
const { values } = parseArgs({
|
|
@@ -30,6 +29,7 @@ const { values } = parseArgs({
|
|
|
30
29
|
crate: { type: "string" },
|
|
31
30
|
node: { type: "string" },
|
|
32
31
|
python: { type: "string" },
|
|
32
|
+
"python-module": { type: "string" },
|
|
33
33
|
"cargo-target": { type: "string" },
|
|
34
34
|
"node-package-base": { type: "string" },
|
|
35
35
|
ubrn: { type: "string" },
|
|
@@ -54,10 +54,6 @@ const dependencies =
|
|
|
54
54
|
const extension =
|
|
55
55
|
process.platform === "darwin" ? "dylib" : process.platform === "win32" ? "dll" : "so";
|
|
56
56
|
const prefix = process.platform === "win32" ? "" : "lib";
|
|
57
|
-
const targetDirectory = values["cargo-target"]
|
|
58
|
-
? join(root, "target", values["cargo-target"], "release")
|
|
59
|
-
: join(root, "target", "release");
|
|
60
|
-
const library = join(targetDirectory, `${prefix}${libraryName}.${extension}`);
|
|
61
57
|
const normalizedOutput = (value: string): string =>
|
|
62
58
|
value
|
|
63
59
|
.replace(/[\p{Extended_Pictographic}\uFE0F]/gu, "")
|
|
@@ -75,6 +71,28 @@ const run = (command: string, args: string[]) => {
|
|
|
75
71
|
if (result.status !== 0) throw new Error(`${command} exited with ${result.status}`);
|
|
76
72
|
};
|
|
77
73
|
|
|
74
|
+
const cargoMetadata = spawnSync("cargo", ["metadata", "--format-version", "1", "--no-deps"], {
|
|
75
|
+
cwd: root,
|
|
76
|
+
encoding: "utf8",
|
|
77
|
+
});
|
|
78
|
+
if (cargoMetadata.error) {
|
|
79
|
+
throw new Error(`cargo metadata failed: ${cargoMetadata.error.message}`, {
|
|
80
|
+
cause: cargoMetadata.error,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
if (cargoMetadata.status !== 0) {
|
|
84
|
+
throw new Error(`cargo metadata exited with ${cargoMetadata.status}`);
|
|
85
|
+
}
|
|
86
|
+
const cargoTargetRoot = (JSON.parse(cargoMetadata.stdout) as { target_directory?: unknown })
|
|
87
|
+
.target_directory;
|
|
88
|
+
if (typeof cargoTargetRoot !== "string" || !cargoTargetRoot) {
|
|
89
|
+
throw new Error("cargo metadata returned no target_directory");
|
|
90
|
+
}
|
|
91
|
+
const targetDirectory = values["cargo-target"]
|
|
92
|
+
? join(cargoTargetRoot, values["cargo-target"], "release")
|
|
93
|
+
: join(cargoTargetRoot, "release");
|
|
94
|
+
const library = join(targetDirectory, `${prefix}${libraryName}.${extension}`);
|
|
95
|
+
|
|
78
96
|
const replaceGenerated = (source: string, destination: string): void => {
|
|
79
97
|
const deadline = Date.now() + 5_000;
|
|
80
98
|
const signal = new Int32Array(new SharedArrayBuffer(4));
|
|
@@ -243,12 +261,10 @@ if (values.node) {
|
|
|
243
261
|
}
|
|
244
262
|
|
|
245
263
|
if (values.python) {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
crate.replace(/^dbx-tools-/, "").replaceAll("-", "_"),
|
|
251
|
-
);
|
|
264
|
+
if (!values["python-module"]) {
|
|
265
|
+
throw new Error("Expected --python-module with --python");
|
|
266
|
+
}
|
|
267
|
+
const pythonPackage = resolve(root, values.python, "src", ...values["python-module"].split("."));
|
|
252
268
|
const pythonOutput = mkdtempSync(join(tmpdir(), `${libraryName}-python-`));
|
|
253
269
|
run(
|
|
254
270
|
join(targetDirectory, `${crate}-uniffi-bindgen${process.platform === "win32" ? ".exe" : ""}`),
|
|
@@ -269,14 +285,7 @@ if (values.python) {
|
|
|
269
285
|
stampGeneratedPython(pythonBindings);
|
|
270
286
|
const pythonInit = join(pythonPackage, "__init__.py");
|
|
271
287
|
makeWritable(pythonInit);
|
|
272
|
-
writeFileSync(
|
|
273
|
-
pythonInit,
|
|
274
|
-
mergePythonBindingExports(
|
|
275
|
-
existsSync(pythonInit) ? readFileSync(pythonInit, "utf8") : "",
|
|
276
|
-
readFileSync(pythonBindings, "utf8"),
|
|
277
|
-
{ crate, file: pythonInit },
|
|
278
|
-
),
|
|
279
|
-
);
|
|
288
|
+
writeFileSync(pythonInit, "");
|
|
280
289
|
makeReadonly(pythonInit);
|
|
281
290
|
const pythonLibrary = join(pythonPackage, basename(library));
|
|
282
291
|
makeWritable(pythonLibrary);
|