Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift package dependency with unsafe build flags: target integrity error

TL;DR

I am getting the following error for a local Swift package dependency when attempting to use it within an iOS project in Xcode:

The package product 'DlibWrapper' cannot be used as a dependency of this target because it uses unsafe build flags.

(I am using unsafe flags to specify static library location)

I also tried to import the package as a remote branch-based dependency but it fails anyway.

According to this post on Swift forums the issue was addressed a while ago and a corresponding pull request was already merged.

The problem appears both with Swift 5.2.4 (Xcode 11.6) and 5.3 (Xcode 12 beta 3).

Any clues what could be the issue?


Details

I am trying to build a Swift package that wraps dlib library and use it within an iOS app. Because of the platform I can't use the .systemLibrary target to link the dlib. So I precompiled it in a static lib and packaged together with the wrapper code like so:

DlibWrapper/
  Libraries/
    dlib/
      include/
          ...
      lib/
        arm64/
          libdlib.a

  Sources/
    CWrapper/
      include/
        module.modulemap
        cwrapper.h
      cwrapper.cpp

    SwiftWrapper/
      SwiftWrapper.swift

  Package.swift

Simplified contents of the DlibWrapper/Package.swift:

// swift-tools-version:5.3

import PackageDescription

let package = Package(
    name: "DlibWrapper",
    platforms: [
        .iOS(.v13)
    ],
    products: [
        .library(
            name: "DlibWrapper",
            targets: ["CWrapper", "SwiftWrapper"])
    ],
    dependencies: [],
    targets: [
        .target(
            name: "SwiftWrapper",
            dependencies: ["CWrapper"]
        ),
        .target(
            name: "CWrapper",

            cxxSettings: [.headerSearchPath("../../Libraries/dlib/include")],

            linkerSettings: [
                .linkedLibrary("dlib"),
                .linkedFramework("Accelerate", .when(platforms: [.iOS])),

                // The error is caused by this line
                .unsafeFlags(["-LLibraries/dlib/lib/arm64"], .when(platforms: [.iOS])),
            ]
        ),
    ],
    cxxLanguageStandard: .cxx1z
)

I tried to use the link property inside the module.modulemap but compiler seems to ignore it. Also, giving an absolute path to the library in .linkedLibrary() in target manifest doesn't help, the linker complains it can't find the library.

Any ideas for workarounds? (As a last resort I would probably package everything in a framework)

Would appreciate any help.

Thanks

like image 484
Boris Avatar asked Jul 23 '20 22:07

Boris


1 Answers

Update

Turns out the method below doesn't actually work. Swift completely ignores .linkedLibrary when the package is compiled as a static library (check with swift build --verbose). The setting is only used with dynamic libraries. Nevertheless, the : syntax does nothing in the latter case — the library cannot be found anyway.

(The answer below is kept for historical reasons, maybe it helps someone to find a solution)


Old answer

The workaround is to use the clang/gcc colon syntax in linker options, i.e. -l: (minus-el-colon). See this answer for more details.

Here is the relevant snippet from the package manifest:

// swift-tools-version:5.3

import PackageDescription

let package = Package(
    name: "DlibWrapper",
    //...
    targets: [
        //...
        .target(
            name: "CWrapper",

            cxxSettings: [.headerSearchPath("../../Libraries/dlib/include")],

            linkerSettings: [
                // Note the `:` in front of the relative path
                .linkedLibrary(":Libraries/dlib/lib/arm64/libdlib.a", .when(platforms: [.iOS])),
                //...
            ]
        ),
    ],
    //...
)

Explanation

The .linkedLibrary linker setting essential provides the -l flag to the linker. So, whatever you put there as a parameter will be translated into something like this

swift build -Xlinker -l<your-linkedLibrary-string>

The : syntax allows one to specify a library with a non-canonical name, i.e. the actual file name. Finally, because the linker's work directory is set to package root we can supply it with the relative path to the library.

I still consider this as a workaround because the right way would be to have something like librarySearchPath, i.e. -L linker option, analogous to headerSearchPath in cxxSettings.

like image 126
Boris Avatar answered Nov 10 '22 00:11

Boris