UNPKG

12 kBJavaScriptView Raw
1#!/usr/bin/env node
2
3var _ = require('lodash');
4var fs = require('fs');
5var path = require("path");
6var xml2js = require('xml2js');
7var commandExists = require('command-exists');
8var spawn = require('child_process').spawn;
9var parser = new xml2js.Parser();
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 iosMinVersion = configParser.getPreference('pods_ios_min_version', 'ios') || configParser.getPreference('pods_ios_min_version') || '7.0';
24 var useFrameworks = configParser.getPreference('pods_use_frameworks', 'ios') || configParser.getPreference('pods_use_frameworks') || 'false';
25 var podConfigPath = path.join(rootPath, 'platforms', 'ios', '.pods.json');
26 var pod, podId;
27 var podified = fs.existsSync(podConfigPath);
28 var currentPods = podified ? JSON.parse(fs.readFileSync(podConfigPath)) : {};
29 var workspaceDir = path.join(rootPath, 'platforms', 'ios', '' + appName + '.xcworkspace');
30 var sharedDataDir = path.join(workspaceDir, 'xcshareddata');
31 var pluginDir = context.opts.plugin.pluginInfo.dir;
32 var schemesSrcDir = path.join(pluginDir, 'schemes');
33 var schemesTargetDir = path.join(sharedDataDir, 'xcschemes');
34 var newPods = {
35 pods: {}
36 };
37
38 console.log('Searching for new pods');
39
40 return Q.all(parsePluginXmls())
41 .then(parseConfigXml)
42 .then(createFiles)
43 .then(installPods)
44 .then(updateBuild);
45
46 function parseConfigXml() {
47
48 parser.parseString(fs.readFileSync('config.xml'), function (err, data) {
49
50 if (data.widget.platform) {
51 console.log('Checking config.xml for pods.');
52 data.widget.platform.forEach(function (platform) {
53 if (platform.$.name === 'ios') {
54 (platform.pod || []).forEach(function (pod) {
55 newPods.pods[pod.$.id] = pod.$;
56 console.log('config.xml requires pod: %s', pod.$.id);
57 });
58 }
59 });
60 }
61 });
62 }
63
64 function parsePluginXmls() {
65
66 var promises = [];
67 context.opts.cordova.plugins.forEach(function (id) {
68
69 var deferred = Q.defer();
70
71 parser.parseString(fs.readFileSync('plugins/' + id + '/plugin.xml'), function (err, data) {
72
73 if (err) {
74 deferred.reject(err);
75 } else {
76 if (data.plugin.platform) {
77 console.log('Checking %s for pods.', id);
78 data.plugin.platform.forEach(function (platform) {
79
80 if (platform.$.name === 'ios') {
81 if (platform.$.name === 'ios') {
82 var podsConfig = (platform['pods-config'] || [])[0];
83 if(podsConfig) {
84 iosMinVersion = maxVer(iosMinVersion, podsConfig.$['ios-min-version']);
85 useFrameworks = podsConfig.$['use-frameworks'] === 'true' ? 'true' : useFrameworks;
86 }
87 (platform.pod || []).forEach(function (pod) {
88 newPods.pods[pod.$.id] = pod.$;
89 console.log('%s requires pod: %s', id, pod.$.id);
90 });
91 }
92 }
93 });
94 }
95
96 deferred.resolve();
97 }
98 });
99
100 promises.push(deferred.promise);
101 });
102 return promises;
103 }
104
105 function createFiles() {
106
107 newPods.iosMinVersion = iosMinVersion;
108 newPods.useFrameworks = useFrameworks === 'true';
109
110 if (!podified || !_.isEqual(newPods, currentPods)) {
111
112 podfileContents.push("platform :ios, '" + iosMinVersion + "'");
113 if (useFrameworks === 'true') {
114 podfileContents.push("use_frameworks!");
115 }
116 podfileContents.push("target '" + appName + "' do");
117
118 for (podId in newPods.pods) {
119 pod = newPods.pods[podId];
120 var entry = "\tpod '" + pod.id + "'";
121 if (pod.version) {
122 entry += ", '" + pod.version + "'";
123 } else if (pod.git) {
124 entry += ", :git => '" + pod.git + "'";
125 if (pod.tag) {
126 entry += ", :tag => '" + pod.tag + "'";
127 } else if (pod.branch) {
128 entry += ", :branch => '" + pod.branch + "'";
129 } else if (pod.commit) {
130 entry += ", :commit => '" + pod.commit + "'";
131 }
132
133 } else if (pod.path) {
134 entry += ", :path => '" + pod.path + "'";
135 } else if (pod.subspecs) {
136 var specs = pod.subspec.split(',').map(function (spec) {
137 return "'" + spec.trim() + "'";
138 });
139 entry += ", :subspecs => [" + specs.join() + "]";
140 } else if (pod.configuration) {
141 entry += ", :configuration => '" + pod.configuration + "'";
142 } else if (pod.configurations) {
143 var configs = pod.configurations.split(',').map(function (config) {
144 return "'" + config.trim() + "'";
145 });
146 entry += ", :subspecs => [" + configs.join() + "]";
147 }
148 podfileContents.push(entry);
149 }
150 podfileContents.push('end');
151 fs.writeFileSync('platforms/ios/Podfile', podfileContents.join('\n'));
152
153 var debugXcContents = fs.readFileSync('platforms/ios/cordova/build-debug.xcconfig', 'utf8');
154 var includeRegex = new RegExp('#include "Pods/Target Support Files/Pods-' + appName + '/Pods-' + appName + '\\.\\w+\\.xcconfig"');
155 if (!includeRegex.test(debugXcContents)) {
156 fs.writeFileSync('platforms/ios/cordova/build-debug.xcconfig', debugXcContents + '\n' + '#include "Pods/Target Support Files/Pods-' + appName + '/Pods-' + appName + '.debug.xcconfig"');
157 }
158 var releaseXcContents = fs.readFileSync('platforms/ios/cordova/build-release.xcconfig', 'utf8');
159 if (!includeRegex.test(releaseXcContents)) {
160 fs.writeFileSync('platforms/ios/cordova/build-release.xcconfig', releaseXcContents + '\n' + '#include "Pods/Target Support Files/Pods-' + appName + '/Pods-' + appName + '.release.xcconfig"');
161 }
162
163 var buildConfigContext = fs.readFileSync('platforms/ios/cordova/build.xcconfig', 'utf8');
164 var bridgedHeaderRegex;
165 if (useFrameworks) {
166 bridgedHeaderRegex = /SWIFT_OBJC_BRIDGING_HEADER/g;
167 fs.writeFileSync('platforms/ios/cordova/build.xcconfig', buildConfigContext.replace(bridgedHeaderRegex, '//SWIFT_OBJC_BRIDGING_HEADER'));
168 } else {
169 bridgedHeaderRegex = /\/\/SWIFT_OBJC_BRIDGING_HEADER/g;
170 fs.writeFileSync('platforms/ios/cordova/build.xcconfig', buildConfigContext.replace(bridgedHeaderRegex, 'SWIFT_OBJC_BRIDGING_HEADER'));
171
172 }
173
174 fs.writeFileSync(podConfigPath, JSON.stringify(newPods, null, '\t'));
175 } else {
176 console.log('No new pods detects');
177 }
178 }
179
180 function installPods() {
181
182 var deferred = Q.defer();
183 commandExists('pod', function (err, exists) {
184
185 if (exists) {
186
187 if (!podified || !_.isEqual(newPods, currentPods)) {
188 console.log("Installing pods");
189 console.log("Sit back and relax this could take a while.");
190 var podInstall = spawn('pod', ['install'], {
191 cwd: 'platforms/ios'
192 });
193 podInstall.stdout.on('data', function(data) {
194 console.log(data.toString('utf8'));
195 });
196 podInstall.stderr.on('data', function(data) {
197 console.error(data.toString('utf8'));
198 });
199 podInstall.on('close', function(exitCode) {
200 deferred.resolve(exitCode === 0);
201 });
202 } else {
203 deferred.resolve(false);
204 }
205
206 } else {
207 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.");
208 deferred.resolve(false);
209 }
210 });
211
212 return deferred.promise;
213 }
214
215 function updateBuild(shouldRun) {
216
217 if(shouldRun) {
218 console.log('Updating ios build to use workspace.');
219 var buildContent = fs.readFileSync('platforms/ios/cordova/lib/build.js', 'utf8');
220 var targetRegex = new RegExp("'-target',\\s*projectName\\s*,", 'g');
221 var targetFix = "'-scheme', projectName,";
222 var projectRegex = new RegExp("'-project'\\s*,\\s*projectName\\s*\\+\\s*'\\.xcodeproj'\\s*,", 'g');
223 var projectFix = "'-workspace', projectName + '.xcworkspace',";
224 var xcodeprojRegex = /\.xcodeproj/g;
225 var xcodeprojFix = '.xcworkspace';
226 var fixedBuildContent = buildContent
227 .replace(targetRegex, targetFix)
228 .replace(projectRegex, projectFix)
229 .replace(xcodeprojRegex, xcodeprojFix);
230
231 fs.writeFileSync('platforms/ios/cordova/lib/build.js', fixedBuildContent);
232
233 if (!podified) {
234 console.log('Adding schemes');
235 fs.mkdirSync(sharedDataDir);
236 fs.mkdirSync(schemesTargetDir);
237 copyTpl(schemesSrcDir + '/CordovaLib.xcscheme', schemesTargetDir + '/CordovaLib.xcscheme', {
238 appName: appName
239 });
240 copyTpl(schemesSrcDir + '/App.xcscheme', schemesTargetDir + '/' + appName + '.xcscheme', {
241 appName: appName,
242 appId: '1D6058900D05DD3D006BFB54'
243 });
244 }
245 }
246 }
247
248 function templify(str, data) {
249
250 return str.replace(/{[^{}]+}/g, function (key) {
251 var k = key.replace(/[{}]+/g, "");
252 return data.hasOwnProperty(k) ? data[k] : "";
253 });
254 }
255
256 function copyTpl(src, dest, data) {
257 fs.writeFileSync(dest, templify(fs.readFileSync(src, 'utf8'), data));
258 }
259
260 function getConfigParser(context, config) {
261 var semver = context.requireCordovaModule('semver');
262 var ConfigParser;
263
264 if (semver.lt(context.opts.cordova.version, '5.4.0')) {
265 ConfigParser = context.requireCordovaModule('cordova-lib/src/ConfigParser/ConfigParser');
266 } else {
267 ConfigParser = context.requireCordovaModule('cordova-common/src/ConfigParser/ConfigParser');
268 }
269
270 return new ConfigParser(config);
271 }
272
273 function maxVer(v1, v2) {
274
275 if(!v2) {
276 return v1;
277 }
278
279 var v1Parts = v1.split('.');
280 var v2Parts = v2.split('.');
281
282 if(+v1Parts[0] > +v2Parts[0]) {
283 return v1;
284 } else if(+v1Parts[0] < +v2Parts[0]) {
285 return v2;
286 } else if(+v1Parts[1] > +v2Parts[1]) {
287 return v1;
288 } else {
289 return v2;
290 }
291 }
292};
293