Skip to content

Commit fb5e87b

Browse files
committed
Make pkgconfig line parser more strict and care for comments
1 parent 8d38e90 commit fb5e87b

File tree

2 files changed

+44
-16
lines changed

2 files changed

+44
-16
lines changed

Sources/Build/PkgConfig.swift

+42-14
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import func POSIX.getenv
1313

1414
enum PkgConfigError: ErrorProtocol {
1515
case CouldNotFindConfigFile
16-
case ParsingError
16+
case ParsingError(String)
1717
}
1818

1919
struct PkgConfig {
@@ -22,7 +22,6 @@ struct PkgConfig {
2222
"/usr/lib/pkgconfig",
2323
"/usr/share/pkgconfig",
2424
]
25-
2625
let name: String
2726
let pcFile: String
2827
let cFlags: [String]
@@ -86,23 +85,46 @@ struct PkgConfigParser {
8685
}
8786

8887
mutating func parse() throws {
88+
89+
func removeComment(line: String) -> String {
90+
if let commentIndex = line.characters.index(of: "#") {
91+
return line[line.characters.startIndex..<commentIndex]
92+
}
93+
return line
94+
}
95+
8996
let file = File(path: self.pcFile)
9097
for line in try file.enumerate() {
91-
if !line.characters.contains(":") && line.characters.contains("=") {
92-
let equalsIndex = line.characters.index(of: "=")!
98+
// Ignore any commented line.
99+
if line.hasPrefix("#") || line.isEmpty { continue }
100+
// Remove any trailing comment from the line.
101+
let line = removeComment(line: line)
102+
103+
if let colonIndex = line.characters.index(of: ":") where line[colonIndex.successor()] == " " {
104+
// Found a key-value pair.
105+
try parseKeyValue(line: line)
106+
} else if let equalsIndex = line.characters.index(of: "=") {
107+
// Found a variable.
93108
let name = line[line.startIndex..<equalsIndex]
94109
let value = line[equalsIndex.successor()..<line.endIndex]
95110
variables[name] = try resolveVariables(value)
96-
} else if line.hasPrefix("Requires: ") {
97-
dependencies = try parseDependencies(value(line: line))
98-
} else if line.hasPrefix("Libs: ") {
99-
libs = try resolveVariables(value(line: line)).chomp()
100-
} else if line.hasPrefix("Cflags: ") {
101-
cFlags = try resolveVariables( value(line: line)).chomp()
111+
} else {
112+
// Unexpected thing in the pc file, abort.
113+
throw PkgConfigError.ParsingError("Unexpecting line: \(line) in \(pcFile)")
102114
}
103115
}
104116
}
105117

118+
private mutating func parseKeyValue(line: String) throws {
119+
if line.hasPrefix("Requires: ") {
120+
dependencies = try parseDependencies(value(line: line))
121+
} else if line.hasPrefix("Libs: ") {
122+
libs = try resolveVariables(value(line: line)).chomp()
123+
} else if line.hasPrefix("Cflags: ") {
124+
cFlags = try resolveVariables(value(line: line)).chomp()
125+
}
126+
}
127+
106128
/// Parses `Requires: ` string into array of dependencies.
107129
/// The dependecy string has seperator which can be (multiple) space or a comma.
108130
/// Additionally each there can be an optional version constaint to a dependency.
@@ -112,7 +134,7 @@ struct PkgConfigParser {
112134

113135
// Look at a char at an index if present.
114136
func peek(idx: Int) -> Character? {
115-
guard idx <= depString.characters.count else { return nil }
137+
guard idx <= depString.characters.count - 1 else { return nil }
116138
return depString.characters[depString.characters.startIndex.advanced(by: idx)]
117139
}
118140

@@ -133,16 +155,20 @@ struct PkgConfigParser {
133155
token += String(char)
134156
}
135157
}
158+
// Append the last collected token if present.
159+
if !token.isEmpty { tokens += [token] }
136160
return tokens
137161
}
138-
162+
139163
var deps = [String]()
140164
var it = tokenize().makeIterator()
141165
while let arg = it.next() {
142166
// If we encounter an operator then we need to skip the next token.
143167
if operators.contains(arg) {
144168
// We should have a version number next, skip.
145-
guard let _ = it.next() else { throw PkgConfigError.ParsingError }
169+
guard let _ = it.next() else {
170+
throw PkgConfigError.ParsingError("Expected version number after \(deps.last) \(arg) in \"\(depString)\" in \(pcFile)")
171+
}
146172
} else {
147173
// Otherwise it is a dependency.
148174
deps.append(arg)
@@ -174,7 +200,9 @@ struct PkgConfigParser {
174200
if let variable = findVariable(fragment) {
175201
// Append the contents before the variable.
176202
result += fragment[fragment.characters.startIndex..<variable.startIndex]
177-
guard let variableValue = variables[variable.name] else { throw PkgConfigError.ParsingError }
203+
guard let variableValue = variables[variable.name] else {
204+
throw PkgConfigError.ParsingError("Expected variable in \(pcFile)")
205+
}
178206
// Append the value of the variable.
179207
result += variableValue
180208
// Update the fragment with post variable string.

Tests/Build/pkgconfigInputs/gtk+-3.0.pc

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ exec_prefix=${prefix}
33
libdir=${exec_prefix}/lib
44
includedir=${prefix}/include
55
targets=quartz
6-
6+
#some comment
77
gtk_binary_version=3.0.0
88
gtk_host=x86_64-apple-darwin15.3.0
99

1010
Name: GTK+
1111
Description: GTK+ Graphical UI Library
1212
Version: 3.18.9
13-
Requires: gdk-3.0,atk >= 2.15.1 cairo >= 1.14.0 cairo-gobject >= 1.14.0 gdk-pixbuf-2.0 >= 2.30.0 gio-2.0 >= 2.45.8
13+
Requires: gdk-3.0,atk >= 2.15.1 cairo >= 1.14.0 cairo-gobject >= 1.14.0 gdk-pixbuf-2.0 >= 2.30.0 gio-2.0 >= 2.45.8 #some random comment
1414
Requires.private: atk epoxy >= 1.0 gio-unix-2.0 >= 2.45.8
1515
Libs: -L${libdir} -lgtk-3
1616
Cflags: -I${includedir}/gtk-3.0

0 commit comments

Comments
 (0)