[
  {
    "path": ".gitignore",
    "content": ".DS_Store\n/.build\n/Packages\n/*.xcodeproj\nxcuserdata/\nDerivedData/\n.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata\n"
  },
  {
    "path": ".swiftpm/xcode/xcshareddata/xcschemes/XCFrameworkMaker.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1250\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"XCFrameworkMaker\"\n               BuildableName = \"XCFrameworkMaker\"\n               BlueprintName = \"XCFrameworkMaker\"\n               ReferencedContainer = \"container:\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      codeCoverageEnabled = \"YES\">\n      <Testables>\n         <TestableReference\n            skipped = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"XCFrameworkMakerTests\"\n               BuildableName = \"XCFrameworkMakerTests\"\n               BlueprintName = \"XCFrameworkMakerTests\"\n               ReferencedContainer = \"container:\">\n            </BuildableReference>\n         </TestableReference>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"XCFrameworkMaker\"\n            BuildableName = \"XCFrameworkMaker\"\n            BlueprintName = \"XCFrameworkMaker\"\n            ReferencedContainer = \"container:\">\n         </BuildableReference>\n      </MacroExpansion>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": ".swiftpm/xcode/xcshareddata/xcschemes/make-xcframework.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1250\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"make-xcframework\"\n               BuildableName = \"make-xcframework\"\n               BlueprintName = \"make-xcframework\"\n               ReferencedContainer = \"container:\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n         <TestableReference\n            skipped = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"XCFrameworkMakerTests\"\n               BuildableName = \"XCFrameworkMakerTests\"\n               BlueprintName = \"XCFrameworkMakerTests\"\n               ReferencedContainer = \"container:\">\n            </BuildableReference>\n         </TestableReference>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"make-xcframework\"\n            BuildableName = \"make-xcframework\"\n            BlueprintName = \"make-xcframework\"\n            ReferencedContainer = \"container:\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"make-xcframework\"\n            BuildableName = \"make-xcframework\"\n            BlueprintName = \"make-xcframework\"\n            ReferencedContainer = \"container:\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021 Dariusz Rybicki Darrarski\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Package.resolved",
    "content": "{\n  \"object\": {\n    \"pins\": [\n      {\n        \"package\": \"arm64-to-sim\",\n        \"repositoryURL\": \"https://github.com/darrarski/arm64-to-sim.git\",\n        \"state\": {\n          \"branch\": null,\n          \"revision\": \"37962d62d7f8d34875f793de510950112d860083\",\n          \"version\": \"1.0.0\"\n        }\n      },\n      {\n        \"package\": \"ShellOut\",\n        \"repositoryURL\": \"https://github.com/JohnSundell/ShellOut.git\",\n        \"state\": {\n          \"branch\": null,\n          \"revision\": \"e1577acf2b6e90086d01a6d5e2b8efdaae033568\",\n          \"version\": \"2.3.0\"\n        }\n      },\n      {\n        \"package\": \"swift-argument-parser\",\n        \"repositoryURL\": \"https://github.com/apple/swift-argument-parser.git\",\n        \"state\": {\n          \"branch\": null,\n          \"revision\": \"986d191f94cec88f6350056da59c2e59e83d1229\",\n          \"version\": \"0.4.3\"\n        }\n      }\n    ]\n  },\n  \"version\": 1\n}\n"
  },
  {
    "path": "Package.swift",
    "content": "// swift-tools-version:5.4\nimport PackageDescription\n\nlet package = Package(\n  name: \"xcframework-maker\",\n  platforms: [\n    .macOS(.v11),\n  ],\n  products: [\n    .library(\n      name: \"XCFrameworkMaker\",\n      targets: [\n        \"XCFrameworkMaker\",\n      ]\n    ),\n    .executable(\n      name: \"make-xcframework\",\n      targets: [\n        \"make-xcframework\",\n      ]\n    ),\n  ],\n  dependencies: [\n    .package(\n      name: \"swift-argument-parser\",\n      url: \"https://github.com/apple/swift-argument-parser.git\",\n      .upToNextMajor(from: \"0.4.3\")\n    ),\n    .package(\n      name: \"ShellOut\",\n      url: \"https://github.com/JohnSundell/ShellOut.git\",\n      .upToNextMajor(from: \"2.3.0\")\n    ),\n    .package(\n      name: \"arm64-to-sim\",\n      url: \"https://github.com/darrarski/arm64-to-sim.git\",\n      .upToNextMajor(from: \"1.0.0\")\n    ),\n  ],\n  targets: [\n    .target(\n      name: \"XCFrameworkMaker\",\n      dependencies: [\n        .product(\n          name: \"ShellOut\",\n          package: \"ShellOut\"\n        ),\n        .product(\n          name: \"Arm64ToSim\",\n          package: \"arm64-to-sim\"\n        ),\n      ]\n    ),\n    .testTarget(\n      name: \"XCFrameworkMakerTests\",\n      dependencies: [\n        .target(name: \"XCFrameworkMaker\"),\n      ]\n    ),\n    .executableTarget(\n      name: \"make-xcframework\",\n      dependencies: [\n        .target(name: \"XCFrameworkMaker\"),\n        .product(\n          name: \"ArgumentParser\",\n          package: \"swift-argument-parser\"\n        ),\n      ]\n    ),\n  ]\n)\n"
  },
  {
    "path": "README.md",
    "content": "# xcframework-maker\n\n![swift 5.4](https://img.shields.io/badge/swift-5.4-orange.svg)\n![platform macOS](https://img.shields.io/badge/platform-macOS-blue)\n![SPM supported](https://img.shields.io/badge/SPM-supported-green)\n\nmacOS utility for converting fat-frameworks to SPM-compatible XCFramework with arm64-simulator support.\n\n## 📝 Description\n\n`make-xcframework` is a simple command-line utility written in Swift that creates **XCFramework** file from fat framework files. The resulting XCFramework file can be added as a dependency to your **Swift Package**, using `.binaryTarget` (read more in [official documentation](https://docs.swift.org/package-manager/PackageDescription/PackageDescription.html)).\n\nOptionally, **arm64-simulator** support can be included in the resulting XCFramework to allow development on a computer with **Apple Silicon (M1)** processor without a need to run Xcode through Rosetta.\n\nThe `xcframework-maker` Swift Package contains `make-xcframework` that can be used from the command line and `XCFrameworkMaker` library that you can integrate with your Swift Package and use programmatically.\n\n**Note:** [arm64-to-sim](http://github.com/darrarski/arm64-to-sim) is used to \"hack\" the native (device) **arm64** architecture slice so it can be used in a simulator running on Apple Silicon. This is an experimental feature, and **it can fail for many reasons**. It was tested and proved to be working with the `GoogleInteractiveMediaAds` dynamic fat framework, but your experience may vary.\n\n## 🛠 Build\n\nUse Swift 5.4 for building the utility on macOS:\n\n```sh\nswift build -c release\n```\n\nYou can copy the executable or run it directly from the build directory:\n\n```sh\n.build/release/make-xcframework\n```\n\n## ▶️ Usage\n\n```\nOVERVIEW: Utility for creating XCFramework from legacy fat-framework files.\n\nUse this tool to create XCFramework from legacy fat-framework files. Resulting XCFramework can be\nadded as a dependency to your Swift Package. Optionally arm64-simulator support can be included in\nthe resulting XCFramework, so it can be used on M1 Mac without the need to run Xcode through\nRosetta.\n\nUSAGE: make-xcframework [-ios <path>] [-tvos <path>] [-arm64sim] -output <path> [-verbose]\n\nOPTIONS:\n  -ios <path>             iOS input framework path.\n        Provide a path to the iOS fat framework that should be included in the resulting\n        XCFramework. Eg \"path/to/iOS/Framework.framework\"\n  -tvos <path>            tvOS input framework path.\n        Provide a path to the tvOS fat framework that should be included in the resulting\n        XCFramework. Eg \"path/to/tvOS/Framework.framework\"\n  -arm64sim               Add support for arm64 simulator.\n        Use device-arm64 architecture slice as a simulator-arm64 architecture slice and include it\n        the resulting XCFramework. This makes development possible on M1 Mac without using Rosetta.\n  -output <path>          Output directory path.\n        Provide a path to a directory where the resulting XCFramework should be created. Eg\n        \"path/to/output/directory\"\n  -verbose                Log detailed info to standard output.\n        When this flag is provided, detailed information about each performed action is logged to\n        standard output.\n  -help, -h               Show help information.\n```\n\n### Example - GoogleInteractiveMediaAds\n\n1. Download GoogleInteractiveMediaAds fat-frameworks from Google website:\n    - [IMA SDK for iOS](https://developers.google.com/interactive-media-ads/docs/sdks/ios/dai/download)\n    - [IMA SDK for tvOS](https://developers.google.com/interactive-media-ads/docs/sdks/tvos/dai/download)\n2. Unzip downloaded files.\n3. Run `make-xcframework`:\n\n    ```sh\n    make-xcframework \\\n      -ios path/to/ios/GoogleInteractiveMediaAds.framework \\\n      -tvos path/to/tvos/GoogleInteractiveMediaAds.framework \\\n      -arm64sim \\\n      -output output/path\n    ```\n\n4. Resulting XCFramework will be created in the provided output directory:\n\n    ```sh\n    output/path/GoogleInteractiveMediaAds.xcframework\n    ```\n\n## ☕️ Do you like the project?\n\n<a href=\"https://www.buymeacoffee.com/darrarski\" target=\"_blank\"><img src=\"https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png\" alt=\"Buy Me A Coffee\" height=\"60\" width=\"217\" style=\"height: 60px !important;width: 217px !important;\" ></a>\n\n## 📄 License\n\nCopyright © 2021 Dariusz Rybicki Darrarski\n\nLicense: [MIT](LICENSE)\n"
  },
  {
    "path": "Sources/XCFrameworkMaker/Actions/AddArm64Simulator.swift",
    "content": "import Arm64ToSim\n\n/// Adds arm64 simulator support to a framework\npublic struct AddArm64Simulator {\n  var run: (Path, Path, Log?) throws -> Void\n\n  /// Add arm64 simulator support to a framework\n  /// - Parameters:\n  ///   - deviceFramework: Path to device framework file\n  ///   - simulatorFramework: Path to simulator framework file\n  ///   - log: Log action (defaults to nil for no logging)\n  /// - Throws: Error\n  public func callAsFunction(deviceFramework: Path, simulatorFramework: Path, _ log: Log? = nil) throws {\n    try run(deviceFramework, simulatorFramework, log)\n  }\n}\n\npublic extension AddArm64Simulator {\n  static func live(\n    lipoThin: LipoThin = .live(),\n    lipoCrate: LipoCreate = .live(),\n    arm64ToSim: @escaping (String) throws -> Void = arm64ToSim(_:),\n    deletePath: DeletePath = .live()\n  ) -> Self {\n    .init { deviceFramework, simulatorFramework, log in\n      log?(.normal, \"[AddArm64Simulator]\")\n      log?(.verbose, \"- deviceFramework: \\(deviceFramework.string)\")\n      log?(.verbose, \"- simulatorFramework: \\(simulatorFramework.string)\")\n      let deviceBinary = deviceFramework.addingComponent(deviceFramework.filenameExcludingExtension)\n      let simulatorBinary = simulatorFramework.addingComponent(simulatorFramework.filenameExcludingExtension)\n      let arm64Binary = Path(\"\\(simulatorBinary.string)-arm64\")\n      try lipoThin(input: deviceBinary, arch: .arm64, output: arm64Binary, log?.indented())\n      try arm64ToSim(arm64Binary.string)\n      try lipoCrate(inputs: [simulatorBinary, arm64Binary], output: simulatorBinary, log?.indented())\n      try deletePath(arm64Binary, log?.indented())\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/XCFrameworkMaker/Actions/CopyFramework.swift",
    "content": "/// Creates copy of a framework with provided architectures\npublic struct CopyFramework {\n  var run: (Path, [Arch], Path, Log?) throws -> Path\n\n  /// Create copy of a framework with provided architectures\n  /// - Parameters:\n  ///   - input: Path to the framework\n  ///   - archs: Architectures to be included in the copied framework\n  ///   - path: Path to a directory where framework copy will be created\n  ///   - log: Log action (defaults to nil for no logging)\n  /// - Throws: Error\n  /// - Returns: Path to the copied framework\n  public func callAsFunction(_ input: Path, archs: [Arch], path: Path, _ log: Log? = nil) throws -> Path {\n    try run(input, archs, path, log)\n  }\n}\n\npublic extension CopyFramework {\n  static func live(\n    createDir: CreateDir = .live(),\n    copyPath: CopyPath = .live(),\n    deletePath: DeletePath = .live(),\n    lipoExtract: LipoExtract = .live()\n  ) -> Self {\n    .init { input, archs, path, log in\n      log?(.normal, \"[CopyFramework]\")\n      log?(.verbose, \"- input: \\(input.string)\")\n      log?(.verbose, \"- archs: \\(archs.map(\\.rawValue).joined(separator: \", \"))\")\n      log?(.verbose, \"- path: \\(path.string)\")\n      try createDir(path, log?.indented())\n      let output = path.addingComponent(input.lastComponent)\n      try copyPath(of: input, at: output, log?.indented())\n      let outputBinary = output.addingComponent(output.filenameExcludingExtension)\n      try deletePath(outputBinary, log?.indented())\n      let inputBinary = input.addingComponent(input.filenameExcludingExtension)\n      try lipoExtract.callAsFunction(input: inputBinary, archs: archs, output: outputBinary, log?.indented())\n      log?(.verbose, \"- output: \\(output.string)\")\n      return output\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/XCFrameworkMaker/Actions/CopyPath.swift",
    "content": "/// Creates copy of file or directory\npublic struct CopyPath {\n  var run: (Path, Path, Log?) throws -> Void\n\n  /// Create copy of file or directory\n  /// - Parameters:\n  ///   - source: Source file or directory path\n  ///   - destination: Destination path\n  ///   - log: Log action (defaults to nil for no logging)\n  /// - Throws: Error\n  public func callAsFunction(of source: Path, at destination: Path, _ log: Log? = nil) throws {\n    try run(source, destination, log)\n  }\n}\n\npublic extension CopyPath {\n  static func live(\n    runShellCommand: RunShellCommand = .live()\n  ) -> Self {\n    .init { source, destination, log in\n      log?(.normal, \"[CopyPath]\")\n      log?(.verbose, \"- source: \\(source.string)\")\n      log?(.verbose, \"- destination: \\(destination.string)\")\n      _ = try runShellCommand(\"cp -fR \\(source.string) \\(destination.string)\", log?.indented())\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/XCFrameworkMaker/Actions/CreateDir.swift",
    "content": "/// Creates directory\npublic struct CreateDir {\n  var run: (Path, Log?) throws -> Void\n\n  /// Create directory at provied path\n  /// - Parameters:\n  ///   - path: Path of the directory to create\n  ///   - log: Log action (defaults to nil for no logging)\n  /// - Throws: Error\n  public func callAsFunction(_ path: Path, _ log: Log? = nil) throws {\n    try run(path, log)\n  }\n}\n\npublic extension CreateDir {\n  static func live(\n    runShellCommand: RunShellCommand = .live()\n  ) -> Self {\n    .init { path, log in\n      log?(.normal, \"[CreateDir]\")\n      log?(.verbose, \"- path: \\(path.string)\")\n      _ = try runShellCommand(\"mkdir -p \\(path.string)\", log?.indented())\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/XCFrameworkMaker/Actions/CreateTempDir.swift",
    "content": "import Foundation\n\n/// Creates new temporary directory\npublic struct CreateTempDir {\n  var run: (Log?) throws -> Path\n\n  /// Create new temporary directory and return its path\n  /// - Parameter log: Log action (defaults to nil for no logging)\n  /// - Throws: Error\n  /// - Returns: Path to new temporary directory\n  public func callAsFunction(_ log: Log? = nil) throws -> Path {\n    try run(log)\n  }\n}\n\npublic extension CreateTempDir {\n  static func live(\n    basePath: String = FileManager.default.temporaryDirectory.path,\n    randomString: @escaping () -> String = { UUID().uuidString },\n    createDir: CreateDir = .live()\n  ) -> Self {\n    .init { log in\n      log?(.normal, \"[CreateTempDir]\")\n      let path = Path(basePath).addingComponent(\"XCFrameworkMaker_\\(randomString())\")\n      try createDir(path, log?.indented())\n      log?(.verbose, \"- path: \\(path.string)\")\n      return path\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/XCFrameworkMaker/Actions/CreateXCFramework.swift",
    "content": "/// Creates XCFramework from provided thin frameworks\npublic struct CreateXCFramework {\n  var run: ([Path], Path, Log?) throws -> Void\n\n  /// Create XCFramework from provided thin frameworks\n  /// - Parameters:\n  ///   - frameworks: Paths to thin frameworks\n  ///   - path: Path to a directory where resulting XCFramework will be created\n  ///   - log: Log action (defaults to nil for no logging)\n  /// - Throws: Error\n  public func callAsFunction(from frameworks: [Path], at path: Path, _ log: Log? = nil) throws {\n    try run(frameworks, path, log)\n  }\n}\n\npublic extension CreateXCFramework {\n  static func live(\n    deletePath: DeletePath = .live(),\n    runShellCommand: RunShellCommand = .live()\n  ) -> Self {\n    .init { frameworks, path, log in\n      log?(.normal, \"[CreateXCFramework]\")\n      log?(.verbose, \"- frameworks: \\n\\t\\(frameworks.map(\\.string).joined(separator: \"\\n\\t\"))\")\n      log?(.verbose, \"- path: \\(path.string)\")\n      let frameworkOptions = frameworks.map { \"-framework \\($0.string)\" }.joined(separator: \" \")\n      let frameworkName = frameworks.first?.filenameExcludingExtension ?? \"\"\n      let output = path.addingComponent(\"\\(frameworkName).xcframework\")\n      try deletePath(output, log?.indented())\n      _ = try runShellCommand(\n        \"xcodebuild -create-xcframework \\(frameworkOptions) -output \\(output.string)\",\n        log?.indented()\n      )\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/XCFrameworkMaker/Actions/DeletePath.swift",
    "content": "/// Deletes file or directory\npublic struct DeletePath {\n  var run: (Path, Log?) throws -> Void\n\n  /// Delete file or directory\n  /// - Parameters:\n  ///   - path: Path to file or directory to be removed\n  ///   - log: Log action (defaults to nil for no logging)\n  /// - Throws: Error\n  public func callAsFunction(_ path: Path, _ log: Log? = nil) throws {\n    try run(path, log)\n  }\n}\n\npublic extension DeletePath {\n  static func live(\n    runShellCommand: RunShellCommand = .live()\n  ) -> Self {\n    .init { path, log in\n      log?(.normal, \"[DeletePath]\")\n      log?(.verbose, \"- path: \\(path.string)\")\n      _ = try runShellCommand(\"rm -Rf \\(path.string)\", log?.indented())\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/XCFrameworkMaker/Actions/GetArchs.swift",
    "content": "/// Gets architectures contained in a framework\npublic struct GetArchs {\n  var run: (Path, Log?) throws -> [Arch]\n\n  /// Get architectures contained in framework\n  /// - Parameters:\n  ///   - frameworkPath: Path to framework\n  ///   - log: Log action (defaults to nil for no logging)\n  /// - Throws: Error\n  /// - Returns: Architectures contained in the framework\n  public func callAsFunction(inFramework frameworkPath: Path, _ log: Log? = nil) throws -> [Arch] {\n    try run(frameworkPath, log)\n  }\n}\n\npublic extension GetArchs {\n  static func live(\n    runShellCommand: RunShellCommand = .live()\n  ) -> Self {\n    .init { frameworkPath, log in\n      log?(.normal, \"[GetArchs]\")\n      log?(.verbose, \"- frameworkPath: \\(frameworkPath.string)\")\n      let frameworkName = frameworkPath.filenameExcludingExtension\n      let binaryPath = frameworkPath.addingComponent(frameworkName)\n      let archs = try runShellCommand(\"lipo \\(binaryPath.string) -archs\", log?.indented())\n        .components(separatedBy: \" \")\n        .compactMap(Arch.init(rawValue:))\n      log?(.verbose, \"- archs: \\(archs.map(\\.rawValue).joined(separator: \", \"))\")\n      return archs\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/XCFrameworkMaker/Actions/LipoCreate.swift",
    "content": "/// Use lipo to create binary file from other binary files\npublic struct LipoCreate {\n  var run: ([Path], Path, Log?) throws -> Void\n\n  /// Create binary file from other binary files\n  /// - Parameters:\n  ///   - inputs: Paths to input files\n  ///   - output: Path to output file\n  ///   - log: Log action (defaults to nil for no logging)\n  /// - Throws: Error\n  public func callAsFunction(inputs: [Path], output: Path, _ log: Log? = nil) throws {\n    try run(inputs, output, log)\n  }\n}\n\npublic extension LipoCreate {\n  static func live(\n    runShellCommand: RunShellCommand = .live()\n  ) -> Self {\n    .init { inputs, output, log in\n      log?(.normal, \"[LipoCreate]\")\n      log?(.verbose, \"- inputs: \\n\\t\\(inputs.map(\\.string).joined(separator: \"\\n\\t\"))\")\n      log?(.verbose, \"- output: \\(output.string)\")\n      let input = inputs.map(\\.string).joined(separator: \" \")\n      _ = try runShellCommand(\"lipo \\(input) -create -output \\(output.string)\", log?.indented())\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/XCFrameworkMaker/Actions/LipoExtract.swift",
    "content": "/// Use lipo to extract provided architectures from binary file\npublic struct LipoExtract {\n  var run: (Path, [Arch], Path, Log?) throws -> Void\n\n  /// Extract provided architectures from binary file\n  /// - Parameters:\n  ///   - input: Path to the binary file\n  ///   - archs: Architectures to be extracted\n  ///   - output: Path to the extracted binary file\n  ///   - log: Log action (defaults to nil for no logging)\n  /// - Throws: Error\n  public func callAsFunction(input: Path, archs: [Arch], output: Path, _ log: Log? = nil) throws {\n    try run(input, archs, output, log)\n  }\n}\n\npublic extension LipoExtract {\n  static func live(\n    runShellCommand: RunShellCommand = .live()\n  ) -> Self {\n    .init { input, archs, output, log in\n      log?(.normal, \"[LipoExtract]\")\n      log?(.verbose, \"- input: \\(input.string)\")\n      log?(.verbose, \"- archs: \\(archs.map(\\.rawValue).joined(separator: \", \"))\")\n      log?(.verbose, \"- output: \\(input.string)\")\n      let extract = archs.map { \"-extract \\($0)\" }.joined(separator: \" \")\n      _ = try runShellCommand(\"lipo \\(input.string) \\(extract) -output \\(output.string)\", log?.indented())\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/XCFrameworkMaker/Actions/LipoThin.swift",
    "content": "/// Use lipo to create thin binary file with provided architecture\npublic struct LipoThin {\n  var run: (Path, Arch, Path, Log?) throws -> Void\n\n  /// Create thin binary file with provided architecture\n  /// - Parameters:\n  ///   - input: Path to the input file\n  ///   - arch: Architecture to be included in the output\n  ///   - output: Path to the output file\n  ///   - log: Log action (defaults to nil for no logging)\n  /// - Throws: Error\n  public func callAsFunction(input: Path, arch: Arch, output: Path, _ log: Log? = nil) throws {\n    try run(input, arch, output, log)\n  }\n}\n\npublic extension LipoThin {\n  static func live(\n    runShellCommand: RunShellCommand = .live()\n  ) -> Self {\n    .init { input, arch, output, log in\n      log?(.normal, \"[LipoThin]\")\n      log?(.verbose, \"- input: \\(input.string)\")\n      log?(.verbose, \"- arch: \\(arch.rawValue)\")\n      log?(.verbose, \"- output: \\(output.string)\")\n      _ = try runShellCommand(\n        \"lipo \\(input.string) -thin \\(arch.rawValue) -output \\(output.string)\",\n        log?.indented()\n      )\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/XCFrameworkMaker/Actions/Log.swift",
    "content": "/// Logs messages\npublic struct Log {\n  var run: (LogLevel, String) -> Void\n\n  /// Log message\n  /// - Parameters:\n  ///   - level: Log level\n  ///   - message: Message to be logged\n  public func callAsFunction(_ level: LogLevel, _ message: String) {\n    run(level, message)\n  }\n}\n\npublic extension Log {\n  static func live(\n    level logginLevel: LogLevel = .normal,\n    print: @escaping (String) -> Void = { print($0) }\n  ) -> Self {\n    .init { level, message in\n      guard level <= logginLevel else { return }\n      print(message)\n    }\n  }\n}\n\npublic extension Log {\n  /// Returns modified Log that indents messages with a tab character\n  /// - Returns: Log\n  func indented() -> Self {\n    .init { level, message in\n      let indentation = \"\\t\"\n      let indentedMessage = message\n        .split(separator: \"\\n\")\n        .map { indentation + $0 }\n        .joined(separator: \"\\n\")\n      self.callAsFunction(level, indentedMessage)\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/XCFrameworkMaker/Actions/MakeXCFramework.swift",
    "content": "import Foundation\n\n/// Creates XCFramework from provided fat frameworks\npublic struct MakeXCFramework {\n  var run: (Path?, Path?, Bool, Path, Log?) throws -> Void\n\n  /// Create XCFramework from provided fat frameworks\n  /// - Parameters:\n  ///   - iOSPath: Path to iOS fat framework\n  ///   - tvOSPath: Path to tvOS fat framework\n  ///   - arm64sim: If true, add arm64-simulator support\n  ///   - path: Path to a directory where resulting XCFramework will be created\n  ///   - log: Log action (defaults to nil for no logging)\n  /// - Throws: Error\n  public func callAsFunction(\n    iOS iOSPath: Path?,\n    tvOS tvOSPath: Path?,\n    arm64sim: Bool,\n    at path: Path,\n    _ log: Log? = nil\n  ) throws {\n    try run(iOSPath, tvOSPath, arm64sim, path, log)\n  }\n}\n\nextension MakeXCFramework {\n  public struct EmptyInputError: LocalizedError, Equatable {\n    public init() {}\n\n    public var errorDescription: String? {\n      \"Empty inputs. Provide at least one input fat framework.\"\n    }\n  }\n}\n\npublic extension MakeXCFramework {\n  static func live(\n    createTempDir: CreateTempDir = .live(),\n    getArchs: GetArchs = .live(),\n    copyFramework: CopyFramework = .live(),\n    addArm64Simulator: AddArm64Simulator = .live(),\n    createXCFramework: CreateXCFramework = .live()\n  ) -> Self {\n    .init { iOSPath, tvOSPath, arm64sim, output, log in\n      log?(.normal, \"[MakeXCFramework]\")\n      log?(.verbose, \"- iOSPath: \\(iOSPath?.string ?? \"nil\")\")\n      log?(.verbose, \"- tvOSPath: \\(tvOSPath?.string ?? \"nil\")\")\n      log?(.verbose, \"- arm64sim: \\(arm64sim)\")\n      log?(.verbose, \"- output: \\(output.string)\")\n\n      guard iOSPath != nil || tvOSPath != nil else {\n        throw EmptyInputError()\n      }\n\n      let tempDir = try createTempDir(log?.indented())\n      var thinFrameworks = [Path]()\n\n      if let path = iOSPath {\n        let archs = try getArchs(inFramework: path, log?.indented())\n        let deviceArchs = [Arch.armv7, .arm64].filter(archs.contains(_:))\n        let simulatorArchs = [Arch.i386, .x86_64].filter(archs.contains(_:))\n        let deviceOutput = tempDir.addingComponent(\"ios-device\")\n        let simulatorOutput = tempDir.addingComponent(\"ios-simulator\")\n        let deviceFramework = try copyFramework(path, archs: deviceArchs, path: deviceOutput, log?.indented())\n        let simulatorFramework = try copyFramework(path, archs: simulatorArchs, path: simulatorOutput, log?.indented())\n        if arm64sim {\n          try addArm64Simulator(\n            deviceFramework: deviceFramework, simulatorFramework: simulatorFramework,\n            log?.indented()\n          )\n        }\n        thinFrameworks.append(contentsOf: [deviceFramework, simulatorFramework])\n      }\n\n      if let path = tvOSPath {\n        let archs = try getArchs(inFramework: path, log?.indented())\n        let deviceArchs = [Arch.armv7, .arm64].filter(archs.contains(_:))\n        let simulatorArchs = [Arch.i386, .x86_64].filter(archs.contains(_:))\n        let deviceOutput = tempDir.addingComponent(\"tvos-device\")\n        let simulatorOutput = tempDir.addingComponent(\"tvos-simulator\")\n        let deviceFramework = try copyFramework(path, archs: deviceArchs, path: deviceOutput, log?.indented())\n        let simulatorFramework = try copyFramework(path, archs: simulatorArchs, path: simulatorOutput, log?.indented())\n        if arm64sim {\n          try addArm64Simulator(\n            deviceFramework: deviceFramework, simulatorFramework: simulatorFramework,\n            log?.indented()\n          )\n        }\n        thinFrameworks.append(contentsOf: [deviceFramework, simulatorFramework])\n      }\n\n      try createXCFramework(from: thinFrameworks, at: output, log?.indented())\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/XCFrameworkMaker/Actions/RunShellCommand.swift",
    "content": "import ShellOut\n\n/// Execute shell command\npublic struct RunShellCommand {\n  var run: (String, Log?) throws -> String\n\n  /// Execure shell command\n  /// - Parameters:\n  ///   - command: Command to execute\n  ///   - log: Log action (defaults to nil for no logging)\n  /// - Throws: Error\n  /// - Returns: Shell command output\n  public func callAsFunction(_ command: String, _ log: Log? = nil) throws -> String {\n    try run(command, log)\n  }\n}\n\npublic extension RunShellCommand {\n  static func live(\n    shellOut: @escaping (String) throws -> String = { try shellOut(to: $0) }\n  ) -> Self {\n    .init { command, log in\n      log?(.normal, \"[RunShellCommand]\")\n      log?(.verbose, \"- command: \\(command)\")\n      let output = try shellOut(command)\n      log?(.verbose, \"- output: \\(output)\")\n      return output\n    }\n  }\n}\n"
  },
  {
    "path": "Sources/XCFrameworkMaker/Models/Arch.swift",
    "content": "public enum Arch: String {\n  case i386\n  case x86_64\n  case armv7\n  case arm64\n}\n"
  },
  {
    "path": "Sources/XCFrameworkMaker/Models/LogLevel.swift",
    "content": "public enum LogLevel: Equatable, CaseIterable {\n  case normal\n  case verbose\n}\n\nextension LogLevel: Comparable {\n  public static func < (lhs: LogLevel, rhs: LogLevel) -> Bool {\n    allCases.firstIndex(of: lhs)! < allCases.firstIndex(of: rhs)!\n  }\n}\n"
  },
  {
    "path": "Sources/XCFrameworkMaker/Models/Path.swift",
    "content": "import Foundation\n\npublic struct Path: Equatable, Hashable {\n  public init(_ string: String) {\n    self.string = string\n  }\n\n  var string: String\n}\n\nextension Path {\n  var lastComponent: String {\n    string.split(separator: \"/\").last.map(String.init) ?? \"\"\n  }\n\n  var fileExtension: String? {\n    let lastComponent = self.lastComponent\n    guard lastComponent.contains(\".\") else { return nil }\n    return lastComponent.split(separator: \".\").last.map(String.init)\n  }\n\n  var filenameExcludingExtension: String {\n    let lastComponent = self.lastComponent\n    guard lastComponent.contains(\".\") else { return lastComponent }\n    var filenameComponents = lastComponent.split(separator: \".\").map(String.init)\n    guard filenameComponents.count > 1 else { return lastComponent }\n    filenameComponents.removeLast()\n    return filenameComponents.joined(separator: \".\")\n  }\n\n  func addingComponent(_ component: String) -> Path {\n    var newString = string\n    if newString.hasSuffix(\"/\") == false {\n      newString.append(\"/\")\n    }\n    newString.append(component)\n    return Path(newString)\n  }\n}\n"
  },
  {
    "path": "Sources/make-xcframework/MainCommand.swift",
    "content": "import ArgumentParser\nimport Foundation\nimport XCFrameworkMaker\n\nstruct MainCommand: ParsableCommand {\n  static var makeXCFramework: MakeXCFramework = .live()\n\n  // MARK: - ParsableCommand\n\n  static let configuration = CommandConfiguration(\n    commandName: \"make-xcframework\",\n    abstract: \"Utility for creating XCFramework from legacy fat-framework files.\",\n    discussion: \"Use this tool to create XCFramework from legacy fat-framework files. Resulting XCFramework can be added as a dependency to your Swift Package. Optionally arm64-simulator support can be included in the resulting XCFramework, so it can be used on M1 Mac without the need to run Xcode through Rosetta.\",\n    helpNames: [.short, .customLong(\"help\", withSingleDash: true)]\n  )\n\n  struct InputOptions: ParsableArguments {\n    @Option(\n      name: .customLong(\"ios\", withSingleDash: true),\n      help: ArgumentHelp(\n        \"iOS input framework path.\",\n        discussion: \"Provide a path to the iOS fat framework that should be included in the resulting XCFramework. Eg \\\"path/to/iOS/Framework.framework\\\"\",\n        valueName: \"path\"\n      ),\n      completion: .file(extensions: [\"framework\"])\n    )\n    var ios: String?\n\n    @Option(\n      name: .customLong(\"tvos\", withSingleDash: true),\n      help: ArgumentHelp(\n        \"tvOS input framework path.\",\n        discussion: \"Provide a path to the tvOS fat framework that should be included in the resulting XCFramework. Eg \\\"path/to/tvOS/Framework.framework\\\"\",\n        valueName: \"path\"\n      ),\n      completion: .file(extensions: [\"framework\"])\n    )\n    var tvos: String?\n\n    func validate() throws {\n      guard ios != nil || tvos != nil else {\n        throw MakeXCFramework.EmptyInputError()\n      }\n    }\n  }\n\n  @OptionGroup\n  var inputs: InputOptions\n\n  @Flag(\n    name: .customLong(\"arm64sim\", withSingleDash: true),\n    help: ArgumentHelp(\n      \"Add support for arm64 simulator.\",\n      discussion: \"Use device-arm64 architecture slice as a simulator-arm64 architecture slice and include it the resulting XCFramework. This makes development possible on M1 Mac without using Rosetta.\"\n    )\n  )\n  var arm64sim: Bool = false\n\n  @Option(\n    name: .customLong(\"output\", withSingleDash: true),\n    help: ArgumentHelp(\n      \"Output directory path.\",\n      discussion: \"Provide a path to a directory where the resulting XCFramework should be created. Eg \\\"path/to/output/directory\\\"\",\n      valueName: \"path\"\n    ),\n    completion: .directory\n  )\n  var output: String\n\n  @Flag(\n    name: .customLong(\"verbose\", withSingleDash: true),\n    help: ArgumentHelp(\n      \"Log detailed info to standard output.\",\n      discussion: \"When this flag is provided, detailed information about each performed action is logged to standard output.\"\n    )\n  )\n  var verbose: Bool = false\n\n  func run() throws {\n    try Self.makeXCFramework(\n      iOS: inputs.ios.map(Path.init(_:)),\n      tvOS: inputs.tvos.map(Path.init(_:)),\n      arm64sim: arm64sim,\n      at: Path(output),\n      verbose ? Log.live(level: .verbose) : nil\n    )\n  }\n}\n"
  },
  {
    "path": "Sources/make-xcframework/main.swift",
    "content": "MainCommand.main()\n"
  },
  {
    "path": "Tests/XCFrameworkMakerTests/Actions/AddArm64SimulatorTests.swift",
    "content": "import XCTest\n@testable import XCFrameworkMaker\n\nfinal class AddArm64SimulatorTests: XCTestCase {\n  enum Action: Equatable {\n    case didLipoThin(Path, Arch, Path)\n    case didLipoCreate([Path], Path)\n    case didArm64ToSim(String)\n    case didDeletePath(Path)\n    case didLog(LogLevel, String)\n  }\n\n  func testHappyPath() throws {\n    var didPerformActions = [Action]()\n    let sut = AddArm64Simulator.live(\n      lipoThin: .init { input, arch, output, _ in\n        didPerformActions.append(.didLipoThin(input, arch, output))\n      },\n      lipoCrate: .init { input, output, _ in\n        didPerformActions.append(.didLipoCreate(input, output))\n      },\n      arm64ToSim: { path in\n        didPerformActions.append(.didArm64ToSim(path))\n      },\n      deletePath: .init { path, _ in\n        didPerformActions.append(.didDeletePath(path))\n      }\n    )\n    let deviceFramework = Path(\"device/Framework.framework\")\n    let simulatorFramework = Path(\"simulator/Framework.framework\")\n    let log = Log { level, message in\n      didPerformActions.append(.didLog(level, message))\n    }\n\n    try sut(deviceFramework: deviceFramework, simulatorFramework: simulatorFramework, log)\n\n    XCTAssertEqual(didPerformActions, [\n      .didLog(.normal, \"[AddArm64Simulator]\"),\n      .didLog(.verbose, \"- deviceFramework: \\(deviceFramework.string)\"),\n      .didLog(.verbose, \"- simulatorFramework: \\(simulatorFramework.string)\"),\n      .didLipoThin(Path(\"device/Framework.framework/Framework\"), .arm64, Path(\"simulator/Framework.framework/Framework-arm64\")),\n      .didArm64ToSim(\"simulator/Framework.framework/Framework-arm64\"),\n      .didLipoCreate([Path(\"simulator/Framework.framework/Framework\"), Path(\"simulator/Framework.framework/Framework-arm64\")], Path(\"simulator/Framework.framework/Framework\")),\n      .didDeletePath(Path(\"simulator/Framework.framework/Framework-arm64\"))\n    ])\n  }\n}\n"
  },
  {
    "path": "Tests/XCFrameworkMakerTests/Actions/CopyFrameworkTests.swift",
    "content": "import XCTest\n@testable import XCFrameworkMaker\n\nfinal class CopyFrameworkTests: XCTestCase {\n  enum Action: Equatable {\n    case didCreateDir(Path)\n    case didCopyPath(Path, Path)\n    case didDeletePath(Path)\n    case didLipoExtract(Path, [Arch], Path)\n    case didLog(LogLevel, String)\n  }\n\n  func testHappyPath() throws {\n    var didPerformActions = [Action]()\n    let sut = CopyFramework.live(\n      createDir: .init { path, _ in\n        didPerformActions.append(.didCreateDir(path))\n      },\n      copyPath: .init { source, destination, _ in\n        didPerformActions.append(.didCopyPath(source, destination))\n      },\n      deletePath: .init { path, _ in\n        didPerformActions.append(.didDeletePath(path))\n      },\n      lipoExtract: .init { input, archs, output, _ in\n        didPerformActions.append(.didLipoExtract(input, archs, output))\n      }\n    )\n    let input = Path(\"input/Framework.framework\")\n    let archs = [Arch.i386, .arm64]\n    let path = Path(\"output/path\")\n    let log = Log { level, message in\n      didPerformActions.append(.didLog(level, message))\n    }\n\n    let output = try sut(input, archs: archs, path: path, log)\n\n    XCTAssertEqual(didPerformActions, [\n      .didLog(.normal, \"[CopyFramework]\"),\n      .didLog(.verbose, \"- input: \\(input.string)\"),\n      .didLog(.verbose, \"- archs: \\(archs.map(\\.rawValue).joined(separator: \", \"))\"),\n      .didLog(.verbose, \"- path: \\(path.string)\"),\n      .didCreateDir(Path(\"output/path\")),\n      .didCopyPath(Path(\"input/Framework.framework\"), Path(\"output/path/Framework.framework\")),\n      .didDeletePath(Path(\"output/path/Framework.framework/Framework\")),\n      .didLipoExtract(Path(\"input/Framework.framework/Framework\"), [.i386, .arm64], Path(\"output/path/Framework.framework/Framework\")),\n      .didLog(.verbose, \"- output: \\(output.string)\")\n    ])\n    XCTAssertEqual(output, path.addingComponent(input.lastComponent))\n  }\n}\n"
  },
  {
    "path": "Tests/XCFrameworkMakerTests/Actions/CopyPathTests.swift",
    "content": "import XCTest\n@testable import XCFrameworkMaker\n\nfinal class CopyPathTests: XCTestCase {\n  enum Action: Equatable {\n    case didRunShellCommand(String)\n    case didLog(LogLevel, String)\n  }\n\n  func testHappyPath() throws {\n    var didPerformActions = [Action]()\n    let sut = CopyPath.live(runShellCommand: .init { command, _ in\n      didPerformActions.append(.didRunShellCommand(command))\n      return \"\"\n    })\n    let source = Path(\"source\")\n    let destination = Path(\"destination\")\n    let log = Log { level, message in\n      didPerformActions.append(.didLog(level, message))\n    }\n\n    try sut(of: source, at: destination, log)\n\n    XCTAssertEqual(didPerformActions, [\n      .didLog(.normal, \"[CopyPath]\"),\n      .didLog(.verbose, \"- source: \\(source.string)\"),\n      .didLog(.verbose, \"- destination: \\(destination.string)\"),\n      .didRunShellCommand(\"cp -fR source destination\")\n    ])\n  }\n}\n"
  },
  {
    "path": "Tests/XCFrameworkMakerTests/Actions/CreateDirTests.swift",
    "content": "import XCTest\n@testable import XCFrameworkMaker\n\nfinal class CreateDirTests: XCTestCase {\n  enum Action: Equatable {\n    case didRunShellCommand(String)\n    case didLog(LogLevel, String)\n  }\n\n  func testHappyPath() throws {\n    var didPerformActions = [Action]()\n    let sut = CreateDir.live(runShellCommand: .init { command, _ in\n      didPerformActions.append(.didRunShellCommand(command))\n      return \"\"\n    })\n    let path = Path(\"new/directory/path\")\n    let log = Log { level, message in\n      didPerformActions.append(.didLog(level, message))\n    }\n\n    try sut(path, log)\n\n    XCTAssertEqual(didPerformActions, [\n      .didLog(.normal, \"[CreateDir]\"),\n      .didLog(.verbose, \"- path: \\(path.string)\"),\n      .didRunShellCommand(\"mkdir -p new/directory/path\")\n    ])\n  }\n}\n"
  },
  {
    "path": "Tests/XCFrameworkMakerTests/Actions/CreateTempDirTests.swift",
    "content": "import XCTest\n@testable import XCFrameworkMaker\n\nfinal class CreateTempDirTests: XCTestCase {\n  enum Action: Equatable {\n    case didCreateDir(Path)\n    case didLog(LogLevel, String)\n  }\n\n  func testHappyPath() throws {\n    var didPerformActions = [Action]()\n    let basePath = \"temp/dir/base/path\"\n    let randomString = \"random\"\n    let sut = CreateTempDir.live(\n      basePath: basePath,\n      randomString: { randomString },\n      createDir: .init { path, _ in\n        didPerformActions.append(.didCreateDir(path))\n      }\n    )\n    let log = Log { level, message in\n      didPerformActions.append(.didLog(level, message))\n    }\n\n    let path = try sut(log)\n\n    let expectedPath = Path(basePath).addingComponent(\"XCFrameworkMaker_\\(randomString)\")\n    XCTAssertEqual(didPerformActions, [\n      .didLog(.normal, \"[CreateTempDir]\"),\n      .didCreateDir(expectedPath),\n      .didLog(.verbose, \"- path: \\(path.string)\")\n    ])\n    XCTAssertEqual(path, expectedPath)\n  }\n}\n"
  },
  {
    "path": "Tests/XCFrameworkMakerTests/Actions/CreateXCFrameworkTests.swift",
    "content": "import XCTest\n@testable import XCFrameworkMaker\n\nfinal class CreateXCFrameworkTests: XCTestCase {\n  enum Action: Equatable {\n    case didDeletePath(Path)\n    case didRunShellCommand(String)\n    case didLog(LogLevel, String)\n  }\n\n  func testHappyPath() throws {\n    var didPerformActions = [Action]()\n    let sut = CreateXCFramework.live(\n      deletePath: .init { path, _ in\n        didPerformActions.append(.didDeletePath(path))\n      },\n      runShellCommand: .init { command, _ in\n        didPerformActions.append(.didRunShellCommand(command))\n        return \"\"\n      }\n    )\n    let frameworks = [\n      Path(\"device/Framework.framework\"),\n      Path(\"simulator/Framework.framework\")\n    ]\n    let path = Path(\"output/path\")\n    let log = Log { level, message in\n      didPerformActions.append(.didLog(level, message))\n    }\n\n    try sut(from: frameworks, at: path, log)\n\n    XCTAssertEqual(didPerformActions, [\n      .didLog(.normal, \"[CreateXCFramework]\"),\n      .didLog(.verbose, \"- frameworks: \\n\\t\\(frameworks.map(\\.string).joined(separator: \"\\n\\t\"))\"),\n      .didLog(.verbose, \"- path: \\(path.string)\"),\n      .didDeletePath(Path(\"output/path/Framework.xcframework\")),\n      .didRunShellCommand(\"xcodebuild -create-xcframework -framework device/Framework.framework -framework simulator/Framework.framework -output output/path/Framework.xcframework\")\n    ])\n  }\n}\n"
  },
  {
    "path": "Tests/XCFrameworkMakerTests/Actions/DeletePathTests.swift",
    "content": "import XCTest\n@testable import XCFrameworkMaker\n\nfinal class DeletePathTests: XCTestCase {\n  enum Action: Equatable {\n    case didRunShellCommand(String)\n    case didLog(LogLevel, String)\n  }\n\n  func testHappyPath() throws {\n    var didPerformActions = [Action]()\n    let sut = DeletePath.live(runShellCommand: .init { command, _ in\n      didPerformActions.append(.didRunShellCommand(command))\n      return \"\"\n    })\n    let path = Path(\"some/path\")\n    let log = Log { level, message in\n      didPerformActions.append(.didLog(level, message))\n    }\n\n    try sut(path, log)\n\n    XCTAssertEqual(didPerformActions, [\n      .didLog(.normal, \"[DeletePath]\"),\n      .didLog(.verbose, \"- path: \\(path.string)\"),\n      .didRunShellCommand(\"rm -Rf some/path\")\n    ])\n  }\n}\n"
  },
  {
    "path": "Tests/XCFrameworkMakerTests/Actions/GetArchsTests.swift",
    "content": "import XCTest\n@testable import XCFrameworkMaker\n\nfinal class GetArchsTests: XCTestCase {\n  enum Action: Equatable {\n    case didRunShellCommand(String)\n    case didLog(LogLevel, String)\n  }\n\n  func testHappyPath() throws {\n    var didPerformActions = [Action]()\n    let sut = GetArchs.live(runShellCommand: .init { command, _ in\n      didPerformActions.append(.didRunShellCommand(command))\n      return \"i386 arm64 unknown\"\n    })\n    let frameworkPath = Path(\"path/to/Some.framework\")\n    let log = Log { level, message in\n      didPerformActions.append(.didLog(level, message))\n    }\n\n    let archs = try sut(inFramework: frameworkPath, log)\n\n    XCTAssertEqual(didPerformActions, [\n      .didLog(.normal, \"[GetArchs]\"),\n      .didLog(.verbose, \"- frameworkPath: \\(frameworkPath.string)\"),\n      .didRunShellCommand(\"lipo path/to/Some.framework/Some -archs\"),\n      .didLog(.verbose, \"- archs: \\(archs.map(\\.rawValue).joined(separator: \", \"))\"),\n    ])\n    XCTAssertEqual(archs, [.i386, .arm64])\n  }\n}\n"
  },
  {
    "path": "Tests/XCFrameworkMakerTests/Actions/LipoCreateTests.swift",
    "content": "import XCTest\n@testable import XCFrameworkMaker\n\nfinal class LipoCreateTests: XCTestCase {\n  enum Action: Equatable {\n    case didRunShellCommand(String)\n    case didLog(LogLevel, String)\n  }\n\n  func testHappyPath() throws {\n    var didPerformActions = [Action]()\n    let sut = LipoCreate.live(runShellCommand: .init { command, _ in\n      didPerformActions.append(.didRunShellCommand(command))\n      return \"\"\n    })\n    let inputs = [Path(\"input/file1\"), Path(\"input/file2\")]\n    let output = Path(\"output/file\")\n    let log = Log { level, message in\n      didPerformActions.append(.didLog(level, message))\n    }\n\n    try sut(inputs: inputs, output: output, log)\n\n    XCTAssertEqual(didPerformActions, [\n      .didLog(.normal, \"[LipoCreate]\"),\n      .didLog(.verbose, \"- inputs: \\n\\t\\(inputs.map(\\.string).joined(separator: \"\\n\\t\"))\"),\n      .didLog(.verbose, \"- output: \\(output.string)\"),\n      .didRunShellCommand(\"lipo input/file1 input/file2 -create -output output/file\")\n    ])\n  }\n}\n"
  },
  {
    "path": "Tests/XCFrameworkMakerTests/Actions/LipoExtractTests.swift",
    "content": "import XCTest\n@testable import XCFrameworkMaker\n\nfinal class LipoExtractTests: XCTestCase {\n  enum Action: Equatable {\n    case didRunShellCommand(String)\n    case didLog(LogLevel, String)\n  }\n\n  func testHappyPath() throws {\n    var didPerformActions = [Action]()\n    let sut = LipoExtract.live(runShellCommand: .init { command, _ in\n      didPerformActions.append(.didRunShellCommand(command))\n      return \"\"\n    })\n    let input = Path(\"input/file\")\n    let archs = [Arch.i386, .arm64]\n    let output = Path(\"output/file\")\n    let log = Log { level, message in\n      didPerformActions.append(.didLog(level, message))\n    }\n\n    try sut(input: input, archs: archs, output: output, log)\n\n    XCTAssertEqual(didPerformActions, [\n      .didLog(.normal, \"[LipoExtract]\"),\n      .didLog(.verbose, \"- input: \\(input.string)\"),\n      .didLog(.verbose, \"- archs: \\(archs.map(\\.rawValue).joined(separator: \", \"))\"),\n      .didLog(.verbose, \"- output: \\(input.string)\"),\n      .didRunShellCommand(\"lipo input/file -extract i386 -extract arm64 -output output/file\")\n    ])\n  }\n}\n"
  },
  {
    "path": "Tests/XCFrameworkMakerTests/Actions/LipoThinTests.swift",
    "content": "import XCTest\n@testable import XCFrameworkMaker\n\nfinal class LipoThinTests: XCTestCase {\n  enum Action: Equatable {\n    case didRunShellCommand(String)\n    case didLog(LogLevel, String)\n  }\n\n  func testHappyPath() throws {\n    var didPerformActions = [Action]()\n    let sut = LipoThin.live(runShellCommand: .init { command, _ in\n      didPerformActions.append(.didRunShellCommand(command))\n      return \"\"\n    })\n    let input = Path(\"input/file\")\n    let arch = Arch.arm64\n    let output = Path(\"output/file\")\n    let log = Log { level, message in\n      didPerformActions.append(.didLog(level, message))\n    }\n\n    try sut(input: input, arch: arch, output: output, log)\n\n    XCTAssertEqual(didPerformActions, [\n      .didLog(.normal, \"[LipoThin]\"),\n      .didLog(.verbose, \"- input: \\(input.string)\"),\n      .didLog(.verbose, \"- arch: \\(arch.rawValue)\"),\n      .didLog(.verbose, \"- output: \\(output.string)\"),\n      .didRunShellCommand(\"lipo input/file -thin arm64 -output output/file\")\n    ])\n  }\n}\n"
  },
  {
    "path": "Tests/XCFrameworkMakerTests/Actions/LogTests.swift",
    "content": "import XCTest\n@testable import XCFrameworkMaker\n\nfinal class LogTests: XCTestCase {\n  func testNormalLogging() {\n    var logged = [String]()\n    let sut = Log.live(\n      level: .normal,\n      print: { logged.append($0) }\n    )\n\n    sut(.normal, \"Normal level log\")\n    sut(.verbose, \"Verbose level log\")\n\n    XCTAssertEqual(logged, [\"Normal level log\"])\n  }\n\n  func testVerboseLogging() {\n    var logged = [String]()\n    let sut = Log.live(\n      level: .verbose,\n      print: { logged.append($0) }\n    )\n\n    sut(.normal, \"Normal level log\")\n    sut(.verbose, \"Verbose level log\")\n\n    XCTAssertEqual(logged, [\n      \"Normal level log\",\n      \"Verbose level log\"\n    ])\n  }\n\n  func testIndentedLogging() {\n    var logged = [String]()\n    let sut = Log.live(\n      level: .normal,\n      print: { logged.append($0) }\n    ).indented()\n\n    sut(.normal, \"multiline\\nlog\\nmessage\")\n\n    XCTAssertEqual(logged, [\"\\tmultiline\\n\\tlog\\n\\tmessage\"])\n  }\n}\n"
  },
  {
    "path": "Tests/XCFrameworkMakerTests/Actions/MakeXCFrameworkTests.swift",
    "content": "import XCTest\n@testable import XCFrameworkMaker\n\nfinal class MakeXCFrameworkTests: XCTestCase {\n  enum Action: Equatable {\n    case didCreateTempDir\n    case didGetArchs(Path)\n    case didCopyFramework(Path, [Arch], Path)\n    case didAddArm64Simulator(Path, Path)\n    case didCreateXCFramework([Path], Path)\n    case didLog(LogLevel, String)\n  }\n\n  func testHappyPath() throws {\n    var didPerformActions = [Action]()\n    let iOSPath = Path(\"ios/Framework.framework\")\n    let tvOSPath = Path(\"tvos/Framework.framework\")\n    let createdTempDir = Path(\"temp/path\")\n    let archs: [Path: [Arch]] = [\n      iOSPath: [.i386, .x86_64, .armv7, .arm64],\n      tvOSPath: [.x86_64, .arm64]\n    ]\n    let copiedFrameworks: [Path: Path] = [\n      iOSPath: Path(\"copy/ios/Framework.framework\"),\n      tvOSPath: Path(\"copy/tvos/Framework.framework\")\n    ]\n    let sut = MakeXCFramework.live(\n      createTempDir: .init { _ in\n        didPerformActions.append(.didCreateTempDir)\n        return createdTempDir\n      },\n      getArchs: .init { path, _ in\n        didPerformActions.append(.didGetArchs(path))\n        return archs[path]!\n      },\n      copyFramework: .init { input, archs, path, _ in\n        didPerformActions.append(.didCopyFramework(input, archs, path))\n        return copiedFrameworks[input]!\n      },\n      addArm64Simulator: .init { device, simulator, _ in\n        didPerformActions.append(.didAddArm64Simulator(device, simulator))\n      },\n      createXCFramework: .init { frameworks, path, _ in\n        didPerformActions.append(.didCreateXCFramework(frameworks, path))\n      }\n    )\n    let output = Path(\"output/path\")\n    let log = Log { level, message in\n      didPerformActions.append(.didLog(level, message))\n    }\n\n    try sut.callAsFunction(iOS: iOSPath, tvOS: tvOSPath, arm64sim: true, at: output, log)\n\n    XCTAssertEqual(didPerformActions, [\n      .didLog(.normal, \"[MakeXCFramework]\"),\n      .didLog(.verbose, \"- iOSPath: \\(iOSPath.string)\"),\n      .didLog(.verbose, \"- tvOSPath: \\(tvOSPath.string)\"),\n      .didLog(.verbose, \"- arm64sim: true\"),\n      .didLog(.verbose, \"- output: \\(output.string)\"),\n\n      .didCreateTempDir,\n\n      .didGetArchs(iOSPath),\n      .didCopyFramework(iOSPath, [.armv7, .arm64], createdTempDir.addingComponent(\"ios-device\")),\n      .didCopyFramework(iOSPath, [.i386, .x86_64], createdTempDir.addingComponent(\"ios-simulator\")),\n      .didAddArm64Simulator(copiedFrameworks[iOSPath]!, copiedFrameworks[iOSPath]!),\n\n      .didGetArchs(tvOSPath),\n      .didCopyFramework(tvOSPath, [.arm64], createdTempDir.addingComponent(\"tvos-device\")),\n      .didCopyFramework(tvOSPath, [.x86_64], createdTempDir.addingComponent(\"tvos-simulator\")),\n      .didAddArm64Simulator(copiedFrameworks[tvOSPath]!, copiedFrameworks[tvOSPath]!),\n\n      .didCreateXCFramework([\n        copiedFrameworks[iOSPath]!,\n        copiedFrameworks[iOSPath]!,\n        copiedFrameworks[tvOSPath]!,\n        copiedFrameworks[tvOSPath]!\n      ], output)\n    ])\n  }\n\n  func testEmptyInputFailure() {\n    let sut = MakeXCFramework.live(\n      createTempDir: .init { _ in fatalError() },\n      getArchs: .init { _, _ in fatalError() },\n      copyFramework: .init { _, _, _, _ in fatalError() },\n      createXCFramework: .init { _, _, _ in fatalError() }\n    )\n    var catchedError: Error?\n\n    do { try sut(iOS: nil, tvOS: nil, arm64sim: true, at: Path(\"\")) }\n    catch { catchedError = error }\n\n    XCTAssertEqual(catchedError as? MakeXCFramework.EmptyInputError, MakeXCFramework.EmptyInputError())\n  }\n}\n"
  },
  {
    "path": "Tests/XCFrameworkMakerTests/Actions/RunShellCommandTests.swift",
    "content": "import XCTest\n@testable import XCFrameworkMaker\n\nfinal class RunShellCommandTests: XCTestCase {\n  enum Action: Equatable {\n    case didShellOut(String)\n    case didLog(LogLevel, String)\n  }\n\n  func testHappyPath() throws {\n    var didPerformActions = [Action]()\n    let shellOutput = \"shell output\"\n    let sut = RunShellCommand.live(shellOut: { command in\n      didPerformActions.append(.didShellOut(command))\n      return shellOutput\n    })\n    let shellCommand = \"shell command\"\n    let log = Log { level, message in\n      didPerformActions.append(.didLog(level, message))\n    }\n\n    let result = try sut(shellCommand, log)\n\n    XCTAssertEqual(didPerformActions, [\n      .didLog(.normal, \"[RunShellCommand]\"),\n      .didLog(.verbose, \"- command: \\(shellCommand)\"),\n      .didShellOut(shellCommand),\n      .didLog(.verbose, \"- output: \\(shellOutput)\")\n    ])\n    XCTAssertEqual(result, shellOutput)\n  }\n}\n"
  },
  {
    "path": "Tests/XCFrameworkMakerTests/Models/LogLevelTests.swift",
    "content": "import XCTest\n@testable import XCFrameworkMaker\n\nfinal class LogLevelTests: XCTestCase {\n  func testComparable() {\n    XCTAssertTrue(LogLevel.normal < LogLevel.verbose)\n    XCTAssertTrue(LogLevel.normal <= LogLevel.verbose)\n    XCTAssertFalse(LogLevel.normal > LogLevel.verbose)\n    XCTAssertFalse(LogLevel.normal >= LogLevel.verbose)\n  }\n}\n"
  },
  {
    "path": "Tests/XCFrameworkMakerTests/Models/PathTests.swift",
    "content": "import XCTest\n@testable import XCFrameworkMaker\n\nfinal class PathTests: XCTestCase {\n  func testAddingComponent() {\n    XCTAssertEqual(Path(\"some/path\").addingComponent(\"component\").string, \"some/path/component\")\n    XCTAssertEqual(Path(\"some/path/\").addingComponent(\"component\").string, \"some/path/component\")\n  }\n\n  func testLastComponent() {\n    XCTAssertEqual(Path(\"some/file\").lastComponent, \"file\")\n    XCTAssertEqual(Path(\"some/file.extension\").lastComponent, \"file.extension\")\n    XCTAssertEqual(Path(\"some/directory/\").lastComponent, \"directory\")\n    XCTAssertEqual(Path(\"path\").lastComponent, \"path\")\n    XCTAssertEqual(Path(\"\").lastComponent, \"\")\n    XCTAssertEqual(Path(\"/\").lastComponent, \"\")\n    XCTAssertEqual(Path(\"//\").lastComponent, \"\")\n    XCTAssertEqual(Path(\"path/\").lastComponent, \"path\")\n    XCTAssertEqual(Path(\"path//\").lastComponent, \"path\")\n    XCTAssertEqual(Path(\"/path/\").lastComponent, \"path\")\n    XCTAssertEqual(Path(\"some/path///\").lastComponent, \"path\")\n  }\n\n  func testFileExtension() {\n    XCTAssertNil(Path(\"file\").fileExtension)\n    XCTAssertNil(Path(\"path/to/some/file\").fileExtension)\n    XCTAssertEqual(Path(\"file.ext\").fileExtension, \"ext\")\n    XCTAssertEqual(Path(\"file.name.ext\").fileExtension, \"ext\")\n    XCTAssertEqual(Path(\"path/to/file.ext\").fileExtension, \"ext\")\n    XCTAssertEqual(Path(\"/path/to/file.ext\").fileExtension, \"ext\")\n    XCTAssertEqual(Path(\"path/to/file.ext/\").fileExtension, \"ext\")\n    XCTAssertEqual(Path(\"/path/to/file.ext//\").fileExtension, \"ext\")\n  }\n\n  func testFilenameExcludingExtension() {\n    XCTAssertEqual(Path(\"file\").filenameExcludingExtension, \"file\")\n    XCTAssertEqual(Path(\"file.ext\").filenameExcludingExtension, \"file\")\n    XCTAssertEqual(Path(\"file.name.ext\").filenameExcludingExtension, \"file.name\")\n    XCTAssertEqual(Path(\"path/to/file.ext\").filenameExcludingExtension, \"file\")\n    XCTAssertEqual(Path(\"/path/to/file.ext\").filenameExcludingExtension, \"file\")\n    XCTAssertEqual(Path(\"path/to/file.ext/\").filenameExcludingExtension, \"file\")\n    XCTAssertEqual(Path(\"/path/to/file.ext//\").filenameExcludingExtension, \"file\")\n  }\n}\n"
  }
]