Skip to content

PackageLoading: support non-Unix platforms for PkgConfig #5517

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions Sources/PackageLoading/PkgConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,11 @@ public struct PkgConfig {

private static var envSearchPaths: [AbsolutePath] {
if let configPath = ProcessEnv.vars["PKG_CONFIG_PATH"] {
#if os(Windows)
return configPath.split(separator: ";").map({ AbsolutePath(String($0)) })
#else
return configPath.split(separator: ":").map({ AbsolutePath(String($0)) })
#endif
}
return []
}
Expand Down Expand Up @@ -174,7 +178,7 @@ internal struct PkgConfigParser {
variables["pcfiledir"] = pcFile.parentDirectory.pathString

// Add pc_sysrootdir variable. This is the path of the sysroot directory for pc files.
variables["pc_sysrootdir"] = ProcessEnv.vars["PKG_CONFIG_SYSROOT_DIR"] ?? "/"
variables["pc_sysrootdir"] = ProcessEnv.vars["PKG_CONFIG_SYSROOT_DIR"] ?? AbsolutePath.root.pathString

let fileContents: String = try fileSystem.readFileContents(pcFile)
for line in fileContents.components(separatedBy: "\n") {
Expand Down Expand Up @@ -382,18 +386,24 @@ internal struct PCFileFinder {
///
/// This is needed because on Linux machines, the search paths can be different
/// from the standard locations that we are currently searching.
public init(brewPrefix: AbsolutePath? = .none) {
public init(brewPrefix: AbsolutePath? = .none, pkgConfig: AbsolutePath? = .none) {
if PCFileFinder.pkgConfigPaths == nil {
do {
let pkgConfigPath: String
if let brewPrefix = brewPrefix {
if let pkgConfig = pkgConfig {
pkgConfigPath = pkgConfig.pathString
} else if let brewPrefix = brewPrefix {
pkgConfigPath = brewPrefix.appending(components: "bin", "pkg-config").pathString
} else {
pkgConfigPath = "pkg-config"
}
let searchPaths = try Process.checkNonZeroExit(
args: pkgConfigPath, "--variable", "pc_path", "pkg-config").spm_chomp()
#if os(Windows)
PCFileFinder.pkgConfigPaths = searchPaths.split(separator: ";").map { AbsolutePath(String($0)) }
#else
PCFileFinder.pkgConfigPaths = searchPaths.split(separator: ":").map({ AbsolutePath(String($0)) })
#endif
} catch {
PCFileFinder.shouldEmitPkgConfigPathsDiagnostic = true
PCFileFinder.pkgConfigPaths = []
Expand Down
40 changes: 27 additions & 13 deletions Tests/PackageLoadingTests/PkgConfigParserTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ final class PkgConfigParserTests: XCTestCase {
"exec_prefix": "/usr/local/Cellar/gtk+3/3.18.9",
"targets": "quartz",
"pcfiledir": parser.pcFile.parentDirectory.pathString,
"pc_sysrootdir": "/"
"pc_sysrootdir": AbsolutePath.root.pathString
])
XCTAssertEqual(parser.dependencies, ["gdk-3.0", "atk", "cairo", "cairo-gobject", "gdk-pixbuf-2.0", "gio-2.0"])
XCTAssertEqual(parser.privateDependencies, ["atk", "epoxy", "gio-unix-2.0"])
Expand All @@ -58,7 +58,7 @@ final class PkgConfigParserTests: XCTestCase {
"prefix": "/usr/local/bin",
"exec_prefix": "/usr/local/bin",
"pcfiledir": parser.pcFile.parentDirectory.pathString,
"pc_sysrootdir": "/"
"pc_sysrootdir": AbsolutePath.root.pathString
])
XCTAssertEqual(parser.dependencies, ["gdk-3.0", "atk"])
XCTAssertEqual(parser.cFlags, [])
Expand All @@ -73,7 +73,7 @@ final class PkgConfigParserTests: XCTestCase {
"exec_prefix": "/usr/local/bin",
"my_dep": "atk",
"pcfiledir": parser.pcFile.parentDirectory.pathString,
"pc_sysrootdir": "/"
"pc_sysrootdir": AbsolutePath.root.pathString
])
XCTAssertEqual(parser.dependencies, ["gdk-3.0", "atk"])
XCTAssertEqual(parser.cFlags, ["-I"])
Expand All @@ -97,7 +97,7 @@ final class PkgConfigParserTests: XCTestCase {
"exec_prefix": "/usr/local/bin",
"my_dep": "atk",
"pcfiledir": parser.pcFile.parentDirectory.pathString,
"pc_sysrootdir": "/"
"pc_sysrootdir": AbsolutePath.root.pathString
])
XCTAssertEqual(parser.dependencies, ["gdk-3.0", "atk"])
XCTAssertEqual(parser.cFlags, ["-I/usr/local/Wine Cellar/gtk+3/3.18.9/include/gtk-3.0", "-I/after/extra/spaces"])
Expand All @@ -117,22 +117,22 @@ final class PkgConfigParserTests: XCTestCase {
"/usr/local/opt/foo/lib/pkgconfig/foo.pc",
"/custom/foo.pc")
XCTAssertEqual(
"/custom/foo.pc",
try PCFileFinder().locatePCFile(name: "foo", customSearchPaths: [AbsolutePath("/custom")], fileSystem: fs, observabilityScope: observability.topScope).pathString
AbsolutePath("/custom/foo.pc"),
try PCFileFinder().locatePCFile(name: "foo", customSearchPaths: [AbsolutePath("/custom")], fileSystem: fs, observabilityScope: observability.topScope)
)
XCTAssertEqual(
"/custom/foo.pc",
try PkgConfig(name: "foo", additionalSearchPaths: [AbsolutePath("/custom")], fileSystem: fs, observabilityScope: observability.topScope).pcFile.pathString
AbsolutePath("/custom/foo.pc"),
try PkgConfig(name: "foo", additionalSearchPaths: [AbsolutePath("/custom")], fileSystem: fs, observabilityScope: observability.topScope).pcFile
)
XCTAssertEqual(
"/usr/lib/pkgconfig/foo.pc",
AbsolutePath("/usr/lib/pkgconfig/foo.pc").pathString,
try PCFileFinder().locatePCFile(name: "foo", customSearchPaths: [], fileSystem: fs, observabilityScope: observability.topScope).pathString
)
try withCustomEnv(["PKG_CONFIG_PATH": "/usr/local/opt/foo/lib/pkgconfig"]) {
XCTAssertEqual("/usr/local/opt/foo/lib/pkgconfig/foo.pc", try PkgConfig(name: "foo", fileSystem: fs, observabilityScope: observability.topScope).pcFile.pathString)
XCTAssertEqual(AbsolutePath("/usr/local/opt/foo/lib/pkgconfig/foo.pc"), try PkgConfig(name: "foo", fileSystem: fs, observabilityScope: observability.topScope).pcFile)
}
try withCustomEnv(["PKG_CONFIG_PATH": "/usr/local/opt/foo/lib/pkgconfig:/usr/lib/pkgconfig"]) {
XCTAssertEqual("/usr/local/opt/foo/lib/pkgconfig/foo.pc", try PkgConfig(name: "foo", fileSystem: fs, observabilityScope: observability.topScope).pcFile.pathString)
XCTAssertEqual(AbsolutePath("/usr/local/opt/foo/lib/pkgconfig/foo.pc"), try PkgConfig(name: "foo", fileSystem: fs, observabilityScope: observability.topScope).pcFile)
}
}

Expand All @@ -141,19 +141,33 @@ final class PkgConfigParserTests: XCTestCase {
PCFileFinder.resetCachedPkgConfigPaths()

try testWithTemporaryDirectory { tmpdir in
#if os(Windows)
let fakePkgConfig = tmpdir.appending(components: "bin", "pkg-config.cmd")
#else
let fakePkgConfig = tmpdir.appending(components: "bin", "pkg-config")
#endif
try localFileSystem.createDirectory(fakePkgConfig.parentDirectory)

let stream = BufferedOutputByteStream()
#if os(Windows)
stream <<< """
@echo off
echo /Volumes/BestDrive/pkgconfig
"""
#else
stream <<< """
#!/bin/sh
echo "/Volumes/BestDrive/pkgconfig"
"""
#endif
try localFileSystem.writeFileContents(fakePkgConfig, bytes: stream.bytes)
// `FileSystem` does not support `chmod` on Linux, so we shell out instead.
_ = try Process.popen(args: "chmod", "+x", fakePkgConfig.pathString)
try localFileSystem.chmod(.executable, path: fakePkgConfig, options: [])

#if os(Windows)
_ = PCFileFinder(pkgConfig: fakePkgConfig)
#else
_ = PCFileFinder(brewPrefix: fakePkgConfig.parentDirectory.parentDirectory)
#endif
}

XCTAssertEqual(PCFileFinder.pkgConfigPaths, [AbsolutePath("/Volumes/BestDrive/pkgconfig")])
Expand Down