关于repo
repo只是谷歌做的,方便下载安卓源码的工具,本质上是对下载清单进行批量处理,然后使用git克隆。
在windows上下载源码只需要自己处理即可。
具体做法
首先使用git克隆安卓源码清单
shell
git clone https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest
随后进入manifest
目录,查看所有tag
shell
git tag
tag名就是安卓源码的版本,选择你要的版本,记下来。例如android-13.0.0_r9
随后切换分支
shell
git checkout android-13.0.0_r9
记住文件夹中的default.xml
的路径
以上操作完成了清单的下载,接下来我们只需要执行按清单下载的操作即可。
我提供两个我编写的脚本,一个python环境,一个golang环境。
注意:该脚本不支持断点续传,如需使用,请保持网络环境良好或自行修改脚本。
Python环境
该Python下载脚本仅支持单线程下载,不过git仍可以跑满你的网速
python3
import xml.dom.minidom
import os
from subprocess import call
version = "android-13.0.0_r9"
# 1. 源码要保存的路径
rootdir = "D:/AOSP/"+version
# 2. git 的路径
git = r"C:\Program Files\Git\cmd\git.exe"
# 3. default.xml 的路径
dom = xml.dom.minidom.parse(r"D:\AOSP\manifest\default.xml")
root = dom.documentElement
# 4. 只支持单一镜像源
prefix = git + " clone https://aosp.tuna.tsinghua.edu.cn/"
suffix = ".git"
if __name__ == '__main__':
if not os.path.exists(rootdir):
os.mkdir(rootdir)
for node in root.getElementsByTagName("project"):
os.chdir(rootdir)
d = node.getAttribute("path")
last = d.rfind("/")
if last != -1:
d = rootdir + "/" + d[:last]
if not os.path.exists(d):
os.makedirs(d)
os.chdir(d)
cmd = prefix + node.getAttribute("name") + suffix
# 单线程下载
call(cmd)
Golang环境
该golang脚本支持从多个镜像站并发下载,可限制每个镜像站同时下载的线程个数,以及总线程个数。
注意: * 总线程个数应小于等于各个镜像站线程个数相加*
resource.go
go
package main
import (
"sync"
)
type Resource struct {
Url string
Lock chan struct{}
}
type ResourcePool struct {
resources []*Resource
cond *sync.Cond
}
func NewResourcePool(list []*Resource) *ResourcePool {
pool := &ResourcePool{
cond: sync.NewCond(&sync.Mutex{}),
}
pool.resources = list
return pool
}
func (pool ResourcePool) GetResource() *Resource {
pool.cond.L.Lock()
defer pool.cond.L.Unlock()
for {
for _, resource := range pool.resources {
select {
case resource.Lock <- struct{}{}:
return resource
default:
continue
}
}
pool.cond.Wait()
}
}
func (pool ResourcePool) ReleaseResource(resource *Resource) {
<-resource.Lock
pool.cond.Signal()
}
main.go
go
package main
import (
"encoding/xml"
"flag"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"sync"
)
type Project struct {
XMLName xml.Name `xml:"project"`
Name string `xml:"name,attr"`
Path string `xml:"path,attr"`
}
type Manifest struct {
XMLName xml.Name `xml:"manifest"`
Project []Project `xml:"project"`
}
var (
concurrentNum int
rootDir string
manifestPath string
wg sync.WaitGroup
)
var resourceA = &Resource{Url: "https://mirrors.bfsu.edu.cn/git/AOSP/%s.git", Lock: make(chan struct{}, 4)}
var resourceB = &Resource{Url: "https://aosp.tuna.tsinghua.edu.cn/%s.git", Lock: make(chan struct{}, 3)}
//var resourceC = &Resource{Url: "https://mirror.nju.edu.cn/%s.git", Lock: make(chan struct{}, 3)}
//var resourceC = &Resource{Url: "https://mirrors.shanghaitech.edu.cn/%s.git", Lock: make(chan struct{}, 3)}
func init() {
flag.IntVar(&concurrentNum, "concurrentNum", 7, "Number of concurrent goroutines")
flag.StringVar(&rootDir, "root", "D:/AOSP/android-13.0.0_r9", "Root dir of AOSP.")
flag.StringVar(&manifestPath, "manifest", "D:/AOSP/manifest/default.xml", "Manifest of AOSP.")
}
func errorHandle(str string, err error) {
if err != nil {
log.Fatalf("Failed to %s: %v", str, err)
os.Exit(-6)
}
}
func main() {
flag.Parse()
manifestXML, err := os.ReadFile(manifestPath)
errorHandle("Read manifest file", err)
var manifest Manifest
err = xml.Unmarshal(manifestXML, &manifest)
errorHandle("Parse manifest XML", err)
err = os.MkdirAll(rootDir, 0755)
errorHandle("Create root directory", err)
sem := make(chan struct{}, concurrentNum)
pool := NewResourcePool([]*Resource{
resourceA, resourceB,
})
for _, node := range manifest.Project {
wg.Add(1)
go func(project Project) {
defer wg.Done()
sem <- struct{}{}
defer func() { <-sem }()
cmdDir := rootDir
d := project.Path
last := strings.LastIndex(d, "/")
if last != -1 {
d = filepath.Join(rootDir, d[:last])
if err := os.MkdirAll(d, 0755); err != nil {
errorHandle("Create d directory", err)
}
cmdDir = d
}
resource := pool.GetResource()
if resource == nil {
fmt.Println("No resource available")
os.Exit(2)
return
}
cmd := exec.Command("git", "clone", fmt.Sprintf(resource.Url, project.Name))
cmdDir = strings.ReplaceAll(cmdDir+"\\", "/", "\\")
cmd.Dir = cmdDir
if err := cmd.Run(); err != nil {
fmt.Printf("Failed to clone node: %v %s \n", err, cmd)
return
}
pool.ReleaseResource(resource)
fmt.Printf("Cloned node: %s \n", project.Name)
}(node)
}
wg.Wait()
}
截至到23年10月3日,完全可用的镜像站仅剩清华,其他AOSP的镜像或多或少均有问题
python的脚本足矣,经测试,均速16m/s,一晚上可以下载好android-13.0.0_r9的所有源码
go的脚本推荐大家不要乱用,过多的下载进程会对镜像站造成巨大的负载