Skip to content

Commit 3b6d856

Browse files
committed
Merge pull request swiftlang#329 from aciidb0mb3r/xcodeproj-fixes
[Xcodeproj] Use OutputByteStream for generating files, don't re-write generated files unless needed.
2 parents 6cfa353 + 0b1b917 commit 3b6d856

File tree

2 files changed

+50
-43
lines changed

2 files changed

+50
-43
lines changed

Sources/Xcodeproj/generate().swift

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -40,52 +40,59 @@ public func generate(dstdir: String, projectName: String, srcroot: String, modul
4040
let schemeName = "\(projectName).xcscheme"
4141

4242
////// the pbxproj file describes the project and its targets
43-
try open(xcodeprojPath, "project.pbxproj") { fwrite in
44-
try pbxproj(srcroot: srcroot, projectRoot: dstdir, xcodeprojPath: xcodeprojPath, modules: modules, externalModules: externalModules, products: products, options: options, printer: fwrite)
43+
try open(xcodeprojPath, "project.pbxproj") { stream in
44+
try pbxproj(srcroot: srcroot, projectRoot: dstdir, xcodeprojPath: xcodeprojPath, modules: modules, externalModules: externalModules, products: products, options: options, printer: stream)
4545
}
4646

4747
////// the scheme acts like an aggregate target for all our targets
4848
/// it has all tests associated so CMD+U works
49-
try open(schemesDirectory, schemeName) { fwrite in
50-
xcscheme(container: xcodeprojName, modules: modules, printer: fwrite)
49+
try open(schemesDirectory, schemeName) { stream in
50+
xcscheme(container: xcodeprojName, modules: modules, printer: stream)
5151
}
5252

5353
////// we generate this file to ensure our main scheme is listed
5454
/// before any inferred schemes Xcode may autocreate
55-
try open(schemesDirectory, "xcschememanagement.plist") { fwrite in
56-
fwrite("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
57-
fwrite("<plist version=\"1.0\">")
58-
fwrite("<dict>")
59-
fwrite(" <key>SchemeUserState</key>")
60-
fwrite(" <dict>")
61-
fwrite(" <key>\(schemeName)</key>")
62-
fwrite(" <dict></dict>")
63-
fwrite(" </dict>")
64-
fwrite(" <key>SuppressBuildableAutocreation</key>")
65-
fwrite(" <dict></dict>")
66-
fwrite("</dict>")
67-
fwrite("</plist>")
55+
try open(schemesDirectory, "xcschememanagement.plist") { print in
56+
print("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
57+
print("<plist version=\"1.0\">")
58+
print("<dict>")
59+
print(" <key>SchemeUserState</key>")
60+
print(" <dict>")
61+
print(" <key>\(schemeName)</key>")
62+
print(" <dict></dict>")
63+
print(" </dict>")
64+
print(" <key>SuppressBuildableAutocreation</key>")
65+
print(" <dict></dict>")
66+
print("</dict>")
67+
print("</plist>")
6868
}
6969

7070
return xcodeprojPath
7171
}
7272

73+
import class Foundation.NSData
7374

74-
private func open(_ path: String..., body: ((String) -> Void) throws -> Void) throws {
75-
var error: ErrorProtocol? = nil
76-
77-
try Utility.fopen(Path.join(path), mode: .Write) { fp in
78-
try body { line in
79-
if error == nil {
80-
do {
81-
try fputs(line, fp)
82-
try fputs("\n", fp)
83-
} catch let caught {
84-
error = caught
85-
}
86-
}
75+
/// Writes the contents to the file specified.
76+
/// Doesn't re-writes the file in case the new and old contents of file are same.
77+
func open(_ path: String..., body: ((String) -> Void) throws -> Void) throws {
78+
let path = Path.join(path)
79+
let stream = OutputByteStream()
80+
try body { line in
81+
stream <<< line
82+
stream <<< "\n"
83+
}
84+
// If file is already present compare its content with our stream
85+
// and re-write only if its new.
86+
if path.isFile, let data = NSData(contentsOfFile: path) {
87+
var contents = [UInt8](repeating: 0, count: data.length / sizeof(UInt8))
88+
data.getBytes(&contents, length: data.length)
89+
// If contents are same then no need to re-write.
90+
if contents == stream.bytes.bytes {
91+
return
8792
}
8893
}
89-
90-
guard error == nil else { throw error! }
94+
// Write the real file.
95+
try fopen(path, mode: .Write) { fp in
96+
try fputs(stream.bytes.bytes, fp)
97+
}
9198
}

Sources/Xcodeproj/pbxproj().swift

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -172,9 +172,9 @@ public func pbxproj(srcroot: String, projectRoot: String, xcodeprojPath: String,
172172
// FIXME: Generate these into a sane path.
173173
let projectXCConfig = fileRef(inProjectRoot: Path.join(xcodeprojPath.basename, "Configs", "Project.xcconfig"), srcroot: Path.join(srcroot, projectRoot))
174174
try mkdir(projectXCConfig.2.parentDirectory)
175-
try Utility.fopen(projectXCConfig.2, mode: .Write) { fp in
175+
try open(projectXCConfig.2) { print in
176176
// Set the standard PRODUCT_NAME.
177-
try fputs("PRODUCT_NAME = $(TARGET_NAME)\n", fp)
177+
print("PRODUCT_NAME = $(TARGET_NAME)")
178178

179179
// Set SUPPORTED_PLATFORMS to all platforms.
180180
//
@@ -187,7 +187,7 @@ public func pbxproj(srcroot: String, projectRoot: String, xcodeprojPath: String,
187187
"iphoneos", "iphonesimulator",
188188
"tvos", "tvsimulator",
189189
"watchos", "watchsimulator"]
190-
try fputs("SUPPORTED_PLATFORMS = \(supportedPlatforms.joined(separator: " "))\n", fp)
190+
print("SUPPORTED_PLATFORMS = \(supportedPlatforms.joined(separator: " "))")
191191

192192
// Set a conservative default deployment target.
193193
//
@@ -197,39 +197,39 @@ public func pbxproj(srcroot: String, projectRoot: String, xcodeprojPath: String,
197197
//
198198
// FIXME: Eventually there should be a way for the project using Xcode
199199
// generation to have control over this.
200-
try fputs("MACOSX_DEPLOYMENT_TARGET = 10.10\n", fp)
200+
print("MACOSX_DEPLOYMENT_TARGET = 10.10")
201201

202202
// Default to @rpath-based install names.
203203
//
204204
// The expectation is that the application or executable consuming these
205205
// products will need to establish the appropriate runpath search paths
206206
// so that all the products can be found in a relative manner.
207-
try fputs("DYLIB_INSTALL_NAME_BASE = @rpath\n", fp)
207+
print("DYLIB_INSTALL_NAME_BASE = @rpath")
208208

209209
// Propagate any user provided build flag overrides.
210210
//
211211
// FIXME: Need to get quoting correct here.
212212
if !options.Xcc.isEmpty {
213-
try fputs("OTHER_CFLAGS = \(options.Xcc.joined(separator: " "))\n", fp)
213+
print("OTHER_CFLAGS = \(options.Xcc.joined(separator: " "))")
214214
}
215215
if !options.Xld.isEmpty {
216-
try fputs("OTHER_LDFLAGS = \(options.Xld.joined(separator: " "))\n", fp)
216+
print("OTHER_LDFLAGS = \(options.Xld.joined(separator: " "))")
217217
}
218-
try fputs("OTHER_SWIFT_FLAGS = \((options.Xswiftc+["-DXcode"]).joined(separator: " "))\n", fp)
218+
print("OTHER_SWIFT_FLAGS = \((options.Xswiftc+["-DXcode"]).joined(separator: " "))")
219219

220220
// Prevents Xcode project upgrade warnings.
221-
try fputs("COMBINE_HIDPI_IMAGES = YES\n", fp)
221+
print("COMBINE_HIDPI_IMAGES = YES")
222222

223223
// Always disable use of headermaps.
224224
//
225225
// The semantics of the build should be explicitly defined by the
226226
// project structure, we don't want any additional behaviors not shared
227227
// with `swift build`.
228-
try fputs("USE_HEADERMAP = NO\n", fp)
228+
print("USE_HEADERMAP = NO")
229229

230230
// If the user provided an overriding xcconfig path, include it here.
231231
if let path = options.xcconfigOverrides {
232-
try fputs("\n#include \"\(path)\"\n", fp)
232+
print("\n#include \"\(path)\"")
233233
}
234234
}
235235
let configs = [projectXCConfig]

0 commit comments

Comments
 (0)