The Swift package deal manifest file


If you wish to be taught tips on how to use the Swift Package deal Supervisor it’s best to learn my different article, as a result of that’s extra like an introduction for individuals who have by no means labored with SPM but.

Package deal varieties

There are a number of package deal varieties that you may create with the swift package deal init command. You possibly can specify the --type flag with the next values: empty, library, executable, system-module, manifest. You too can outline a customized package deal title by way of the --name flag.

  • The empty package deal will create the default file construction with out the pattern code recordsdata.
  • The library kind will create a reusable library product template.
  • The executable kind will create a Swift software with an executable product definition within the package deal and a most important.swift file as a place to begin.
  • The system-module kind will create a wrapper round a system supplied package deal, akin to libxml, we’ll speak about this in a while.
  • The manifest kind will solely create a Package deal.swift file with out the rest.

The Package deal manifest file

Each single SPM venture has this particular file inside it known as Package deal.swift. I already wrote a put up about how the package deal supervisor and the Swift toolchain works behind the scenes, this time we will focus solely on the manifest file itself. Let’s get began. 📦

Each single Package deal.swift file begins with a particular remark line the place it’s a must to outline the model of the used Swift instruments. The newest model is kind of completely different from the older ones.

Subsequent it's a must to import the PackageDescription framework as a way to outline your Swift package deal. This framework comprises the package deal manifest construction as Swift objects.

import PackageDescription

That is it now you might be prepared to explain the package deal itself. Oh by the way in which you may change the model of the used instruments, you may learn extra about this within the Package deal Supervisor utilization docs.

Package deal

A package deal is only a bunch of Swift (or different) recordsdata. The manifest file is the outline of what and tips on how to construct from these sources. Each single package deal ought to have a reputation, however this isn't sufficient to truly generate one thing from it. You possibly can solely have precisely one package deal definition contained in the file. That is the shortest and most ineffective one that you may create. 🙈

let package deal = Package deal(title: "myPackage")

The package deal title goes for use if you end up importing packages as dependencies, so title your pacages fastidiously. In the event you select a reserved title by a system framework there will be points with linking. If there is a battle it's a must to use static linking as an alternative of dynamic. In the event you generate a venture by way of the swift package deal generate-xcodeproj command that venture will attempt to hyperlink every part dynamically, however in case you open the Package deal.swift file utilizing Xcode 11, the dependencies can be linked statically if this was not set explicitly within the product definition part.

Platform

A platform is mainly an working system with a given model that you may help.

let package deal = Package deal(
    title: "myPackage",
    platforms: [
        .iOS(.v13),         
        .macOS(.v10_15),    
        .tvOS(.v13),        
        .watchOS(.v6),      
    ]
)

Once you add a platform you might be placing a constraint on it by way of the required model. Each single dependency ought to match the requirement of the primary package deal platforms. Lengthy story quick if you should add help for Apple platforms, it's best to specify a platform flag with a supported model, in any other case SPM will use the oldest deployment goal based mostly on the put in SDK, aside from macOS, that is going to be v10_10. Each package deal has Linux help by default, you may't add such restrictions but, however possibly it will change within the close to future, additionally Home windows is coming.

Product

A package deal can have a number of ultimate merchandise (construct artifacts). At the moment there are two varieties of construct merchandise: executables and libraries. The executable is a binary that may be executed, for instance this is usually a command line software. A library is one thing that others can use, it's mainly the general public API product illustration in your targets.


import PackageDescription

let package deal = Package deal(title: "myPackage", merchandise: [
    .library(name: "myPackageLib", targets: ["myPackageLib"]),
    .library(title: "myPackageStaticLib", kind: .static, targets: ["myPackageLib"]),
    .library(title: "myPackageDynLib", kind: .dynamic, targets: ["myPackageLib"]),
    .executable(title: "myPackageCli", targets: ["myPackage"])
], targets: [
    .target(name: "myPackageLib"),
    .target(name: "myPackageCli"),
])

If the library kind is unspecified, the Package deal Supervisor will routinely select it based mostly on the shopper's choice. As I discussed this earlier generated Xcode tasks choose dynamic linking, however in case you merely open the manifest file the app can be statically linked.

Dependency

Packages can depend on different packages. You possibly can outline your dependencies by specifying an area path or a repository URL with a given model tag. Including a dependency into this part isn't sufficient to make use of it in your targets. You even have so as to add the product supplied by the package deal on the goal degree.

let package deal = Package deal(
    title: "myPackage",
    dependencies: [
        .package(path: "/local/path/to/myOtherPackage"),
        .package(url: "<git-repository-url>", from: "1.0.0"),
        .package(url: "<git-repository-url>", .branch("dev")),
        .package(url: "<git-repository-url>", .exact("1.3.2")),
        .package(url: "<git-repository-url>", .revision("<hash>")),
        .package(url: "<git-repository-url>", .upToNextMajor(from: "1.0.0")),
        .package(url: "<git-repository-url>", .upToNextMinor(from: "1.0.0")),
        .package(url: "<git-repository-url>", "1.0.0"..<"1.3.0"),
    ]
)

The URL is usually a GitHub URL, happily you may add personal repositories as properly through the use of an ssh key based mostly authentication. Simply use the [email protected]:BinaryBirds/viper-kit.git URL format, as an alternative of the HTTP based mostly, if you wish to add personal packages. 🤫

Goal

A goal is one thing that you may construct, in different phrases it is a construct goal that may end up in a library or an executable. It is best to have not less than one goal in your venture file in any other case you may't construct something. A goal ought to all the time have a reputation, each different settings is elective.

Settings

There are lots of settings that you should use to configure your goal. Targets can rely on different targets or merchandise outlined in exterior packages. A goal can have a customized location, you may specify this by setting the trail attribute. Additionally you may exclude supply recordsdata from the goal or explicitly outline the sources you need to use. Targets can have their very own public headers path and you'll present construct settings each for the C, C++ and the Swift language, and compiler flags.

.goal(title: "myPackage",
        dependencies: [
            .target(name: "other"),
            .product(name: "package", package: "package-kit")
        ],
        path: "./Sources/myPackage",
        exclude: ["foo.swift"],
        sources: ["main.swift"],
        publicHeadersPath: "./Sources/myPackage/headers",
        cSettings: [
            .define("DEBUG"),
            .define("DEBUG", .when(platforms: [.iOS, .macOS, .tvOS, .watchOS], configuration: .debug)),
            .outline("DEBUG", to: "yes-please", .when(platforms: [.iOS], configuration: .debug)),
            .headerSearchPath(""),
            .headerSearchPath("", .when(platforms: [.android, .linux, .windows], configuration: .launch)),
            .unsafeFlags(["-D EXAMPLE"]),
            .unsafeFlags(["-D EXAMPLE"], .when(platforms: [.iOS], configuration: .debug)),
        ],
        cxxSettings: [
            
        ],
        swiftSettings: [
            .define("DEBUG"),
            .define("DEBUG", .when(platforms: [.iOS, .macOS, .tvOS, .watchOS], configuration: .debug)),
            .unsafeFlags(["-D EXAMPLE"]),
            .unsafeFlags(["-D EXAMPLE"], .when(platforms: [.iOS], configuration: .debug)),
        ],
        linkerSettings: [
            .linkedFramework("framework"),
            .linkedLibrary("framework", .when(platforms: [.iOS], configuration: .debug)),
            .linkedLibrary("library"),
            .linkedLibrary("library", .when(platforms: [.macOS], configuration: .launch)),
            .unsafeFlags(["-L example"]),
            .unsafeFlags(["-L example"], .when(platforms: [.linux], configuration: .launch)),
        ]),

As you may see you may outline preprocessor macros for each single language. You should use the secure instances for primary stuff, however there's an unsafeFlags case for the reckless ones. The good factor is that you may help a platform situation filter together with construct configuration to each single settings because the final param.

Accessible platforms are:

  • .iOS
  • .macOS
  • .watchOS
  • .tvOS
  • .android
  • .linux
  • .home windows

The construct configuration will be .debug or .launch

Check targets

Check targets are used to outline check suites. They can be utilized to unit check different targets utilizing the XCTest framework. They seem like precisely the identical as common targets.

.testTarget(title: String,
    dependencies: [Target.Dependency],
    path: String?,
    exclude: [String],
    sources: [String]?,
    cSettings: [CSetting]?,
    cxxSettings: [CXXSetting]?,
    swiftSettings: [SwiftSetting]?,
    linkerSettings: [LinkerSetting]?)

I believe the one distinction between a goal and a check goal is that you may run a check goal utilizing the swift check command, however from a structural viewpoint, they're mainly the identical.

Package deal configs and system libraries

You possibly can wrap an present system library utilizing Swift, the fantastic thing about that is that you should use packages written in C, CPP or different languages. I am going to present you a fast instance by way of the superb Kanna(鉋) - XML/HTML parser repository. I am utilizing this device so much, thanks for making it Atsushi Kiwaki. 🙏


#if swift(>=5.2) && !os(Linux)
let pkgConfig: String? = nil
#else
let pkgConfig = "libxml-2.0"
#endif

#if swift(>=5.2)
let suppliers: [SystemPackageProvider] = [
    .apt(["libxml2-dev"])
]
#else
let suppliers: [SystemPackageProvider] = [
    .apt(["libxml2-dev"]),
    .brew(["libxml2"])
]
#endif

let package deal = Package deal(title: "Kanna",
pkgConfig: "",
suppliers: [
  .apt(["libsqlite-dev"]),
  .brew(["sqlite3"])
],
merchandise: [
  .library(name: "Kanna", targets: ["Kanna"])
],
targets: [
.target(name: "myPackage"),
.systemLibrary(name: "libxml2",
               path: "Modules",
               pkgConfig: pkgConfig,
               providers: providers)
])

There's a module definition file on the Modules listing. You will want a module.modulemap file to export a given library, you may learn extra about Modules on the LLVM web site.

module libxml2 [system] {
    hyperlink "xml2"
    umbrella header "libxml2-kanna.h"
    export *
    module * { export * }
}

You possibly can outline your individual umbrella header and inform the system what to import.

#import <libxml2/libxml/HTMLtree.h>
#import <libxml2/libxml/xpath.h>
#import <libxml2/libxml/xpathInternals.h>

I barely use system libraries, however this can be a good reference level. In any case, if you should wrap a system library I assume that you will have the required information to make it occur. 😅

Language settings

You too can specify the listing of Swift verisons that the package deal is appropriate with. If you're making a package deal that comprises C or C++ code you may inform the compiler to make use of a particular language normal through the construct course of.


swiftLanguageVersions: [.v4, .v4_2, .v5, .version("5.1")],


cLanguageStandard: .c11,


cxxLanguageStandard: .gnucxx11)

You possibly can see all of the presently out there choices within the feedback. I do not know what number of of you employ these directives, however personally I by no means needed to work with them. I am not writing an excessive amount of code from the C language household these days, but it surely's nonetheless good that SPM has this feature built-in. 👍

Abstract

The Swift Package deal Supervisor isn't the right device simply but, but it surely's on monitor to change into the de facto normal by slowly changing CocoaPods and Carthage. There are nonetheless some lacking options which might be necessities for a lot of the builders. Don't fret, SPM will enhance so much within the close to future. For instance the binary dependency and useful resource help is coming alongside Swift 5.3. You possibly can monitor the package deal evolution course of on the official Swift Evolution dashboard.

You possibly can learn extra concerning the Package deal Supervisor on the official Swift web site, but it surely's fairly obsolate. The documentation on Apple's web site can be very outdated, however nonetheless helpful. There's a good learn me file on GitHub concerning the utilization of the Swift Package deal Supervisor, however nothing is up to date ceaselessly. 😢

Recent Articles

Related Stories

Leave A Reply

Please enter your comment!
Please enter your name here

Stay on op - Ge the daily news in your inbox