Cordova CLI源码分析(五)——添加平台

添加平台支持,其实本质是利用各平台SDK环境创建原生工程文件。所以,所有函数也是围绕这一主题展开。

入口当然是platform函数

module.exports = function platform(command, targets, callback) {

.....

};

先不看具体源码,再看其他几个函数

(1)module.exports.supports = function(project_root, name, callback) {

这个函数主要执行以下几步:

//检查传入参数

//判断平台是否支持

//获得顶层目录下platforms.jsparser参数指定的各平台解析文件

//检查各平台依赖的SDK是否存在

/**
 * Check Platform Support.
 *
 * Options:
 *
 *   - {String} `name` of the platform to test.
 *   - {Function} `callback` is triggered with the answer.
 *     - {Error} `e` null when a platform is supported otherwise describes error.
 */

module.exports.supports = function(project_root, name, callback) {
    // required parameters
    if (!name) throw new Error('requires a platform name parameter');
    if (!callback) throw new Error('requires a callback parameter');

    // check if platform exists
    var platform = platforms[name];
    if (!platform) {
        callback(new Error(util.format('"%s" platform does not exist', name)));
        return;
    }

    // look up platform meta-data parser
    var platformParser = platforms[name].parser;
    if (!platformParser) {
        callback(new Error(util.format('"%s" platform parser does not exist', name)));
        return;
    }

    // check for platform support
    platformParser.check_requirements(project_root, function(e) {
        // typecast String to Error
        e = (typeof e == 'string') ? new Error(e) : e;
        // typecast false Boolean to null
        e = (e) ? e : null;

        callback(e);
    });
};

(2)function call_into_create(target, projectRoot, cfg, id, version, callback, end) {

这个函数调用各个移动开发平台SDK环境中提供的创建原生工程文件的方法,创建工程

function call_into_create(target, projectRoot, cfg, id, version, callback, end) {
    var output = path.join(projectRoot, 'platforms', target);

    // Check if output directory already exists.
    if (fs.existsSync(output)) {
        var err = new Error('Platform "' + target + '" already exists at "' + output + '"');
        if (callback) callback(err);
        else throw err;
    } else {
        // Make sure we have minimum requirements to work with specified platform
        events.emit('log', 'Checking if platform "' + target + '" passes minimum requirements...');
        module.exports.supports(projectRoot, target, function(err) {
            if (err) {
                if (callback) callback(err);
                else throw err;
            } else {
                // Create a platform app using the ./bin/create scripts that exist in each repo.
                // Run platform's create script
                var bin = path.join(cordova_util.libDirectory, target, id, version, 'bin', 'create');
                //调用不同平台下创建工程命令
                if(target == 'wp7') bin = path.join(cordova_util.libDirectory, 'wp', id, version, 'wp7', 'bin', 'create');
                if(target == 'wp8') bin = path.join(cordova_util.libDirectory, 'wp', id, version, 'wp8', 'bin', 'create');
                var args = (target=='ios') ? '--arc' : '';
                var pkg = cfg.packageName().replace(/[^\w.]/g,'_');
                var name = cfg.name();
                var command = util.format('"%s" %s "%s" "%s" "%s"', bin, args, output, pkg, name);
                events.emit('log', 'Running bin/create for platform "' + target + '" with command: "' + command + '" (output to follow)');

                shell.exec(command, {silent:true,async:true}, function(code, create_output) {
                    events.emit('log', create_output);
                    if (code > 0) {
                        var err = new Error('An error occured during creation of ' + target + ' sub-project. ' + create_output);
                        if (callback) callback(err);
                        else throw err;
                    } else {
                        require('../cordova').prepare(target, function(err) {
                            if (err) {
                                if (callback) callback(err);
                                else throw err;
                            } else {
                                createOverrides(projectRoot, target);
                                end(); //platform add is done by now.
                                // Install all currently installed plugins into this new platform.
                                var plugins_dir = path.join(projectRoot, 'plugins');
                                var plugins = cordova_util.findPlugins(plugins_dir);
                                var parser = new platforms[target].parser(output);
                                plugins && plugins.forEach(function(plugin) {
                                    events.emit('log', 'Installing plugin "' + plugin + '" following successful platform add of ' + target);
                                    plugman.install(target, output, path.basename(plugin), plugins_dir, { www_dir: parser.staging_dir() });
                                });
                            }
                        });
                    }
                });
            }
        });
    }
}

(3)最会回到platform函数

module.exports = function platform(command, targets, callback) {
    var projectRoot = cordova_util.isCordova(process.cwd());
    //检查是否在根目录下存在.cordova目录,如果存在此目录才是cordova项目,返回此绝对路径;否则返回false

    if (!projectRoot) {
        var err = new Error('Current working directory is not a Cordova-based project.');
        if (callback) callback(err);
        else throw err;
        return;
    }

    var hooks = new hooker(projectRoot);

    if (arguments.length === 0) command = 'ls';//参数为空,默认为ls
    if (targets) {
        if (!(targets instanceof Array)) targets = [targets];//遍历targets对象是否是系统支持平台参数
        targets.forEach(function(t) {
            if (!(t in platforms)) {
                var err = new Error('Platform "' + t + '" not recognized as core cordova platform.');
                if (callback) return callback(err);
                else throw err;
            }
        });
    } else {
        if (command == 'add' || command == 'rm') {
            var err = new Error('You need to qualify `add` or `remove` with one or more platforms!');
            if (callback) return callback(err);
            else throw err;
        }
    }

    var xml = cordova_util.projectConfig(projectRoot);//根目录/www/config.xml
    var cfg = new cordova_util.config_parser(xml);//解析xml文件
    var opts = {
        platforms:targets
    };

    switch(command) {
        case 'add':
            var end = n(targets.length, function() {
                hooks.fire('after_platform_add', opts, function(err) {
                    if (err) {
                        if (callback) callback(err);
                        else throw err;
                    } else {
                        if (callback) callback();
                    }
                });
            });
            hooks.fire('before_platform_add', opts, function(err) {
                if (err) {
                    if (callback) callback(err);
                    else throw err;
                } else {
                    var config_json = config.read(projectRoot);
                    targets.forEach(function(t) {
                        lazy_load.based_on_config(projectRoot, t, function(err) {
                            if (err) {
                                if (callback) callback(err);
                                else throw err;
                            } else {
                                if (config_json.lib && config_json.lib[t]) {
                                    call_into_create(t, projectRoot, cfg, config_json.lib[t].id, config_json.lib[t].version, callback, end);
                                } else {
                                    call_into_create(t, projectRoot, cfg, 'cordova', platforms[t].version, callback, end);
                                }
                            }
                        });
                    });
                }
            });
            break;
        case 'rm':
        case 'remove':
            var end = n(targets.length, function() {
                hooks.fire('after_platform_rm', opts, function(err) {
                    if (err) {
                        if (callback) callback(err);
                        else throw err;
                    } else {
                        if (callback) callback();
                    }
                });
            });
            hooks.fire('before_platform_rm', opts, function(err) {
                if (err) {
                    if (callback) callback(err);
                    else throw err;
                } else {
                    targets.forEach(function(target) {
                        shell.rm('-rf', path.join(projectRoot, 'platforms', target));
                        shell.rm('-rf', path.join(cordova_util.appDir(projectRoot), 'merges', target));
                        var plugins_json = path.join(projectRoot, 'plugins', target + '.json');
                        if (fs.existsSync(plugins_json)) shell.rm(plugins_json);
                        end();
                    });
                }
            });
            break;
        case 'ls':
        case 'list':
        default:
            var platforms_on_fs = cordova_util.listPlatforms(projectRoot);
            hooks.fire('before_platform_ls', function(err) {
                if (err) {
                    if (callback) callback(err);
                    else throw err;
                } else {
                    events.emit('results', (platforms_on_fs.length ? platforms_on_fs : 'No platforms added. Use `cordova platform add <platform>`.'));
                    hooks.fire('after_platform_ls', function(err) {
                        if (err) {
                            if (callback) callback(err);
                            else throw err;
                        }
                    });
                }
            });
            break;
    }
};

结构比较简单,参数检查,然后根据不同参数执行不同动作,需要唯一说明的是,在switch函数中出现的

 var end = n(targets.length, function() {
                hooks.fire('after_platform_add', opts, function(err) {
                    if (err) {
                        if (callback) callback(err);
                        else throw err;
                    } else {
                        if (callback) callback();
                    }
                });
            });

这个函数是使用的nodenCallbacks插件,https://npmjs.org/package/ncallbacks

官方文档解释是function that executes n times,在上面这段代码中意思就是,end函数只能被执行targets.length次,调用超过targets.length次再调用end,其内部函数也不会再被执行,其实就是限制次数,避免当平台列表中为空后还误执行



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值