mirror of
https://github.com/apache/cordova-android.git
synced 2025-03-04 00:13:20 +08:00
Remove unused builders and add unit tests
This commit is contained in:
parent
46a036ef26
commit
8ee3a73dd1
28
bin/templates/cordova/Api.js
vendored
28
bin/templates/cordova/Api.js
vendored
@ -56,7 +56,7 @@ function setupEvents (externalEventEmitter) {
|
|||||||
function Api (platform, platformRootDir, events) {
|
function Api (platform, platformRootDir, events) {
|
||||||
this.platform = PLATFORM;
|
this.platform = PLATFORM;
|
||||||
this.root = path.resolve(__dirname, '..');
|
this.root = path.resolve(__dirname, '..');
|
||||||
this.builder = 'gradle';
|
this.builder = 'studio';
|
||||||
|
|
||||||
setupEvents(events);
|
setupEvents(events);
|
||||||
|
|
||||||
@ -64,33 +64,19 @@ function Api (platform, platformRootDir, events) {
|
|||||||
|
|
||||||
this.locations = {
|
this.locations = {
|
||||||
root: self.root,
|
root: self.root,
|
||||||
www: path.join(self.root, 'assets/www'),
|
www: path.join(self.root, 'app/src/main/assets/www'),
|
||||||
res: path.join(self.root, 'res'),
|
res: path.join(self.root, 'app/src/main/res'),
|
||||||
platformWww: path.join(self.root, 'platform_www'),
|
platformWww: path.join(self.root, 'platform_www'),
|
||||||
configXml: path.join(self.root, 'res/xml/config.xml'),
|
configXml: path.join(self.root, 'app/src/main/res/xml/config.xml'),
|
||||||
defaultConfigXml: path.join(self.root, 'cordova/defaults.xml'),
|
defaultConfigXml: path.join(self.root, 'cordova/defaults.xml'),
|
||||||
strings: path.join(self.root, 'res/values/strings.xml'),
|
strings: path.join(self.root, 'app/src/main/res/values/strings.xml'),
|
||||||
manifest: path.join(self.root, 'AndroidManifest.xml'),
|
manifest: path.join(self.root, 'app/src/main/AndroidManifest.xml'),
|
||||||
build: path.join(self.root, 'build'),
|
build: path.join(self.root, 'build'),
|
||||||
javaSrc: path.join(self.root, 'src'),
|
javaSrc: path.join(self.root, 'app/src/main/java/'),
|
||||||
// NOTE: Due to platformApi spec we need to return relative paths here
|
// NOTE: Due to platformApi spec we need to return relative paths here
|
||||||
cordovaJs: 'bin/templates/project/assets/www/cordova.js',
|
cordovaJs: 'bin/templates/project/assets/www/cordova.js',
|
||||||
cordovaJsSrc: 'cordova-js-src'
|
cordovaJsSrc: 'cordova-js-src'
|
||||||
};
|
};
|
||||||
|
|
||||||
// XXX Override some locations for Android Studio projects
|
|
||||||
if (AndroidStudio.isAndroidStudioProject(self.root) === true) {
|
|
||||||
selfEvents.emit('log', 'Android Studio project detected');
|
|
||||||
this.builder = 'studio';
|
|
||||||
this.android_studio = true;
|
|
||||||
this.locations.configXml = path.join(self.root, 'app/src/main/res/xml/config.xml');
|
|
||||||
this.locations.strings = path.join(self.root, 'app/src/main/res/values/strings.xml');
|
|
||||||
this.locations.manifest = path.join(self.root, 'app/src/main/AndroidManifest.xml');
|
|
||||||
// We could have Java Source, we could have other languages
|
|
||||||
this.locations.javaSrc = path.join(self.root, 'app/src/main/java/');
|
|
||||||
this.locations.www = path.join(self.root, 'app/src/main/assets/www');
|
|
||||||
this.locations.res = path.join(self.root, 'app/src/main/res');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
122
bin/templates/cordova/lib/builders/GenericBuilder.js
vendored
122
bin/templates/cordova/lib/builders/GenericBuilder.js
vendored
@ -1,122 +0,0 @@
|
|||||||
/*
|
|
||||||
Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
or more contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. The ASF licenses this file
|
|
||||||
to you under the Apache License, Version 2.0 (the
|
|
||||||
"License"); you may not use this file except in compliance
|
|
||||||
with the License. You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing,
|
|
||||||
software distributed under the License is distributed on an
|
|
||||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
KIND, either express or implied. See the License for the
|
|
||||||
specific language governing permissions and limitations
|
|
||||||
under the License.
|
|
||||||
*/
|
|
||||||
/* eslint no-self-assign: 0 */
|
|
||||||
/* eslint no-unused-vars: 0 */
|
|
||||||
|
|
||||||
var Q = require('q');
|
|
||||||
var fs = require('fs');
|
|
||||||
var path = require('path');
|
|
||||||
var shell = require('shelljs');
|
|
||||||
var events = require('cordova-common').events;
|
|
||||||
|
|
||||||
function GenericBuilder (projectDir) {
|
|
||||||
this.root = projectDir || path.resolve(__dirname, '../../..');
|
|
||||||
this.binDirs = {
|
|
||||||
studio: path.join(this.root, 'app', 'build', 'outputs', 'apk'),
|
|
||||||
gradle: path.join(this.root, 'build', 'outputs', 'apk')
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
GenericBuilder.prototype.prepEnv = function () {
|
|
||||||
return Q();
|
|
||||||
};
|
|
||||||
|
|
||||||
GenericBuilder.prototype.build = function () {
|
|
||||||
events.emit('log', 'Skipping build...');
|
|
||||||
return Q(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
GenericBuilder.prototype.clean = function () {
|
|
||||||
return Q();
|
|
||||||
};
|
|
||||||
|
|
||||||
GenericBuilder.prototype.findOutputApks = function (build_type, arch) {
|
|
||||||
var self = this;
|
|
||||||
return Object.keys(this.binDirs).reduce(function (result, builderName) {
|
|
||||||
var binDir = self.binDirs[builderName];
|
|
||||||
return result.concat(findOutputApksHelper(binDir, build_type, builderName === 'ant' ? null : arch));
|
|
||||||
}, []).sort(apkSorter);
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = GenericBuilder;
|
|
||||||
|
|
||||||
function apkSorter (fileA, fileB) {
|
|
||||||
// De-prioritize arch specific builds
|
|
||||||
var archSpecificRE = /-x86|-arm/;
|
|
||||||
if (archSpecificRE.exec(fileA)) {
|
|
||||||
return 1;
|
|
||||||
} else if (archSpecificRE.exec(fileB)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// De-prioritize unsigned builds
|
|
||||||
var unsignedRE = /-unsigned/;
|
|
||||||
if (unsignedRE.exec(fileA)) {
|
|
||||||
return 1;
|
|
||||||
} else if (unsignedRE.exec(fileB)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var timeDiff = fs.statSync(fileB).mtime - fs.statSync(fileA).mtime;
|
|
||||||
return timeDiff === 0 ? fileA.length - fileB.length : timeDiff;
|
|
||||||
}
|
|
||||||
|
|
||||||
function findOutputApksHelper (dir, build_type, arch) {
|
|
||||||
var shellSilent = shell.config.silent;
|
|
||||||
shell.config.silent = true;
|
|
||||||
|
|
||||||
// list directory recursively
|
|
||||||
var ret = shell.ls('-R', dir).map(function (file) {
|
|
||||||
// ls does not include base directory
|
|
||||||
return path.join(dir, file);
|
|
||||||
}).filter(function (file) {
|
|
||||||
// find all APKs
|
|
||||||
return file.match(/\.apk?$/i);
|
|
||||||
}).filter(function (candidate) {
|
|
||||||
var apkName = path.basename(candidate);
|
|
||||||
// Need to choose between release and debug .apk.
|
|
||||||
if (build_type === 'debug') {
|
|
||||||
return /-debug/.exec(apkName) && !/-unaligned|-unsigned/.exec(apkName);
|
|
||||||
}
|
|
||||||
if (build_type === 'release') {
|
|
||||||
return /-release/.exec(apkName) && !/-unaligned/.exec(apkName);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}).sort(apkSorter);
|
|
||||||
|
|
||||||
shellSilent = shellSilent;
|
|
||||||
|
|
||||||
if (ret.length === 0) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
// Assume arch-specific build if newest apk has -x86 or -arm.
|
|
||||||
var archSpecific = !!/-x86|-arm/.exec(path.basename(ret[0]));
|
|
||||||
// And show only arch-specific ones (or non-arch-specific)
|
|
||||||
ret = ret.filter(function (p) {
|
|
||||||
return !!/-x86|-arm/.exec(path.basename(p)) === archSpecific;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (archSpecific && ret.length > 1 && arch) {
|
|
||||||
ret = ret.filter(function (p) {
|
|
||||||
return path.basename(p).indexOf('-' + arch) !== -1;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
329
bin/templates/cordova/lib/builders/GradleBuilder.js
vendored
329
bin/templates/cordova/lib/builders/GradleBuilder.js
vendored
@ -1,329 +0,0 @@
|
|||||||
/*
|
|
||||||
Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
or more contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. The ASF licenses this file
|
|
||||||
to you under the Apache License, Version 2.0 (the
|
|
||||||
"License"); you may not use this file except in compliance
|
|
||||||
with the License. You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing,
|
|
||||||
software distributed under the License is distributed on an
|
|
||||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
KIND, either express or implied. See the License for the
|
|
||||||
specific language governing permissions and limitations
|
|
||||||
under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var Q = require('q');
|
|
||||||
var fs = require('fs');
|
|
||||||
var util = require('util');
|
|
||||||
var path = require('path');
|
|
||||||
var shell = require('shelljs');
|
|
||||||
var superspawn = require('cordova-common').superspawn;
|
|
||||||
var CordovaError = require('cordova-common').CordovaError;
|
|
||||||
var events = require('cordova-common').events;
|
|
||||||
var check_reqs = require('../check_reqs');
|
|
||||||
|
|
||||||
var GenericBuilder = require('./GenericBuilder');
|
|
||||||
|
|
||||||
var MARKER = 'YOUR CHANGES WILL BE ERASED!';
|
|
||||||
var SIGNING_PROPERTIES = '-signing.properties';
|
|
||||||
var TEMPLATE =
|
|
||||||
'# This file is automatically generated.\n' +
|
|
||||||
'# Do not modify this file -- ' + MARKER + '\n';
|
|
||||||
|
|
||||||
function GradleBuilder (projectRoot) {
|
|
||||||
GenericBuilder.call(this, projectRoot);
|
|
||||||
|
|
||||||
this.binDirs = { gradle: this.binDirs.gradle };
|
|
||||||
}
|
|
||||||
|
|
||||||
util.inherits(GradleBuilder, GenericBuilder);
|
|
||||||
|
|
||||||
GradleBuilder.prototype.getArgs = function (cmd, opts) {
|
|
||||||
if (cmd === 'release') {
|
|
||||||
cmd = 'cdvBuildRelease';
|
|
||||||
} else if (cmd === 'debug') {
|
|
||||||
cmd = 'cdvBuildDebug';
|
|
||||||
}
|
|
||||||
var args = [cmd, '-b', path.join(this.root, 'build.gradle')];
|
|
||||||
if (opts.arch) {
|
|
||||||
args.push('-PcdvBuildArch=' + opts.arch);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 10 seconds -> 6 seconds
|
|
||||||
args.push('-Dorg.gradle.daemon=true');
|
|
||||||
// to allow dex in process
|
|
||||||
args.push('-Dorg.gradle.jvmargs=-Xmx2048m');
|
|
||||||
// allow NDK to be used - required by Gradle 1.5 plugin
|
|
||||||
args.push('-Pandroid.useDeprecatedNdk=true');
|
|
||||||
args.push.apply(args, opts.extraArgs);
|
|
||||||
// Shaves another 100ms, but produces a "try at own risk" warning. Not worth it (yet):
|
|
||||||
// args.push('-Dorg.gradle.parallel=true');
|
|
||||||
return args;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This returns a promise
|
|
||||||
*/
|
|
||||||
|
|
||||||
GradleBuilder.prototype.runGradleWrapper = function (gradle_cmd, gradle_file) {
|
|
||||||
var gradlePath = path.join(this.root, 'gradlew');
|
|
||||||
gradle_file = path.join(this.root, (gradle_file || 'wrapper.gradle'));
|
|
||||||
if (fs.existsSync(gradlePath)) {
|
|
||||||
// Literally do nothing, for some reason this works, while !fs.existsSync didn't on Windows
|
|
||||||
} else {
|
|
||||||
return superspawn.spawn(gradle_cmd, ['-p', this.root, 'wrapper', '-b', gradle_file], { stdio: 'pipe' })
|
|
||||||
.progress(function (stdio) {
|
|
||||||
suppressJavaOptionsInfo(stdio);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We need to kill this in a fire.
|
|
||||||
*/
|
|
||||||
|
|
||||||
GradleBuilder.prototype.readProjectProperties = function () {
|
|
||||||
function findAllUniq (data, r) {
|
|
||||||
var s = {};
|
|
||||||
var m;
|
|
||||||
while ((m = r.exec(data))) {
|
|
||||||
s[m[1]] = 1;
|
|
||||||
}
|
|
||||||
return Object.keys(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
var data = fs.readFileSync(path.join(this.root, 'project.properties'), 'utf8');
|
|
||||||
return {
|
|
||||||
libs: findAllUniq(data, /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg),
|
|
||||||
gradleIncludes: findAllUniq(data, /^\s*cordova\.gradle\.include\.\d+=(.*)(?:\s|$)/mg),
|
|
||||||
systemLibs: findAllUniq(data, /^\s*cordova\.system\.library\.\d+=(.*)(?:\s|$)/mg)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
GradleBuilder.prototype.extractRealProjectNameFromManifest = function () {
|
|
||||||
var manifestPath = path.join(this.root, 'AndroidManifest.xml');
|
|
||||||
var manifestData = fs.readFileSync(manifestPath, 'utf8');
|
|
||||||
var m = /<manifest[\s\S]*?package\s*=\s*"(.*?)"/i.exec(manifestData);
|
|
||||||
if (!m) {
|
|
||||||
throw new CordovaError('Could not find package name in ' + manifestPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
var packageName = m[1];
|
|
||||||
var lastDotIndex = packageName.lastIndexOf('.');
|
|
||||||
return packageName.substring(lastDotIndex + 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Makes the project buildable, minus the gradle wrapper.
|
|
||||||
GradleBuilder.prototype.prepBuildFiles = function () {
|
|
||||||
// Update the version of build.gradle in each dependent library.
|
|
||||||
var pluginBuildGradle = path.join(this.root, 'cordova', 'lib', 'plugin-build.gradle');
|
|
||||||
var propertiesObj = this.readProjectProperties();
|
|
||||||
var subProjects = propertiesObj.libs;
|
|
||||||
|
|
||||||
// Check and copy the gradle file into the subproject.
|
|
||||||
// Called by the loop below this function def.
|
|
||||||
var checkAndCopy = function (subProject, root) {
|
|
||||||
var subProjectGradle = path.join(root, subProject, 'build.gradle');
|
|
||||||
// This is the future-proof way of checking if a file exists
|
|
||||||
// This must be synchronous to satisfy a Travis test
|
|
||||||
try {
|
|
||||||
fs.accessSync(subProjectGradle, fs.F_OK);
|
|
||||||
} catch (e) {
|
|
||||||
shell.cp('-f', pluginBuildGradle, subProjectGradle);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Some dependencies on Android don't use gradle, or don't have default
|
|
||||||
// gradle files. This copies a dummy gradle file into them
|
|
||||||
for (var i = 0; i < subProjects.length; ++i) {
|
|
||||||
if (subProjects[i] !== 'CordovaLib' && subProjects[i] !== 'app') {
|
|
||||||
checkAndCopy(subProjects[i], this.root);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var name = this.extractRealProjectNameFromManifest();
|
|
||||||
// Remove the proj.id/name- prefix from projects: https://issues.apache.org/jira/browse/CB-9149
|
|
||||||
var settingsGradlePaths = subProjects.map(function (p) {
|
|
||||||
var realDir = p.replace(/[/\\]/g, ':');
|
|
||||||
var libName = realDir.replace(name + '-', '');
|
|
||||||
var str = 'include ":' + libName + '"\n';
|
|
||||||
if (realDir.indexOf(name + '-') !== -1) { str += 'project(":' + libName + '").projectDir = new File("' + p + '")\n'; }
|
|
||||||
return str;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Write the settings.gradle file.
|
|
||||||
fs.writeFileSync(path.join(this.root, 'settings.gradle'),
|
|
||||||
'// GENERATED FILE - DO NOT EDIT\n' +
|
|
||||||
'include ":"\n' + settingsGradlePaths.join(''));
|
|
||||||
// Update dependencies within build.gradle.
|
|
||||||
var buildGradle = fs.readFileSync(path.join(this.root, 'build.gradle'), 'utf8');
|
|
||||||
var depsList = '';
|
|
||||||
var root = this.root;
|
|
||||||
|
|
||||||
// Cordova Plugins can be written as library modules that would use Cordova as a
|
|
||||||
// dependency. Because we need to make sure that Cordova is compiled only once for
|
|
||||||
// dexing, we make sure to exclude CordovaLib from these modules
|
|
||||||
var insertExclude = function (p) {
|
|
||||||
var gradlePath = path.join(root, p, 'build.gradle');
|
|
||||||
var projectGradleFile = fs.readFileSync(gradlePath, 'utf-8');
|
|
||||||
if (projectGradleFile.indexOf('CordovaLib') !== -1) {
|
|
||||||
depsList += '{\n exclude module:("CordovaLib")\n }\n';
|
|
||||||
} else {
|
|
||||||
depsList += '\n';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
subProjects.forEach(function (p) {
|
|
||||||
events.emit('log', 'Subproject Path: ' + p);
|
|
||||||
var libName = p.replace(/[/\\]/g, ':').replace(name + '-', '');
|
|
||||||
depsList += ' implementation(project(path: "' + libName + '"))';
|
|
||||||
insertExclude(p);
|
|
||||||
});
|
|
||||||
|
|
||||||
// For why we do this mapping: https://issues.apache.org/jira/browse/CB-8390
|
|
||||||
var SYSTEM_LIBRARY_MAPPINGS = [
|
|
||||||
[/^\/?extras\/android\/support\/(.*)$/, 'com.android.support:support-$1:+'],
|
|
||||||
[/^\/?google\/google_play_services\/libproject\/google-play-services_lib\/?$/, 'com.google.android.gms:play-services:+']
|
|
||||||
];
|
|
||||||
propertiesObj.systemLibs.forEach(function (p) {
|
|
||||||
var mavenRef;
|
|
||||||
// It's already in gradle form if it has two ':'s
|
|
||||||
if (/:.*:/.exec(p)) {
|
|
||||||
mavenRef = p;
|
|
||||||
} else {
|
|
||||||
for (var i = 0; i < SYSTEM_LIBRARY_MAPPINGS.length; ++i) {
|
|
||||||
var pair = SYSTEM_LIBRARY_MAPPINGS[i];
|
|
||||||
if (pair[0].exec(p)) {
|
|
||||||
mavenRef = p.replace(pair[0], pair[1]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!mavenRef) {
|
|
||||||
throw new CordovaError('Unsupported system library (does not work with gradle): ' + p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
depsList += ' compile "' + mavenRef + '"\n';
|
|
||||||
});
|
|
||||||
|
|
||||||
// This code is dangerous and actually writes gradle declarations directly into the build.gradle
|
|
||||||
// Try not to mess with this if possible
|
|
||||||
buildGradle = buildGradle.replace(/(SUB-PROJECT DEPENDENCIES START)[\s\S]*(\/\/ SUB-PROJECT DEPENDENCIES END)/, '$1\n' + depsList + ' $2');
|
|
||||||
var includeList = '';
|
|
||||||
propertiesObj.gradleIncludes.forEach(function (includePath) {
|
|
||||||
includeList += 'apply from: "' + includePath + '"\n';
|
|
||||||
});
|
|
||||||
buildGradle = buildGradle.replace(/(PLUGIN GRADLE EXTENSIONS START)[\s\S]*(\/\/ PLUGIN GRADLE EXTENSIONS END)/, '$1\n' + includeList + '$2');
|
|
||||||
fs.writeFileSync(path.join(this.root, 'build.gradle'), buildGradle);
|
|
||||||
};
|
|
||||||
|
|
||||||
GradleBuilder.prototype.prepEnv = function (opts) {
|
|
||||||
var self = this;
|
|
||||||
return check_reqs.check_gradle().then(function (gradlePath) {
|
|
||||||
return self.runGradleWrapper(gradlePath);
|
|
||||||
}).then(function () {
|
|
||||||
return self.prepBuildFiles();
|
|
||||||
}).then(function () {
|
|
||||||
// We now copy the gradle out of the framework
|
|
||||||
// This is a dirty patch to get the build working
|
|
||||||
/*
|
|
||||||
var wrapperDir = path.join(self.root, 'CordovaLib');
|
|
||||||
if (process.platform == 'win32') {
|
|
||||||
shell.rm('-f', path.join(self.root, 'gradlew.bat'));
|
|
||||||
shell.cp(path.join(wrapperDir, 'gradlew.bat'), self.root);
|
|
||||||
} else {
|
|
||||||
shell.rm('-f', path.join(self.root, 'gradlew'));
|
|
||||||
shell.cp(path.join(wrapperDir, 'gradlew'), self.root);
|
|
||||||
}
|
|
||||||
shell.rm('-rf', path.join(self.root, 'gradle', 'wrapper'));
|
|
||||||
shell.mkdir('-p', path.join(self.root, 'gradle'));
|
|
||||||
shell.cp('-r', path.join(wrapperDir, 'gradle', 'wrapper'), path.join(self.root, 'gradle'));
|
|
||||||
*/
|
|
||||||
// If the gradle distribution URL is set, make sure it points to version we want.
|
|
||||||
// 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.
|
|
||||||
// For some reason, using ^ and $ don't work. This does the job, though.
|
|
||||||
var distributionUrlRegex = /distributionUrl.*zip/;
|
|
||||||
var distributionUrl = process.env['CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL'] || 'https\\://services.gradle.org/distributions/gradle-4.4-all.zip';
|
|
||||||
var gradleWrapperPropertiesPath = path.join(self.root, 'gradle', 'wrapper', 'gradle-wrapper.properties');
|
|
||||||
shell.chmod('u+w', gradleWrapperPropertiesPath);
|
|
||||||
shell.sed('-i', distributionUrlRegex, 'distributionUrl=' + distributionUrl, gradleWrapperPropertiesPath);
|
|
||||||
|
|
||||||
var propertiesFile = opts.buildType + SIGNING_PROPERTIES;
|
|
||||||
var propertiesFilePath = path.join(self.root, propertiesFile);
|
|
||||||
if (opts.packageInfo) {
|
|
||||||
fs.writeFileSync(propertiesFilePath, TEMPLATE + opts.packageInfo.toProperties());
|
|
||||||
} else if (isAutoGenerated(propertiesFilePath)) {
|
|
||||||
shell.rm('-f', propertiesFilePath);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Builds the project with gradle.
|
|
||||||
* Returns a promise.
|
|
||||||
*/
|
|
||||||
GradleBuilder.prototype.build = function (opts) {
|
|
||||||
var wrapper = path.join(this.root, 'gradlew');
|
|
||||||
var args = this.getArgs(opts.buildType === 'debug' ? 'debug' : 'release', opts);
|
|
||||||
|
|
||||||
return superspawn.spawn(wrapper, args, { stdio: 'pipe' })
|
|
||||||
.progress(function (stdio) {
|
|
||||||
suppressJavaOptionsInfo(stdio);
|
|
||||||
}).catch(function (error) {
|
|
||||||
if (error.toString().indexOf('failed to find target with hash string') >= 0) {
|
|
||||||
return check_reqs.check_android_target(error).then(function () {
|
|
||||||
// If due to some odd reason - check_android_target succeeds
|
|
||||||
// we should still fail here.
|
|
||||||
return Q.reject(error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return Q.reject(error);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
GradleBuilder.prototype.clean = function (opts) {
|
|
||||||
var builder = this;
|
|
||||||
var wrapper = path.join(this.root, 'gradlew');
|
|
||||||
var args = builder.getArgs('clean', opts);
|
|
||||||
return Q().then(function () {
|
|
||||||
return superspawn.spawn(wrapper, args, { stdio: 'inherit' });
|
|
||||||
}).then(function () {
|
|
||||||
shell.rm('-rf', path.join(builder.root, 'out'));
|
|
||||||
|
|
||||||
['debug', 'release'].forEach(function (config) {
|
|
||||||
var propertiesFilePath = path.join(builder.root, config + SIGNING_PROPERTIES);
|
|
||||||
if (isAutoGenerated(propertiesFilePath)) {
|
|
||||||
shell.rm('-f', propertiesFilePath);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = GradleBuilder;
|
|
||||||
|
|
||||||
function suppressJavaOptionsInfo (stdio) {
|
|
||||||
if (stdio.stderr) {
|
|
||||||
/*
|
|
||||||
* Workaround for the issue with Java printing some unwanted information to
|
|
||||||
* stderr instead of stdout.
|
|
||||||
* This function suppresses 'Picked up _JAVA_OPTIONS' message from being
|
|
||||||
* printed to stderr. See https://issues.apache.org/jira/browse/CB-9971 for
|
|
||||||
* explanation.
|
|
||||||
*/
|
|
||||||
var suppressThisLine = /^Picked up _JAVA_OPTIONS: /i.test(stdio.stderr.toString());
|
|
||||||
if (suppressThisLine) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
process.stderr.write(stdio.stderr);
|
|
||||||
} else {
|
|
||||||
process.stdout.write(stdio.stdout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isAutoGenerated (file) {
|
|
||||||
return fs.existsSync(file) && fs.readFileSync(file, 'utf8').indexOf(MARKER) > 0;
|
|
||||||
}
|
|
@ -16,10 +16,11 @@
|
|||||||
specific language governing permissions and limitations
|
specific language governing permissions and limitations
|
||||||
under the License.
|
under the License.
|
||||||
*/
|
*/
|
||||||
|
/* eslint no-self-assign: 0 */
|
||||||
|
/* eslint no-unused-vars: 0 */
|
||||||
|
|
||||||
var Q = require('q');
|
var Q = require('q');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var util = require('util');
|
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var shell = require('shelljs');
|
var shell = require('shelljs');
|
||||||
var spawn = require('cordova-common').superspawn.spawn;
|
var spawn = require('cordova-common').superspawn.spawn;
|
||||||
@ -27,23 +28,21 @@ var events = require('cordova-common').events;
|
|||||||
var CordovaError = require('cordova-common').CordovaError;
|
var CordovaError = require('cordova-common').CordovaError;
|
||||||
var check_reqs = require('../check_reqs');
|
var check_reqs = require('../check_reqs');
|
||||||
|
|
||||||
var GenericBuilder = require('./GenericBuilder');
|
const MARKER = 'YOUR CHANGES WILL BE ERASED!';
|
||||||
|
const SIGNING_PROPERTIES = '-signing.properties';
|
||||||
var MARKER = 'YOUR CHANGES WILL BE ERASED!';
|
const TEMPLATE =
|
||||||
var SIGNING_PROPERTIES = '-signing.properties';
|
|
||||||
var TEMPLATE =
|
|
||||||
'# This file is automatically generated.\n' +
|
'# This file is automatically generated.\n' +
|
||||||
'# Do not modify this file -- ' + MARKER + '\n';
|
'# Do not modify this file -- ' + MARKER + '\n';
|
||||||
|
|
||||||
function StudioBuilder (projectRoot) {
|
function ProjectBuilder (projectRoot) {
|
||||||
GenericBuilder.call(this, projectRoot);
|
this.root = projectRoot || path.resolve(__dirname, '../../..');
|
||||||
|
this.binDirs = {
|
||||||
this.binDirs = {gradle: this.binDirs.studio};
|
studio: path.join(this.root, 'app', 'build', 'outputs', 'apk'),
|
||||||
|
gradle: path.join(this.root, 'app', 'build', 'outputs', 'apk')
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
util.inherits(StudioBuilder, GenericBuilder);
|
ProjectBuilder.prototype.getArgs = function (cmd, opts) {
|
||||||
|
|
||||||
StudioBuilder.prototype.getArgs = function (cmd, opts) {
|
|
||||||
if (cmd === 'release') {
|
if (cmd === 'release') {
|
||||||
cmd = 'cdvBuildRelease';
|
cmd = 'cdvBuildRelease';
|
||||||
} else if (cmd === 'debug') {
|
} else if (cmd === 'debug') {
|
||||||
@ -70,7 +69,7 @@ StudioBuilder.prototype.getArgs = function (cmd, opts) {
|
|||||||
* This returns a promise
|
* This returns a promise
|
||||||
*/
|
*/
|
||||||
|
|
||||||
StudioBuilder.prototype.runGradleWrapper = function (gradle_cmd) {
|
ProjectBuilder.prototype.runGradleWrapper = function (gradle_cmd) {
|
||||||
var gradlePath = path.join(this.root, 'gradlew');
|
var gradlePath = path.join(this.root, 'gradlew');
|
||||||
var wrapperGradle = path.join(this.root, 'wrapper.gradle');
|
var wrapperGradle = path.join(this.root, 'wrapper.gradle');
|
||||||
if (fs.existsSync(gradlePath)) {
|
if (fs.existsSync(gradlePath)) {
|
||||||
@ -80,8 +79,7 @@ StudioBuilder.prototype.runGradleWrapper = function (gradle_cmd) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
StudioBuilder.prototype.readProjectProperties = function () {
|
ProjectBuilder.prototype.readProjectProperties = function () {
|
||||||
|
|
||||||
function findAllUniq (data, r) {
|
function findAllUniq (data, r) {
|
||||||
var s = {};
|
var s = {};
|
||||||
var m;
|
var m;
|
||||||
@ -99,7 +97,7 @@ StudioBuilder.prototype.readProjectProperties = function () {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
StudioBuilder.prototype.extractRealProjectNameFromManifest = function () {
|
ProjectBuilder.prototype.extractRealProjectNameFromManifest = function () {
|
||||||
var manifestPath = path.join(this.root, 'app', 'src', 'main', 'AndroidManifest.xml');
|
var manifestPath = path.join(this.root, 'app', 'src', 'main', 'AndroidManifest.xml');
|
||||||
var manifestData = fs.readFileSync(manifestPath, 'utf8');
|
var manifestData = fs.readFileSync(manifestPath, 'utf8');
|
||||||
var m = /<manifest[\s\S]*?package\s*=\s*"(.*?)"/i.exec(manifestData);
|
var m = /<manifest[\s\S]*?package\s*=\s*"(.*?)"/i.exec(manifestData);
|
||||||
@ -113,7 +111,7 @@ StudioBuilder.prototype.extractRealProjectNameFromManifest = function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Makes the project buildable, minus the gradle wrapper.
|
// Makes the project buildable, minus the gradle wrapper.
|
||||||
StudioBuilder.prototype.prepBuildFiles = function () {
|
ProjectBuilder.prototype.prepBuildFiles = function () {
|
||||||
// Update the version of build.gradle in each dependent library.
|
// Update the version of build.gradle in each dependent library.
|
||||||
var pluginBuildGradle = path.join(this.root, 'cordova', 'lib', 'plugin-build.gradle');
|
var pluginBuildGradle = path.join(this.root, 'cordova', 'lib', 'plugin-build.gradle');
|
||||||
var propertiesObj = this.readProjectProperties();
|
var propertiesObj = this.readProjectProperties();
|
||||||
@ -212,7 +210,7 @@ StudioBuilder.prototype.prepBuildFiles = function () {
|
|||||||
fs.writeFileSync(path.join(this.root, 'app', 'build.gradle'), buildGradle);
|
fs.writeFileSync(path.join(this.root, 'app', 'build.gradle'), buildGradle);
|
||||||
};
|
};
|
||||||
|
|
||||||
StudioBuilder.prototype.prepEnv = function (opts) {
|
ProjectBuilder.prototype.prepEnv = function (opts) {
|
||||||
var self = this;
|
var self = this;
|
||||||
return check_reqs.check_gradle()
|
return check_reqs.check_gradle()
|
||||||
.then(function (gradlePath) {
|
.then(function (gradlePath) {
|
||||||
@ -243,7 +241,7 @@ StudioBuilder.prototype.prepEnv = function (opts) {
|
|||||||
* Builds the project with gradle.
|
* Builds the project with gradle.
|
||||||
* Returns a promise.
|
* Returns a promise.
|
||||||
*/
|
*/
|
||||||
StudioBuilder.prototype.build = function (opts) {
|
ProjectBuilder.prototype.build = function (opts) {
|
||||||
var wrapper = path.join(this.root, 'gradlew');
|
var wrapper = path.join(this.root, 'gradlew');
|
||||||
var args = this.getArgs(opts.buildType === 'debug' ? 'debug' : 'release', opts);
|
var args = this.getArgs(opts.buildType === 'debug' ? 'debug' : 'release', opts);
|
||||||
|
|
||||||
@ -277,7 +275,7 @@ StudioBuilder.prototype.build = function (opts) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
StudioBuilder.prototype.clean = function (opts) {
|
ProjectBuilder.prototype.clean = function (opts) {
|
||||||
var builder = this;
|
var builder = this;
|
||||||
var wrapper = path.join(this.root, 'gradlew');
|
var wrapper = path.join(this.root, 'gradlew');
|
||||||
var args = builder.getArgs('clean', opts);
|
var args = builder.getArgs('clean', opts);
|
||||||
@ -296,7 +294,80 @@ StudioBuilder.prototype.clean = function (opts) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = StudioBuilder;
|
ProjectBuilder.prototype.findOutputApks = function (build_type, arch) {
|
||||||
|
var self = this;
|
||||||
|
return Object.keys(this.binDirs).reduce(function (result, builderName) {
|
||||||
|
var binDir = self.binDirs[builderName];
|
||||||
|
return result.concat(findOutputApksHelper(binDir, build_type, builderName === 'ant' ? null : arch));
|
||||||
|
}, []).sort(apkSorter);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = ProjectBuilder;
|
||||||
|
|
||||||
|
function apkSorter (fileA, fileB) {
|
||||||
|
// De-prioritize arch specific builds
|
||||||
|
var archSpecificRE = /-x86|-arm/;
|
||||||
|
if (archSpecificRE.exec(fileA)) {
|
||||||
|
return 1;
|
||||||
|
} else if (archSpecificRE.exec(fileB)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// De-prioritize unsigned builds
|
||||||
|
var unsignedRE = /-unsigned/;
|
||||||
|
if (unsignedRE.exec(fileA)) {
|
||||||
|
return 1;
|
||||||
|
} else if (unsignedRE.exec(fileB)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var timeDiff = fs.statSync(fileB).mtime - fs.statSync(fileA).mtime;
|
||||||
|
return timeDiff === 0 ? fileA.length - fileB.length : timeDiff;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findOutputApksHelper (dir, build_type, arch) {
|
||||||
|
var shellSilent = shell.config.silent;
|
||||||
|
shell.config.silent = true;
|
||||||
|
|
||||||
|
// list directory recursively
|
||||||
|
var ret = shell.ls('-R', dir).map(function (file) {
|
||||||
|
// ls does not include base directory
|
||||||
|
return path.join(dir, file);
|
||||||
|
}).filter(function (file) {
|
||||||
|
// find all APKs
|
||||||
|
return file.match(/\.apk?$/i);
|
||||||
|
}).filter(function (candidate) {
|
||||||
|
var apkName = path.basename(candidate);
|
||||||
|
// Need to choose between release and debug .apk.
|
||||||
|
if (build_type === 'debug') {
|
||||||
|
return /-debug/.exec(apkName) && !/-unaligned|-unsigned/.exec(apkName);
|
||||||
|
}
|
||||||
|
if (build_type === 'release') {
|
||||||
|
return /-release/.exec(apkName) && !/-unaligned/.exec(apkName);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}).sort(apkSorter);
|
||||||
|
|
||||||
|
shellSilent = shellSilent;
|
||||||
|
|
||||||
|
if (ret.length === 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
// Assume arch-specific build if newest apk has -x86 or -arm.
|
||||||
|
var archSpecific = !!/-x86|-arm/.exec(path.basename(ret[0]));
|
||||||
|
// And show only arch-specific ones (or non-arch-specific)
|
||||||
|
ret = ret.filter(function (p) {
|
||||||
|
return !!/-x86|-arm/.exec(path.basename(p)) === archSpecific;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (archSpecific && ret.length > 1 && arch) {
|
||||||
|
ret = ret.filter(function (p) {
|
||||||
|
return path.basename(p).indexOf('-' + arch) !== -1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
function isAutoGenerated (file) {
|
function isAutoGenerated (file) {
|
||||||
return fs.existsSync(file) && fs.readFileSync(file, 'utf8').indexOf(MARKER) > 0;
|
return fs.existsSync(file) && fs.readFileSync(file, 'utf8').indexOf(MARKER) > 0;
|
@ -20,9 +20,8 @@
|
|||||||
var CordovaError = require('cordova-common').CordovaError;
|
var CordovaError = require('cordova-common').CordovaError;
|
||||||
|
|
||||||
var knownBuilders = {
|
var knownBuilders = {
|
||||||
gradle: 'GradleBuilder',
|
gradle: 'ProjectBuilder',
|
||||||
studio: 'StudioBuilder',
|
studio: 'ProjectBuilder'
|
||||||
none: 'GenericBuilder'
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,7 +29,7 @@ var knownBuilders = {
|
|||||||
* type.
|
* type.
|
||||||
*
|
*
|
||||||
* @param {String} builderType Builder name to construct and return. Must
|
* @param {String} builderType Builder name to construct and return. Must
|
||||||
* be one of 'ant', 'gradle' or 'none'
|
* be one of gradle' or 'studio'
|
||||||
*
|
*
|
||||||
* @return {Builder} A builder instance for specified build type.
|
* @return {Builder} A builder instance for specified build type.
|
||||||
*/
|
*/
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
|
|
||||||
var Gradle_builder = require('../../../bin/templates/cordova/lib/builders/GradleBuilder.js');
|
|
||||||
var fs = require('fs');
|
|
||||||
var Q = require('q');
|
|
||||||
var superspawn = require('cordova-common').superspawn;
|
|
||||||
var builder;
|
|
||||||
|
|
||||||
describe('Gradle Builder', function () {
|
|
||||||
beforeEach(function () {
|
|
||||||
spyOn(fs, 'existsSync').and.returnValue(true);
|
|
||||||
builder = new Gradle_builder('/root');
|
|
||||||
var deferred = Q.defer();
|
|
||||||
spyOn(superspawn, 'spawn').and.returnValue(deferred.promise);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('runGradleWrapper method', function () {
|
|
||||||
it('should run the provided gradle command if a gradle wrapper does not already exist', function () {
|
|
||||||
fs.existsSync.and.returnValue(false);
|
|
||||||
builder.runGradleWrapper('/my/sweet/gradle');
|
|
||||||
expect(superspawn.spawn).toHaveBeenCalledWith('/my/sweet/gradle', jasmine.any(Array), jasmine.any(Object));
|
|
||||||
});
|
|
||||||
it('should do nothing if a gradle wrapper exists in the project directory', function () {
|
|
||||||
fs.existsSync.and.returnValue(true);
|
|
||||||
builder.runGradleWrapper('/my/sweet/gradle');
|
|
||||||
expect(superspawn.spawn).not.toHaveBeenCalledWith('/my/sweet/gradle', jasmine.any(Array), jasmine.any(Object));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
289
spec/unit/builders/ProjectBuilder.spec.js
Normal file
289
spec/unit/builders/ProjectBuilder.spec.js
Normal file
@ -0,0 +1,289 @@
|
|||||||
|
/*
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const Q = require('q');
|
||||||
|
const rewire = require('rewire');
|
||||||
|
|
||||||
|
const CordovaError = require('cordova-common').CordovaError;
|
||||||
|
|
||||||
|
describe('ProjectBuilder', () => {
|
||||||
|
const rootDir = '/root';
|
||||||
|
|
||||||
|
let builder;
|
||||||
|
let ProjectBuilder;
|
||||||
|
let spawnSpy;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
spawnSpy = jasmine.createSpy('spawn').and.returnValue(Q.defer().promise);
|
||||||
|
ProjectBuilder = rewire('../../../bin/templates/cordova/lib/builders/ProjectBuilder');
|
||||||
|
ProjectBuilder.__set__('spawn', spawnSpy);
|
||||||
|
|
||||||
|
builder = new ProjectBuilder(rootDir);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('constructor', () => {
|
||||||
|
it('should set the project directory to the passed argument', () => {
|
||||||
|
expect(builder.root).toBe(rootDir);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set the project directory to three folders up', () => {
|
||||||
|
ProjectBuilder.__set__('__dirname', 'projecttest/platforms/android/app');
|
||||||
|
builder = new ProjectBuilder();
|
||||||
|
expect(builder.root).toMatch(/projecttest$/);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getArgs', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(fs, 'existsSync').and.returnValue(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set release argument', () => {
|
||||||
|
const args = builder.getArgs('release', {});
|
||||||
|
expect(args[0]).toBe('cdvBuildRelease');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set debug argument', () => {
|
||||||
|
const args = builder.getArgs('debug', {});
|
||||||
|
expect(args[0]).toBe('cdvBuildDebug');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add architecture if it is passed', () => {
|
||||||
|
const arch = 'unittest';
|
||||||
|
const args = builder.getArgs('debug', { arch });
|
||||||
|
|
||||||
|
expect(args).toContain(`-PcdvBuildArch=${arch}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('runGradleWrapper', () => {
|
||||||
|
it('should run the provided gradle command if a gradle wrapper does not already exist', () => {
|
||||||
|
spyOn(fs, 'existsSync').and.returnValue(false);
|
||||||
|
builder.runGradleWrapper('/my/sweet/gradle');
|
||||||
|
expect(spawnSpy).toHaveBeenCalledWith('/my/sweet/gradle', jasmine.any(Array), jasmine.any(Object));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should do nothing if a gradle wrapper exists in the project directory', () => {
|
||||||
|
spyOn(fs, 'existsSync').and.returnValue(true);
|
||||||
|
builder.runGradleWrapper('/my/sweet/gradle');
|
||||||
|
expect(spawnSpy).not.toHaveBeenCalledWith('/my/sweet/gradle', jasmine.any(Array), jasmine.any(Object));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('extractRealProjectNameFromManifest', () => {
|
||||||
|
it('should get the project name from the Android Manifest', () => {
|
||||||
|
const projectName = 'unittestproject';
|
||||||
|
const projectId = `io.cordova.${projectName}`;
|
||||||
|
const manifest = `<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="${projectId}"></manifest>`;
|
||||||
|
|
||||||
|
spyOn(fs, 'readFileSync').and.returnValue(manifest);
|
||||||
|
|
||||||
|
expect(builder.extractRealProjectNameFromManifest()).toBe(projectName);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if there is no package in the Android manifest', () => {
|
||||||
|
const manifest = `<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"></manifest>`;
|
||||||
|
|
||||||
|
spyOn(fs, 'readFileSync').and.returnValue(manifest);
|
||||||
|
|
||||||
|
expect(() => builder.extractRealProjectNameFromManifest()).toThrow(jasmine.any(CordovaError));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('build', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(builder, 'getArgs');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set build type to debug', () => {
|
||||||
|
const opts = { buildType: 'debug' };
|
||||||
|
builder.build(opts);
|
||||||
|
expect(builder.getArgs).toHaveBeenCalledWith('debug', opts);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set build type to release', () => {
|
||||||
|
const opts = { buildType: 'release' };
|
||||||
|
builder.build(opts);
|
||||||
|
expect(builder.getArgs).toHaveBeenCalledWith('release', opts);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should default build type to release', () => {
|
||||||
|
const opts = {};
|
||||||
|
builder.build(opts);
|
||||||
|
expect(builder.getArgs).toHaveBeenCalledWith('release', opts);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should spawn gradle with correct args', () => {
|
||||||
|
const testArgs = ['test', 'args', '-c'];
|
||||||
|
builder.getArgs.and.returnValue(testArgs);
|
||||||
|
|
||||||
|
builder.build({});
|
||||||
|
|
||||||
|
expect(spawnSpy).toHaveBeenCalledWith(path.join(rootDir, 'gradlew'), testArgs, jasmine.anything());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reject if the spawn fails', () => {
|
||||||
|
const errorMessage = 'ERROR: Failed to spawn';
|
||||||
|
spawnSpy.and.returnValue(Q.reject(errorMessage));
|
||||||
|
|
||||||
|
return builder.build({}).then(
|
||||||
|
() => fail('Unexpectedly resolved'),
|
||||||
|
err => {
|
||||||
|
expect(err).toBe(errorMessage);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check the Android target if failed to find target', () => {
|
||||||
|
const checkReqsSpy = jasmine.createSpyObj('check_reqs', ['check_android_target']);
|
||||||
|
const errorMessage = 'ERROR: failed to find target with hash string';
|
||||||
|
|
||||||
|
ProjectBuilder.__set__('check_reqs', checkReqsSpy);
|
||||||
|
checkReqsSpy.check_android_target.and.returnValue(Q.resolve());
|
||||||
|
spawnSpy.and.returnValue(Q.reject(errorMessage));
|
||||||
|
|
||||||
|
return builder.build({}).then(
|
||||||
|
() => fail('Unexpectedly resolved'),
|
||||||
|
err => {
|
||||||
|
expect(checkReqsSpy.check_android_target).toHaveBeenCalledWith(errorMessage);
|
||||||
|
expect(err).toBe(errorMessage);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('clean', () => {
|
||||||
|
let shellSpy;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
shellSpy = jasmine.createSpyObj('shell', ['rm']);
|
||||||
|
ProjectBuilder.__set__('shell', shellSpy);
|
||||||
|
spyOn(builder, 'getArgs');
|
||||||
|
spawnSpy.and.returnValue(Promise.resolve());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get arguments for cleaning', () => {
|
||||||
|
const opts = {};
|
||||||
|
builder.clean(opts);
|
||||||
|
|
||||||
|
expect(builder.getArgs).toHaveBeenCalledWith('clean', opts);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should spawn gradle', () => {
|
||||||
|
const opts = {};
|
||||||
|
const gradleArgs = ['test', 'args', '-f'];
|
||||||
|
builder.getArgs.and.returnValue(gradleArgs);
|
||||||
|
|
||||||
|
return builder.clean(opts).then(() => {
|
||||||
|
expect(spawnSpy).toHaveBeenCalledWith(path.join(rootDir, 'gradlew'), gradleArgs, jasmine.anything());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove "out" folder', () => {
|
||||||
|
return builder.clean({}).then(() => {
|
||||||
|
expect(shellSpy.rm).toHaveBeenCalledWith('-rf', path.join(rootDir, 'out'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove signing files if they are autogenerated', () => {
|
||||||
|
const debugSigningFile = path.join(rootDir, 'debug-signing.properties');
|
||||||
|
const releaseSigningFile = path.join(rootDir, 'release-signing.properties');
|
||||||
|
|
||||||
|
const isAutoGeneratedSpy = jasmine.createSpy('isAutoGenerated');
|
||||||
|
ProjectBuilder.__set__('isAutoGenerated', isAutoGeneratedSpy);
|
||||||
|
isAutoGeneratedSpy.and.returnValue(true);
|
||||||
|
|
||||||
|
return builder.clean({}).then(() => {
|
||||||
|
expect(shellSpy.rm).toHaveBeenCalledWith(jasmine.any(String), debugSigningFile);
|
||||||
|
expect(shellSpy.rm).toHaveBeenCalledWith(jasmine.any(String), releaseSigningFile);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not remove signing files if they are not autogenerated', () => {
|
||||||
|
const debugSigningFile = path.join(rootDir, 'debug-signing.properties');
|
||||||
|
const releaseSigningFile = path.join(rootDir, 'release-signing.properties');
|
||||||
|
|
||||||
|
const isAutoGeneratedSpy = jasmine.createSpy('isAutoGenerated');
|
||||||
|
ProjectBuilder.__set__('isAutoGenerated', isAutoGeneratedSpy);
|
||||||
|
isAutoGeneratedSpy.and.returnValue(false);
|
||||||
|
|
||||||
|
return builder.clean({}).then(() => {
|
||||||
|
expect(shellSpy.rm).not.toHaveBeenCalledWith(jasmine.any(String), debugSigningFile);
|
||||||
|
expect(shellSpy.rm).not.toHaveBeenCalledWith(jasmine.any(String), releaseSigningFile);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('apkSorter', () => {
|
||||||
|
it('should sort APKs from most recent to oldest, deprioritising unsigned arch-specific builds', () => {
|
||||||
|
const APKs = {
|
||||||
|
'app-debug.apk': new Date('2018-04-20'),
|
||||||
|
'app-release.apk': new Date('2018-05-20'),
|
||||||
|
'app-debug-x86.apk': new Date('2018-06-01'),
|
||||||
|
'app-release-x86.apk': new Date('2018-06-20'),
|
||||||
|
'app-debug-arm.apk': new Date('2018-05-24'),
|
||||||
|
'app-release-arm.apk': new Date('2018-06-24'),
|
||||||
|
'app-release-unsigned.apk': new Date('2018-06-28')
|
||||||
|
};
|
||||||
|
|
||||||
|
const expectedResult = ['app-release.apk', 'app-debug.apk', 'app-release-unsigned.apk',
|
||||||
|
'app-release-arm.apk', 'app-debug-arm.apk', 'app-release-x86.apk', 'app-debug-x86.apk'];
|
||||||
|
|
||||||
|
const fsSpy = jasmine.createSpyObj('fs', ['statSync']);
|
||||||
|
fsSpy.statSync.and.callFake(filename => {
|
||||||
|
return { mtime: APKs[filename].getTime() };
|
||||||
|
});
|
||||||
|
ProjectBuilder.__set__('fs', fsSpy);
|
||||||
|
|
||||||
|
const apkArray = Object.keys(APKs);
|
||||||
|
const sortedApks = apkArray.sort(ProjectBuilder.__get__('apkSorter'));
|
||||||
|
|
||||||
|
expect(sortedApks).toEqual(expectedResult);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isAutoGenerated', () => {
|
||||||
|
let fsSpy;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fsSpy = jasmine.createSpyObj('fs', ['existsSync', 'readFileSync']);
|
||||||
|
fsSpy.existsSync.and.returnValue(true);
|
||||||
|
ProjectBuilder.__set__('fs', fsSpy);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true if the file contains the autogenerated marker', () => {
|
||||||
|
const fileContents = `# DO NOT MODIFY - YOUR CHANGES WILL BE ERASED!`;
|
||||||
|
fsSpy.readFileSync.and.returnValue(fileContents);
|
||||||
|
|
||||||
|
expect(ProjectBuilder.__get__('isAutoGenerated')()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false if the file does not contain the autogenerated marker', () => {
|
||||||
|
const fileContents = `# My modified file`;
|
||||||
|
fsSpy.readFileSync.and.returnValue(fileContents);
|
||||||
|
|
||||||
|
expect(ProjectBuilder.__get__('isAutoGenerated')()).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
54
spec/unit/builders/builders.spec.js
Normal file
54
spec/unit/builders/builders.spec.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/**
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const rewire = require('rewire');
|
||||||
|
|
||||||
|
const CordovaError = require('cordova-common').CordovaError;
|
||||||
|
const ProjectBuilder = require('../../../bin/templates/cordova/lib/builders/ProjectBuilder');
|
||||||
|
|
||||||
|
describe('builders', () => {
|
||||||
|
let builders;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
builders = rewire('../../../bin/templates/cordova/lib/builders/builders');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getBuilder', () => {
|
||||||
|
it('should return an instance of ProjectBuilder when gradle is requested', () => {
|
||||||
|
const newBuilder = builders.getBuilder('gradle');
|
||||||
|
expect(newBuilder).toEqual(jasmine.any(ProjectBuilder));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an instance of ProjectBuilder when studio is requested', () => {
|
||||||
|
const newBuilder = builders.getBuilder('studio');
|
||||||
|
expect(newBuilder).toEqual(jasmine.any(ProjectBuilder));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if the selected builder does not exist', () => {
|
||||||
|
expect(() => builders.getBuilder('NonExistentBuilder')).toThrow(jasmine.any(CordovaError));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if a builder cannot be instantiated', () => {
|
||||||
|
const requireSpy = jasmine.createSpy('require').and.throwError();
|
||||||
|
builders.__set__('require', requireSpy);
|
||||||
|
|
||||||
|
expect(() => builders.getBuilder('gradle')).toThrow(jasmine.any(CordovaError));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -22,11 +22,11 @@
|
|||||||
var Q = require('q');
|
var Q = require('q');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var superspawn = require('cordova-common').superspawn;
|
var superspawn = require('cordova-common').superspawn;
|
||||||
var GradleBuilder = require('../bin/templates/cordova/lib/builders/GradleBuilder');
|
var ProjectBuilder = require('../bin/templates/cordova/lib/builders/ProjectBuilder');
|
||||||
|
|
||||||
Q.resolve()
|
Q.resolve()
|
||||||
.then(_ => console.log('Preparing Gradle wrapper for Java unit tests.'))
|
.then(_ => console.log('Preparing Gradle wrapper for Java unit tests.'))
|
||||||
.then(_ => new GradleBuilder(__dirname).runGradleWrapper('gradle'))
|
.then(_ => new ProjectBuilder(__dirname).runGradleWrapper('gradle'))
|
||||||
.then(_ => gradlew('--version'))
|
.then(_ => gradlew('--version'))
|
||||||
|
|
||||||
.then(_ => console.log('Gradle wrapper is ready. Running tests now.'))
|
.then(_ => console.log('Gradle wrapper is ready. Running tests now.'))
|
||||||
|
Loading…
Reference in New Issue
Block a user