commonroutesServer/www/platforms/android/cordova/lib/builders/GradleBuilder.js
www/platforms/android/cordova/lib/builders/GradleBuilder.jsRaw
1/*
2 Licensed to the Apache Software Foundation (ASF) under one
3 or more contributor license agreements. See the NOTICE file
4 distributed with this work for additional information
5 regarding copyright ownership. The ASF licenses this file
6 to you under the Apache License, Version 2.0 (the
7 "License"); you may not use this file except in compliance
8 with the License. You may obtain a copy of the License at
9
10 http://www.apache.org/licenses/LICENSE-2.0
11
12 Unless required by applicable law or agreed to in writing,
13 software distributed under the License is distributed on an
14 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 KIND, either express or implied. See the License for the
16 specific language governing permissions and limitations
17 under the License.
18*/
19
20var Q = require('q');
21var fs = require('fs');
22var util = require('util');
23var path = require('path');
24var shell = require('shelljs');
25var spawn = require('cordova-common').superspawn.spawn;
26var CordovaError = require('cordova-common').CordovaError;
27var check_reqs = require('../check_reqs');
28
29var GenericBuilder = require('./GenericBuilder');
30
31var MARKER = 'YOUR CHANGES WILL BE ERASED!';
32var SIGNING_PROPERTIES = '-signing.properties';
33var TEMPLATE =
34 '# This file is automatically generated.\n' +
35 '# Do not modify this file -- ' + MARKER + '\n';
36
37function GradleBuilder (projectRoot) {
38 GenericBuilder.call(this, projectRoot);
39
40 this.binDirs = {gradle: this.binDirs.gradle};
41}
42
43util.inherits(GradleBuilder, GenericBuilder);
44
45GradleBuilder.prototype.getArgs = function(cmd, opts) {
46 if (cmd == 'release') {
47 cmd = 'cdvBuildRelease';
48 } else if (cmd == 'debug') {
49 cmd = 'cdvBuildDebug';
50 }
51 var args = [cmd, '-b', path.join(this.root, 'build.gradle')];
52 if (opts.arch) {
53 args.push('-PcdvBuildArch=' + opts.arch);
54 }
55
56 // 10 seconds -> 6 seconds
57 args.push('-Dorg.gradle.daemon=true');
58 // allow NDK to be used - required by Gradle 1.5 plugin
59 args.push('-Pandroid.useDeprecatedNdk=true');
60 args.push.apply(args, opts.extraArgs);
61 // Shaves another 100ms, but produces a "try at own risk" warning. Not worth it (yet):
62 // args.push('-Dorg.gradle.parallel=true');
63 return args;
64};
65
66// Makes the project buildable, minus the gradle wrapper.
67GradleBuilder.prototype.prepBuildFiles = function() {
68 // Update the version of build.gradle in each dependent library.
69 var pluginBuildGradle = path.join(this.root, 'cordova', 'lib', 'plugin-build.gradle');
70 var propertiesObj = this.readProjectProperties();
71 var subProjects = propertiesObj.libs;
72 for (var i = 0; i < subProjects.length; ++i) {
73 if (subProjects[i] !== 'CordovaLib') {
74 shell.cp('-f', pluginBuildGradle, path.join(this.root, subProjects[i], 'build.gradle'));
75 }
76 }
77
78 var name = this.extractRealProjectNameFromManifest();
79 //Remove the proj.id/name- prefix from projects: https://issues.apache.org/jira/browse/CB-9149
80 var settingsGradlePaths = subProjects.map(function(p){
81 var realDir=p.replace(/[/\\]/g, ':');
82 var libName=realDir.replace(name+'-','');
83 var str='include ":'+libName+'"\n';
84 if(realDir.indexOf(name+'-')!==-1)
85 str+='project(":'+libName+'").projectDir = new File("'+p+'")\n';
86 return str;
87 });
88
89 // Write the settings.gradle file.
90 fs.writeFileSync(path.join(this.root, 'settings.gradle'),
91 '// GENERATED FILE - DO NOT EDIT\n' +
92 'include ":"\n' + settingsGradlePaths.join(''));
93 // Update dependencies within build.gradle.
94 var buildGradle = fs.readFileSync(path.join(this.root, 'build.gradle'), 'utf8');
95 var depsList = '';
96 subProjects.forEach(function(p) {
97 var libName=p.replace(/[/\\]/g, ':').replace(name+'-','');
98 depsList += ' debugCompile project(path: "' + libName + '", configuration: "debug")\n';
99 depsList += ' releaseCompile project(path: "' + libName + '", configuration: "release")\n';
100 });
101 // For why we do this mapping: https://issues.apache.org/jira/browse/CB-8390
102 var SYSTEM_LIBRARY_MAPPINGS = [
103 [/^\/?extras\/android\/support\/(.*)$/, 'com.android.support:support-$1:+'],
104 [/^\/?google\/google_play_services\/libproject\/google-play-services_lib\/?$/, 'com.google.android.gms:play-services:+']
105 ];
106 propertiesObj.systemLibs.forEach(function(p) {
107 var mavenRef;
108 // It's already in gradle form if it has two ':'s
109 if (/:.*:/.exec(p)) {
110 mavenRef = p;
111 } else {
112 for (var i = 0; i < SYSTEM_LIBRARY_MAPPINGS.length; ++i) {
113 var pair = SYSTEM_LIBRARY_MAPPINGS[i];
114 if (pair[0].exec(p)) {
115 mavenRef = p.replace(pair[0], pair[1]);
116 break;
117 }
118 }
119 if (!mavenRef) {
120 throw new CordovaError('Unsupported system library (does not work with gradle): ' + p);
121 }
122 }
123 depsList += ' compile "' + mavenRef + '"\n';
124 });
125 buildGradle = buildGradle.replace(/(SUB-PROJECT DEPENDENCIES START)[\s\S]*(\/\/ SUB-PROJECT DEPENDENCIES END)/, '$1\n' + depsList + ' $2');
126 var includeList = '';
127 propertiesObj.gradleIncludes.forEach(function(includePath) {
128 includeList += 'apply from: "' + includePath + '"\n';
129 });
130 buildGradle = buildGradle.replace(/(PLUGIN GRADLE EXTENSIONS START)[\s\S]*(\/\/ PLUGIN GRADLE EXTENSIONS END)/, '$1\n' + includeList + '$2');
131 fs.writeFileSync(path.join(this.root, 'build.gradle'), buildGradle);
132};
133
134GradleBuilder.prototype.prepEnv = function(opts) {
135 var self = this;
136 return check_reqs.check_gradle()
137 .then(function() {
138 return self.prepBuildFiles();
139 }).then(function() {
140 // Copy the gradle wrapper on each build so that:
141 // A) we don't require the Android SDK at project creation time, and
142 // B) we always use the SDK's latest version of it.
143 // check_reqs ensures that this is set.
144 /*jshint -W069 */
145 var sdkDir = process.env['ANDROID_HOME'];
146 /*jshint +W069 */
147 var wrapperDir = path.join(sdkDir, 'tools', 'templates', 'gradle', 'wrapper');
148 if (process.platform == 'win32') {
149 shell.rm('-f', path.join(self.root, 'gradlew.bat'));
150 shell.cp(path.join(wrapperDir, 'gradlew.bat'), self.root);
151 } else {
152 shell.rm('-f', path.join(self.root, 'gradlew'));
153 shell.cp(path.join(wrapperDir, 'gradlew'), self.root);
154 }
155 shell.rm('-rf', path.join(self.root, 'gradle', 'wrapper'));
156 shell.mkdir('-p', path.join(self.root, 'gradle'));
157 shell.cp('-r', path.join(wrapperDir, 'gradle', 'wrapper'), path.join(self.root, 'gradle'));
158
159 // If the gradle distribution URL is set, make sure it points to version we want.
160 // If it's not set, do nothing, assuming that we're using a future version of gradle that we don't want to mess with.
161 // For some reason, using ^ and $ don't work. This does the job, though.
162 var distributionUrlRegex = /distributionUrl.*zip/;
163 /*jshint -W069 */
164 var distributionUrl = process.env['CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL'] || 'http\\://services.gradle.org/distributions/gradle-2.13-all.zip';
165 /*jshint +W069 */
166 var gradleWrapperPropertiesPath = path.join(self.root, 'gradle', 'wrapper', 'gradle-wrapper.properties');
167 shell.chmod('u+w', gradleWrapperPropertiesPath);
168 shell.sed('-i', distributionUrlRegex, 'distributionUrl='+distributionUrl, gradleWrapperPropertiesPath);
169
170 var propertiesFile = opts.buildType + SIGNING_PROPERTIES;
171 var propertiesFilePath = path.join(self.root, propertiesFile);
172 if (opts.packageInfo) {
173 fs.writeFileSync(propertiesFilePath, TEMPLATE + opts.packageInfo.toProperties());
174 } else if (isAutoGenerated(propertiesFilePath)) {
175 shell.rm('-f', propertiesFilePath);
176 }
177 });
178};
179
180/*
181 * Builds the project with gradle.
182 * Returns a promise.
183 */
184GradleBuilder.prototype.build = function(opts) {
185 var wrapper = path.join(this.root, 'gradlew');
186 var args = this.getArgs(opts.buildType == 'debug' ? 'debug' : 'release', opts);
187
188 return spawn(wrapper, args, {stdio: 'pipe'})
189 .progress(function (stdio){
190 if (stdio.stderr) {
191 /*
192 * Workaround for the issue with Java printing some unwanted information to
193 * stderr instead of stdout.
194 * This function suppresses 'Picked up _JAVA_OPTIONS' message from being
195 * printed to stderr. See https://issues.apache.org/jira/browse/CB-9971 for
196 * explanation.
197 */
198 var suppressThisLine = /^Picked up _JAVA_OPTIONS: /i.test(stdio.stderr.toString());
199 if (suppressThisLine) {
200 return;
201 }
202 process.stderr.write(stdio.stderr);
203 } else {
204 process.stdout.write(stdio.stdout);
205 }
206 }).catch(function (error) {
207 if (error.toString().indexOf('failed to find target with hash string') >= 0) {
208 return check_reqs.check_android_target(error).then(function() {
209 // If due to some odd reason - check_android_target succeeds
210 // we should still fail here.
211 return Q.reject(error);
212 });
213 }
214 return Q.reject(error);
215 });
216};
217
218GradleBuilder.prototype.clean = function(opts) {
219 var builder = this;
220 var wrapper = path.join(this.root, 'gradlew');
221 var args = builder.getArgs('clean', opts);
222 return Q().then(function() {
223 return spawn(wrapper, args, {stdio: 'inherit'});
224 })
225 .then(function () {
226 shell.rm('-rf', path.join(builder.root, 'out'));
227
228 ['debug', 'release'].forEach(function(config) {
229 var propertiesFilePath = path.join(builder.root, config + SIGNING_PROPERTIES);
230 if(isAutoGenerated(propertiesFilePath)){
231 shell.rm('-f', propertiesFilePath);
232 }
233 });
234 });
235};
236
237module.exports = GradleBuilder;
238
239function isAutoGenerated(file) {
240 return fs.existsSync(file) && fs.readFileSync(file, 'utf8').indexOf(MARKER) > 0;
241}