Skip to content

Commit bb1979d

Browse files
Merge pull request #2256 from NativeScript/milanov/merge-podfile-post-install
Merge post_install hooks in Podfile
2 parents 417397f + 48e2f6b commit bb1979d

File tree

5 files changed

+271
-28
lines changed

5 files changed

+271
-28
lines changed

lib/definitions/project.d.ts

+8
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,12 @@ interface ICocoaPodsService {
112112
* @return {string} The footer which needs to be placed at the end of a Podfile.
113113
*/
114114
getPodfileFooter(): string;
115+
116+
/**
117+
* Merges the content of hooks with the provided name if there are more than one hooks with this name in the Podfile.
118+
* @param {string} hookName The name of the hook.
119+
* @param {string} pathToPodfile The path to the Podfile.
120+
* @return {IFuture<void>}
121+
*/
122+
mergePodfileHookContent(sectionName: string, pathToPodfile: string): IFuture<void>
115123
}

lib/services/cocoapods-service.ts

+57
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,70 @@
11
import {EOL} from "os";
22

3+
interface IRubyFunction {
4+
functionName: string;
5+
functionParameters?: string;
6+
}
7+
38
export class CocoaPodsService implements ICocoaPodsService {
9+
constructor(private $fs: IFileSystem) { }
10+
411
public getPodfileHeader(targetName: string): string {
512
return `use_frameworks!${EOL}${EOL}target "${targetName}" do${EOL}`;
613
}
714

815
public getPodfileFooter(): string {
916
return `${EOL}end`;
1017
}
18+
19+
public mergePodfileHookContent(hookName: string, pathToPodfile: string): IFuture<void> {
20+
return (() => {
21+
if (!this.$fs.exists(pathToPodfile).wait()) {
22+
throw new Error(`The Podfile ${pathToPodfile} does not exist.`);
23+
}
24+
25+
let podfileContent = this.$fs.readText(pathToPodfile).wait();
26+
let hookStart = `${hookName} do`;
27+
28+
let hookDefinitionRegExp = new RegExp(`${hookStart} *(\\|(\\w+)\\|)?`, "g");
29+
let newFunctionNameIndex = 1;
30+
let newFunctions: IRubyFunction[] = [];
31+
32+
let replacedContent = podfileContent.replace(hookDefinitionRegExp, (substring: string, firstGroup: string, secondGroup: string, index: number): string => {
33+
let newFunctionName = `${hookName}${newFunctionNameIndex++}`;
34+
let newDefinition = `def ${newFunctionName}`;
35+
36+
let rubyFunction: IRubyFunction = { functionName: newFunctionName };
37+
// firstGroup is the block parameter, secondGroup is the block parameter name.
38+
if (firstGroup && secondGroup) {
39+
newDefinition = `${newDefinition} (${secondGroup})`;
40+
rubyFunction.functionParameters = secondGroup;
41+
}
42+
43+
newFunctions.push(rubyFunction);
44+
return newDefinition;
45+
});
46+
47+
if (newFunctions.length > 1) {
48+
// Execute all methods in the hook and pass the parameter to them.
49+
let blokParameterName = "installer";
50+
let mergedHookContent = `${hookStart} |${blokParameterName}|${EOL}`;
51+
52+
_.each(newFunctions, (rubyFunction: IRubyFunction) => {
53+
let functionExecution = rubyFunction.functionName;
54+
if (rubyFunction.functionParameters && rubyFunction.functionParameters.length) {
55+
functionExecution = `${functionExecution} ${blokParameterName}`;
56+
}
57+
58+
mergedHookContent = `${mergedHookContent} ${functionExecution}${EOL}`;
59+
});
60+
61+
mergedHookContent = `${mergedHookContent}end`;
62+
63+
let newPodfileContent = `${replacedContent}${EOL}${mergedHookContent}`;
64+
this.$fs.writeFile(pathToPodfile, newPodfileContent).wait();
65+
}
66+
}).future<void>()();
67+
}
1168
}
1269

1370
$injector.register("cocoapodsService", CocoaPodsService);

lib/services/ios-project-service.ts

+13-13
Original file line numberDiff line numberDiff line change
@@ -269,10 +269,10 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
269269
args = args.concat((buildConfig && buildConfig.architectures) || defaultArchitectures);
270270

271271
let xcodeBuildVersion = this.getXcodeVersion();
272-
if (helpers.versionCompare(xcodeBuildVersion, "8.0")>=0) {
272+
if (helpers.versionCompare(xcodeBuildVersion, "8.0") >= 0) {
273273
let teamId = this.getDevelopmentTeam();
274274
if (teamId) {
275-
args = args.concat("DEVELOPMENT_TEAM="+teamId);
275+
args = args.concat("DEVELOPMENT_TEAM=" + teamId);
276276
}
277277
}
278278
} else {
@@ -478,10 +478,10 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
478478
try {
479479
this.$fs.createDirectory(path.dirname(compatabilityXibPath)).wait();
480480
this.$fs.writeFile(compatabilityXibPath, content).wait();
481-
} catch(e) {
481+
} catch (e) {
482482
this.$logger.warn("We have failed to add compatability LaunchScreen.xib due to: " + e);
483483
}
484-
} catch(e) {
484+
} catch (e) {
485485
this.$logger.warn("We have failed to check if we need to add a compatability LaunchScreen.xib due to: " + e);
486486
}
487487
}
@@ -687,7 +687,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
687687

688688
let firstPostInstallIndex = projectPodfileContent.indexOf(IOSProjectService.PODFILE_POST_INSTALL_SECTION_NAME);
689689
if (firstPostInstallIndex !== -1 && firstPostInstallIndex !== projectPodfileContent.lastIndexOf(IOSProjectService.PODFILE_POST_INSTALL_SECTION_NAME)) {
690-
this.$logger.warn(`Podfile contains more than one post_install sections. You need to open ${this.projectPodFilePath} file and manually resolve this issue.`);
690+
this.$cocoapodsService.mergePodfileHookContent(IOSProjectService.PODFILE_POST_INSTALL_SECTION_NAME, this.projectPodFilePath).wait();
691691
}
692692

693693
let xcuserDataPath = path.join(this.xcodeprojPath, "xcuserdata");
@@ -948,7 +948,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
948948
let pluginPlatformsFolderPath = plugin.pluginPlatformsFolderPath(IOSProjectService.IOS_PLATFORM_NAME);
949949
let pluginXcconfigFilePath = path.join(pluginPlatformsFolderPath, "build.xcconfig");
950950
if (this.$fs.exists(pluginXcconfigFilePath).wait()) {
951-
this.mergeXcconfigFiles(pluginXcconfigFilePath,this.$options.release ? this.pluginsReleaseXcconfigFilePath : this.pluginsDebugXcconfigFilePath).wait();
951+
this.mergeXcconfigFiles(pluginXcconfigFilePath, this.$options.release ? this.pluginsReleaseXcconfigFilePath : this.pluginsDebugXcconfigFilePath).wait();
952952
}
953953
}
954954

@@ -999,7 +999,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
999999
return xcodeBuildVersion;
10001000
}
10011001

1002-
private getDevelopmentTeams(): Array<{id:string, name: string}> {
1002+
private getDevelopmentTeams(): Array<{ id: string, name: string }> {
10031003
let dir = path.join(process.env.HOME, "Library/MobileDevice/Provisioning Profiles/");
10041004
let files = this.$fs.readDirectory(dir).wait();
10051005
let teamIds: any = {};
@@ -1012,18 +1012,18 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
10121012
teamIds[teamId] = teamName;
10131013
}
10141014
}
1015-
let teamIdsArray = new Array<{id:string, name: string}>();
1015+
let teamIdsArray = new Array<{ id: string, name: string }>();
10161016
for (let teamId in teamIds) {
1017-
teamIdsArray.push({ id:teamId, name:teamIds[teamId] });
1017+
teamIdsArray.push({ id: teamId, name: teamIds[teamId] });
10181018
}
10191019
return teamIdsArray;
10201020
}
10211021

10221022
private getProvisioningProfileValue(name: string, text: string): string {
1023-
let findStr = "<key>"+name+"</key>";
1023+
let findStr = "<key>" + name + "</key>";
10241024
let index = text.indexOf(findStr);
10251025
if (index > 0) {
1026-
index = text.indexOf("<string>", index+findStr.length);
1026+
index = text.indexOf("<string>", index + findStr.length);
10271027
if (index > 0) {
10281028
index += "<string>".length;
10291029
let endIndex = text.indexOf("</string>", index);
@@ -1043,7 +1043,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
10431043
line = line.replace(/\/(\/)[^\n]*$/, "");
10441044
if (line.indexOf("DEVELOPMENT_TEAM") >= 0) {
10451045
teamId = line.split("=")[1].trim();
1046-
if (teamId[teamId.length-1] === ';') {
1046+
if (teamId[teamId.length - 1] === ';') {
10471047
teamId = teamId.slice(0, -1);
10481048
}
10491049
}
@@ -1086,7 +1086,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
10861086
"Yes, persist the team id in platforms folder.",
10871087
"No, don't persist this setting."
10881088
];
1089-
let choicePersist = this.$prompter.promptForChoice("Do you want to make teamId: "+teamId+" a persistent choice for your app?", choicesPersist).wait();
1089+
let choicePersist = this.$prompter.promptForChoice("Do you want to make teamId: " + teamId + " a persistent choice for your app?", choicesPersist).wait();
10901090
switch (choicesPersist.indexOf(choicePersist)) {
10911091
case 0:
10921092
let xcconfigFile = path.join(this.$projectData.appResourcesDirectoryPath, this.platformData.normalizedPlatformName, "build.xcconfig");

test/cocoapods-service.ts

+178
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
import * as yok from "../lib/common/yok";
2+
import {assert} from "chai";
3+
import {CocoaPodsService} from "../lib/services/cocoapods-service";
4+
import {EOL} from "os";
5+
import Future = require("fibers/future");
6+
7+
interface IMergePodfileHooksTestCase {
8+
input: string;
9+
output: string;
10+
testCaseDescription: string;
11+
}
12+
13+
function createTestInjector(): IInjector {
14+
let testInjector: IInjector = new yok.Yok();
15+
16+
testInjector.register("fs", {});
17+
testInjector.register("cocoapodsService", CocoaPodsService);
18+
19+
return testInjector;
20+
}
21+
22+
// The newline characters should be replaced with EOL because on Windows the EOL is \r\n
23+
// but the character which is placed in `` for newline is only \n
24+
// if we do not replace the newline characters the tests will pass only on linux and mac.
25+
function changeNewLineCharacter(input: string): string {
26+
return input ? input.replace(/\r?\n/g, EOL) : input;
27+
}
28+
29+
describe("Cocoapods service", () => {
30+
describe("merge Podfile hooks", () => {
31+
let testInjector: IInjector;
32+
let cocoapodsService: ICocoaPodsService;
33+
let newPodfileContent: string;
34+
35+
let mockFileSystem = (injector: IInjector, podfileContent: string): void => {
36+
let fs: IFileSystem = injector.resolve("fs");
37+
38+
fs.exists = () => Future.fromResult(true);
39+
fs.readText = () => Future.fromResult(podfileContent);
40+
fs.writeFile = (pathToFile: string, content: any) => {
41+
newPodfileContent = content;
42+
return Future.fromResult();
43+
};
44+
};
45+
46+
let testCaces: IMergePodfileHooksTestCase[] = [
47+
{
48+
input: `
49+
target 'MyApp' do
50+
pod 'GoogleAnalytics', '~> 3.1'
51+
target 'MyAppTests' do
52+
inherit! :search_paths
53+
pod 'OCMock', '~> 2.0.1'
54+
end
55+
end
56+
57+
post_install do |installer|
58+
installer.pods_project.targets.each do |target|
59+
puts target.name
60+
end
61+
end
62+
post_install do |installer|
63+
installer.pods_project.targets.each do |target|
64+
puts target.name
65+
end
66+
end
67+
post_install do |installer|
68+
installer.pods_project.targets.each do |target|
69+
puts target.name
70+
end
71+
end`,
72+
output: `
73+
target 'MyApp' do
74+
pod 'GoogleAnalytics', '~> 3.1'
75+
target 'MyAppTests' do
76+
inherit! :search_paths
77+
pod 'OCMock', '~> 2.0.1'
78+
end
79+
end
80+
81+
def post_install1 (installer)
82+
installer.pods_project.targets.each do |target|
83+
puts target.name
84+
end
85+
end
86+
def post_install2 (installer)
87+
installer.pods_project.targets.each do |target|
88+
puts target.name
89+
end
90+
end
91+
def post_install3 (installer)
92+
installer.pods_project.targets.each do |target|
93+
puts target.name
94+
end
95+
end
96+
post_install do |installer|
97+
post_install1 installer
98+
post_install2 installer
99+
post_install3 installer
100+
end`,
101+
testCaseDescription: "should merge more than one hooks with block parameter correctly."
102+
}, {
103+
input: `
104+
target 'MyApp' do
105+
pod 'GoogleAnalytics', '~> 3.1'
106+
target 'MyAppTests' do
107+
inherit! :search_paths
108+
pod 'OCMock', '~> 2.0.1'
109+
end
110+
111+
post_install do |installer_representation|
112+
installer_representation.pods_project.targets.each do |target|
113+
puts target.name
114+
end
115+
end
116+
post_install do
117+
puts "Hello World!"
118+
end
119+
end`,
120+
output: `
121+
target 'MyApp' do
122+
pod 'GoogleAnalytics', '~> 3.1'
123+
target 'MyAppTests' do
124+
inherit! :search_paths
125+
pod 'OCMock', '~> 2.0.1'
126+
end
127+
128+
def post_install1 (installer_representation)
129+
installer_representation.pods_project.targets.each do |target|
130+
puts target.name
131+
end
132+
end
133+
def post_install2
134+
puts "Hello World!"
135+
end
136+
end
137+
post_install do |installer|
138+
post_install1 installer
139+
post_install2
140+
end`,
141+
testCaseDescription: "should merge more than one hooks with and without block parameter correctly."
142+
}, {
143+
input: `
144+
target 'MyApp' do
145+
pod 'GoogleAnalytics', '~> 3.1'
146+
target 'MyAppTests' do
147+
inherit! :search_paths
148+
pod 'OCMock', '~> 2.0.1'
149+
end
150+
end
151+
152+
post_install do |installer|
153+
installer.pods_project.targets.each do |target|
154+
puts target.name
155+
end
156+
end`,
157+
output: null,
158+
testCaseDescription: "should not change the Podfile when there is only one hook."
159+
}
160+
];
161+
162+
beforeEach(() => {
163+
testInjector = createTestInjector();
164+
cocoapodsService = testInjector.resolve("cocoapodsService");
165+
newPodfileContent = null;
166+
});
167+
168+
_.each(testCaces, (testCase: IMergePodfileHooksTestCase) => {
169+
it(testCase.testCaseDescription, () => {
170+
mockFileSystem(testInjector, testCase.input);
171+
172+
cocoapodsService.mergePodfileHookContent("post_install", "").wait();
173+
174+
assert.deepEqual(changeNewLineCharacter(newPodfileContent), changeNewLineCharacter(testCase.output));
175+
});
176+
});
177+
});
178+
});

0 commit comments

Comments
 (0)