Skip to content

Commit

Permalink
Merge pull request #9008 from janvennemann/TIMOB-24446
Browse files Browse the repository at this point in the history
[TIMOB-24446] Allow replacing bundled Android Support Libraries
  • Loading branch information
Lokesh Choudhary committed Nov 10, 2017
2 parents 3b2fb78 + e8800a4 commit d877ab0
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 17 deletions.
42 changes: 28 additions & 14 deletions android/cli/commands/_build.js
Original file line number Diff line number Diff line change
Expand Up @@ -1658,16 +1658,16 @@ process.exit(1);
if (unresolvedDependencies.length) {
/*
let msg = 'could not find required module dependencies:';
for (let dependency of unresolvedDependencies) {
msg += __('\n id: %s version: %s platform: %s required by %s',
dependency.id,
dependency.version ? dependency.version : 'latest',
dependency.platform ? dependency.platform : 'all',
dependency.depended.id);
}
logger.error(msg);
process.exit(1);
*/
for (let dependency of unresolvedDependencies) {
msg += __('\n id: %s version: %s platform: %s required by %s',
dependency.id,
dependency.version ? dependency.version : 'latest',
dependency.platform ? dependency.platform : 'all',
dependency.depended.id);
}
logger.error(msg);
process.exit(1);
*/

// re-validate modules
return this.validateTiModules('android', this.deployType, validateTiModulesCallback.bind(this));
Expand Down Expand Up @@ -2887,7 +2887,7 @@ AndroidBuilder.prototype.getNativeModuleBindings = function getNativeModuleBindi
};

AndroidBuilder.prototype.processTiSymbols = function processTiSymbols(next) {
const depMap = JSON.parse(fs.readFileSync(path.join(this.platformPath, 'dependency.json'))),
var depMap = this.dependencyMap,
modulesMap = JSON.parse(fs.readFileSync(path.join(this.platformPath, 'modules.json'))),
modulesPath = path.join(this.platformPath, 'modules'),
moduleBindings = {},
Expand Down Expand Up @@ -2949,7 +2949,9 @@ AndroidBuilder.prototype.processTiSymbols = function processTiSymbols(next) {
let jar = moduleJarMap[namespace];
if (jar) {
jar = jar === 'titanium.jar' ? path.join(this.platformPath, jar) : path.join(this.platformPath, 'modules', jar);
if (fs.existsSync(jar) && !jarLibraries[jar]) {
if (this.isExternalAndroidLibraryAvailable(jar)) {
this.logger.debug('Excluding library ' + jar.cyan);
} else if (fs.existsSync(jar) && !jarLibraries[jar]) {
this.logger.debug(__('Adding library %s', jar.cyan));
jarLibraries[jar] = 1;
}
Expand All @@ -2958,7 +2960,13 @@ AndroidBuilder.prototype.processTiSymbols = function processTiSymbols(next) {
}

depMap.libraries[namespace] && depMap.libraries[namespace].forEach(function (jar) {
if (fs.existsSync(jar = path.join(this.platformPath, jar)) && !jarLibraries[jar]) {
jar = path.join(this.platformPath, jar);
if (this.isExternalAndroidLibraryAvailable(jar)) {
this.logger.debug('Excluding dependency library ' + jar.cyan);
return;
}

if (fs.existsSync(jar) && !jarLibraries[jar]) {
this.logger.debug(__('Adding dependency library %s', jar.cyan));
jarLibraries[jar] = 1;
}
Expand Down Expand Up @@ -3144,7 +3152,13 @@ AndroidBuilder.prototype.copyModuleResources = function copyModuleResources(next
resPkgFile = jarFile.replace(/\.jar$/, '.respackage');

if (fs.existsSync(resPkgFile) && fs.existsSync(resFile)) {
this.resPackages[resFile] = fs.readFileSync(resPkgFile).toString().split('\n').shift().trim();
const packageName = fs.readFileSync(resPkgFile).toString().split(/\r?\n/).shift().trim();
if (!this.hasAndroidLibrary(packageName)) {
this.resPackages[resFile] = packageName;
} else {
this.logger.info(__('Excluding core module resources of %s (%s) because Android Library with same package name is available.', jarFile, packageName));
return done();
}
}

if (!fs.existsSync(jarFile) || !fs.existsSync(resFile)) {
Expand Down
33 changes: 30 additions & 3 deletions android/cli/commands/_buildModule.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ AndroidModuleBuilder.prototype.run = function run(logger, config, cli, finished)
cli.emit('build.module.pre.compile', this, next);
},

'replaceBundledSupportLibraries',
'processResources',
'compileAidlFiles',
'compileModuleJavaSrc',
Expand Down Expand Up @@ -426,6 +427,27 @@ AndroidModuleBuilder.prototype.loginfo = function loginfo() {
this.logger.info(__('Resources Dir: %s', this.resourcesDir.cyan));
};

/**
* Replaces any .jar file in the Class Path that comes bundled with our SDK
* with a user provided one if available.
*
* We need to do this in this in an extra step because by the time our bundled
* Support Libraries will be added, we haven't parsed any other Android
* Libraries yet.
*
* @param {Function} next Callback function
*/
AndroidModuleBuilder.prototype.replaceBundledSupportLibraries = function replaceBundledSupportLibraries(next) {
Object.keys(this.classPaths).forEach(function (libraryPathAndFilename) {
if (this.isExternalAndroidLibraryAvailable(libraryPathAndFilename)) {
this.logger.debug('Excluding library ' + libraryPathAndFilename.cyan);
delete this.classPaths[libraryPathAndFilename];
}
}, this);

next();
};

/**
* Processes resources for this module.
*
Expand Down Expand Up @@ -497,8 +519,13 @@ AndroidModuleBuilder.prototype.processResources = function processResources(next
const resArchivePathAndFilename = path.join(modulesPath, file.replace(/\.jar$/, '.res.zip'));
const respackagePathAndFilename = path.join(modulesPath, file.replace(/\.jar$/, '.respackage'));
if (fs.existsSync(resArchivePathAndFilename) && fs.existsSync(respackagePathAndFilename)) {
extraPackages.push(fs.readFileSync(respackagePathAndFilename).toString().split('\n').shift().trim());
resArchives.push(resArchivePathAndFilename);
const packageName = fs.readFileSync(respackagePathAndFilename).toString().split(/\r?\n/).shift().trim();
if (!this.hasAndroidLibrary(packageName)) {
extraPackages.push(packageName);
resArchives.push(resArchivePathAndFilename);
} else {
this.logger.info(__('Excluding core module resources of %s (%s) because Android Library with same package name is available.', file, packageName));
}
}
}, this);

Expand Down Expand Up @@ -1200,7 +1227,7 @@ AndroidModuleBuilder.prototype.compileJsClosure = function (next) {

this.logger.info(__('Generating v8 bindings'));

const dependsMap = JSON.parse(fs.readFileSync(this.dependencyJsonFile));
const dependsMap = this.dependencyMap;
Array.prototype.push.apply(this.metaData, dependsMap.required);

Object.keys(dependsMap.dependencies).forEach(function (key) {
Expand Down
37 changes: 37 additions & 0 deletions android/cli/hooks/aar-transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,43 @@ exports.init = function (logger, config, cli, appc) {
scanModuleAndStartTransform(builder, logger, callback);
}
});

cli.on('build.android.dexer', {
priority: 1100,
/**
* Fixes an issue with Hyperloop 2.1.0 which causes a crash when trying to
* override the Android Support Libraries with local .aar files. Hyperloop
* 2.1.0 will always manually add our bundled Android Support Libraries
* to the dexer paths even if they were replaced by the builder. To fix this
* we check the altered dexer paths again and remove any replaced libraries.
*
* @param {Object} data Hook data
* @param {Function} callback Callback function
*/
pre: function (data, callback) {
const builder = data.ctx;
const dexerOptions = data.args[1].slice(0, 6);
const dexerPaths = data.args[1].slice(6);
let hyperloopModule = null;
builder.nativeLibModules.forEach(function (module) {
if (module.id === 'hyperloop' && module.version === '2.1.0') {
hyperloopModule = module;
}
});
if (hyperloopModule && builder.androidLibraries.length > 0) {
let fixedDexerPaths = [];
dexerPaths.forEach(function (entryPathAndFilename) {
if (!this.isExternalAndroidLibraryAvailable(entryPathAndFilename)) {
fixedDexerPaths.push(entryPathAndFilename);
} else {
logger.trace('Removed duplicate library ' + entryPathAndFilename + ' from dexer paths.');
}
}, builder);
data.args[1] = dexerOptions.concat(fixedDexerPaths);
}
callback();
}
});
};

/**
Expand Down
76 changes: 76 additions & 0 deletions android/cli/lib/base-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,30 @@ const appc = require('node-appc'),
__ = i18nLib.__,
xml = appc.xml;

/**
* The base Android builder that includes common functionality that is used
* in both app and module builds.
*/
function AndroidBaseBuilder() {
Builder.apply(this, arguments);

this.androidLibraries = [];
this.dependencyMap = JSON.parse(fs.readFileSync(path.join(this.platformPath, 'dependency.json')));
}

util.inherits(AndroidBaseBuilder, Builder);

/**
* Utility function to merge the contents of XML files.
*
* If the destination file does not exists, the source file will simply
* be copied over. If both source and destination already exists AND the source
* document contains Android resources, the files will be merged. Otherwise the
* destination file will be overwritten.
*
* @param {string|Document} srcOrDoc Path to source XML file or already parsed Document object
* @param {string} dest Path of the destination XML file
*/
AndroidBaseBuilder.prototype.writeXmlFile = function writeXmlFile(srcOrDoc, dest) {
let destExists = fs.existsSync(dest),
destDoc;
Expand Down Expand Up @@ -99,4 +115,64 @@ AndroidBaseBuilder.prototype.writeXmlFile = function writeXmlFile(srcOrDoc, dest
fs.writeFileSync(dest, '<?xml version="1.0" encoding="UTF-8"?>\n' + dom.documentElement.toString());
};

/**
* Checks wether an Android Library with the given package name is available.
*
* @param {string} packageName Package name of the Android Library to search for
* @return {Boolean} True if the Android Library is available, false if not
*/
AndroidBaseBuilder.prototype.hasAndroidLibrary = function hasAndroidLibrary(packageName) {
return this.androidLibraries.some(function (libraryInfo) {
return libraryInfo.packageName === packageName;
});
};

/**
* Checks if one of our bundled Android Support Libraries (.jar) is also available
* as an Android Library (.aar) provided by the user.
*
* This is used during the build process to allow users to replace any of our
* bundled Android Support Libraries with one of their own choosing. Currently
* supported Android Support Library versions are 24.2.0 - 25.x.
*
* To find out which .jar library can be replaces by which Android Library,
* we depend on a hardcoded list of Android Library package names and the bundled
* library filenames they can replace. This list is manually taken from
* android/dependency.json and needs to be maintained if anything changes there.
*
* @param {string} libraryPathAndFilename Path and filename to the .jar file to check
* @return {Boolean} True if the given library is available as an Android Library, false if not
*/
AndroidBaseBuilder.prototype.isExternalAndroidLibraryAvailable = function isExternalAndroidLibraryAvailable(libraryPathAndFilename) {
const replaceableAndroidLibraries = {
'android.support.graphics.drawable': [ 'android-support-vector-drawable.jar' ],
'android.support.graphics.drawable.animated': [ 'android-support-animated-vector-drawable.jar' ],
'android.support.v4': [ 'android-support-v4.jar' ],
'android.support.compat': [ 'android-support-compat.jar' ],
'android.support.coreui': [ 'android-support-core-ui.jar' ],
'android.support.coreutils': [ 'android-support-core-utils.jar' ],
'android.support.design': [ 'android-support-design.jar' ],
'android.support.fragment': [ 'android-support-fragment.jar' ],
'android.support.mediacompat': [ 'android-support-media-compat.jar' ],
'android.support.transition': [ 'android-support-transition.jar' ],
'android.support.v7.appcompat': [ 'android-support-v7-appcompat.jar' ],
'android.support.v7.cardview': [ 'android-support-v7-cardview.jar' ],
'android.support.v7.recyclerview': [ 'android-support-v7-recyclerview.jar' ]
};
return this.androidLibraries.some(function (libraryInfo) {
if (!replaceableAndroidLibraries[libraryInfo.packageName]) {
return false;
}

const libraryFilename = path.basename(libraryPathAndFilename);
const shouldExcludeLibrary = replaceableAndroidLibraries[libraryInfo.packageName].indexOf(libraryFilename) !== -1;
if (shouldExcludeLibrary) {
this.logger.trace(__('Android library %s (%s) available, marking %s to be excluded.', libraryInfo.task.aarPathAndFilename, libraryInfo.packageName.cyan, libraryPathAndFilename.cyan));
return true;
}

return false;
}, this);
};

module.exports = AndroidBaseBuilder;

0 comments on commit d877ab0

Please sign in to comment.