Skip to content

Commit

Permalink
Merge pull request #22 from algorandfoundation/build/refactor_build_s…
Browse files Browse the repository at this point in the history
…cript

build: refactor scripts/build
  • Loading branch information
joe-p authored Mar 4, 2025
2 parents 469a875 + 7c084a4 commit cc45db9
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 129 deletions.
14 changes: 7 additions & 7 deletions .github/workflows/swift_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
- crates/algo_models_ffi/**
- crates/algo_models/**
- packages/swift/AlgoModels/**
- scripts/build/swift.ts
- scripts/build/languages/swift.ts
- scripts/build/index.ts
- "!*.md"
- .github/workflows/swift_ci.yml
Expand All @@ -20,7 +20,7 @@ on:
- crates/algo_models_ffi/**
- crates/algo_models/**
- packages/swift/AlgoModels/**
- scripts/build/swift.ts
- scripts/build/languages/swift.ts
- scripts/build/index.ts
- "!*.md"
- .github/workflows/swift_ci.yml
Expand All @@ -30,7 +30,7 @@ permissions:
contents: write

env:
CRATE: algo_models_ffi
CRATE: algo_models
PACKAGE: AlgoModels

jobs:
Expand All @@ -56,14 +56,14 @@ jobs:
- name: Install iOS Simulator
run: xcodebuild -downloadPlatform iOS
- name: Build
run: bun scripts/build/swift.ts ${{ env.CRATE }}
run: bun scripts/build ${{ env.CRATE }} swift
# Ideally we'd use a matrix for the platforms, but due to the limitations of Mac runners on GitHub it's probably better to just have a single job with multiple steps
- name: Test (macOS)
run: cd crates/${{ env.CRATE}}/tests/swift/ && xcodebuild -scheme ${{ env.PACKAGE }}Tests-Package test -destination "platform=macOS"
run: cd crates/${{ env.CRATE}}_ffi/tests/swift/ && xcodebuild -scheme ${{ env.PACKAGE }}Tests-Package test -destination "platform=macOS"
- name: Test (iOS)
run: cd crates/${{ env.CRATE}}/tests/swift/ && xcodebuild -scheme ${{ env.PACKAGE }}Tests-Package test -destination "platform=iOS Simulator,name=iPhone 16,OS=latest"
run: cd crates/${{ env.CRATE}}_ffi/tests/swift/ && xcodebuild -scheme ${{ env.PACKAGE }}Tests-Package test -destination "platform=iOS Simulator,name=iPhone 16,OS=latest"
- name: Test (Catalyst)
run: cd crates/${{ env.CRATE}}/tests/swift/ && xcodebuild -scheme ${{ env.PACKAGE }}Tests-Package test -destination "platform=macOS,variant=Mac Catalyst"
run: cd crates/${{ env.CRATE}}_ffi/tests/swift/ && xcodebuild -scheme ${{ env.PACKAGE }}Tests-Package test -destination "platform=macOS,variant=Mac Catalyst"
- name: Commit Package
uses: stefanzweifel/git-auto-commit-action@v5
if: github.event_name == 'push'
Expand Down
48 changes: 39 additions & 9 deletions scripts/build/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import path from "path";
import { spawn } from "child_process";
import { resolve } from "path";
import { buildPython } from "./languages/python.ts";
import { buildSwift } from "./languages/swift.ts";
import { buildTypescript } from "./languages/typescript.ts";

export const REPO_ROOT = path.resolve(__dirname, "../../");

process.chdir(REPO_ROOT);

export function toPascalCase(string: string): string {
return string
.replaceAll("_", " ")
Expand All @@ -13,14 +16,6 @@ export function toPascalCase(string: string): string {
.join("");
}

export function getCrateNanme(): string {
if (process.argv[2] == undefined) {
throw new Error("Crate must be specified as the argument");
}

return process.argv[2].replace("_ffi", "");
}

export function run(command: string, cwd: string | null = null): Promise<void> {
return new Promise<void>((resolvePromise) => {
console.log(`Running '${command}'`);
Expand Down Expand Up @@ -52,3 +47,38 @@ export function run(command: string, cwd: string | null = null): Promise<void> {
});
});
}

const languages = {
python: buildPython,
swift: buildSwift,
typescript: buildTypescript,
};

const crates = ["algo_models"];

if (process.argv.length !== 4) {
throw new Error("Usage: bun scripts/build <crate> <language>");
}

const crate = process.argv[2];
const language = process.argv[3];

if (language !== "all" && !Object.keys(languages).includes(language)) {
throw new Error(
"Language must be one of: all, " + Object.keys(languages).join(", "),
);
}

if (!crates.includes(crate)) {
throw new Error("Crate must be one of: " + crates.join(", "));
}

if (language === "all") {
await Promise.all(
Object.keys(languages).map(async (language) => {
await languages[language](crate);
}),
);
} else {
await languages[language](crate);
}
5 changes: 5 additions & 0 deletions scripts/build/languages/python.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { getCrateNanme, run } from "..";

export async function buildPython(crate: string) {
await run(`maturin build -m crates/${crate}_ffi/Cargo.toml`);
}
94 changes: 94 additions & 0 deletions scripts/build/languages/swift.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { toPascalCase, run } from "..";
import * as fs from "fs";

export async function buildSwift(crate: string) {
const targets = ["aarch64-apple-ios"];
const fatTargets: Record<string, string[]> = {
"ios-sim": ["x86_64-apple-ios", "aarch64-apple-ios-sim"],
catalyst: ["x86_64-apple-ios-macabi", "aarch64-apple-ios-macabi"],
macos: ["x86_64-apple-darwin", "aarch64-apple-darwin"],
};

let cargoBuildCmd = `cargo --color always build --manifest-path crates/${crate}_ffi/Cargo.toml --features ffi_uniffi`;

const allTargets = [...Object.values(fatTargets).flat(), ...targets];

await Promise.all(
allTargets.map(async (target) => {
await run(`rustup target add ${target}`);
cargoBuildCmd += ` --target ${target}`;
}),
);

await run(cargoBuildCmd);

await run(
`cargo --color always run -p uniffi-bindgen generate --no-format --library target/aarch64-apple-darwin/debug/lib${crate}_ffi.dylib --language swift --out-dir target/debug/swift/${crate}`,
);

let createXcfCmd = "xcodebuild -create-xcframework";
targets.forEach((target) => {
createXcfCmd += ` -library target/${target}/debug/lib${crate}_ffi.dylib -headers target/debug/swift/${crate}/`;
});

await Promise.all(
Object.keys(fatTargets).map(async (fatTargetName) => {
const libPaths: string[] = [];
fatTargets[fatTargetName].forEach((target) => {
libPaths.push(`target/${target}/debug/lib${crate}_ffi.dylib`);
});

await run(
`lipo -create ${libPaths.join(" ")} -output target/debug/lib${crate}_ffi-${fatTargetName}.dylib`,
);

createXcfCmd += ` -library target/debug/lib${crate}_ffi-${fatTargetName}.dylib -headers target/debug/swift/${crate}/`;
}),
);

const swiftPackage = toPascalCase(crate);
createXcfCmd += ` -output packages/swift/${swiftPackage}/Frameworks/${crate}.xcframework`;

if (
fs.existsSync(
`packages/swift/${swiftPackage}/Frameworks/${crate}.xcframework`,
)
) {
fs.rmdirSync(
`packages/swift/${swiftPackage}/Frameworks/${crate}.xcframework`,
{
recursive: true,
},
);
}

// xcframework needs the modulemap to be named module.modulemap
fs.renameSync(
`target/debug/swift/${crate}/${crate}FFI.modulemap`,
`target/debug/swift/${crate}/module.modulemap`,
);

// replace var with let to resolve swift concurrency issues
// I believe this is fixed in https://github.com/mozilla/uniffi-rs/pull/2294
// The above PR is available in uniffi-rs 0.29.0, but we won't be updating until
// Nord generators (i.e. Golang) are updated to use 0.29.0
let content = fs.readFileSync(
`target/debug/swift/${crate}/${crate}.swift`,
"utf-8",
);
content = content.replace(
"private var initializationResult",
"private let initializationResult",
);

fs.writeFileSync(`target/debug/swift/${crate}/${crate}.swift`, content);

await run(createXcfCmd);

fs.renameSync(
`target/debug/swift/${crate}/${crate}.swift`,
`packages/swift/${swiftPackage}/Sources/${swiftPackage}/${swiftPackage}.swift`,
);

console.log(`Updated ${swiftPackage} in packages/swift/${swiftPackage}/`);
}
13 changes: 13 additions & 0 deletions scripts/build/languages/typescript.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { run } from "..";
import * as fs from "fs";

export async function buildTypescript(crate: string) {
await run(
`wasm-pack build crates/${crate}_ffi --target web --out-dir ../../packages/typescript/${crate} -- --color always --no-default-features --features ffi_wasm`,
);

// Remove the generated .gitignore file from the pkg directory
if (fs.existsSync(`packages/typescript/${crate}/.gitignore`)) {
fs.rmSync(`packages/typescript/${crate}/.gitignore`);
}
}
4 changes: 0 additions & 4 deletions scripts/build/python.ts

This file was deleted.

96 changes: 0 additions & 96 deletions scripts/build/swift.ts

This file was deleted.

13 changes: 0 additions & 13 deletions scripts/build/typescript.ts

This file was deleted.

0 comments on commit cc45db9

Please sign in to comment.