diff --git a/Sources/XcodeGen/main.swift b/Sources/XcodeGen/main.swift index 5393c39b1..f14670c61 100644 --- a/Sources/XcodeGen/main.swift +++ b/Sources/XcodeGen/main.swift @@ -47,21 +47,11 @@ func generate(spec: String, project: String, isQuiet: Bool, justVersion: Bool) { let xcodeProject = try projectGenerator.generateXcodeProject() logger.info("⚙️ Writing project...") + let projectWriter = ProjectWriter(project: project) + try projectWriter.writeXcodeProject(xcodeProject) + try projectWriter.writePlists() - try projectGenerator.generateFiles() - - let projectFile = projectPath + "\(project.name).xcodeproj" - let tempPath = Path.temporary + "XcodeGen_\(Int(NSTimeIntervalSince1970))" - try? tempPath.delete() - if projectFile.exists { - try projectFile.copy(tempPath) - } - try xcodeProject.write(path: tempPath, override: true) - try? projectFile.delete() - try tempPath.copy(projectFile) - try? tempPath.delete() - - logger.success("💾 Saved project to \(projectFile.string)") + logger.success("💾 Saved project to \(project.projectPath.string)") } catch let error as SpecValidationError { fatalError(error.description) } catch { diff --git a/Sources/XcodeGenKit/InfoPlistGenerator.swift b/Sources/XcodeGenKit/InfoPlistGenerator.swift new file mode 100644 index 000000000..b2725a314 --- /dev/null +++ b/Sources/XcodeGenKit/InfoPlistGenerator.swift @@ -0,0 +1,42 @@ +import Foundation +import ProjectSpec +import PathKit + +public class InfoPlistGenerator { + + /** + Default info plist attributes taken from: + /Applications/Xcode.app/Contents/Developer/Library/Xcode/Templates/Project Templates/Base/Base_DefinitionsInfoPlist.xctemplate/TemplateInfo.plist + */ + var defaultInfoPlist: [String: Any] = { + var dictionary: [String: Any] = [:] + dictionary["CFBundleIdentifier"] = "$(PRODUCT_BUNDLE_IDENTIFIER)" + dictionary["CFBundleInfoDictionaryVersion"] = "6.0" + dictionary["CFBundleExecutable"] = "$(EXECUTABLE_NAME)" + dictionary["CFBundleName"] = "$(PRODUCT_NAME)" + dictionary["CFBundleDevelopmentRegion"] = "$(DEVELOPMENT_LANGUAGE)" + dictionary["CFBundleShortVersionString"] = "1.0" + dictionary["CFBundleVersion"] = "1" + return dictionary + }() + + public func generateAttributes(target: Target) -> [String: Any] { + var targetInfoPlist = defaultInfoPlist + switch target.type { + case .uiTestBundle, + .unitTestBundle: + targetInfoPlist["CFBundlePackageType"] = "BNDL" + case .application, + .watch2App: + targetInfoPlist["CFBundlePackageType"] = "APPL" + case .framework: + targetInfoPlist["CFBundlePackageType"] = "FMWK" + case .bundle: + targetInfoPlist["CFBundlePackageType"] = "BNDL" + case .xpcService: + targetInfoPlist["CFBundlePackageType"] = "XPC" + default: break + } + return targetInfoPlist + } +} diff --git a/Sources/XcodeGenKit/ProjectGenerator.swift b/Sources/XcodeGenKit/ProjectGenerator.swift index ab0a51ea3..b6a83d49e 100644 --- a/Sources/XcodeGenKit/ProjectGenerator.swift +++ b/Sources/XcodeGenKit/ProjectGenerator.swift @@ -33,57 +33,6 @@ public class ProjectGenerator { return XcodeProj(workspace: workspace, pbxproj: pbxProj, sharedData: sharedData) } - public func generateFiles() throws { - - /* - Default info plist attributes taken from: - /Applications/Xcode.app/Contents/Developer/Library/Xcode/Templates/Project Templates/Base/Base_DefinitionsInfoPlist.xctemplate/TemplateInfo.plist - */ - var defaultInfoPlist: [String: Any] = [:] - defaultInfoPlist["CFBundleIdentifier"] = "$(PRODUCT_BUNDLE_IDENTIFIER)" - defaultInfoPlist["CFBundleInfoDictionaryVersion"] = "6.0" - defaultInfoPlist["CFBundleExecutable"] = "$(EXECUTABLE_NAME)" - defaultInfoPlist["CFBundleName"] = "$(PRODUCT_NAME)" - defaultInfoPlist["CFBundleDevelopmentRegion"] = "$(DEVELOPMENT_LANGUAGE)" - defaultInfoPlist["CFBundleShortVersionString"] = "1.0" - defaultInfoPlist["CFBundleVersion"] = "1" - - for target in project.targets { - if let plist = target.info { - var targetInfoPlist = defaultInfoPlist - switch target.type { - case .uiTestBundle, - .unitTestBundle: - targetInfoPlist["CFBundlePackageType"] = "BNDL" - case .application, - .watch2App: - targetInfoPlist["CFBundlePackageType"] = "APPL" - case .framework: - targetInfoPlist["CFBundlePackageType"] = "FMWK" - case .bundle: - targetInfoPlist["CFBundlePackageType"] = "BNDL" - case .xpcService: - targetInfoPlist["CFBundlePackageType"] = "XPC" - default: break - } - let path = project.basePath + plist.path - let attributes = targetInfoPlist.merged(plist.attributes) - let data = try PropertyListSerialization.data(fromPropertyList: attributes, format: .xml, options: 0) - try? path.delete() - try path.parent().mkpath() - try path.write(data) - } - - if let plist = target.entitlements { - let path = project.basePath + plist.path - let data = try PropertyListSerialization.data(fromPropertyList: plist.attributes, format: .xml, options: 0) - try? path.delete() - try path.parent().mkpath() - try path.write(data) - } - } - } - func generateWorkspace() throws -> XCWorkspace { let dataElement: XCWorkspaceDataElement = .file(XCWorkspaceDataFileRef(location: .self(""))) let workspaceData = XCWorkspaceData(children: [dataElement]) diff --git a/Sources/XcodeGenKit/ProjectWriter.swift b/Sources/XcodeGenKit/ProjectWriter.swift new file mode 100644 index 000000000..fad938d7f --- /dev/null +++ b/Sources/XcodeGenKit/ProjectWriter.swift @@ -0,0 +1,51 @@ +import Foundation +import ProjectSpec +import xcodeproj +import PathKit + +public class ProjectWriter { + + let project: Project + + public init(project: Project) { + self.project = project + } + + public func writeXcodeProject(_ xcodeProject: XcodeProj) throws { + let projectPath = project.projectPath + let tempPath = Path.temporary + "XcodeGen_\(Int(NSTimeIntervalSince1970))" + try? tempPath.delete() + if projectPath.exists { + try projectPath.copy(tempPath) + } + try xcodeProject.write(path: tempPath, override: true) + try? projectPath.delete() + try tempPath.copy(projectPath) + try? tempPath.delete() + } + + public func writePlists() throws { + + let infoPlistGenerator = InfoPlistGenerator() + for target in project.targets { + // write Info.plist + if let plist = target.info { + let path = project.basePath + plist.path + let attributes = infoPlistGenerator.generateAttributes(target: target).merged(plist.attributes) + let data = try PropertyListSerialization.data(fromPropertyList: attributes, format: .xml, options: 0) + try? path.delete() + try path.parent().mkpath() + try path.write(data) + } + + // write entitlements + if let plist = target.entitlements { + let path = project.basePath + plist.path + let data = try PropertyListSerialization.data(fromPropertyList: plist.attributes, format: .xml, options: 0) + try? path.delete() + try path.parent().mkpath() + try path.write(data) + } + } + } +} diff --git a/Tests/XcodeGenKitTests/ProjectFixtureTests.swift b/Tests/XcodeGenKitTests/ProjectFixtureTests.swift index 68a96039c..6a40b2bed 100644 --- a/Tests/XcodeGenKitTests/ProjectFixtureTests.swift +++ b/Tests/XcodeGenKitTests/ProjectFixtureTests.swift @@ -62,8 +62,9 @@ fileprivate func generateXcodeProject(specPath: Path, file: String = #file, line let project = try Project(path: specPath) let generator = ProjectGenerator(project: project) let xcodeProject = try generator.generateXcodeProject() - try generator.generateFiles() - try xcodeProject.write(path: project.projectPath, override: true) + let writer = ProjectWriter(project: project) + try writer.writePlists() + try writer.writeXcodeProject(xcodeProject) return xcodeProject } diff --git a/Tests/XcodeGenKitTests/ProjectGeneratorTests.swift b/Tests/XcodeGenKitTests/ProjectGeneratorTests.swift index 33bbd9541..46bc730ae 100644 --- a/Tests/XcodeGenKitTests/ProjectGeneratorTests.swift +++ b/Tests/XcodeGenKitTests/ProjectGeneratorTests.swift @@ -836,8 +836,8 @@ class ProjectGeneratorTests: XCTestCase { let tempPath = Path.temporary + "info" let project = Project(basePath: tempPath, name: "", targets: [Target(name: "", type: .application, platform: .iOS, info: plist)]) let pbxProject = try project.generatePbxProj() - let generator = ProjectGenerator(project: project) - try generator.generateFiles() + let writer = ProjectWriter(project: project) + try writer.writePlists() guard let targetConfig = pbxProject.nativeTargets.first?.buildConfigurationList?.buildConfigurations.first else { throw failure("Couldn't find Target config")