# Use Swift Package Manager in your own Swift Package

As a Swift developer, you are probably aware of Apple's [Swift Package Manager](https://swift.org/package-manager/) (SPM) as a command-line interface to create and work with Swift packages 📦.

But did you know that you can also use Swift Package Manager as a package dependency in your own Swift Package?

Yes, you can 😊 because SPM is also a package that exposes the library `SwiftPM` under the same product name. The community generally refers to it as `libSwiftPM` and I will continue to use that term in this blog post.

For example, it is possible to create an executable Swift package in which you can programmatically access manifest information from other Swift packages on your local machine and print that information in your terminal.

The  [GitHub repository](https://github.com/apple/swift-package-manager) of Swift Package Manager contains such an example: [package-info](https://github.com/apple/swift-package-manager/tree/main/Examples/package-info)

However, the package dependency to `libSwiftPM` is declared a local, relative package. We want to use `libSwiftPM` as a remote package dependency, so let's re-build the example.

First, we create a folder and name it `swift-package-info`. Then we use Swift Package Manager to create our executable package.

```Swift
swift package init --type executable
```

> **IMPORTANT**: The description below was written using Swift Package Manager for Swift 5.3.

> In November 2022 I updated my [swift-package-info](https://github.com/MarcoEidinger/swift-package-info) to use and be compatible with Swift 5.7

Now let's add Swift Package Manager as a dependency to our package. There is a caveat for Swift 5.3 as libSwiftPM does not have a version tagged for 5.3 (status: March 2021)

Thankfully there is a community fork that has a version tagged and otherwise no modifications.

```Swift
.package(name: "SwiftPM", url: "https://github.com/SDGGiesbrecht/swift-package-manager.git", .exact("0.50302.0")),
```

The complete manifest of our package

```Swift

// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription

let package = Package(
    name: "swift-package-info",
    dependencies: [
        // Dependencies declare other packages that this package depends on.
        .package(name: "SwiftPM", url: "https://github.com/SDGGiesbrecht/swift-package-manager.git", .exact("0.50302.0")),
    ],
    targets: [
        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
        // Targets can depend on other targets in this package, and on products in packages this package depends on.
        .target(
            name: "swift-package-info",
            dependencies: ["SwiftPM"]),
        .testTarget(
            name: "swift-package-infoTests",
            dependencies: ["swift-package-info"]),
    ]
)
```

Now we can make use of modules in `libSwiftPM` to access several levels of information. In this blog post I'll focus on the manifest itself. 

The `ManifestLoader` of module `PackageLoading` is a utility class for reading the manifest data and produce a properly formed `PackageModel.Manifest` object.  Hence, we import the necessary module in our `main.swift` file

```Swift
import PackageLoading
```

The API requires knowing where the Swift compiler is. The following code snippet(s) are straight from the  [original example](https://github.com/apple/swift-package-manager/blob/main/Examples/package-info/Sources/package-info/main.swift) and some are slightly simplified. 

```
// PREREQUISITES
// ============
// We will need to know where the Swift compiler is.
let swiftCompiler: AbsolutePath = {
    let string: String
    #if os(macOS)
    string = try! Process.checkNonZeroExit(args: "xcrun", "--sdk", "macosx", "-f", "swiftc").spm_chomp()
    #else
    string = try! Process.checkNonZeroExit(args: "which", "swiftc").spm_chomp()
    #endif
    return AbsolutePath(string)
}()
```

For sake of simplification let's assume that our executable Swift Package will run in a directory in which a root package is available. 

```Swift
// We need a package to work with.
// This assumes there is one in the current working directory:
let packagePath = localFileSystem.currentWorkingDirectory!
```

With the path to the package in question as well as the Swift compiler we can finally load the manifest.

```Swift
let diagnostics = DiagnosticsEngine()
let manifest = try ManifestLoader.loadManifest(packagePath: packagePath, swiftCompiler: swiftCompiler, packageKind: .root)
```

Now we can programmatically access information like products or targets.

```Swift
// Manifest
let products = manifest.products.map({ $0.name }).joined(separator: ", ")
print("Products:", products)
let targets = manifest.targets.map({ $0.name }).joined(separator: ", ")
print("Targets:", targets)
```

The full source code, including other APIs, is available here.

%[https://github.com/MarcoEidinger/swift-package-info]
