Electron源码学习: Electron程序体积裁剪(减小体积)

Electron源码学习: Electron程序体积裁剪(减小体积)

前言

​ 此篇专题名称可能叫Electron的编译(虽然并没有具体的编译步骤,但实际上本文里面的各种技术都需要源码编译Electron)更为合适。因为此篇文章基本不涉及到源码的解析和修改,但是因为也是基于Electron的学习和理解之上的,勉勉强强也叫这个名字吧。我测试编译的环境为Electron V4.2.9,为2019年年底的版本,本文中部分的方法在最新的版本Electron版本上不能直接适用; 那这种情况怎么办呢?自己去改改就好了。

​ 如果对体积有强烈需求的人,可以去看Miniblink:https://miniblink.net/,该方案几乎是重写了整个浏览器,除了V8之类的东西,所以程序体积可以非常小,压缩了之后小于10M完全妥妥的。 本文所列举出的方法改变不了Chromium浏览器体积庞大的本质问题;V8的二进制文件达到了30M,你让我去改成10M,20M?这个不现实;体积大有体积大的优势和理由。我这里的方法最多能减省各种看起来不需要的东西;程序的本质我是没那个能力去改变的。比如物理阉割代码什么的,欢迎大佬指教。

目的

​ 针对Electron体积削减的话题讨论也是由来已久,因为Electron的根本就是Chromium, 也因此Chromium的裁剪办法基本上也都适用于Electron,Electron是一个独立的exe, 针对Electron也有一些特殊的办法减小一些体积。本文的目的就是介绍一些我现在用到的一些用于裁剪程序的方法,看下效果:

在这里插入图片描述

途径1:GN编译开关

​ 注意:该方法不适用于老版本的Electron,老版本通过static_library的方法在链接的时候太慢,我个人认为没必要在这上面花功夫。

整理的开关如下:(以下开关主要作用是关闭一些不太可能用到的功能,对体积影响不是很大, 也相当于是源码裁剪,但是力度不大,做不到把具体某个庞大的部件减去)

is_debug = false
target_cpu = "x86" #编译目标程序为32位
enable_nacl = false
is_component_build = false
is_component_ffmpeg=false
symbol_level = 2 #设置为0的时候,将不会产生PDB,少量减少体积和极大加快编译速度
blink_symbol_level=2 # 同上
enable_basic_printing = false 
enable_resource_whitelist_generation=false
debug_devtools = false
#enable_plugins  = false
enable_pdf = false
enable_webrtc = false
enable_plugin_installation =false
enable_spellcheck = false
use_browser_spellchecker = false
enable_print_preview = false
enable_web_speech = false
#enable_electron_extensions = false
enable_task_manager = false
enable_themes = false
win_pdf_metafile_for_printing  = false
enable_service_discovery = false
enable_wifi_bootstrapping  = false
enable_webvr  = false
enable_notifications  = false
disable_ftp_support = true
#enable_electron_extensions = false
enable_fake_location_provider = false
enable_message_center = false
enable_nacl_nonsfi = false
enable_native_notifications = false
enable_one_click_signin = false
#enable_openvr = false
enable_session_service = false
enable_tts = false
enable_vr = false
enable_windows_mr = false
#pdf_is_standalone = true
pdf_use_skia_paths = false
pdf_use_skia = false
pdf_is_complete_lib = false
pdf_enable_xfa_tiff = false
pdf_enable_xfa_png = false
pdf_enable_xfa_gif = false
pdf_enable_xfa_bmp = false
pdf_enable_xfa = false
pdf_enable_v8 = false
pdf_enable_click_logging = false
pdf_bundle_freetype = false
rtc_build_examples = false
rtc_build_tools = false
#toolkit_views = false
treat_warnings_as_errors = false
start_daemons_for_testing = false
is_unsafe_developer_build = true
angle_enable_custom_vulkan_cmd_buffers=false
angle_enable_d3d11=true
angle_enable_d3d9=true
angle_enable_vulkan=false
angle_enable_gl=false
angle_enable_gl_null=false
angle_enable_essl=false
enable_swiftshader=false
enable_reporting = flase
enable_net_mojo=false

那么这些开关放在哪里呢? 通过执行命令gn args out\Release会打开你对应的编译目录的配置,然后将这些东西放进去。保存,然后关闭。然后这些配置会被存储到out\Release目录下的args.gn中。如图示:Electron源码学习: Electron程序体积裁剪(减小体积)_第1张图片

途径2: 修改优化编译选项(可以直接减10+M)

在4.2.9版本中,绝大部分的项目的编译优化选项是/O1(最小体积优先);部分项目的编译优化选项是/O2(速度优先),如图:

Electron源码学习: Electron程序体积裁剪(减小体积)_第2张图片

我们这里来个极限优化。

/O2/O1的优化选项都改成/Ox /Os /GS- /GF /GR- /Oy , 为了快速方便的改,这里推荐一个工具grepWin(下载页面:https://sourceforge.net/projects/grepwin/),可以直接对目录进行搜索替换,一步直接到位。

  • 第一步, 需要先将编译选项中的/Oy-/GS 分别都去掉,(因为和第二步中的开关冲突了)

    Electron源码学习: Electron程序体积裁剪(减小体积)_第3张图片

  • 第二步,把/O2/O1分别都替换成/Ox /Os /GS- /GF /GR- /Oy

    Electron源码学习: Electron程序体积裁剪(减小体积)_第4张图片

途径3:使用VC-LTL来移除基础运行库(省掉2M左右的体积)

这里可能会让人有点儿疑惑, 这个VC-LTL是个什么东西? VC-LTL很NB啊,VC-LTL是一套由国内的初雨团队打造的一套用于链接Windows自带运行库的套件;

简单来说,我们在编写程序的时候,经常会用到memcpy,strcpy类似的函数;通常来讲,在DEBUG的模式或者非/MT下,memcpy,strcpy这一类的函数会直接链接到类似于msvcp100.dll,msvcr100.dll(链接到具体版本的DLL,由编译器的版本决定)这一类的DLL库中,所以当程序调用memcpy的时候,就会实际调用对应模块中的函数。

当程序选择使用/MT编译的时候,这一类函数的就会直接以嵌入到程序中,这就会直接增加程序的体积。然而其实Windows操作系统在发行的时候,这一类的函数的包含库它自己已经携带了。意思就是,操作系统自己有一套自己的运行库。

如果能仔细看到这里,那么想必你已经明白了。VC-LTL干的事情就是,让目标程序也能用上这个Windows自带的库。Windows自带的库的名称为msvcrt.dll,每个版本的Windows的这个库都叫这个名字。

VC-LTL的作用就是将以/MT编译的程序所使用的基础库以/MD的形式链接到msvcrt.dll中。

VC-LTL在Electron中的使用方法:https://zhuanlan.zhihu.com/p/64070741, 该篇中对于添加引用目录的地方不是很详细,这里再做说明。

安装VC-LTL, 并确定当前的Electron所使用的编译器版本,并将VC-LTL引用到Electron

  1. toolchain.ninja中确定SDK和库的版本,例如我这里4.2.9使用的版本是SDK,10.0.17763.0和14.16.27023。为什么要确定?引用VC-LTL需要手动将头文件目录,LIB库目录加入到当前的编译环境中。

  2. 添加VC-LTL的引用;假定你已经正确安装了VC-LTL,并且已经确定了版本号。那么接下来需要将VC-LTL的目录分别添加到toolchain.ninjaelectron_app.ninja中。假定VC-LTL的目录在F:\Open_Source\VC-LTL

    • 处理toolchain.ninja

      1. 找到rule cc下的/showIncludes,将如下目录添加到/showIncludes后面,
         "-imsvcF:\Open_Source\VC-LTL\config\Vista" "-imsvcF:\Open_Source\VC-LTL\VC\14.16.27023\include" "-imsvcF:\Open_Source\VC-LTL\VC\14.16.27023\atlmfc\include" "-imsvcF:\Open_Source\VC-LTL\ucrt\10.0.17763.0" 
         
      
      1. 找到rule cxx下的/showIncludes,执行上一步的操作。
    • 处理electron_app.ninja,假定你的SDK安装在C:/Program Files (x86)/Windows Kits/10/lib/10.0.17763.0/um/x86

  • 搜索electron_app.ninja,将/LIBPATH:"C$:/Program$ Files$ (x86)/Windows$ Kits/10/lib/10.0.17763.0/um/x86" 替换为:

       /LIBPATH:"F$:/Open_Source/VC-LTL/VC/VC-LTL-4.0.1.10-Binary-VS2017/lib/x86/Vista" /LIBPATH:"F$:/Open_Source/VC-LTL/VC/VC-LTL-4.0.1.10-Binary-VS2017/lib/x86/Vista/Advanced" /LIBPATH:"F$:/Open_Source/VC-LTL/VC/VC-LTL-4.0.1.10-Binary-VS2017/VC/14.16.27023/lib/x86" /LIBPATH:"F$:/Open_Source/VC-LTL/VC/VC-LTL-4.0.1.10-Binary-VS2017/VC/14.16.27023/lib/x86/Vista" /LIBPATH:"F$:/Open_Source/VC-LTL/VC/VC-LTL-4.0.1.10-Binary-VS2017/ucrt/10.0.17763.0/lib/x86" /LIBPATH:"C$:/Program$ Files$ (x86)/Windows$ Kits/10/lib/10.0.17763.0/um/x86" 
    

    如果你的程序是64位的,那么就引用到64位对应的目录即可。

注意:因为不同的操作系统的附带的msvcrt.dll各不相同,那么很容易存在新编译器支持的特性在老版本的操作系统上不受支持。例如:新版本的vcruntime肯定支持类似于%zu这样的格式控制串,但是老版本的操作系统,例如Win7,甚至老一点儿的Win10所附带的msvcrt.dll不支持该格式控制串。但如果采用常规的方式以/MT的方式编译的话,那么自然没有问题;因为%zu的支持是直接嵌入到程序里面的,因此哪怕是老版本的操作系统也不受链接文件影响。但是VC-LTL的模式天生决定了这个问题不好处理,因此读者在使用VC-LTL的时候应注意相关的问题。

途径4:移除exe的重定位表 (又可以减少2M左右)

熟悉PE结构的读者肯定知道重定位表是个什么东西,其实这个玩意儿就是为了让DLL能够每次都加载到不同的基址上的技术。PE结构中,DLL为了解决DLL默认基址被占用的问题,弄出了一个重定位表;一旦DLL不能被加载到预期的位置上,那么通过重定位表就能重新将变量,函数等调用重新指向对应的位置。

当然现在有一个链接选项就是动态基址的/DYNAMICBASE,所有有时候哪怕DLL的默认基址没有被占用,也会重新定位到新的基址。

上述的东西只是对DLL有限制,Electron是一个单独的exe程序,exe程序是不需要这个重定位表的,因为最先映射的就是exe,所以不存在抢占问题。指哪儿哪儿就是基址。再而Electron的代码体积巨大,也因此这个重定位表的体积也是相当的吓人。看下图:

Electron源码学习: Electron程序体积裁剪(减小体积)_第5张图片
这个4.0.0版本的Electron的重定位表体积,就已经达到了惊人的2M+。所以还是移除比较科学。

准备修改编译选项,打开 electron_app.ninja文件:

  • 搜索/FIXED$:NO并替换成/FIXED,(V4.2.9里面有两处)
  • 搜索/DYNAMICBASE并替换成/DYNAMICBASE$:NO

至此编译出来的程序就没有重定位表了,且不会对原本程序有任何影响。

途径5:移除devtools_resources.pak(又可以减少5M左右)

devtools_resources.pak的文件在新版本的Electron目录中已经不能直接被看到了,这个文件被嵌入到了resources.pak中,devtools_resources.pak发行的版本并不需要这个文件,那么完全可以干掉。裁掉之前文件约为8.31M,移除之后文件体积约为2.42M。如图:

Electron源码学习: Electron程序体积裁剪(减小体积)_第6张图片

准备修改编译选项,打开 toolchain.ninja文件:

  • 搜索repack resources.pak, 删除该条命令中的gen/content/browser/devtools/devtools_resources.pak,然后保存即可。
  • 执行resources的编译任务;命令行ninja -C out/Release/ resources.pak

其他途径:

  • 删除代码里面的字符串:函数PostTask用到的FROM_HERE宏里面传递了函数名称和代码位置,移除可以使体积略微减小;他的反调试意义大于裁剪意义;(有关于安全保护相关的帖子,请看我的另一篇同系列帖子《Electron源码学习:Electron加密与安全》)
  • 使用7Z极限压缩;当使用7Z极限压缩时,文件的体积可以压缩接近至30%。意思是100M的文件,压缩下来大概为30M左右。
  • 删除目录中非必须的文件,如下:
    1. osmesa.dll,;
    2. locales目录中的不用支持的语言;
    3. d3dcompiler_47.dll;
    4. libEGL.dlllibGLESv2.dll (视使用场景而定)
  • 移除node_modules打包目录中的文件,主要包含如下类型文件:
    1. README.md
    2. AUTHORS
    3. CHANGES.md
    4. LICENSE
    5. SAMPLES
    6. 工程临时文件
    7. 等等非依赖文件

你可能感兴趣的:(Electron源码学习,Electron,c++)