Scipio Documentation

repository·main·Indexed 17 days ago

https://github.com/giginet/scipio

A dependency management tool that uses SwiftPM to resolve dependencies and converts them into portable XCFrameworks to improve build caching and portability. It includes a CLI for preparing dependencies and creating frameworks, as well as ScipioKit for implementing custom build pipelines with advanced configurations, remote cache storage (such as Amazon S3), and custom build flags.

Tokens
7.1K
Snippets
24
Records
33
Agent score
67%

What's inside Scipio

  1. How Scipio works: A hybrid dependency workflow

    main

    Scipio provides a hybrid approach to dependency management to solve the problem of difficult-to-cache Xcode build artifacts while maintaining the ease of SwiftPM.

    1. Resolve: Use SwiftPM to resolve dependencies and check out repositories.
    2. Convert: Scipio converts each resolved dependency into a portable XCFramework.

    This allows you to use SwiftPM for dependency resolution but benefit from the portability and caching advantages of XCFrameworks.

  2. Configure custom and remote cache storage

    main

    Unlike the CLI version which is limited to Project or Local disk caches, ScipioKit allows you to use remote storage (like Amazon S3 via ScipioS3Storage) or implement your own by conforming to the FrameworkCacheStorage protocol.

    Cache Actors

    When defining a FrameworkCachePolicy, you can specify which actors are associated with the storage:

    • consumer: An actor that fetches cache from the storage.
    • producer: An actor that attempts to save build artifacts to the storage.

    Multiple Cache Policies

    You can chain multiple policies. If a framework is not found in the first storage (e.g., S3), Scipio will attempt to fetch it from the next one (e.g., .localDisk). If it is not found anywhere, it will be built and then cached in the storages associated with the producer actor.

    import ScipioS3Storage
    
    // Using S3 with a consumer actor, and falling back to local disk
    let s3Storage: some FrameworkCacheStorage = ScipioS3Storage.S3Storage(config: ...)
    let runner = Runner(
        mode: .prepareDependencies,
        options: .init(
            baseBuildOptions: .init(
                buildConfiguration: .release,
                isSimulatorSupported: true
            ),
            cachePolicies: [
                .init(storage: s3Storage, actors: [.consumer]),
                .localDisk,
            ]
        )
    )
  3. How the Scipio cache system works

    main

    Scipio uses a cache system to reuse valid build artifacts and avoid unnecessary builds. It achieves this by generating a VersionFile for every built framework.

    When Scipio runs, it compares the existing VersionFile in the output directory against the current build context. If the following parameters match, the binary is considered valid and the build is skipped:

    • Revision: The revision of the packages (changes if resolved versions are updated).
    • Build Options: The configuration used (e.g., buildConfiguration, frameworkType, sdks).
    • Compiler Version: The Xcode or Swift compiler version used for the build.

    If a match is found, Scipio skips the build process for that framework.

  4. Implement a custom build pipeline with ScipioKit

    main

    While the Scipio CLI is suitable for simple tasks, ScipioKit allows you to build complex build pipelines using Swift code. This enables advanced configurations such as passing custom C/Linker/Swift flags, overriding build options per target, using remote cache storage (like Amazon S3), and implementing custom logic or user interfaces.

    To implement a build script, create an executable Swift package, add ScipioKit as a dependency, and use the Runner class to execute the build process.

    import Foundation
    import ScipioKit
    
    @main
    struct EntryPoint {
        private static let myPackageDirectory = URL(fileURLWithPath: "/path/to/MyPackage")
    
        static func main() async throws {
            let runner = Runner(
                mode: .prepareDependencies,
                options: .init(
                    baseBuildOptions: .init(
                        buildConfiguration: .release,
                        isSimulatorSupported: true
                    )
                )
            )
    
            try await runner.run(
                packageDirectory: myPackageDirectory,
                frameworkOutputDir: .default
            )
        }
    }
  5. Advanced Usage of Scipio

    main

    For complex build environments, Scipio provides advanced features including:

    • Cache System: Managing how dependencies and build artifacts are stored.
    • Build Pipeline: Customizing the sequence of build operations.
    • S3 Storage: Using Amazon S3 as a remote storage backend for your cache/artifacts.
    • Mergeable Library: Handling specific requirements for mergeable library support.
  6. Basic Usage of Scipio

    main

    The basic workflow for using Scipio involves three main steps:

    1. Installation: Setting up the Scipio tool on your machine.
    2. Prepare Cache: Preparing all necessary dependencies for your application to ensure build reliability.
    3. Create Frameworks: Executing the generation of XCFrameworks from your Swift Packages.
  7. Convert a single Swift Package to XCFramework using `create`

    main

    Use the scipio create command to generate XCFrameworks from an existing Swift Package. Unlike the prepare command, create does not require you to prepare a new package manifest; it works directly with the existing package structure. This is ideal for converting third-party Swift Packages into XCFrameworks for distribution or easier integration.

    $ scipio create path/to/swift-package
  8. Create a mergeable library with Scipio

    main

    Scipio supports the mergeable framework type, which allows a framework to switch its linking style via build configuration using metadata. This is useful for distributing packages as mergeable libraries as introduced by Apple in WWDC23.

    Note: Mergeable frameworks typically result in a binary size approximately 2x larger than standard dynamic frameworks.

    $ scipio create path/to/MyPackage --framework-type mergeable --enable-library-evolution
  9. Build Scipio CLI from source

    main

    If you prefer to build Scipio from source, clone the repository and use swift run to build the release version. After building, you should add the resulting binary to your PATH to use the scipio command directly.

    $ git clone https://github.com/giginet/Scipio.git
    $ cd Scipio
    $ swift run -c release scipio --help
    # Add reference .build/release/scipio to the PATH variable.
    $ export PATH=/path/to/scipio:$PATH
  10. Prepare all dependencies for your application using `prepare` mode

    main

    Scipio's prepare mode allows you to build all dependencies defined in a Swift Package manifest as XCFrameworks. This is useful for pre-building dependencies to speed up build times or to manage binary dependencies in an Xcode project.

    Workflow

    1. Create a Dependency Package: Create a new Swift Package (ideally in the same directory as your Xcode project) to act as a manifest for your dependencies.

      mkdir MyAppDependencies
      cd MyAppDependencies
      swift package init
    2. Configure Package.swift: Edit the manifest to include your required dependencies.

      • Platforms: You must specify platforms (e.g., .iOS(.v14)) because Scipio uses this to determine which SDKs to build for.
      • Targets: You must depend on all desired products in a single primary target. Declare all other targets as dependencies of this first target.
    3. Run Scipio: Execute the prepare command pointing to your package directory.

      scipio prepare path/to/MyAppDependencies

    By default, all generated XCFrameworks are placed in [PackageRoot]/XCFramework.

    $ scipio prepare path/to/MyAppDependencies