Referring to libxml2 in Swift Package Description

At least since WWDC'19, many devs have been turning their former Cocoapods into full Swift Packages thanks to the SPM being integrated into Xcode. My current implementation issue is the following: I'm migrating the Fuzi Pod from Cocoapods to SPM (see https://github.com/cezheng/Fuzi/pull/101). This package wraps libxml2 to provide an excellent HTML parser for Swift. To make things simple, I create a Package with a Fuzi target with the following:

.target(name: "Fuzi",
    path: "Sources",
    cSettings: [.headerSearchPath("$(SDKROOT)/usr/include/libxml2")],
    linkerSettings: [.linkedLibrary("xml2")]
)

This worked up until Xcode 11b4 because it did not yet enforce the .headerSearchPath to be exclusively relative. After that, it seemed impossible to point to libxml2 in iOS projects because $(SDKROOT) could not be used anymore. It goes without saying that all unsafe options were looked at but not considered since Xcode won't even look at packages using them.

My next idea was using a .systemLibrary target. For context, the SPM PackageDescription has been updated for Swift 5/5.1 as well, including changes to how "System libraries" should be declared but this has not followed the new availability of SPM for iOS projects. Indeed, Usage.md still refers to system packages which have been deprecated some time ago (https://github.com/apple/swift-package-manager/blob/master/Documentation/Usage.md#requiring-system-libraries). In the description for PackageDescription, the .systemLibrary target type is referred to but just as the system packages of yore, require to be installed via homebrew (https://github.com/apple/swift-package-manager/blob/master/Documentation/PackageDescription.md#methods-5). This makes no sense on non-macOS or Linux platforms. And it does not make any sense either that such system libraries should be nearly inaccessible under iOS. My newest try is having that target:

.systemLibrary(
            name: "libxmlFuzi",
            path: "Module")

Of course the main Fuzi target depends on libxmlFuzi. The Module folder contains this modulemap:

module libxmlFuzi [system] {
    link "xml2"
    umbrella header "libxml2-fuzi.h"
    export *
    module * { export * }
}

And the following umbrella header:

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

My question to the Swift community is the following: how can I possibly get this to build and be used in other projects? It did build at random once or twice, but hasn't since, and never built when include in another Package/project. Any help is greatly appreciated!

EDIT: To clarify, the build errors when building Fuzi are the following, for all Swift files contained in the Package:

<module-includes>:1:9: note: in file included from <module-includes>:1:
#import "libxml/HTMLparser.h"
        ^
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.0.sdk/usr/include/libxml2/libxml/HTMLparser.h:15:10: error: 'libxml/xmlversion.h' file not found
#include <libxml/xmlversion.h>
         ^
/Users/xxx/Documents/Projects/Fuzi/Sources/Document.swift:23:8: error: could not build Objective-C module 'libxml2'
import libxml2
       ^
2 Likes

Unfortunately there is no way to properly reference libxml2 in a Swift package, but we are aware of this issue.

I am afraid the only workaround right now is copying and patching the libxml headers but that might cause other issues if your copy of headers get out-of-sync with the ones in SDK :confused:

That would indeed be a solution, but a very dirty one. IMO the SPM should have .systemLibrary usable in broader contexts such as iOS. Using .systemLibrary(name: "libxml2") would for example point to the headers of libxml2 in each SDK and also incorporate the linking steps.

Is there a swift-evolution proposal already or a pitch at least?

For the record, here is a previous discussion: Dependency only for Linux?

Terms of Service

Privacy Policy

Cookie Policy