[记录点滴]Ionic编译过程的研究

    技术2022-07-10  92

    [记录点滴]Ionic编译过程的研究

    0x00 摘要

    之前研究Ionic编译过程的笔记,发出来做个记录。当时是因为有些图片没有拷贝到应用中,所以需要调试编译过程。

    0x01 入口

    编译的入口在platforms\android\cordova,具体是以下脚本文件:

    android_sdk_version build.bat clean lib loggingHelper.js run.bat Api.js check_reqs clean.bat log node_modules version build check_reqs.bat defaults.xml log.bat run version.bat

    对应的命令就是ionic run/build/clean…

    0x02 执行

    以run为例,其会调用build下面的run函数 platforms\android\cordova\lib\run.js

    build = require('./build'), module.exports.run = function(runOptions) { return Q() ... return build.run.call(self, runOptions, resolvedTarget) ... } };

    以build为例,build.bat会直接调用build文件,进行编译。

    真正执行

    build真正执行的命令在这里:

    new Api().build(buildOpts) ./android/cordova/Api.js:Api.prototype.prepare = function (cordovaProject, prepareOptions) { ./android/cordova/Api.js: return require('./lib/prepare').prepare.call(this, cordovaProject, prepareOptions); ./android/cordova/Api.js: return require('./lib/prepare').clean.call(self, cleanOptions);

    在Api.js中,build代码如下:

    Api.prototype.build = function (buildOptions) { var self = this; return require('./lib/check_reqs').run() .then(function () { return require('./lib/build').run.call(self, buildOptions); }) .then(function (buildResults) { // Cast build result to array of build artifacts return buildResults.apkPaths.map(function (apkPath) { return { buildType: buildResults.buildType, buildMethod: buildResults.buildMethod, path: apkPath, type: 'apk' }; }); }); };

    Check

    cordova\lib下面的代码 platforms\android\cordova\lib\check_reqs.js 做了各种check。

    module.exports.check_all = function() { var requirements = [ new Requirement('java', 'Java JDK'), new Requirement('androidSdk', 'Android SDK'), new Requirement('androidTarget', 'Android target'), new Requirement('gradle', 'Gradle') ]; var checkFns = [ this.check_java, this.check_android, this.check_android_target, this.check_gradle ]; }

    选择builder

    platforms\android\cordova\lib\build.js 会选择一个builder,然后调用其的build函数

    var builders = require('./builders/builders'); module.exports.run = function(options, optResolvedTarget) { var opts = parseOpts(options, optResolvedTarget, this.root); var builder = builders.getBuilder(opts.buildMethod); return builder.prepEnv(opts) .then(function() { if (opts.prepEnv) { events.emit('verbose', 'Build file successfully prepared.'); return; } return builder.build(opts) .then(function() { var apkPaths = builder.findOutputApks(opts.buildType, opts.arch); events.emit('log', 'Built the following apk(s): \n\t' + apkPaths.join('\n\t')); return { apkPaths: apkPaths, buildType: opts.buildType, buildMethod: opts.buildMethod }; }); }); };

    cordova\lib\builders下面的函数 会调用具体的builder,比如ant还是gradle。

    platforms\android\cordova\lib\builders\builder.js 具体做选什么builder。

    var knownBuilders = { ant: 'AntBuilder', gradle: 'GradleBuilder', none: 'GenericBuilder' }; module.exports.getBuilder = function (builderType, projectRoot) { if (!knownBuilders[builderType]) throw new CordovaError('Builder ' + builderType + ' is not supported.'); try { var Builder = require('./' + knownBuilders[builderType]); return new Builder(projectRoot); } catch (err) { throw new CordovaError('Failed to instantiate ' + knownBuilders[builderType] + ' builder: ' + err); } };

    GradleBuilder

    Gradle的编译调用在 platforms\android\cordova\lib\builders\GradleBuilder.js

    GradleBuilder.prototype.build = function(opts) { var wrapper = path.join(this.root, 'gradlew'); var args = this.getArgs(opts.buildType == 'debug' ? 'debug' : 'release', opts); return spawn(wrapper, args, {stdio: 'pipe'}) .progress(function (stdio){ if (stdio.stderr) { 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); } }).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); }); };

    0x03 排查拷贝文件

    关键词的查找

    { platforms } ? find -type f | xargs grep -w "shell.cp" ./android/cordova/lib/builders/GradleBuilder.js: shell.cp('-f', pluginBuildGradle, path.join(this.root, subProjects[i], 'build.gradle')); ./android/cordova/lib/builders/GradleBuilder.js: shell.cp(path.join(wrapperDir, 'gradlew.bat'), self.root); ./android/cordova/lib/builders/GradleBuilder.js: shell.cp(path.join(wrapperDir, 'gradlew'), self.root); ./android/cordova/lib/builders/GradleBuilder.js: shell.cp('-r', path.join(wrapperDir, 'gradle', 'wrapper'), path.join(self.root, 'gradle')); ./android/cordova/lib/pluginHandlers.js: shell.cp('-Rf', src+'/*', dest); ./android/cordova/lib/pluginHandlers.js: shell.cp('-f', src, dest); ./android/cordova/lib/prepare.js: shell.cp('-f', locations.defaultConfigXml, locations.configXml); ./android/cordova/node_modules/cordova-common/src/FileUpdater.js: shell.cp("-f", sourceFullPath, targetFullPath); ./android/cordova/node_modules/cordova-common/src/FileUpdater.js: shell.cp("-f", sourceFullPath, targetFullPath); ./android/cordova/node_modules/cordova-common/src/FileUpdater.js: shell.cp("-f", sourceFullPath, targetFullPath); { platforms } ? find -type f | xargs grep -w mergeAndUpdateDir ./android/cordova/lib/prepare.js: FileUpdater.mergeAndUpdateDir( ./android/cordova/lib/prepare.js: // No source paths are specified, so mergeAndUpdateDir() will clear the target directory. ./android/cordova/lib/prepare.js: FileUpdater.mergeAndUpdateDir( ./android/cordova/node_modules/cordova-common/src/FileUpdater.js:function mergeAndUpdateDir(sourceDirs, targetDir, options, log) { ./android/cordova/node_modules/cordova-common/src/FileUpdater.js: mergeAndUpdateDir: mergeAndUpdateDir

    第一步,看看是否拷贝文件正确,初步怀疑是在这里。因为这里都是js文件,所以可以用console.log()等函数打印log, 然后把编译过程输入到文件中看,比如ionic build android > log.txt, 命令执行结束之后,看log.txt文件中的log

    ./android/cordova/lib/prepare.js module.exports.prepare = function (cordovaProject, options) { var self = this; var platformResourcesDir = path.relative(cordovaProject.root, path.join(this.locations.root, 'res')); -------- 打印platformResourcesDir,看看这个数据是否正确 // Update own www dir with project's www assets and plugins' assets and js-files return Q.when(updateWww(cordovaProject, this.locations)) .then(function () { // update project according to config.xml changes. return updateProjectAccordingTo(self._config, self.locations); }) .then(function () { ------------- 在这里更新图标和启动界面的,所以以图标为例,看看updateIcons是否拷贝成功 updateIcons(cordovaProject, platformResourcesDir); updateSplashes(cordovaProject, platformResourcesDir); }) .then(function () { events.emit('verbose', 'Prepared android project successfully'); }); };

    第二步,实验添加log代码:

    cordova\lib\run.js module.exports.run = function(runOptions) { console.log("================= cordova lib run ================="); } cordova\lib\prepare.js module.exports.prepare = function (cordovaProject) { console.log("================= cordova lib prepare ================="); ..... var projectRoot = path.dirname(projectConfig.path); var destination = path.join(platformRoot, 'res'); console.log("================= cordova lib handleIcons =================projectRoot: " + projectRoot); console.log("================= cordova lib prepare =================destination: " + destination); .... }

    第三步,执行看看log

    C:\>ionic prepare android ================= cordova lib prepare ================= ================= cordova lib handleIcons =================projectRoot: C ================= cordova lib prepare =================destination: C:\platforms\android\res Running command: "C:\Program Files\nodejs\node.exe" add to body class: platform-android will push strings array {"name":"lang","titles":["English (US)","English (UK)"], "values":["en-us","en-gb"]} android preferences file was successfully generated C:\>ionic build android ================= cordova lib prepare ================= ================= cordova lib handleIcons =================projectRoot: BUILD SUCCESSFUL Total time: 19.631 secs

    ★★★★★★关于生活和技术的思考★★★★★★ 微信公众账号:罗西的思考 如果您想及时得到个人撰写文章的消息推送,或者想看看个人推荐的技术资料,可以扫描下面二维码(或者长按识别二维码)关注个人公众号)。

    Processed: 0.015, SQL: 9