UNPKG

14.3 kBJavaScriptView Raw
1#!/usr/bin/env node
2
3var _ = require('lodash');
4var fs = require('fs');
5var path = require("path");
6var xml2js = require('xml2js');
7var spawn = require('child_process').spawn;
8var parser = new xml2js.Parser();
9require('shelljs/global');
10
11module.exports = function (context) {
12
13 if (context.opts.platforms.indexOf('ios') === -1) {
14 return;
15 }
16
17 var Q = context.requireCordovaModule('q');
18 var podfileContents = [];
19 var rootPath = context.opts.projectRoot;
20 var configXmlPath = path.join(rootPath, 'config.xml');
21 var configParser = getConfigParser(context, configXmlPath);
22 var appName = configParser.name();
23 var oldMinVersion = configParser.getPreference('pods_ios_min_version', 'ios') ||
24 configParser.getPreference('pods_ios_min_version');
25 var iosMinVersion = configParser.getPreference('deployment-target', 'ios') ||
26 configParser.getPreference('deployment-target') ||
27 oldMinVersion || '7.0';
28 var useFrameworks = configParser.getPreference('pods_use_frameworks', 'ios') || configParser.getPreference('pods_use_frameworks') || 'false';
29 var podConfigPath = path.join(rootPath, 'platforms', 'ios', '.pods.json');
30 var pod, podId;
31 var podified = fs.existsSync(podConfigPath);
32 var currentPods = podified ? JSON.parse(fs.readFileSync(podConfigPath)) : {};
33 var workspaceDir = path.join(rootPath, 'platforms', 'ios', '' + appName + '.xcworkspace');
34 var sharedDataDir = path.join(workspaceDir, 'xcshareddata');
35 var pluginDir = context.opts.plugin.pluginInfo.dir;
36 var schemesSrcDir = path.join(pluginDir, 'schemes');
37 var schemesTargetDir = path.join(sharedDataDir, 'xcschemes');
38 var bundlePathsToFix = [];
39 var newPods = {
40 pods: {}
41 };
42
43 if(oldMinVersion) {
44 console.warn('The preference "pods_ios_min_version" has been deprecated. Please use "deployment-target" instead.');
45 }
46
47 console.log('Searching for new pods');
48
49 return Q.all(parsePluginXmls())
50 .then(parseConfigXml)
51 .then(createFiles)
52 .then(installPods)
53 .then(fixBundlePaths)
54 .then(updateBuild);
55
56 function parseConfigXml() {
57
58 parser.parseString(fs.readFileSync('config.xml'), function (err, data) {
59
60 if (data.widget.platform) {
61 console.log('Checking config.xml for pods.');
62 data.widget.platform.forEach(function (platform) {
63 if (platform.$.name === 'ios') {
64 (platform.pod || []).forEach(function (pod) {
65 newPods.pods[pod.$.id] = pod.$;
66 console.log('config.xml requires pod: %s', pod.$.id);
67 });
68 }
69 });
70 }
71 });
72 }
73
74 function parsePluginXmls() {
75
76 var promises = [];
77 context.opts.cordova.plugins.forEach(function (id) {
78
79 var deferred = Q.defer();
80
81 parser.parseString(fs.readFileSync('plugins/' + id + '/plugin.xml'), function (err, data) {
82
83 if (err) {
84 deferred.reject(err);
85 } else {
86 if (data.plugin.platform) {
87 console.log('Checking %s for pods.', id);
88 data.plugin.platform.forEach(function (platform) {
89
90 if (platform.$.name === 'ios') {
91 if (platform.$.name === 'ios') {
92 var podsConfig = (platform['pods-config'] || [])[0];
93 if (podsConfig) {
94 iosMinVersion = maxVer(iosMinVersion, podsConfig.$['ios-min-version']);
95 useFrameworks = podsConfig.$['use-frameworks'] === 'true' ? 'true' : useFrameworks;
96 }
97 (platform.pod || []).forEach(function (pod) {
98 newPods.pods[pod.$.id] = pod.$;
99 console.log('%s requires pod: %s', id, pod.$.id);
100 });
101 }
102 }
103 });
104 }
105
106 deferred.resolve();
107 }
108 });
109
110 promises.push(deferred.promise);
111 });
112 return promises;
113 }
114
115 function createFiles() {
116
117 newPods.iosMinVersion = iosMinVersion;
118 newPods.useFrameworks = useFrameworks === 'true';
119
120 if (!podified || !_.isEqual(newPods, currentPods)) {
121
122 podfileContents.push("platform :ios, '" + iosMinVersion + "'");
123 if (useFrameworks === 'true') {
124 podfileContents.push("use_frameworks!");
125 }
126 podfileContents.push("target '" + appName + "' do");
127
128 for (podId in newPods.pods) {
129 pod = newPods.pods[podId];
130 var entry = "\tpod '" + pod.id + "'";
131 if (pod['fix-bundle-path']) {
132 bundlePathsToFix.push(pod['fix-bundle-path']);
133 }
134 if (pod.version) {
135 entry += ", '" + pod.version + "'";
136 } else if (pod.git) {
137 entry += ", :git => '" + pod.git + "'";
138 if (pod.tag) {
139 entry += ", :tag => '" + pod.tag + "'";
140 } else if (pod.branch) {
141 entry += ", :branch => '" + pod.branch + "'";
142 } else if (pod.commit) {
143 entry += ", :commit => '" + pod.commit + "'";
144 }
145
146 } else if (pod.path) {
147 entry += ", :path => '" + pod.path + "'";
148 } else if (pod.subspecs) {
149 var specs = pod.subspec.split(',').map(function (spec) {
150 return "'" + spec.trim() + "'";
151 });
152 entry += ", :subspecs => [" + specs.join() + "]";
153 } else if (pod.configuration) {
154 entry += ", :configuration => '" + pod.configuration + "'";
155 } else if (pod.configurations) {
156 var configs = pod.configurations.split(',').map(function (config) {
157 return "'" + config.trim() + "'";
158 });
159 entry += ", :subspecs => [" + configs.join() + "]";
160 }
161 podfileContents.push(entry);
162 }
163 podfileContents.push('end');
164 fs.writeFileSync('platforms/ios/Podfile', podfileContents.join('\n'));
165
166 var debugXcContents = fs.readFileSync('platforms/ios/cordova/build-debug.xcconfig', 'utf8');
167 var includeRegex = new RegExp('#include "Pods/Target Support Files/Pods-' + appName + '/Pods-' + appName + '\\.\\w+\\.xcconfig"');
168 if (!includeRegex.test(debugXcContents)) {
169 fs.writeFileSync('platforms/ios/cordova/build-debug.xcconfig', debugXcContents + '\n' + '#include "Pods/Target Support Files/Pods-' + appName + '/Pods-' + appName + '.debug.xcconfig"');
170 }
171 var releaseXcContents = fs.readFileSync('platforms/ios/cordova/build-release.xcconfig', 'utf8');
172 if (!includeRegex.test(releaseXcContents)) {
173 fs.writeFileSync('platforms/ios/cordova/build-release.xcconfig', releaseXcContents + '\n' + '#include "Pods/Target Support Files/Pods-' + appName + '/Pods-' + appName + '.release.xcconfig"');
174 }
175
176 var buildConfigContext = fs.readFileSync('platforms/ios/cordova/build.xcconfig', 'utf8');
177 var bridgedHeaderRegex;
178 if (useFrameworks) {
179 bridgedHeaderRegex = /SWIFT_OBJC_BRIDGING_HEADER/g;
180 fs.writeFileSync('platforms/ios/cordova/build.xcconfig', buildConfigContext.replace(bridgedHeaderRegex, '//SWIFT_OBJC_BRIDGING_HEADER'));
181 } else {
182 bridgedHeaderRegex = /\/\/SWIFT_OBJC_BRIDGING_HEADER/g;
183 fs.writeFileSync('platforms/ios/cordova/build.xcconfig', buildConfigContext.replace(bridgedHeaderRegex, 'SWIFT_OBJC_BRIDGING_HEADER'));
184
185 }
186
187 fs.writeFileSync(podConfigPath, JSON.stringify(newPods, null, '\t'));
188 } else {
189 console.log('No new pods detects');
190 }
191 }
192
193 function installPods() {
194
195 var deferred = Q.defer();
196
197 if (which('pod')) {
198
199 if (!podified || !_.isEqual(newPods, currentPods)) {
200 console.log("Installing pods");
201 console.log("Sit back and relax this could take a while.");
202 var podInstall = spawn('pod', ['install'], {
203 cwd: 'platforms/ios'
204 });
205 podInstall.stdout.on('data', function (data) {
206 console.log(data.toString('utf8'));
207 });
208 podInstall.stderr.on('data', function (data) {
209 console.error(data.toString('utf8'));
210 });
211 podInstall.on('close', function (exitCode) {
212 deferred.resolve(exitCode === 0);
213 });
214 } else {
215 deferred.resolve(false);
216 }
217
218 } else {
219 console.log("\nAh man!. It doesn't look like you have CocoaPods installed.\n\nYou have two choices.\n\n1. Install Cocoapods:\n$ sudo gem install cocoapods\n2. Manually install the dependencies.");
220 deferred.resolve(false);
221 }
222
223 return deferred.promise;
224 }
225
226 function fixBundlePaths(shouldRun) {
227
228 if (bundlePathsToFix.length) {
229 var podsResourcesSh = 'platforms/ios/Pods/Target Support Files/Pods-' + appName + '/Pods-' + appName + '-resources.sh';
230 var content = fs.readFileSync(podsResourcesSh, 'utf8');
231
232 bundlePathsToFix.forEach(function (path) {
233 var fixedPath = appName + '.app/' + path.split('/').slice(1).join('/');
234 var regex = new RegExp('(install_resource.*)' + path, 'gi');
235 content = content.replace(regex, "$1" + fixedPath);
236
237 });
238 fs.writeFileSync(podsResourcesSh, content);
239 }
240
241
242 return shouldRun;
243 }
244
245 function updateBuild(shouldRun) {
246
247 if (shouldRun) {
248 console.log('Updating ios build to use workspace.');
249 var buildContent = fs.readFileSync('platforms/ios/cordova/lib/build.js', 'utf8');
250 var targetRegex = new RegExp("'-target',\\s*projectName\\s*,", 'g');
251 var targetFix = "'-scheme', projectName,";
252 var projectRegex = new RegExp("'-project'\\s*,\\s*projectName\\s*\\+\\s*'\\.xcodeproj'\\s*,", 'g');
253 var projectFix = "'-workspace', projectName + '.xcworkspace',";
254 var xcodeprojRegex = /\.xcodeproj/g;
255 var xcodeprojFix = '.xcworkspace';
256 var fixedBuildContent = buildContent
257 .replace(targetRegex, targetFix)
258 .replace(projectRegex, projectFix)
259 .replace(xcodeprojRegex, xcodeprojFix);
260
261 fs.writeFileSync('platforms/ios/cordova/lib/build.js', fixedBuildContent);
262
263 if (!podified) {
264 console.log('Adding schemes');
265 mkdir(sharedDataDir);
266 mkdir(schemesTargetDir);
267 copyTpl(schemesSrcDir + '/CordovaLib.xcscheme', schemesTargetDir + '/CordovaLib.xcscheme', {
268 appName: appName
269 });
270 copyTpl(schemesSrcDir + '/App.xcscheme', schemesTargetDir + '/' + appName + '.xcscheme', {
271 appName: appName,
272 appId: '1D6058900D05DD3D006BFB54'
273 });
274 }
275 }
276 }
277
278 function fixSwiftLegacy(shouldRun){
279 var directories = getDirectories(path.join(__dirname + '/../../../platforms/ios/Pods/Target Support Files')),
280 podXcContents,
281 SWIFT_VERSION_REGX = /SWIFT_VERSION=(?:\d*\.)\d/g;
282 if(useLegacy){
283 for(var i = 0; i < directories.length; i++){
284 if(directories[i].indexOf(appName) === -1){
285 podXcContents = fs.readFileSync('platforms/ios/Pods/Target Support Files/' + directories[i] + '/' + directories[i] + '.xcconfig', 'utf8');
286 if(podXcContents.indexOf('SWIFT_VERSION') === -1){
287 fs.writeFileSync('platforms/ios/Pods/Target Support Files/' + directories[i] + '/' + directories[i] + '.xcconfig', podXcContents + '\n' + 'SWIFT_VERSION=' + useLegacy)
288 } else {
289 fs.writeFileSync('platforms/ios/Pods/Target Support Files/' + directories[i] + '/' + directories[i] + '.xcconfig', podXcContents.replace(SWIFT_VERSION_REGX, 'SWIFT_VERSION=' + useLegacy))
290 }
291 }
292 }
293
294 console.log('Using Swift Version ' + useLegacy);
295 }
296
297 return shouldRun;
298 }
299
300 function templify(str, data) {
301
302 return str.replace(/{[^{}]+}/g, function (key) {
303 var k = key.replace(/[{}]+/g, "");
304 return data.hasOwnProperty(k) ? data[k] : "";
305 });
306 }
307
308 function copyTpl(src, dest, data) {
309 fs.writeFileSync(dest, templify(fs.readFileSync(src, 'utf8'), data));
310 }
311
312 function getConfigParser(context, config) {
313 var semver = context.requireCordovaModule('semver');
314 var ConfigParser;
315
316 if (semver.lt(context.opts.cordova.version, '5.4.0')) {
317 ConfigParser = context.requireCordovaModule('cordova-lib/src/ConfigParser/ConfigParser');
318 } else {
319 ConfigParser = context.requireCordovaModule('cordova-common/src/ConfigParser/ConfigParser');
320 }
321
322 return new ConfigParser(config);
323 }
324
325 function maxVer(v1, v2) {
326
327 if (!v2) {
328 return v1;
329 }
330
331 var v1Parts = v1.split('.');
332 var v2Parts = v2.split('.');
333
334 if (+v1Parts[0] > +v2Parts[0]) {
335 return v1;
336 } else if (+v1Parts[0] < +v2Parts[0]) {
337 return v2;
338 } else if (+v1Parts[1] > +v2Parts[1]) {
339 return v1;
340 } else {
341 return v2;
342 }
343 }
344};
345