Skip to content

Commit

Permalink
[TIMOB-26079] Continue build on invalid Android library ABI (#10084)
Browse files Browse the repository at this point in the history
* [TIMOB-26079] Continue build on invalid Android library ABI

* [TIMOB-26079] Remove useless debug output
  • Loading branch information
janvennemann authored and hansemannn committed Jun 30, 2018
1 parent ac2eb09 commit 77f6361
Showing 1 changed file with 152 additions and 163 deletions.
315 changes: 152 additions & 163 deletions android/cli/commands/_build.js
Original file line number Diff line number Diff line change
Expand Up @@ -4393,200 +4393,189 @@ AndroidBuilder.prototype.createUnsignedApk = function createUnsignedApk(next) {
soRegExp = /\.so$/,
trailingSlashRegExp = /\/$/,
nativeLibs = {},
origConsoleError = console.error,
entryNames = [];

// since the archiver library didn't set max listeners, we squelch all error output
console.error = function () {};
fs.existsSync(this.unsignedApkFile) && fs.unlinkSync(this.unsignedApkFile);
const apkStream = fs.createWriteStream(this.unsignedApkFile);
apkStream.on('close', function () {
next();
});
dest.catchEarlyExitAttached = true; // silence exceptions
dest.pipe(apkStream);

this.logger.info(__('Creating unsigned apk'));

// merge files from the app.ap_ file as well as all titanium and 3rd party jar files
const archives = [this.ap_File].concat(Object.keys(this.moduleJars)).concat(Object.keys(this.jarLibraries));

archives.forEach(function (file) {
const src = new AdmZip(file),
entries = src.getEntries();

this.logger.debug(__('Processing %s', file.cyan));

entries.forEach(function (entry) {
if (entry.entryName.indexOf('META-INF/') === -1
&& (entry.entryName.indexOf('org/appcelerator/titanium/bindings/') === -1 || !jsonRegExp.test(entry.name))
&& entry.name.charAt(0) !== '.'
&& !classRegExp.test(entry.name)
&& !trailingSlashRegExp.test(entry.entryName)
) {
// do not add duplicate entries
if (entryNames.indexOf(entry.entryName) > -1) {
this.logger.warn(__('Removing duplicate entry %s', entry.entryName.cyan));
return;
}

try {
fs.existsSync(this.unsignedApkFile) && fs.unlinkSync(this.unsignedApkFile);
const apkStream = fs.createWriteStream(this.unsignedApkFile);
apkStream.on('close', function () {
console.error = origConsoleError;
next();
});
dest.catchEarlyExitAttached = true; // silence exceptions
dest.pipe(apkStream);

this.logger.info(__('Creating unsigned apk'));

// merge files from the app.ap_ file as well as all titanium and 3rd party jar files
const archives = [ this.ap_File ].concat(Object.keys(this.moduleJars)).concat(Object.keys(this.jarLibraries));

archives.forEach(function (file) {
const src = new AdmZip(file),
entries = src.getEntries();

this.logger.debug(__('Processing %s', file.cyan));

entries.forEach(function (entry) {
if (entry.entryName.indexOf('META-INF/') === -1
&& (entry.entryName.indexOf('org/appcelerator/titanium/bindings/') === -1 || !jsonRegExp.test(entry.name))
&& entry.name.charAt(0) !== '.'
&& !classRegExp.test(entry.name)
&& !trailingSlashRegExp.test(entry.entryName)
) {
// do not add duplicate entries
if (entryNames.indexOf(entry.entryName) > -1) {
this.logger.warn(__('Removing duplicate entry %s', entry.entryName.cyan));
return;
}
const store = this.uncompressedTypes.indexOf(entry.entryName.split('.').pop()) !== -1;

const store = this.uncompressedTypes.indexOf(entry.entryName.split('.').pop()) !== -1;
this.logger.debug(store
? __('Adding %s', entry.entryName.cyan)
: __('Deflating %s', entry.entryName.cyan));

this.logger.debug(store
? __('Adding %s', entry.entryName.cyan)
: __('Deflating %s', entry.entryName.cyan));
dest.append(src.readFile(entry), {
name: entry.entryName,
store: store
});
entryNames.push(entry.entryName);
}
}, this);
}, this);

dest.append(src.readFile(entry), {
name: entry.entryName,
store: store
});
entryNames.push(entry.entryName);
// Add dex files
this.logger.info(__('Processing %s', this.buildBinClassesDex.cyan));
fs.readdirSync(this.buildBinClassesDex).forEach(function (name) {
var file = path.join(this.buildBinClassesDex, name);
if (dexRegExp.test(name)) {
this.logger.debug(__('Adding %s', name.cyan));
dest.append(fs.createReadStream(file), { name: name });
}
}, this);

this.logger.info(__('Processing %s', this.buildSrcDir.cyan));
(function copyDir(dir, base) {
base = base || dir;
fs.readdirSync(dir).forEach(function (name) {
var file = path.join(dir, name);
if (fs.existsSync(file)) {
if (fs.statSync(file).isDirectory()) {
copyDir(file, base);
} else if (!javaRegExp.test(name)) {
name = file.replace(base, '').replace(/^[/\\]/, '');
this.logger.debug(__('Adding %s', name.cyan));
dest.append(fs.createReadStream(file), { name: name });
}
}, this);
}
}, this);
}.call(this, this.buildSrcDir));

// Add dex files
this.logger.info(__('Processing %s', this.buildBinClassesDex.cyan));
fs.readdirSync(this.buildBinClassesDex).forEach(function (name) {
var file = path.join(this.buildBinClassesDex, name);
if (dexRegExp.test(name)) {
this.logger.debug(__('Adding %s', name.cyan));
dest.append(fs.createReadStream(file), { name: name });
const addNativeLibs = function (dir) {
if (!fs.existsSync(dir)) {
return;
}

for (let i = 0; i < this.abis.length; i++) {
const abiDir = path.join(dir, this.abis[i]);

// check that we found the desired abi, otherwise we abort the build
if (!fs.existsSync(abiDir) || !fs.statSync(abiDir).isDirectory()) {
throw this.abis[i];
}
}, this);

this.logger.info(__('Processing %s', this.buildSrcDir.cyan));
(function copyDir(dir, base) {
base = base || dir;
fs.readdirSync(dir).forEach(function (name) {
var file = path.join(dir, name);
if (fs.existsSync(file)) {
if (fs.statSync(file).isDirectory()) {
copyDir(file, base);
} else if (!javaRegExp.test(name)) {
name = file.replace(base, '').replace(/^[/\\]/, '');
this.logger.debug(__('Adding %s', name.cyan));
dest.append(fs.createReadStream(file), { name: name });
// copy all the .so files into the archive
fs.readdirSync(abiDir).forEach(function (name) {
if (name !== 'libtiprofiler.so' || (this.allowProfiling && this.profilerPort)) {
const file = path.join(abiDir, name),
rel = 'lib/' + this.abis[i] + '/' + name;
if (!nativeLibs[rel] && soRegExp.test(name) && fs.existsSync(file)) {
nativeLibs[rel] = 1;
this.logger.debug(__('Adding %s', rel.cyan));
dest.append(fs.createReadStream(file), { name: rel });
}
}
}, this);
}.call(this, this.buildSrcDir));
}
}.bind(this);

const addNativeLibs = function (dir) {
if (!fs.existsSync(dir)) {
return;
try {
// add Titanium native modules
addNativeLibs(path.join(this.platformPath, 'native', 'libs'));
} catch (abi) {
// this should never be called since we already validated this
const abis = [];
fs.readdirSync(path.join(this.platformPath, 'native', 'libs')).forEach(function (abi) {
var dir = path.join(this.platformPath, 'native', 'libs', abi);
if (fs.existsSync(dir) && fs.statSync(dir).isDirectory()) {
abis.push(abi);
}
});
this.logger.error(__('Invalid native Titanium library ABI "%s"', abi));
this.logger.error(__('Supported ABIs: %s', abis.join(', ')) + '\n');
process.exit(1);
}

for (let i = 0; i < this.abis.length; i++) {
const abiDir = path.join(dir, this.abis[i]);

// check that we found the desired abi, otherwise we abort the build
if (!fs.existsSync(abiDir) || !fs.statSync(abiDir).isDirectory()) {
throw this.abis[i];
}
try {
// add native modules from the build dir's "libs" dir
addNativeLibs(path.join(this.buildDir, 'libs'));
} catch (e) {
// ignore
}

// copy all the .so files into the archive
fs.readdirSync(abiDir).forEach(function (name) {
if (name !== 'libtiprofiler.so' || (this.allowProfiling && this.profilerPort)) {
const file = path.join(abiDir, name),
rel = 'lib/' + this.abis[i] + '/' + name;
if (!nativeLibs[rel] && soRegExp.test(name) && fs.existsSync(file)) {
nativeLibs[rel] = 1;
this.logger.debug(__('Adding %s', rel.cyan));
dest.append(fs.createReadStream(file), { name: rel });
}
this.modules.forEach(function (m) {
if (m.native) {
try {
// add native modules for each module
addNativeLibs(path.join(m.modulePath, 'libs'));
} catch (abi) {
// this should never be called since we already validated this
const abis = [];
fs.readdirSync(path.join(m.modulePath, 'libs')).forEach(function (abi) {
var dir = path.join(m.modulePath, 'libs', abi);
if (fs.existsSync(dir) && fs.statSync(dir).isDirectory()) {
abis.push(abi);
}
}, this);
});
/* commenting this out to preserve the old, incorrect behavior
this.logger.error(__('The module "%s" does not support the ABI "%s"', m.id, abi));
this.logger.error(__('Supported ABIs: %s', abis.join(', ')) + '\n');
process.exit(1);
*/
this.logger.warn(__('The module %s does not support the ABI: %s', m.id.cyan, abi.cyan));
this.logger.warn(__('It only supports the following ABIs: %s', abis.map(function (a) { return a.cyan; }).join(', '))); // eslint-disable-line max-statements-per-line
this.logger.warn(__('Your application will most likely encounter issues'));
}
}.bind(this);
}
}, this);

this.androidLibraries.forEach(function (libraryInfo) {
if (libraryInfo.nativeLibraries.length === 0) {
return;
}

const libraryJniPath = path.join(libraryInfo.explodedPath, 'jni');
try {
// add Titanium native modules
addNativeLibs(path.join(this.platformPath, 'native', 'libs'));
addNativeLibs(libraryJniPath);
} catch (abi) {
// this should never be called since we already validated this
const abis = [];
fs.readdirSync(path.join(this.platformPath, 'native', 'libs')).forEach(function (abi) {
var dir = path.join(this.platformPath, 'native', 'libs', abi);
fs.readdirSync(libraryJniPath).forEach(function (abi) {
const dir = path.join(libraryJniPath, abi);
if (fs.existsSync(dir) && fs.statSync(dir).isDirectory()) {
abis.push(abi);
}
});
this.logger.error(__('Invalid native Titanium library ABI "%s"', abi));
this.logger.error(__('Supported ABIs: %s', abis.join(', ')) + '\n');
process.exit(1);
}

try {
// add native modules from the build dir's "libs" dir
addNativeLibs(path.join(this.buildDir, 'libs'));
} catch (e) {
// ignore
}

this.modules.forEach(function (m) {
if (m.native) {
try {
// add native modules for each module
addNativeLibs(path.join(m.modulePath, 'libs'));
} catch (abi) {
// this should never be called since we already validated this
const abis = [];
fs.readdirSync(path.join(m.modulePath, 'libs')).forEach(function (abi) {
var dir = path.join(m.modulePath, 'libs', abi);
if (fs.existsSync(dir) && fs.statSync(dir).isDirectory()) {
abis.push(abi);
}
});
/* commenting this out to preserve the old, incorrect behavior
this.logger.error(__('The module "%s" does not support the ABI "%s"', m.id, abi));
this.logger.error(__('Supported ABIs: %s', abis.join(', ')) + '\n');
process.exit(1);
*/
this.logger.warn(__('The module %s does not support the ABI: %s', m.id.cyan, abi.cyan));
this.logger.warn(__('It only supports the following ABIs: %s', abis.map(function (a) { return a.cyan; }).join(', '))); // eslint-disable-line max-statements-per-line
this.logger.warn(__('Your application will most likely encounter issues'));
}
}
}, this);

this.androidLibraries.forEach(function (libraryInfo) {
if (libraryInfo.nativeLibraries.length === 0) {
return;
if (libraryInfo.task.originType === 'Module') {
this.logger.warn(__('The Android Library "%s" from module "%s" does not support the ABI: %s', libraryInfo.packageName.cyan, libraryInfo.task.moduleInfo.id.cyan, abi.cyan));
} else if (libraryInfo.task.originType === 'Project') {
this.logger.warn(__('The Android Library "%s" does not support the ABI: %s', libraryInfo.packageName.cyan, abi.cyan));
}
this.logger.warn(__('It only supports the following ABIs: %s', abis.map(function (a) { return a.cyan; }).join(', '))); // eslint-disable-line max-statements-per-line
this.logger.warn(__('Your application will most likely encounter issues'));
}
}, this);

const libraryJniPath = path.join(libraryInfo.explodedPath, 'jni');
try {
addNativeLibs(libraryJniPath);
} catch (e) {
const abis = [];
fs.readdirSync(libraryJniPath).forEach(function (abi) {
const dir = path.join(libraryJniPath, abi);
if (fs.existsSync(dir) && fs.statSync(dir).isDirectory()) {
abis.push(abi);
}
});
// FIXME This validation code isn't right. Shoudl likley be in the function above!
// if (libraryInfo.task.originType === 'Module') {
// this.logger.error(__('The Android Library "%s" from module "%s" does not support the ABI: %s', libraryInfo.packageName, libraryInfo.task.moduleInfo.id, abi));
// } else if (libraryInfo.task.originType === 'Project') {
// this.logger.error(__('The Android Library "%s" does not support the ABI: %s', libraryInfo.packageName, abi));
// }
this.logger.error(__('Supported ABIs by the Android Library: %s', abis.join(', ')));
this.logger.error(__('Valid ABIs for this project: %s', this.abis(', ')));
process.exit(1);
}
}, this);

this.logger.info(__('Writing unsigned apk: %s', this.unsignedApkFile.cyan));
dest.finalize();
} catch (ex) {
console.error = origConsoleError;
throw ex;
}
this.logger.info(__('Writing unsigned apk: %s', this.unsignedApkFile.cyan));
dest.finalize();
};

AndroidBuilder.prototype.createSignedApk = function createSignedApk(next) {
Expand Down

0 comments on commit 77f6361

Please sign in to comment.