Skip to content

Commit a5bd263

Browse files
authored
Add support for Custom entitlements files (#2753)
* Extract entitlements and xcconfig services. * Fix tests setup with the new services in ios-project-service tests * Adding newline in xcconfig-service to fix lint error * Add Tests for XCConfig-Service * First test for the merging of the Build XCConfig files * trying to add gems to the Travis CI machine setup * Adding patching the xcconfig file with a default entitlements file location if the user hasn't set one * Fix default empty entitlements file - was needless and caused errors. * Extract entitlements and xcconfig services. * Fix tests setup with the new services in ios-project-service tests * Adding newline in xcconfig-service to fix lint error * Add Tests for XCConfig-Service * First test for the merging of the Build XCConfig files * trying to add gems to the Travis CI machine setup * Adding patching the xcconfig file with a default entitlements file location if the user hasn't set one * Fix default empty entitlements file - was needless and caused errors. * Adding merge entitlements tests * Add merge from App_Resources and Plugin integration test * Fix PR comments
1 parent 6b9ffeb commit a5bd263

9 files changed

+674
-53
lines changed

.travis.yml

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ node_js:
99
git:
1010
submodules: true
1111
before_script:
12+
- gem install xcodeproj
13+
- gem install cocoapods
1214
- npm install grunt
1315
- node_modules/.bin/grunt enableScripts:false
1416
- grunt rebuild

lib/bootstrap.ts

+2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ $injector.require("projectData", "./project-data");
99
$injector.require("projectDataService", "./services/project-data-service");
1010
$injector.requirePublic("projectService", "./services/project-service");
1111
$injector.require("androidProjectService", "./services/android-project-service");
12+
$injector.require("iOSEntitlementsService", "./services/ios-entitlements-service");
1213
$injector.require("iOSProjectService", "./services/ios-project-service");
1314
$injector.require("iOSProvisionService", "./services/ios-provision-service");
15+
$injector.require("xCConfigService", "./services/xcconfig-service");
1416

1517
$injector.require("cocoapodsService", "./services/cocoapods-service");
1618

lib/constants.ts

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export const TEST_RUNNER_NAME = "nativescript-unit-test-runner";
1616
export const LIVESYNC_EXCLUDED_FILE_PATTERNS = ["**/*.js.map", "**/*.ts"];
1717
export const XML_FILE_EXTENSION = ".xml";
1818
export const PLATFORMS_DIR_NAME = "platforms";
19+
export const CODE_SIGN_ENTITLEMENTS = "CODE_SIGN_ENTITLEMENTS";
1920

2021
export class PackageVersion {
2122
static NEXT = "next";
+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import * as path from "path";
2+
import * as constants from "../constants";
3+
import { PlistSession } from "plist-merge-patch";
4+
5+
export class IOSEntitlementsService {
6+
constructor(private $fs: IFileSystem,
7+
private $logger: ILogger,
8+
private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
9+
private $mobileHelper: Mobile.IMobileHelper,
10+
private $pluginsService: IPluginsService) {
11+
}
12+
13+
public static readonly DefaultEntitlementsName: string = "app.entitlements";
14+
15+
private getDefaultAppEntitlementsPath(projectData: IProjectData) : string {
16+
const entitlementsName = IOSEntitlementsService.DefaultEntitlementsName;
17+
const entitlementsPath = path.join(projectData.projectDir,
18+
constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME,
19+
this.$mobileHelper.normalizePlatformName(this.$devicePlatformsConstants.iOS),
20+
entitlementsName);
21+
return entitlementsPath;
22+
}
23+
24+
public getPlatformsEntitlementsPath(projectData: IProjectData) : string {
25+
return path.join(projectData.platformsDir, this.$devicePlatformsConstants.iOS,
26+
projectData.projectName, projectData.projectName + ".entitlements");
27+
}
28+
public getPlatformsEntitlementsRelativePath(projectData: IProjectData): string {
29+
return path.join(projectData.projectName, projectData.projectName + ".entitlements");
30+
}
31+
32+
public async merge(projectData: IProjectData): Promise<void> {
33+
let session = new PlistSession({ log: (txt: string) => this.$logger.trace("App.entitlements: " + txt) });
34+
35+
let projectDir = projectData.projectDir;
36+
let makePatch = (plistPath: string) => {
37+
if (!this.$fs.exists(plistPath)) {
38+
this.$logger.trace("No plist found at: " + plistPath);
39+
return;
40+
}
41+
42+
this.$logger.trace("Schedule merge plist at: " + plistPath);
43+
session.patch({
44+
name: path.relative(projectDir, plistPath),
45+
read: () => this.$fs.readText(plistPath)
46+
});
47+
};
48+
49+
let allPlugins = await this.getAllInstalledPlugins(projectData);
50+
for (let plugin of allPlugins) {
51+
let pluginInfoPlistPath = path.join(plugin.pluginPlatformsFolderPath(this.$devicePlatformsConstants.iOS),
52+
IOSEntitlementsService.DefaultEntitlementsName);
53+
makePatch(pluginInfoPlistPath);
54+
}
55+
56+
let appEntitlementsPath = this.getDefaultAppEntitlementsPath(projectData);
57+
if (this.$fs.exists(appEntitlementsPath)) {
58+
makePatch(appEntitlementsPath);
59+
}
60+
61+
let plistContent = session.build();
62+
this.$logger.trace("App.entitlements: Write to: " + this.getPlatformsEntitlementsPath(projectData));
63+
this.$fs.writeFile(this.getPlatformsEntitlementsPath(projectData), plistContent);
64+
return;
65+
}
66+
67+
private getAllInstalledPlugins(projectData: IProjectData): Promise<IPluginData[]> {
68+
return this.$pluginsService.getAllInstalledPlugins(projectData);
69+
}
70+
}
71+
72+
$injector.register("iOSEntitlementsService", IOSEntitlementsService);

lib/services/ios-project-service.ts

+37-33
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import { EOL } from "os";
1111
import * as temp from "temp";
1212
import * as plist from "plist";
1313
import { IOSProvisionService } from "./ios-provision-service";
14+
import { IOSEntitlementsService } from "./ios-entitlements-service";
15+
import { XCConfigService } from "./xcconfig-service";
1416

1517
export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServiceBase implements IPlatformProjectService {
1618
private static XCODE_PROJECT_EXT_NAME = ".xcodeproj";
@@ -40,9 +42,11 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
4042
private $pluginVariablesService: IPluginVariablesService,
4143
private $xcprojService: IXcprojService,
4244
private $iOSProvisionService: IOSProvisionService,
43-
private $sysInfo: ISysInfo,
4445
private $pbxprojDomXcode: IPbxprojDomXcode,
45-
private $xcode: IXcode) {
46+
private $xcode: IXcode,
47+
private $iOSEntitlementsService: IOSEntitlementsService,
48+
private $sysInfo: ISysInfo,
49+
private $xCConfigService: XCConfigService) {
4650
super($fs, $projectDataService);
4751
}
4852

@@ -668,6 +672,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
668672

669673
public async processConfigurationFilesFromAppResources(release: boolean, projectData: IProjectData): Promise<void> {
670674
await this.mergeInfoPlists(projectData);
675+
await this.$iOSEntitlementsService.merge(projectData);
671676
await this.mergeProjectXcconfigFiles(release, projectData);
672677
for (let pluginData of await this.getAllInstalledPlugins(projectData)) {
673678
await this.$pluginVariablesService.interpolatePluginVariables(pluginData, this.getPlatformData(projectData).configurationFilePath, projectData);
@@ -1085,20 +1090,33 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
10851090
}
10861091

10871092
private async mergeProjectXcconfigFiles(release: boolean, projectData: IProjectData): Promise<void> {
1088-
this.$fs.deleteFile(release ? this.getPluginsReleaseXcconfigFilePath(projectData) : this.getPluginsDebugXcconfigFilePath(projectData));
1093+
let pluginsXcconfigFilePath = release ? this.getPluginsReleaseXcconfigFilePath(projectData) : this.getPluginsDebugXcconfigFilePath(projectData);
1094+
this.$fs.deleteFile(pluginsXcconfigFilePath);
10891095

10901096
let allPlugins: IPluginData[] = await (<IPluginsService>this.$injector.resolve("pluginsService")).getAllInstalledPlugins(projectData);
10911097
for (let plugin of allPlugins) {
10921098
let pluginPlatformsFolderPath = plugin.pluginPlatformsFolderPath(IOSProjectService.IOS_PLATFORM_NAME);
10931099
let pluginXcconfigFilePath = path.join(pluginPlatformsFolderPath, "build.xcconfig");
10941100
if (this.$fs.exists(pluginXcconfigFilePath)) {
1095-
await this.mergeXcconfigFiles(pluginXcconfigFilePath, release ? this.getPluginsReleaseXcconfigFilePath(projectData) : this.getPluginsDebugXcconfigFilePath(projectData));
1101+
await this.mergeXcconfigFiles(pluginXcconfigFilePath, pluginsXcconfigFilePath);
10961102
}
10971103
}
10981104

10991105
let appResourcesXcconfigPath = path.join(projectData.projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME, this.getPlatformData(projectData).normalizedPlatformName, "build.xcconfig");
11001106
if (this.$fs.exists(appResourcesXcconfigPath)) {
1101-
await this.mergeXcconfigFiles(appResourcesXcconfigPath, release ? this.getPluginsReleaseXcconfigFilePath(projectData) : this.getPluginsDebugXcconfigFilePath(projectData));
1107+
await this.mergeXcconfigFiles(appResourcesXcconfigPath, pluginsXcconfigFilePath);
1108+
}
1109+
1110+
// Set Entitlements Property to point to default file if not set explicitly by the user.
1111+
let entitlementsPropertyValue = this.$xCConfigService.readPropertyValue(pluginsXcconfigFilePath, constants.CODE_SIGN_ENTITLEMENTS);
1112+
if (entitlementsPropertyValue === null) {
1113+
temp.track();
1114+
const tempEntitlementsDir = temp.mkdirSync("entitlements");
1115+
const tempEntitlementsFilePath = path.join(tempEntitlementsDir, "set-entitlements.xcconfig");
1116+
const entitlementsRelativePath = this.$iOSEntitlementsService.getPlatformsEntitlementsRelativePath(projectData);
1117+
this.$fs.writeFile(tempEntitlementsFilePath, `CODE_SIGN_ENTITLEMENTS = ${entitlementsRelativePath}${EOL}`);
1118+
1119+
await this.mergeXcconfigFiles(tempEntitlementsFilePath, pluginsXcconfigFilePath);
11021120
}
11031121

11041122
let podFilesRootDirName = path.join("Pods", "Target Support Files", `Pods-${projectData.projectName}`);
@@ -1178,51 +1196,37 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
11781196
return null;
11791197
}
11801198

1181-
private readXCConfig(flag: string, projectData: IProjectData): string {
1182-
let xcconfigFile = path.join(projectData.appResourcesDirectoryPath, this.getPlatformData(projectData).normalizedPlatformName, "build.xcconfig");
1183-
if (this.$fs.exists(xcconfigFile)) {
1184-
let text = this.$fs.readText(xcconfigFile);
1185-
let teamId: string;
1186-
text.split(/\r?\n/).forEach((line) => {
1187-
line = line.replace(/\/(\/)[^\n]*$/, "");
1188-
if (line.indexOf(flag) >= 0) {
1189-
teamId = line.split("=")[1].trim();
1190-
if (teamId[teamId.length - 1] === ';') {
1191-
teamId = teamId.slice(0, -1);
1192-
}
1193-
}
1194-
});
1195-
if (teamId) {
1196-
return teamId;
1197-
}
1198-
}
1199+
private getBuildXCConfigFilePath(projectData: IProjectData): string {
1200+
let buildXCConfig = path.join(projectData.appResourcesDirectoryPath,
1201+
this.getPlatformData(projectData).normalizedPlatformName, "build.xcconfig");
1202+
return buildXCConfig;
1203+
}
1204+
1205+
private readTeamId(projectData: IProjectData): string {
1206+
let teamId = this.$xCConfigService.readPropertyValue(this.getBuildXCConfigFilePath(projectData), "DEVELOPMENT_TEAM");
11991207

12001208
let fileName = path.join(this.getPlatformData(projectData).projectRoot, "teamid");
12011209
if (this.$fs.exists(fileName)) {
1202-
return this.$fs.readText(fileName);
1210+
teamId = this.$fs.readText(fileName);
12031211
}
12041212

1205-
return null;
1206-
}
1207-
1208-
private readTeamId(projectData: IProjectData): string {
1209-
return this.readXCConfig("DEVELOPMENT_TEAM", projectData);
1213+
return teamId;
12101214
}
12111215

12121216
private readXCConfigProvisioningProfile(projectData: IProjectData): string {
1213-
return this.readXCConfig("PROVISIONING_PROFILE", projectData);
1217+
return this.$xCConfigService.readPropertyValue(this.getBuildXCConfigFilePath(projectData), "PROVISIONING_PROFILE");
12141218
}
12151219

12161220
private readXCConfigProvisioningProfileForIPhoneOs(projectData: IProjectData): string {
1217-
return this.readXCConfig("PROVISIONING_PROFILE[sdk=iphoneos*]", projectData);
1221+
return this.$xCConfigService.readPropertyValue(this.getBuildXCConfigFilePath(projectData), "PROVISIONING_PROFILE[sdk=iphoneos*]");
12181222
}
12191223

12201224
private readXCConfigProvisioningProfileSpecifier(projectData: IProjectData): string {
1221-
return this.readXCConfig("PROVISIONING_PROFILE_SPECIFIER", projectData);
1225+
return this.$xCConfigService.readPropertyValue(this.getBuildXCConfigFilePath(projectData), "PROVISIONING_PROFILE_SPECIFIER");
12221226
}
12231227

12241228
private readXCConfigProvisioningProfileSpecifierForIPhoneOs(projectData: IProjectData): string {
1225-
return this.readXCConfig("PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]", projectData);
1229+
return this.$xCConfigService.readPropertyValue(this.getBuildXCConfigFilePath(projectData), "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]");
12261230
}
12271231

12281232
private async getDevelopmentTeam(projectData: IProjectData, teamId?: string): Promise<string> {

lib/services/xcconfig-service.ts

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
export class XCConfigService {
2+
constructor(private $fs: IFileSystem) {
3+
}
4+
5+
/**
6+
* Returns the Value of a Property from a XC Config file.
7+
* @param xcconfigFilePath
8+
* @param propertyName
9+
*/
10+
public readPropertyValue(xcconfigFilePath: string, propertyName: string): string {
11+
if (this.$fs.exists(xcconfigFilePath)) {
12+
let text = this.$fs.readText(xcconfigFilePath);
13+
14+
let property: string;
15+
let isPropertyParsed: boolean = false;
16+
text.split(/\r?\n/).forEach((line) => {
17+
line = line.replace(/\/(\/)[^\n]*$/, "");
18+
if (line.indexOf(propertyName) >= 0) {
19+
let parts = line.split("=");
20+
if (parts.length > 1 && parts[1]) {
21+
property = parts[1].trim();
22+
isPropertyParsed = true;
23+
if (property[property.length - 1] === ';') {
24+
property = property.slice(0, -1);
25+
}
26+
}
27+
}
28+
});
29+
30+
if (isPropertyParsed) {
31+
// property can be an empty string, so we don't check for that.
32+
return property;
33+
}
34+
}
35+
36+
return null;
37+
}
38+
}
39+
40+
$injector.register("xCConfigService", XCConfigService);

0 commit comments

Comments
 (0)