Android OTA升級包製作指令碼詳解(一,引數解析)
寫在前面:
“build/tools/releasetools/ota_from_target_files -u lk.bin -n target.zip update.zip”這是製作整包的命令,很顯然這裡支援lk升級。本系列博文主要對該命令的執行流程及原理進行一個系統的分析,涉及到/build/tools/releasetools/目錄下多個模組如ota_from_target_files、common等。由於本人對python瞭解粗淺,文中所涉及到的python語法大都做了註釋,以便幫助部分對python有興趣的同學能夠讀懂相應的語句。
整個流程看起來有些繁瑣冗雜,希望有興趣的同學能夠耐心的去看,已經比較熟悉python語法的同學可以挑選感興趣的部分作為參考。
一,執行指令碼
那麼首先,我們從命令中可以看到整包的製作流程是從ota_from_target_files模組開始的。而ota_from_target_files模組最先執行的方法是從下面這段語句開始的,如下:
#__name__作為模組的內建屬性,指.py檔案的呼叫方式;.py檔案有兩種使用方式:作為模組被呼叫和直接使用。如果它等於"__main__"就表示是直接執行。也就是說在if __name__ == "__main__":之後的語句作為模組被呼叫的時候不執行;直接使用的時候,語句之後的程式碼執行。而這裡很顯然是直接使用。
下面是指令碼的入口函式main(arg)if __name__ == '__main__': try: common.CloseInheritedPipes()"""MAC系統中卻是一些描述檔案(PIPE),這裡在開始之前關閉fds;這裡主要是根據當前所使用的作業系統做的一些調整""" main(sys.argv[1:])"""這是編譯指令碼主要的方法""" except common.ExternalError, e: print print " ERROR: %s" % (e,) print sys.exit(1)
def main(argv): """將使用者設定的 Option 存入 OPTIONS 變數中。OPTIONS是一個Python Class, 我們將其理解為一個C Struct或者一個java的封裝即可""" def option_handler(o, a): if o in ("-b", "--board_config"):#in是一個布林操作符,用來測試左邊的運算元是否包含於右邊的元祖,這裡用來判斷引數o所傳入的值是否包含在("-b", "--board_config")中; pass # deprecated elif o in ("-k", "--package_key"): OPTIONS.package_key = a elif o in ("-i", "--incremental_from"): OPTIONS.incremental_source = a elif o in ("-w", "--wipe_user_data"): OPTIONS.wipe_user_data = True elif o in ("-n", "--no_prereq"): OPTIONS.omit_prereq = True elif o in ("-e", "--extra_script"): OPTIONS.extra_script = a elif o in ("-a", "--aslr_mode"): if a in ("on", "On", "true", "True", "yes", "Yes"): OPTIONS.aslr_mode = True else: OPTIONS.aslr_mode = False elif o in ("--worker_threads"): OPTIONS.worker_threads = int(a) elif o in ("-2", "--two_step"): OPTIONS.two_step = True elif o in ("-r", "--preloader"): OPTIONS.preloader = a elif o in ("-l", "--logo"): OPTIONS.logo = a elif o in ("-u", "--uboot"): OPTIONS.uboot = a elif o in ("-d", "--dsp"): OPTIONS.dsp = a elif o in ("-f", "--special_factory_reset"): OPTIONS.special_factory_reset = True elif o in ("-g", "--ubifs"): OPTIONS.ubifs = True elif o in ("-t", "--tee"): OPTIONS.tee = a elif o in ("-z", "--trustonic"): OPTIONS.trustonic = a else: return False return True
二,解析引數
#解析引數
args = common.ParseOptions(argv, __doc__,
extra_opts="b:k:i:d:wfgne:r:l:u:t:z:d:a:s:2",
extra_long_opts=["board_config=",
"package_key=",
"incremental_from=",
"wipe_user_data",
"special_factory_reset",
"ubifs",
"no_prereq",
"extra_script=",
"preloader=",
"logo=",
"uboot=",
"tee=",
"trustonic=",
"dsp=",
"worker_threads=",
"aslr_mode=",
"two_step",
],
extra_option_handler=option_handler)
上面解析引數的操作實際上是封裝在了common.py模組來進行,這裡我們對extra_option_handler=option_handler簡單的解釋一下,在Python中函式是可以直接複製給一個變數的,option_handler是上面定義的一個方法,而這裡並沒有執行,而是直接賦值給了extra_option_handler,而且我們需要注意的是賦值並不會執行option_handler中內容,而是在實際呼叫的時候才會執行。那麼既然變數可以指向函式,所以一個函式可以接收另外一個函式作為引數。
那麼我們接著來了解函式解析的流程,首先看程式碼:
#在python中,getopt模組是專門用來處理命令列引數的,這裡args是個列表,包含那些沒有"-"或"--"的引數,格式如下['target.zip','update.zip'],而opts是個包含元祖的列表,每個元祖是分析出來的格式資訊,如[('-u', 'lk.bin'),('-n', ''),('-i', 'base.zip')] ;如果想了解getopt具體的邏輯請參考這篇博文python中getopt的使用。
#解析引數
def ParseOptions(argv,
docstring,
extra_opts="", extra_long_opts=(),
extra_option_handler=None):
"""Parse the options in argv and return any arguments that aren't
flags. docstring is the calling module's docstring, to be displayed
for errors and -h. extra_opts and extra_long_opts are for flags
defined by the caller, which are processed by passing them to
extra_option_handler."""
try:
opts, args = getopt.getopt(
argv, "hvp:s:x:" + extra_opts,
["help", "verbose", "path=", "signapk_path=", "extra_signapk_args=",
"java_path=", "public_key_suffix=", "private_key_suffix=",
"device_specific=", "extra="] +
list(extra_long_opts))
#那麼我們可以在這裡新增log來檢視opts、args中的值
print("begin")
for i in range(len(opts)):
print(opts[i])
print("************")
for i in range(len(args)):
print(args[i])
print("end")
except getopt.GetoptError, err:
Usage(docstring)
print "**", str(err), "**"
sys.exit(2)
path_specified = False
for o, a in opts:
if o in ("-h", "--help"):
Usage(docstring)
sys.exit()
elif o in ("-v", "--verbose"):
OPTIONS.verbose = True
elif o in ("-p", "--path"):
OPTIONS.search_path = a
elif o in ("--signapk_path",):
OPTIONS.signapk_path = a
elif o in ("--extra_signapk_args",):
OPTIONS.extra_signapk_args = shlex.split(a)
elif o in ("--java_path",):
OPTIONS.java_path = a
elif o in ("--public_key_suffix",):
OPTIONS.public_key_suffix = a
elif o in ("--private_key_suffix",):
OPTIONS.private_key_suffix = a
elif o in ("-s", "--device_specific"):
OPTIONS.device_specific = a
elif o in ("-x", "--extra"):
key, value = a.split("=", 1)
OPTIONS.extras[key] = value
else:
if extra_option_handler is None or not extra_option_handler(o, a):
assert False, "unknown option \"%s\"" % (o,)
#環境變數
os.environ["PATH"] = (os.path.join(OPTIONS.search_path, "bin") +
os.pathsep + os.environ["PATH"])
return args
下面是具體log列印的資訊:
begin
('-u', 'lk.bin')
('-n', '')
('-i', 'base.zip')
******
target.zip
update.zip
end
那麼引數解析完畢之後,執行流程繼續回到ota_from_target_files中的main函式。我們順著流程接著看。
下面是對完成引數解析的返回值的長度進行一個過濾,這裡固定是2,無論是整包或者差分包的製作。
if len(args) != 2:
common.Usage(__doc__)
sys.exit(1)
#這裡沒有額外的指令碼,因此OPTIONS.extra_script的值為None。 if OPTIONS.extra_script is not None:
OPTIONS.extra_script = open(OPTIONS.extra_script).read()
下面我們來看解壓的流程。