Cleanly handling /usr/local/ with Swift package ma

2019-06-07 21:35发布

问题:

I have 2 dependencies in my project libevent and libressl. Both of which are installed locally ( respectively under /usr/local/include and /usr/local/opt/libressl/include )

What I am looking for to achieve is for SPM to automatically understand to search in those directories.

I know I can pass flags to swift build to achieve this; but my ultimate goal is that I can properly generate xcode projects from the command line without having to constantly add custom build flags in Xcode.

I'm pretty sure it is possible, since I do not have to enter the custom settings for PostgreSQL.

Swift-tools version is at 4.0.x

Package.swift for reference:

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

import PackageDescription

let package = Package(
    name: "CEvent",
    providers: [
        .brew(["libevent"]),
        .apt(["libevent-dev"])
    ],
    products: [
        // Products define the executables and libraries produced by a package, and make them visible to other packages.
        .library(
            name: "CEvent",
            targets: ["CEvent"]),
        ],
    dependencies: [
    ],
    targets: [
        .target(
            name: "CEvent",
            dependencies: []
        ),
    ]
)

Module map:

module CEvent [system] {
    header "shim.h"
    link "event"
    export *
}

And my current build script ( build.sh ):

#!/usr/local/bin/fish
swift build -Xcc -O0 -Xcc -fblocks -Xswiftc -lbcrypt -Xswiftc -I/usr/local/include -Xswiftc -L/usr/local/lib -Xswiftc -ltls -Xswiftc -lcrypto -Xswiftc -lssl -Xswiftc -L/usr/local/opt/postgresql/lib -Xswi$

As for the reason that I want this. If I add/update/remove dependencies in swift I want to generate a new xcode project, and not have to fix its settings on respective build machines; as well as apt/ubuntu /usr/lib instead.

回答1:

What you've found out, and documented in your answer, is a good start but not the full story. Yes, SwiftPM uses pkg-config to determine where certain libraries are installed. Yes, SwiftPM uses the pkgConfig name which it'll pass on to pkg-config. However the search paths are a bit more involved. On macOS it uses the following list as a base search path:

  • /usr/local/lib/pkgconfig
  • /usr/local/share/pkgconfig
  • /usr/lib/pkgconfig
  • /usr/share/pkgconfig
  • PKG_CONFIG_PATH environment variable

However SwiftPM doesn't use the pkg-config command, but instead parses .pc files directly. By setting the pkgConfig parameter on your package, it knows what filename to look for in the paths listed above. And, for the example in your answer, the story stops here. If there's a libevent.pc file found it parses that file, and any flags returned are passed on to the compiler and linker.

However if you were to define package providers, e.g.:

providers: [
    .Brew("libsodium"),
    .Apt("libsodium-dev")
]

Then SwiftPM adds additional search paths depending on the package provider for the platform it is building for. Continuing the example of macOS, SwiftPM will run brew --prefix. If this returns a path, the following path is added as a additional search path:

  • [brewPrefix]/opt/[packageName]/lib/pkgconfig

In my example of libsodium, SwiftPM is now able to infer the location of the library without requiring brew link or symlinks at all. In my verbose build output it lists the libsodium library path in my cellar: -L/usr/local/Cellar/libsodium/1.0.11/lib.



回答2:

Alright so the thing I ignored from analysing other projects ( IBM-Swift/CLibpq in particular ) seems to be making use of the tool pkg-config which is not something I personally ever touched before.

pkg-config looks in /usr/lib/pkgconfig /usr/share/pkgconfig and the local variants for config files used in during the build process.

Inside Package.swift, after the name parameter you need to insert something for example:

let package = Package(
    name: "CEvent",
    pkgConfig: "libevent",

Some caveats I discovered with this:

  • The bcrypt library I am using does not have a full fletched install or build inside the makefile so I compiled it using the new options found in swift4 PM instead found here: BCrypt example on github and the Swift docs for more help here: SPM API Redesign
  • LibreSSL found in Homebrew will not install its pkgconfig on the system; so it is easiest or in my eyes best maintenance wise to either manually add it or to compile LibreSSL-portable from source.

Overall great learning experience for me today.