加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

使用代理下载Chromium源码&依赖工具包

发布时间:2020-12-14 01:37:04 所属栏目:百科 来源:网络整理
导读:说明 我们目前需要使用代理才能访问Chromium开源项目,通过给开发环境配置代理可以顺利完成其源码的下载。但是如果需要编译项目,就得使用Google提供的脚本build/install-build-deps-android.sh和gclient runhooks命令完成依赖工具的下载。 gclient runhooks

说明

我们目前需要使用代理才能访问Chromium开源项目,通过给开发环境配置代理可以顺利完成其源码的下载。但是如果需要编译项目,就得使用Google提供的脚本build/install-build-deps-android.sh和gclient runhooks命令完成依赖工具的下载。
gclient runhooks内部采用gsutil封装了对google的云存储gs://的访问,我使用的代理不能正常访问gs://,于是导致依赖工具包无法完成下载。网上搜索了很多资料,有介绍修改boto,也有说gsutil不能使用代理的,都不能解决问题。本文详细说明了一种绕过gsuitl的方法,成功完成依赖工具包的下载,并完成编译。

本文使用的是ubuntu 14.04版本,通过设置如下环境变量来使用代理访问外网:

export http_proxy=http://{username}:{pass}@{domain}:{port}/
export https_proxy=http://{username}:{pass}@{domain}:{port}/

另外:我之前使用过桌面版ubuntu,可以图形化的配置VPN,之后便可以完全按照Google提供的方法完成下载,暂时没搞清楚VPN跟我这里使用的代理有什么不同之处。

安装depot_tools

该过程很简单,按照官网顺序执行即可,我简单记录下命令,不再赘述。

mkdir chromium
cd chromium
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
export PATH="$PATH:/home/tntzlx/chromium/depot_tools"

下载源码

该过程很简单,按照官网顺序执行即可,不再赘述。

tntzlx@ubuntu:~/chromium$ mkdir source
tntzlx@ubuntu:~/chromium$ cd source/
tntzlx@ubuntu:~/chromium/source$ fetch --nohooks android
Running: gclient root
Running: gclient config --spec 'solutions = [
  {
    "url": "https://chromium.googlesource.com/chromium/src.git","managed": False,"name": "src","deps_file": ".DEPS.git","custom_deps": {},},]
target_os = ["android"]
'
Running: gclient sync --nohooks

经过漫长等待即可完成代码下载

依赖包下载

首先执行如下命令

cd src
build/install-build-deps-android.sh

执行到后面会遇到如下错误,但是不影响最后的编译,暂时没有处理

Err http://ppa.launchpad.net trusty/main Sources                               
  404  Not Found
Err http://ppa.launchpad.net trusty/main amd64 Packages                        
  404  Not Found
Err http://ppa.launchpad.net trusty/main i386 Packages                         
  404  Not Found
Fetched 3,446 kB in 13s (246 kB/s)                                             
W: Failed to fetch http://ppa.launchpad.net/djcj/mediainfo/ubuntu/dists/trusty/main/source/Sources  404  Not Found

W: Failed to fetch http://ppa.launchpad.net/djcj/mediainfo/ubuntu/dists/trusty/main/binary-amd64/Packages  404  Not Found

W: Failed to fetch http://ppa.launchpad.net/djcj/mediainfo/ubuntu/dists/trusty/main/binary-i386/Packages  404  Not Found

E: Some index files failed to download. They have been ignored,or old ones used instead.

然后本文的重点来了。执行如下命令:

gclient runhooks

执行一段时间,会成功更新一部分工具,后续遇到错误。

0> Failed to fetch file gs://chromium-android-tools/play-services/10.2.0/31843001b7ce94fbdf71f2a9db76b28548a795fa for /tmp/tmpgymXSg/LICENSE,skipping. [Err: Failure: [Errno 1] _ssl.c:510: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed.
]
Traceback (most recent call last):
  File "src/build/android/play_services/update.py",line 526,in <module>
    sys.exit(main(sys.argv[1:]))
  File "src/build/android/play_services/update.py",line 96,in main
    return args.func(args)
  File "src/build/android/play_services/update.py",line 191,in Download
    config.version_number)):
  File "src/build/android/play_services/update.py",line 381,in _CheckLicenseAgreement
    with open(expected_license_path) as license_file:
IOError: [Errno 2] No such file or directory: '/tmp/tmpgymXSg/LICENSE'
Error: Command '/usr/bin/python src/build/android/play_services/update.py download' returned non-zero exit status 1 in /home/tntzlx/chromium/source

可以看到build/android/play_services/update.py会触发将gs://chromium-android-tools/play-services/10.2.0/31843001b7ce94fbdf71f2a9db76b28548a795fa下载到本地/tmp/tmpgymXSg/LICENSE。
这里实际是想要下载一份LICENSE文件,需要用户选择接受还是拒绝。当选择接受后,会继续启动下载。但是这里下载失败。原因是我们没有gs://(Google云存储)的访问权限。

解决方案:将"gs://chromium-android-tools/play-services/10.2.0/31843001b7ce94fbdf71f2a9db76b28548a795fa"替换成"https://storage.googleapis.com/chromium-android-tools/play-services/10.2.0/31843001b7ce94fbdf71f2a9db76b28548a795fa",即通过HTTPS协议进行下载。

修改下载脚本

src/build/android/play_services/update.py

def Download(args):
    ......
    license_sha1 = os.path.join(SHA1_DIRECTORY,LICENSE_FILE_NAME + '.sha1')
    _DownloadFromBucket(bucket_path,license_sha1,new_license,args.verbose,args.dry_run)
    ......

update.py首先会对比src/build/android/play_services/google_play_services_library.zip.sha1跟src/third_party/android_tools/sdk/extras/google/m2repository/google_play_services_library.zip.sha1这两个文件是否相等。如果相等,则认为不需要更新play_services库。首次下载代码完成后是没有src/third_party/android_tools/sdk/extras/google/m2repository/google_play_services_library.zip.sha1这个文件的,故这里需要进行后面的下载(这部分逻辑代码未贴出)。
Download方法是通过调用_DownloadFromBucket(bucket_path,args.dry_run)实现下载的。bucket_path是下载url的前缀,license_sha1是下载文件的sha1码文件的本地路径,而目标文件的url就是bucket_path和其sha1码的拼接,new_license是下载的目标地址。

_DownloadFromBucket的实现如下,其调用download_from_google_storage.download_from_google_storage完成下载:

def _DownloadFromBucket(bucket_path,sha1_file,destination,verbose,is_dry_run):
  '''Downloads the file designated by the provided sha1 from a cloud bucket.'''

  download_from_google_storage.download_from_google_storage(
      input_filename=sha1_file,base_url=bucket_path,gsutil=_InitGsutil(is_dry_run),num_threads=1,directory=None,recursive=False,force=False,output=destination,ignore_errors=False,sha1_file=sha1_file,verbose=verbose,auto_platform=True,extract=False)

该方法在depot_tools/download_from_google_storage.py中实现:

def download_from_google_storage(
    input_filename,base_url,gsutil,num_threads,directory,recursive,force,output,ignore_errors,auto_platform,extract):
  # Start up all the worker threads.
    ......
    t = threading.Thread(
        target=_downloader_worker_thread,args=[thread_num,work_queue,stdout_queue,ret_codes,extract])
    t.daemon = True
    t.start()
    all_threads.append(t)
    ......

通过将下载任务放到一个线程中实现下载,线程的工作函数是:_downloader_worker_thread。
分析_downloader_worker_thread的实现:

def _downloader_worker_thread(thread_num,q,out_q,extract,delete=True):
  
    ......
    # Check if file exists.
    file_url = '%s/%s' % (base_url,input_sha1_sum)
    (code,_,err) = gsutil.check_call('ls',file_url)
    if code != 0:
      if code == 404:
        out_q.put('%d> File %s for %s does not exist,skipping.' % (
            thread_num,file_url,output_filename))
        ret_codes.put((1,'File %s for %s does not exist.' % (
            file_url,output_filename)))
      else:
        # Other error,probably auth related (bad ~/.boto,etc).
        out_q.put('%d> Failed to fetch file %s for %s,skipping. [Err: %s]' % (
            thread_num,output_filename,err))
        ret_codes.put((1,'Failed to fetch file %s for %s. [Err: %s]' % (
            file_url,err)))
      continue
    # Fetch the file.
    out_q.put('%d> Downloading %s...' % (thread_num,output_filename))
    try:
      if delete:
        os.remove(output_filename)  # Delete the file if it exists already.
    except OSError:
      if os.path.exists(output_filename):
        out_q.put('%d> Warning: deleting %s failed.' % (
            thread_num,output_filename))
    code,err = gsutil.check_call('cp',output_filename)
    if code != 0:
      out_q.put('%d> %s' % (thread_num,err))
      ret_codes.put((code,err))
      continue
    ......

_downloader_worker_thread使用封装类gsutil实现对google云存储的访问。上述代码中file_url代表下载的url,是gs://格式的。该方法首先采用gsutil.check_call('ls',file_url)检查目标文件是否存在,之后采用gsutil.check_call('cp',output_filename)将file_url下载到目标文件output_filename。
上述的方法均要访问gs://,我们的代理目前访问不了这个地址,
如下的报错信息,就是出自gsutil.check_call('ls',file_url)这一句的调用。

0> Failed to fetch file gs://chromium-android-tools/play-services/10.2.0/31843001b7ce94fbdf71f2a9db76b28548a795fa for /tmp/tmpgymXSg/LICENSE,in _CheckLicenseAgreement
    with open(expected_license_path) as license_file:
IOError: [Errno 2] No such file or directory: '/tmp/tmpgymXSg/LICENSE'
Error: Command '/usr/bin/python src/build/android/play_services/update.py download' returned non-zero exit status 1 in /home/tntzlx/chromium/source

我们知道这个函数的最终目的就是完成文件的下载,于是就采用HTTPS下载,代替gsutil.check_call。这里我们暂且认为要下载的文件一定存在于服务器上,于是直接去掉检查文件是否存在的调用gsutil.check_call('ls',file_url),单纯把gsutil.check_call('cp',output_filename)替换成HTTPS下载。
实现简单的HTTPS文件下载器,代码如下:

import urllib2

httpsPrefix = "https://storage.googleapis.com/"
gsPrefix = "gs://"

def download_gs_to_file(url,fileName):
    download_http_to_file(url.replace(gsPrefix,httpsPrefix),fileName)

def download_http_to_file(url,fileName):
    proxy = urllib2.ProxyHandler({'http': 'http://{username}:{password}@{domain}:{port}/','https': 'http://{username}:{password}@{domain}:{port}/'}
                                 )
    auth = urllib2.HTTPBasicAuthHandler()
    opener = urllib2.build_opener(proxy,auth,urllib2.HTTPHandler)
    urllib2.install_opener(opener)

    response = urllib2.urlopen(url)
    CHUNK = 16 * 1024
    with open(fileName,'wb') as f:
        while True:
            chunk = response.read(CHUNK)
            if not chunk:
                break
            f.write(chunk)

该下载器是通过urllib2模块实现的,注意我们需要使用代理,所以需要给urllib2设置代理服务:

proxy = urllib2.ProxyHandler({'http': 'http://{username}:{password}@{domain}:{port}/','https': 'http://{username}:{password}@{domain}:{port}/'}
                            )

另外,当进行大文件下载时urllib2.urlopen.read方法不能一次读完,所以设置一个CHUNK,每次读取16K的数据,写入到目的文件中。
保存为:download_helper.py
将上述_downloader_worker_thread的代码改为:

def _downloader_worker_thread(thread_num,delete=True):
  
    ......
    file_url = '%s/%s' % (base_url,input_sha1_sum)
    # Fetch the file.
    out_q.put('%d> Downloading %s...' % (thread_num,output_filename))
    download_helper.download_gs_to_file(file_url,output_filename)
    ......

即可完成下载。
注意_downloader_worker_thread的最后

elif sys.platform != 'win32':
      # On non-Windows platforms,key off of the custom header
      # "x-goog-meta-executable".
      code,out,_ = gsutil.check_call('stat',file_url)
      if code != 0:
        out_q.put('%d> %s' % (thread_num,err))
        ret_codes.put((code,err))
      elif re.search(r'executable:s*1',out):
        st = os.stat(output_filename)
        os.chmod(output_filename,st.st_mode | stat.S_IEXEC)

意思是在非win32平台上面,检查下载的文件如果是可执行文件,就加上可执行权限。这里依赖gsutil.check_call('stat',file_url)的第二个返回值来判断是否是可执行文件,我这里偷个懒,暂时给所有下载的文件全部加上可执行权限则修改如下:

elif sys.platform != 'win32':
      st = os.stat(output_filename)
      os.chmod(output_filename,st.st_mode | stat.S_IEXEC)

再次执行gclient runhooks即可完成下载

编译

编译生成ninja文件

gn gen --args='target_os="android"' out/Default

启动正式编译

ninja -C out/Default chrome_public_apk

编译报错信息如下:

Exception in thread "main" java.lang.UnsupportedClassVersionError: com/android/tools/lint/Main : Unsupported major.minor version 52.0
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:803)
        at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
        at java.net.URLClassLoader.defineClass(URLClassLoader.java:442)
        at java.net.URLClassLoader.access$100(URLClassLoader.java:64)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:354)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:348)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:347)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
        at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:482)

根据"Unsupported major.minor version 52.0"得知,这里需要安装jdk1.8。我的单板是jdk1.7.0版本。
解决方案:
oracle官网下载jdk-8u111-linux-x64.tar.gz,解压到单板,并配置环境变量

export JAVA_HOME=/home/java/jdk1.8.0_111
export PATH=${JAVA_HOME}/bin:$PATH

注意这里不要有下面的环境变量,否则会有报错信息。

export CLASSPATH=${JAVA_HOME}/lib
tntzlx@ubuntu:~$ java -version
java version "1.8.0_111"
Java(TM) SE Runtime Environment (build 1.8.0_111-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.111-b14,mixed mode)

说明修改成功。

此时需要删除out/Defualt目录然后

gn gen --args='target_os="android"' out/Default
ninja -C out/Default chrome_public_apk

即可完成编译。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读