1 | #!/usr/bin/env node
|
2 |
|
3 | var _ = require('lodash');
|
4 | var fs = require('fs');
|
5 | var path = require("path");
|
6 | var xml2js = require('xml2js');
|
7 | var spawn = require('child_process').spawn;
|
8 | var parser = new xml2js.Parser();
|
9 | require('shelljs/global');
|
10 |
|
11 | module.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 |
|