个人邮箱:[email protected]
接下来我们对照着官网提供的下载android源码的过程来看一下使用repo的步骤:
http://source.android.com/source/downloading.html
$ curl https://dl-ssl.google.com/dl/googlesource/git-repo/repo > ~/bin/repo
$ chmod a+x ~/bin/repo
$ repo init -u https://android.googlesource.com/platform/manifest
第一句就是从官网上下载repo,到~/bin文件,该路径已经被提前加入到环境变量中了。这样就可以在任何路径下运行repo了。成功后显示:
在进入文件后我们看到了repo就在那里了,如果要运行还需要更改成可执行可执行权限。其实它就是一个几百行的shell脚本,其实这并不是真正的repo,而只是一个bootstrap,这个shell脚本虽然是以shell文法开头,但是内容却全部是python语法,据说这就是这个脚本的神奇之处,我们简要分析。
忽略掉部分注释:
magic='—calling-python-from-/bin/sh--' """exec" python -E "$0" "$@" """#$magic" if __name__ == '__main__': import sys if sys.argv[-1] == '#%s' % magic: del sys.argv[-1] del magic
这个部分可谓是repo脚本很有技巧的地方。也就实现了从shell向python过渡。因为shell和python的执行机制有些不同,首先,执行shell时候,是出现错误再停止,而python是保证语法正确才能执行。而上面的代码,在两种脚本中都是正确的语法。
(1) 在shell中代码是这样的:
magic='—calling-python-from-/bin/sh--'
"""exec" python -E "$0" "$@" """#$magic"
2个"等于空,$0等于脚本名repo,$@为参数(比如init)而其语法等效:
exec python -E repo init #—calling-python-from-/bin/sh--
(2)在python中代码是这样的:
magic='—calling-python-from-/bin/sh--'
"""exec"python -E "$0" "$@" """#$magic"
三个"相当于单纯的字符串,#后为注释
magic='—calling-python-from-/bin/sh--'
而后exec进入到python,shell的exec是创建新进程取代当前进程,并将数据、堆、栈全部转交,PID不变,接下来就全部都是python的活了。于是乎,这里有一个疑问了,repo为什么不直接使用python,而是要先使用shell过度呢?这个问题说实话,个人也不太了解。
在网上也只是找到一个外文的解释如下,实际上也不算解释,这只是一个在linux/unix上执行Python的一种方法而已,方法的话就不需要什么理由了。
http://effbot.org/pyfaq/how-do-i-make-a-python-script-executable-on-unix.htm
接下来我们详细分析repo脚本中,在文章最初时的时候,下载的repo的bootstrap是在~/bin/路径下,这部分代码会在你在不同目录每次敲入repo init的时候调用,通过网络去加载服务器上的完整的repo文件。如下是,repo init之后的.repo的完整的树形图,所以repo的bootstrap要拷贝一份出来下文好好研究一下。
.repo
└── repo
├── color.py
├── color.pyc
├── command.py
├── command.pyc
├── COPYING
├── docs
│ └── manifest-format.txt
├── editor.py
├── editor.pyc
├── error.py
├── error.pyc
├── git_command.py
├── git_command.pyc
├── git_config.py
├── git_config.pyc
├── git_refs.py
├── git_refs.pyc
├── git_ssh
├── hooks
│ ├── commit-msg
│ └── pre-auto-gc
├── main.py
├── main.pyc
├── manifest_xml.py
├── manifest_xml.pyc
├── pager.py
├── pager.pyc
├── progress.py
├── progress.pyc
├── project.py
├── project.pyc
├── repo
├── repoc
├── subcmds
│ ├── abandon.py
│ ├── abandon.pyc
│ ├── branches.py
│ ├── branches.pyc
│ ├── checkout.py
│ ├── checkout.pyc
│ ├── cherry_pick.py
│ ├── cherry_pick.pyc
│ ├── diff.py
│ ├── diff.pyc
│ ├── download.py
│ ├── download.pyc
│ ├── forall.py
│ ├── forall.pyc
│ ├── grep.py
│ ├── grep.pyc
│ ├── help.py
│ ├── help.pyc
│ ├── __init__.py
│ ├── init.py
│ ├── __init__.pyc
│ ├── init.pyc
│ ├── list.py
│ ├── list.pyc
│ ├── manifest.py
│ ├── manifest.pyc
│ ├── overview.py
│ ├── overview.pyc
│ ├── prune.py
│ ├── prune.pyc
│ ├── rebase.py
│ ├── rebase.pyc
│ ├── selfupdate.py
│ ├── selfupdate.pyc
│ ├── smartsync.py
│ ├── smartsync.pyc
│ ├── stage.py
│ ├── stage.pyc
│ ├── start.py
│ ├── start.pyc
│ ├── status.py
│ ├── status.pyc
│ ├── sync.py
│ ├── sync.pyc
│ ├── upload.py
│ ├── upload.pyc
│ ├── version.py
│ └── version.pyc
├── SUBMITTING_PATCHES
├── tests
│ ├── fixtures
│ │ └── test.gitconfig
│ └── test_git_config.py
├── trace.py
└── trace.pyc