Skip to content

Commit

Permalink
generate info plist and entitlements
Browse files Browse the repository at this point in the history
  • Loading branch information
yonaskolb committed Sep 29, 2018
1 parent aa37eb0 commit 0af8843
Show file tree
Hide file tree
Showing 10 changed files with 175 additions and 2 deletions.
26 changes: 26 additions & 0 deletions Sources/ProjectSpec/Plist.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import Foundation
import JSONUtilities

public struct Plist: Equatable {

public let path: String
public let attributes: [String: Any]

public init(path: String, attributes: [String: Any] = [:]) {
self.path = path
self.attributes = attributes
}

public static func == (lhs: Plist, rhs: Plist) -> Bool {
return lhs.path == rhs.path &&
NSDictionary(dictionary: lhs.attributes).isEqual(to: rhs.attributes)
}
}

extension Plist: JSONObjectConvertible {

public init(jsonDictionary: JSONDictionary) throws {
path = try jsonDictionary.json(atKeyPath: "path")
attributes = jsonDictionary.json(atKeyPath: "attributes") ?? [:]
}
}
10 changes: 10 additions & 0 deletions Sources/ProjectSpec/Target.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ public struct Target: ProjectTarget {
public var settings: Settings
public var sources: [TargetSource]
public var dependencies: [Dependency]
public var info: Plist?
public var entitlements: Plist?
public var transitivelyLinkDependencies: Bool?
public var directlyEmbedCarthageDependencies: Bool?
public var requiresObjCLinking: Bool?
Expand Down Expand Up @@ -62,6 +64,8 @@ public struct Target: ProjectTarget {
configFiles: [String: String] = [:],
sources: [TargetSource] = [],
dependencies: [Dependency] = [],
info: Plist? = nil,
entitlements: Plist? = nil,
transitivelyLinkDependencies: Bool? = nil,
directlyEmbedCarthageDependencies: Bool? = nil,
requiresObjCLinking: Bool? = nil,
Expand All @@ -80,6 +84,8 @@ public struct Target: ProjectTarget {
self.configFiles = configFiles
self.sources = sources
self.dependencies = dependencies
self.info = info
self.entitlements = entitlements
self.transitivelyLinkDependencies = transitivelyLinkDependencies
self.directlyEmbedCarthageDependencies = directlyEmbedCarthageDependencies
self.requiresObjCLinking = requiresObjCLinking
Expand Down Expand Up @@ -275,6 +281,10 @@ extension Target: NamedJSONDictionaryConvertible {
} else {
dependencies = try jsonDictionary.json(atKeyPath: "dependencies", invalidItemBehaviour: .fail)
}

info = jsonDictionary.json(atKeyPath: "info")
entitlements = jsonDictionary.json(atKeyPath: "entitlements")

transitivelyLinkDependencies = jsonDictionary.json(atKeyPath: "transitivelyLinkDependencies")
directlyEmbedCarthageDependencies = jsonDictionary.json(atKeyPath: "directlyEmbedCarthageDependencies")
requiresObjCLinking = jsonDictionary.json(atKeyPath: "requiresObjCLinking")
Expand Down
2 changes: 2 additions & 0 deletions Sources/XcodeGen/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ func generate(spec: String, project: String, isQuiet: Bool, justVersion: Bool) {

logger.info("⚙️ Writing project...")

try projectGenerator.generateFiles()

let projectFile = projectPath + "\(project.name).xcodeproj"
let tempPath = Path.temporary + "XcodeGen_\(Int(NSTimeIntervalSince1970))"
try? tempPath.delete()
Expand Down
11 changes: 9 additions & 2 deletions Sources/XcodeGenKit/PBXProjGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -789,8 +789,15 @@ public class PBXProjGenerator {
let configs: [ObjectReference<XCBuildConfiguration>] = project.configs.map { config in
var buildSettings = project.getTargetBuildSettings(target: target, config: config)

// automatically set INFOPLIST_FILE path
if !project.targetHasBuildSetting("INFOPLIST_FILE", basePath: project.basePath, target: target, config: config) {
// Set CODE_SIGN_ENTITLEMENTS
if let entitlements = target.entitlements {
buildSettings["CODE_SIGN_ENTITLEMENTS"] = entitlements.path
}

// Set INFOPLIST_FILE
if let info = target.info {
buildSettings["INFOPLIST_FILE"] = info.path
} else if !project.targetHasBuildSetting("INFOPLIST_FILE", basePath: project.basePath, target: target, config: config) {
if searchForPlist {
plistPath = getInfoPlist(target.sources)
searchForPlist = false
Expand Down
51 changes: 51 additions & 0 deletions Sources/XcodeGenKit/ProjectGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,57 @@ public class ProjectGenerator {
return XcodeProj(workspace: workspace, pbxproj: pbxProject, 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])
Expand Down
2 changes: 2 additions & 0 deletions Tests/Fixtures/TestProject/App_macOS/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>CustomSetting</key>
<string>$CUSTOM_SETTING</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSMainStoryboardFile</key>
Expand Down
8 changes: 8 additions & 0 deletions Tests/Fixtures/TestProject/project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ targets:
App_macOS:
type: application
platform: macOS
info:
path: App_macOS/Info.plist
attributes:
LSMinimumSystemVersion: $(MACOSX_DEPLOYMENT_TARGET)
NSMainStoryboardFile: Main
NSPrincipalClass: NSApplication
CFBundleIconFile: ""
CustomSetting: $CUSTOM_SETTING
attributes:
ProvisioningStyle: Automatic
sources:
Expand Down
1 change: 1 addition & 0 deletions Tests/XcodeGenKitTests/ProjectFixtureTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ fileprivate func generateXcodeProject(specPath: Path, projectPath: Path, file: S
let project = try Project(path: specPath)
let generator = ProjectGenerator(project: project)
let xcodeProject = try generator.generateXcodeProject()
try generator.generateFiles()
try xcodeProject.write(path: projectPath, override: true)

return xcodeProject
Expand Down
34 changes: 34 additions & 0 deletions Tests/XcodeGenKitTests/ProjectGeneratorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,40 @@ class ProjectGeneratorTests: XCTestCase {
try expect(second.outputFiles) == []
try expect(second.outputFilesCompilerFlags) == []
}

$0.it("generates info.plist") {
let plist = Plist(path: "Info.plist", attributes: ["UISupportedInterfaceOrientations": ["UIInterfaceOrientationPortrait", "UIInterfaceOrientationLandscapeLeft"]])
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()

guard let targetConfigListReference = pbxProject.objects.nativeTargets.referenceValues.first?.buildConfigurationList,
let targetConfigReference = pbxProject.objects.configurationLists[targetConfigListReference]?.buildConfigurations.first,
let targetConfig = pbxProject.objects.buildConfigurations[targetConfigReference]
else {
throw failure("Couldn't find Target config")
}

try expect(targetConfig.buildSettings["INFOPLIST_FILE"] as? String) == plist.path

let infoPlistFile = tempPath + plist.path
let data: Data = try infoPlistFile.read()
let infoPlist = try PropertyListSerialization.propertyList(from: data, options: [], format: nil) as! [String: Any]
var expectedInfoPlist: [String: Any] = [:]
expectedInfoPlist["CFBundleIdentifier"] = "$(PRODUCT_BUNDLE_IDENTIFIER)"
expectedInfoPlist["CFBundleInfoDictionaryVersion"] = "6.0"
expectedInfoPlist["CFBundleExecutable"] = "$(EXECUTABLE_NAME)"
expectedInfoPlist["CFBundleName"] = "$(PRODUCT_NAME)"
expectedInfoPlist["CFBundleDevelopmentRegion"] = "$(DEVELOPMENT_LANGUAGE)"
expectedInfoPlist["CFBundleShortVersionString"] = "1.0"
expectedInfoPlist["CFBundleVersion"] = "1"
expectedInfoPlist["CFBundlePackageType"] = "APPL"
expectedInfoPlist["UISupportedInterfaceOrientations"] = ["UIInterfaceOrientationPortrait", "UIInterfaceOrientationLandscapeLeft"]

try expect(NSDictionary.init(dictionary: expectedInfoPlist).isEqual(to: infoPlist)).beTrue()
}
}
}

Expand Down
32 changes: 32 additions & 0 deletions Tests/XcodeGenKitTests/SpecLoadingTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,38 @@ class SpecLoadingTests: XCTestCase {
try expect(target.dependencies[2]) == Dependency(type: .framework, reference: "path")
}

$0.it("parses info plist") {
var targetDictionary = validTarget
targetDictionary["info"] = [
"path": "Info.plist",
"attributes": [
"CFBundleName": "MyAppName",
"UIBackgroundModes": ["fetch"]
]
]

let target = try Target(name: "", jsonDictionary: targetDictionary)
try expect(target.info) == Plist(path: "Info.plist", attributes: [
"CFBundleName": "MyAppName",
"UIBackgroundModes": ["fetch"]
])
}

$0.it("parses entitlement plist") {
var targetDictionary = validTarget
targetDictionary["entitlements"] = [
"path": "app.entitlements",
"attributes": [
"com.apple.security.application-groups": "com.group",
]
]

let target = try Target(name: "", jsonDictionary: targetDictionary)
try expect(target.entitlements) == Plist(path: "app.entitlements", attributes: [
"com.apple.security.application-groups": "com.group",
])
}

$0.it("parses cross platform targets") {
let targetDictionary: [String: Any] = [
"platform": ["iOS", "tvOS"],
Expand Down

0 comments on commit 0af8843

Please sign in to comment.