@@ -44,26 +44,110 @@ internal func xdgTestHelperURL() -> URL {
44
44
45
45
46
46
class BundlePlayground {
47
+ enum ExecutableType : CaseIterable {
48
+ case library
49
+ case executable
50
+
51
+ var pathExtension : String {
52
+ switch self {
53
+ case . library:
54
+ #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
55
+ return " dylib "
56
+ #elseif os(Windows)
57
+ return " dll "
58
+ #else
59
+ return " so "
60
+ #endif
61
+ case . executable:
62
+ #if os(Windows)
63
+ return " exe "
64
+ #else
65
+ return " "
66
+ #endif
67
+ }
68
+ }
69
+
70
+ var flatPathExtension : String {
71
+ #if os(Windows)
72
+ return self . pathExtension
73
+ #else
74
+ return " "
75
+ #endif
76
+ }
77
+
78
+ var fhsPrefix : String {
79
+ switch self {
80
+ case . executable:
81
+ return " bin "
82
+ case . library:
83
+ return " lib "
84
+ }
85
+ }
86
+
87
+ var nonFlatFilePrefix : String {
88
+ switch self {
89
+ case . executable:
90
+ return " "
91
+ case . library:
92
+ return " lib "
93
+ }
94
+ }
95
+ }
96
+
47
97
enum Layout {
48
- case flat
49
- case fhsInstalled
50
- case fhsFreestanding
98
+ case flat( ExecutableType )
99
+ case fhs ( ExecutableType )
100
+ case freestanding ( ExecutableType )
51
101
52
102
static var allApplicable : [ Layout ] {
53
- let layouts : [ Layout ] = [ . flat, . fhsInstalled, . fhsFreestanding ]
54
-
55
- #if NS_FOUNDATION_ALLOWS_TESTABLE_IMPORT
56
- if Bundle . _supportsFHSStyle {
57
- return layouts
58
- } else {
59
- return layouts. filter { !$0. isFHS }
103
+ let layouts : [ Layout ] = [
104
+ . flat( . library) ,
105
+ . flat( . executable) ,
106
+ . fhs( . library) ,
107
+ . fhs( . executable) ,
108
+ . freestanding( . library) ,
109
+ . freestanding( . executable) ,
110
+ ]
111
+
112
+ return layouts. filter { $0. isSupported }
113
+ }
114
+
115
+ var isFreestanding : Bool {
116
+ switch self {
117
+ case . freestanding( _) :
118
+ return true
119
+ default :
120
+ return false
60
121
}
61
- #else
62
- return layouts. filter { !$0. isFHS }
63
- #endif
64
122
}
123
+
65
124
var isFHS : Bool {
66
- return self == . fhsInstalled || self == . fhsFreestanding
125
+ switch self {
126
+ case . fhs( _) :
127
+ return true
128
+ default :
129
+ return false
130
+ }
131
+ }
132
+
133
+ var isFlat : Bool {
134
+ switch self {
135
+ case . flat( _) :
136
+ return true
137
+ default :
138
+ return false
139
+ }
140
+ }
141
+
142
+ var isSupported : Bool {
143
+ switch self {
144
+ case . flat( _) :
145
+ return true
146
+ case . freestanding( _) :
147
+ return Bundle . _supportsFreestandingBundles
148
+ case . fhs( _) :
149
+ return Bundle . _supportsFHSBundles
150
+ }
67
151
}
68
152
}
69
153
@@ -76,6 +160,7 @@ class BundlePlayground {
76
160
let layout : Layout
77
161
78
162
private( set) var bundlePath : String !
163
+ private( set) var mainExecutableURL : URL !
79
164
private var playgroundPath : String ?
80
165
81
166
init ? ( bundleName: String ,
@@ -104,8 +189,8 @@ class BundlePlayground {
104
189
105
190
let temporaryDirectory = FileManager . default. temporaryDirectory. appendingPathComponent ( " TestFoundation_Playground_ " + UUID( ) . uuidString)
106
191
107
- switch ( layout) {
108
- case . flat:
192
+ switch layout {
193
+ case . flat( let executableType ) :
109
194
do {
110
195
try FileManager . default. createDirectory ( atPath: temporaryDirectory. path, withIntermediateDirectories: false , attributes: nil )
111
196
@@ -114,10 +199,18 @@ class BundlePlayground {
114
199
try FileManager . default. createDirectory ( atPath: bundleURL. path, withIntermediateDirectories: false , attributes: nil )
115
200
116
201
// Make a main and an auxiliary executable:
117
- guard FileManager . default. createFile ( atPath: bundleURL. appendingPathComponent ( bundleName) . path, contents: nil ) else {
202
+ self . mainExecutableURL = bundleURL
203
+ . appendingPathComponent ( bundleName)
204
+ . appendingPathExtension ( executableType. flatPathExtension)
205
+
206
+ guard FileManager . default. createFile ( atPath: mainExecutableURL. path, contents: nil ) else {
118
207
return false
119
208
}
120
- guard FileManager . default. createFile ( atPath: bundleURL. appendingPathComponent ( auxiliaryExecutableName) . path, contents: nil ) else {
209
+
210
+ let auxiliaryExecutableURL = bundleURL
211
+ . appendingPathComponent ( auxiliaryExecutableName)
212
+ . appendingPathExtension ( executableType. flatPathExtension)
213
+ guard FileManager . default. createFile ( atPath: auxiliaryExecutableURL. path, contents: nil ) else {
121
214
return false
122
215
}
123
216
@@ -143,7 +236,7 @@ class BundlePlayground {
143
236
return false
144
237
}
145
238
146
- case . fhsInstalled :
239
+ case . fhs ( let executableType ) :
147
240
do {
148
241
149
242
// Create a FHS /usr/local-style hierarchy:
@@ -152,17 +245,18 @@ class BundlePlayground {
152
245
try FileManager . default. createDirectory ( atPath: temporaryDirectory. appendingPathComponent ( " lib " ) . path, withIntermediateDirectories: false , attributes: nil )
153
246
154
247
// Make a main and an auxiliary executable:
155
- #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
156
- let pathExtension = " dylib "
157
- #else
158
- let pathExtension = " so "
159
- #endif
160
-
161
- guard FileManager . default. createFile ( atPath: temporaryDirectory. appendingPathComponent ( " lib " ) . appendingPathComponent ( " lib \( bundleName) . \( pathExtension) " ) . path, contents: nil ) else { return false }
248
+ self . mainExecutableURL = temporaryDirectory
249
+ . appendingPathComponent ( executableType. fhsPrefix)
250
+ . appendingPathComponent ( executableType. nonFlatFilePrefix + bundleName)
251
+ . appendingPathExtension ( executableType. pathExtension)
252
+ guard FileManager . default. createFile ( atPath: mainExecutableURL. path, contents: nil ) else { return false }
162
253
163
254
let executablesDirectory = temporaryDirectory. appendingPathComponent ( " libexec " ) . appendingPathComponent ( " \( bundleName) .executables " )
164
255
try FileManager . default. createDirectory ( atPath: executablesDirectory. path, withIntermediateDirectories: true , attributes: nil )
165
- guard FileManager . default. createFile ( atPath: executablesDirectory. appendingPathComponent ( auxiliaryExecutableName) . path, contents: nil ) else { return false }
256
+ let auxiliaryExecutableURL = executablesDirectory
257
+ . appendingPathComponent ( executableType. nonFlatFilePrefix + auxiliaryExecutableName)
258
+ . appendingPathExtension ( executableType. pathExtension)
259
+ guard FileManager . default. createFile ( atPath: auxiliaryExecutableURL. path, contents: nil ) else { return false }
166
260
167
261
// Make a .resources directory in …/share:
168
262
let resourcesDirectory = temporaryDirectory. appendingPathComponent ( " share " ) . appendingPathComponent ( " \( bundleName) .resources " )
@@ -186,21 +280,27 @@ class BundlePlayground {
186
280
return false
187
281
}
188
282
189
- case . fhsFreestanding :
283
+ case . freestanding ( let executableType ) :
190
284
do {
191
285
let bundleName = URL ( string: self . bundleName) !. deletingPathExtension ( ) . path
192
286
193
287
try FileManager . default. createDirectory ( atPath: temporaryDirectory. path, withIntermediateDirectories: false , attributes: nil )
194
288
195
289
// Make a main executable:
196
- guard FileManager . default. createFile ( atPath: temporaryDirectory. appendingPathComponent ( bundleName) . path, contents: nil ) else { return false }
290
+ self . mainExecutableURL = temporaryDirectory
291
+ . appendingPathComponent ( executableType. nonFlatFilePrefix + bundleName)
292
+ . appendingPathExtension ( executableType. pathExtension)
293
+ guard FileManager . default. createFile ( atPath: mainExecutableURL. path, contents: nil ) else { return false }
197
294
198
295
// Make a .resources directory:
199
296
let resourcesDirectory = temporaryDirectory. appendingPathComponent ( " \( bundleName) .resources " )
200
297
try FileManager . default. createDirectory ( atPath: resourcesDirectory. path, withIntermediateDirectories: false , attributes: nil )
201
298
202
299
// Make an auxiliary executable:
203
- guard FileManager . default. createFile ( atPath: resourcesDirectory. appendingPathComponent ( auxiliaryExecutableName) . path, contents: nil ) else { return false }
300
+ let auxiliaryExecutableURL = resourcesDirectory
301
+ . appendingPathComponent ( executableType. nonFlatFilePrefix + auxiliaryExecutableName)
302
+ . appendingPathExtension ( executableType. pathExtension)
303
+ guard FileManager . default. createFile ( atPath: auxiliaryExecutableURL. path, contents: nil ) else { return false }
204
304
205
305
// Put some resources in the bundle
206
306
for resourceName in resourceFilenames {
@@ -252,6 +352,7 @@ class TestBundle : XCTestCase {
252
352
( " test_bundlePreflight " , test_bundlePreflight) ,
253
353
( " test_bundleFindExecutable " , test_bundleFindExecutable) ,
254
354
( " test_bundleFindAuxiliaryExecutables " , test_bundleFindAuxiliaryExecutables) ,
355
+ ( " test_bundleReverseBundleLookup " , test_bundleReverseBundleLookup) ,
255
356
( " test_mainBundleExecutableURL " , test_mainBundleExecutableURL) ,
256
357
]
257
358
}
@@ -438,6 +539,26 @@ class TestBundle : XCTestCase {
438
539
XCTAssertNil ( bundle. url ( forAuxiliaryExecutable: " does_not_exist_at_all " ) )
439
540
}
440
541
}
542
+
543
+ func test_bundleReverseBundleLookup( ) {
544
+ _withEachPlaygroundLayout { ( playground) in
545
+ #if !os(Windows)
546
+ if playground. layout. isFreestanding {
547
+ // TODO: Freestanding bundles reverse lookup pending to be implemented on non-Windows platforms.
548
+ return
549
+ }
550
+ #endif
551
+
552
+ if playground. layout. isFHS {
553
+ // TODO: FHS bundles reverse lookup pending to be implemented on all platforms.
554
+ return
555
+ }
556
+
557
+ let bundle = Bundle ( _executableURL: playground. mainExecutableURL)
558
+ XCTAssertNotNil ( bundle)
559
+ XCTAssertEqual ( bundle? . bundlePath, playground. bundlePath)
560
+ }
561
+ }
441
562
442
563
func test_mainBundleExecutableURL( ) {
443
564
#if !DARWIN_COMPATIBILITY_TESTS // _CFProcessPath() is unavailable on native Foundation
0 commit comments