Repo脚本分析

         我们可以使用wget、curl命令或者从网络上下载repo的引导脚本(bootstrap)。然后把repo脚本设置为可执行,并放到可执行的路径中。这里的repo引导脚本只是一个帮助完成整个repo程序继续下载和加载的工具。下面来看看repo引导脚本的工作流程。

         repo脚本是作为整个repo工具的入口,只要负责repo环境的初始化和克隆repo库。repo有两处if __name== ‘__main__’,位于开始部分主要是给Shell执行用的,位于末尾的部分是给Python执行用的。

         来看看repo引导脚本的前几行。

         1          #!/bin/sh

         2      

         3       REPO_URL='git://android.git.kernel.org/tools/repo.git'

         4       REPO_REV='stable'

         5

         6       magic='--calling-python-from-/bin/sh--'

         7       """exec"python -E "$0" "$@" """#$magic"

         8       if __name__ == '__main__':

         9                import sys

         10              if sys.argv[-1] == '#%s' % magic:

         11                       delsys.argv[-1]

         12     del magic

         从第1行可以看出,这个脚本是用Shell脚本写的。第7行既是一条合法的Shell语句,又是一条合法的Python语句。如果作为Shell语句,执行exec,用Python调用本脚本,并替换本进程。三引号在这里相当于一个空字符串和一个单独的引号。如果作为Python语句,三引号定义的是一个字符串,字符串后面是一个注释。实际上第1行到第7行,既是合法的Shell语句又是合法的Python语句。从第8行开始都是Python语句。repo引导脚本无论是用Shell执行,还是用Python执行,效果都相当于使用Python执行这个脚本。

         在引导脚本repo的main函数中,首先调用_FindRepo()函数,从当前目录开始依次向上递归查找.repo/repo/main.py文件。如果找到了.repo/repo/main.py脚本,则把程序的控制权交给.repo/repo/main.py脚本。

         在下载repo引导脚本后,没有repoinit之前,自然是不会存在.repo/repo/main.py脚本(手动拷贝除外),这时必须进行初始化操作。

         从main主方法开始看起:

         1、执行_FindRepo()函数,返回找到的.repo/repo/main.py脚本文件,以及包含repo/main.py的.repo目录。

         2、执行_ParseArguments()函数,分析命令行参数,分析出哪些是命令,哪些是参数。

         3、执行_RunSelf()函数,判断本次执行所需的脚本和目录(main.p文件、git_config.py、project.py、.git目录和subcmds目录)是否存在,如果存在返回main.py文件和.git目录的绝对路径。

         4、如果main.py不存在,则执行_Init()函数(如执行repoinit命令)。如果没有指定--repo-url或者--repo-branch,则采用脚本开始定义的REPO_URL或者REPO_REV作为默认值。repo先在本地创建一个空的repo库,然后设置注册远程库,最后调用_Clone()函数克隆repo库。

         5、如果main.py存在(如执行reposync命令),则移交控制权给main.py,并将参数传递给main.py。

         这里完成权利交接,下面就该main.py显身手了。

         在main.py中有这么一句:from subcmds import all_commands,它表示从模块包subcmds中导入所有命令。在包的每个目录中必须包含一个名为”__init__.py”(init的前后均是两条下划线)的文件。”__init__.py”可以是一个空文件,仅用来表示该目录为一个包。__init__.py是初始化模块,from-import语句导入子包时需要用到它。

“__init__.py”的主要用途是设置”__all__”变量以及包含包初始化所需的代码。在导入包内所有名字时在from中使用”*”通配符,”__all__”可以保证名字的正确导入。

         __init__.py的代码如下:

         1       import os

         2       all_commands = {}

         3

         4       my_dir = os.path.dirname(__file__)

         5       print __file__, 'variable --> my_dir',my_dir

         6       for py in os.listdir(my_dir):

         7                ifpy == '__init__.py':

         8                          continue

         9                ifpy.endswith('.py'):

         10                       name = py[:-3]

         11                       clsn = name.capitalize()

         12                       while clsn.find('_') >0:

         13                                 h =clsn.index('_')

         14                                 clsn =clsn[0:h] + clsn[h + 1:].capitalize()

         15                       mod =__import__(__name__,

         16                             globals(),

         17                             locals(),

         18                             ['%s' % name])

         19                       mod = getattr(mod, name)

         20                       try:

         21                                 cmd =getattr(mod, clsn)()

         22                       except AttributeError:

         23                                 raiseSyntaxError, '%s/%s does not define class %s' % (

         24                                   __name__, py, clsn)

         25                       name = name.replace('_','-')

         26                       print __file__, 'variable--> cmd_name', name

         27                       cmd.NAME = name

         28                       all_commands[name]= cmd

         29     if 'help' in all_commands:

         30              all_commands['help'].commands= all_commands

        

         main.py负责具体命令的执行,包括manifests库的克隆的检出。main.py的结构和repo脚本类似。

         1、从subcmds导入repo的相关命令。

         2、执行init_ssh()函数(在git_confit.py中实现),完成ssh验证,包括用户名、邮箱地址等。

         3、执行init_http()函数,进行网络验证。

         4、解析manifest.xml,没有指定的就是默认名称。

         5、通过getattr方式执行命令。_Run()函数根据命令的名称,执行subcmds下相应的文件。例如,repoinit就是执行subcmds/init.py,repo sync就是执行subcmds/sync.py。

你可能感兴趣的:(python,脚本,git,Repo)