我们可以使用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。