arnaucube
arnaucube/commonroutesServerPublic Self-hosted repository
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}