Skip to content

Bring PkgConfig parsing to greater parity with pkg-config #2094

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

Merged
merged 11 commits into from
Sep 20, 2019
31 changes: 21 additions & 10 deletions Sources/TSCUtility/PkgConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -143,26 +143,31 @@ public struct PkgConfig {
var parser = PkgConfigParser(pcFile: pcFile, fileSystem: fileSystem)
try parser.parse()

var cFlags = parser.cFlags
var libs = parser.libs

// If parser found dependencies in pc file, get their flags too.
if !parser.dependencies.isEmpty {
for dep in parser.dependencies {
func getFlags(from dependencies: [String]) throws -> (cFlags: [String], libs: [String]) {
var cFlags = [String]()
var libs = [String]()

for dep in dependencies {
// FIXME: This is wasteful, we should be caching the PkgConfig result.
let pkg = try PkgConfig(
name: dep,
additionalSearchPaths: additionalSearchPaths,
diagnostics: self.diagnostics,
diagnostics: diagnostics,
brewPrefix: brewPrefix
)

cFlags += pkg.cFlags
libs += pkg.libs
}

return (cFlags: cFlags, libs: libs)
}

self.cFlags = cFlags
self.libs = libs
let dependencyFlags = try getFlags(from: parser.dependencies)
let privateDependencyFlags = try getFlags(from: parser.privateDependencies)

self.cFlags = parser.cFlags + dependencyFlags.cFlags + privateDependencyFlags.cFlags
self.libs = parser.libs + dependencyFlags.libs
}

private static var envSearchPaths: [AbsolutePath] {
Expand All @@ -183,6 +188,7 @@ struct PkgConfigParser {
private let fileSystem: FileSystem
private(set) var variables = [String: String]()
var dependencies = [String]()
var privateDependencies = [String]()
var cFlags = [String]()
var libs = [String]()

Expand All @@ -200,9 +206,12 @@ struct PkgConfigParser {
return line
}

// Add pcfiledir variable. This is path to the directory containing the pc file.
// Add pcfiledir variable. This is the path of the directory containing this pc file.
variables["pcfiledir"] = pcFile.parentDirectory.pathString

// Add pc_sysrootdir variable. This is the path of the sysroot directory for pc files.
variables["pc_sysrootdir"] = Process.env["PKG_CONFIG_SYSROOT_DIR"] ?? "/"

let fileContents = try fileSystem.readFileContents(pcFile)
// FIXME: Should we error out instead if content is not UTF8 representable?
for line in fileContents.validDescription?.components(separatedBy: "\n") ?? [] {
Expand Down Expand Up @@ -233,6 +242,8 @@ struct PkgConfigParser {
switch key {
case "Requires":
dependencies = try parseDependencies(value)
case "Requires.private":
privateDependencies = try parseDependencies(value)
case "Libs":
libs = try splitEscapingSpace(value)
case "Cflags":
Expand Down
27 changes: 23 additions & 4 deletions Tests/TSCUtilityTests/PkgConfigParserTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,24 @@ final class PkgConfigParserTests: XCTestCase {
"gtk_binary_version": "3.0.0",
"exec_prefix": "/usr/local/Cellar/gtk+3/3.18.9",
"targets": "quartz",
"pcfiledir": parser.pcFile.parentDirectory.pathString
"pcfiledir": parser.pcFile.parentDirectory.pathString,
"pc_sysrootdir": "/"
])
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"])
XCTAssertEqual(parser.cFlags, ["-I/usr/local/Cellar/gtk+3/3.18.9/include/gtk-3.0"])
XCTAssertEqual(parser.libs, ["-L/usr/local/Cellar/gtk+3/3.18.9/lib", "-lgtk-3"])
}
}

func testEmptyCFlags() {
try! loadPCFile("empty_cflags.pc") { parser in
XCTAssertEqual(parser.variables, ["prefix": "/usr/local/bin", "exec_prefix": "/usr/local/bin", "pcfiledir": parser.pcFile.parentDirectory.pathString])
XCTAssertEqual(parser.variables, [
"prefix": "/usr/local/bin",
"exec_prefix": "/usr/local/bin",
"pcfiledir": parser.pcFile.parentDirectory.pathString,
"pc_sysrootdir": "/"
])
XCTAssertEqual(parser.dependencies, ["gdk-3.0", "atk"])
XCTAssertEqual(parser.cFlags, [])
XCTAssertEqual(parser.libs, ["-L/usr/local/bin", "-lgtk-3"])
Expand All @@ -45,7 +52,13 @@ final class PkgConfigParserTests: XCTestCase {

func testVariableinDependency() {
try! loadPCFile("deps_variable.pc") { parser in
XCTAssertEqual(parser.variables, ["prefix": "/usr/local/bin", "exec_prefix": "/usr/local/bin", "my_dep": "atk", "pcfiledir": parser.pcFile.parentDirectory.pathString])
XCTAssertEqual(parser.variables, [
"prefix": "/usr/local/bin",
"exec_prefix": "/usr/local/bin",
"my_dep": "atk",
"pcfiledir": parser.pcFile.parentDirectory.pathString,
"pc_sysrootdir": "/"
])
XCTAssertEqual(parser.dependencies, ["gdk-3.0", "atk"])
XCTAssertEqual(parser.cFlags, ["-I"])
XCTAssertEqual(parser.libs, ["-L/usr/local/bin", "-lgtk-3"])
Expand All @@ -63,7 +76,13 @@ final class PkgConfigParserTests: XCTestCase {

func testEscapedSpaces() {
try! loadPCFile("escaped_spaces.pc") { parser in
XCTAssertEqual(parser.variables, ["prefix": "/usr/local/bin", "exec_prefix": "/usr/local/bin", "my_dep": "atk", "pcfiledir": parser.pcFile.parentDirectory.pathString])
XCTAssertEqual(parser.variables, [
"prefix": "/usr/local/bin",
"exec_prefix": "/usr/local/bin",
"my_dep": "atk",
"pcfiledir": parser.pcFile.parentDirectory.pathString,
"pc_sysrootdir": "/"
])
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"])
XCTAssertEqual(parser.libs, ["-L/usr/local/bin", "-lgtk 3", "-wantareal\\here", "-one\\", "-two"])
Expand Down