77
88import Foundation
99import TSCBasic
10+ import TSCUtility
1011import Xcodeproj
1112
1213struct ProjectGenerator {
@@ -31,22 +32,107 @@ struct ProjectGenerator {
3132 }
3233
3334 // MARK: - Generation
34-
35+
36+ /// Writes out the Xcconfig file
37+ func writeXcconfig ( ) throws {
38+ try open ( AbsolutePath ( self . package . distributionBuildXcconfig. path) ) { stream in
39+ stream (
40+ """
41+ BUILD_LIBRARY_FOR_DISTRIBUTION=YES
42+ """
43+ )
44+ }
45+ }
46+
47+ /// Generate an Xcode project.
48+ ///
49+ /// This is basically a copy of Xcodeproj.generate()
50+ ///
3551 func generate ( ) throws -> Xcode . Project {
3652 let path = self . projectPath
3753 try makeDirectories ( path)
38-
39- return try Xcodeproj . generate (
40- projectName : self . package . package . name ,
54+
55+ // Generate the contents of project.xcodeproj (inside the .xcodeproj).
56+ let project = try pbxproj (
4157 xcodeprojPath: path,
4258 graph: self . package . graph,
43- options: XcodeprojOptions ( addExtraFiles: false ) ,
59+ extraDirs: [ ] ,
60+ extraFiles: [ ] ,
61+ options: XcodeprojOptions ( xcconfigOverrides: AbsolutePath ( self . package . distributionBuildXcconfig. path) ) ,
4462 diagnostics: self . package . diagnostics
4563 )
64+
65+ return project
4666 }
47-
48- private func createDirectory ( at path: URL ) throws {
49- guard FileManager . default. fileExists ( atPath: path. path) == false else { return }
50- try FileManager . default. createDirectory ( at: path, withIntermediateDirectories: true , attributes: nil )
67+ }
68+
69+
70+ // MARK: - Saving Xcode Projects
71+
72+ extension Xcode . Project {
73+
74+ func save ( to path: AbsolutePath ) throws {
75+ try open ( path. appending ( component: " project.pbxproj " ) ) { stream in
76+ // Serialize the project model we created to a plist, and return
77+ // its string description.
78+ let str = " // !$*UTF8*$! \n " + self . generatePlist ( ) . description
79+ stream ( str)
80+ }
81+
82+ for target in self . frameworkTargets {
83+ ///// For framework targets, generate target.c99Name_Info.plist files in the
84+ ///// directory that Xcode project is generated
85+ let name = " \( target. name. spm_mangledToC99ExtendedIdentifier ( ) ) _Info.plist "
86+ try open ( path. appending ( RelativePath ( name) ) ) { print in
87+ print ( """
88+ <?xml version= " 1.0 " encoding= " UTF-8 " ?>
89+ <plist version= " 1.0 " >
90+ <dict>
91+ <key>CFBundleDevelopmentRegion</key>
92+ <string>en</string>
93+ <key>CFBundleExecutable</key>
94+ <string>$(EXECUTABLE_NAME)</string>
95+ <key>CFBundleIdentifier</key>
96+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
97+ <key>CFBundleInfoDictionaryVersion</key>
98+ <string>6.0</string>
99+ <key>CFBundleName</key>
100+ <string>$(PRODUCT_NAME)</string>
101+ <key>CFBundlePackageType</key>
102+ <string>FMWK</string>
103+ <key>CFBundleShortVersionString</key>
104+ <string>1.0</string>
105+ <key>CFBundleSignature</key>
106+ <string>????</string>
107+ <key>CFBundleVersion</key>
108+ <string>$(CURRENT_PROJECT_VERSION)</string>
109+ <key>NSPrincipalClass</key>
110+ <string></string>
111+ </dict>
112+ </plist>
113+ """ )
114+ }
115+ }
51116 }
52117}
118+
119+ /// Writes the contents to the file specified.
120+ ///
121+ /// This method doesn't rewrite the file in case the new and old contents of
122+ /// file are same.
123+ fileprivate func open( _ path: AbsolutePath , body: ( ( String ) -> Void ) throws -> Void ) throws {
124+ let stream = BufferedOutputByteStream ( )
125+ try body { line in
126+ stream <<< line
127+ stream <<< " \n "
128+ }
129+ // If the file exists with the identical contents, we don't need to rewrite it.
130+ //
131+ // This avoids unnecessarily triggering Xcode reloads of the project file.
132+ if let contents = try ? localFileSystem. readFileContents ( path) , contents == stream. bytes {
133+ return
134+ }
135+
136+ // Write the real file.
137+ try localFileSystem. writeFileContents ( path, bytes: stream. bytes)
138+ }
0 commit comments