嵌入式python的裁剪

CPython的裁剪

为什么要裁剪

1.在嵌入式python移植时,空间资源有限,而python在编译时,会自动编译一些扩展库
2.这些扩展库中,有一些是我们用不到的,所以就需要裁剪掉一部分库
3.未裁剪之前的python库文件大概占用127M空间,其中包含so库和python文件
image.png
动态链接库 ,也就是 lib-dynload这个文件夹大概占用8.9m,其余为python相关的库文件
image.png

目标

1.在满足功能的情况下,让Python尽可能的小。
2.可选择功能进行裁剪。
3.用户体验好,不能让用户修改源码或者Makefile。

计划

1.最好找到python编译模块的数据元素,这样就可以对数据元素中的模块进行修改,达到裁剪的效果。
2.如果找不到的话,能否对生成的文件进行筛选,也能达到裁剪的效果。
两种办法一个是从源头上解决问题,一个是从尽头上解决问题。
当然最好是从源头上解决问题更好一些,因为这样可以防止一些依赖文件被裁剪的现象发生。

准备工作

对python安装包中安装相关的文件进行梳理
其中setup.py文件是用来生成外部扩展库动态链接库的,所有的so库都会在这里配置

setup.py文件分析

配置不编译项

'disabled_module_list’这个变量为不编译的模块列表,如果不想编译某个模块,将其名称的字符串放入列表中即可。
例:disabled_module_list = [“_json”****] # 不编译json模块
所有的模块如下表

_asyncio_crypt_gdbm_posixsubprocess
_bisect_csv_heapq_random
_blake2_ctypes_json_sha1
_bz2_ctypes_test_lsprof_sha256
_codecs_cn_curses_lzma_sha3
_codecs_hk_curses_panel_md5_sha512
_codecs_iso2022_datetime_multibytecodec_socket
_codecs_jp_dbm_multiprocessing_sqlite3
_codecs_kr_decimal_opcode_ssl
_codecs_tw_elementtree_pickle
_testbufferfcntlresource
_testcapigrpselect
_testimportmultiplemathspwd
_testmultiphasemmapsyslog
_tkinternistermios
arrayossaudiodevtime
atexitparserunicodedata
audiooppwdxxlimited
binasciipyexpatzlib
cmathreadline_struct

裁剪工作

前面已经找到配置不编译项的接口了,根据计划,先试一下从源头上解决问题。

通过配置不编译项裁剪

1.建立一个文件,在文件中创建一个元组not_make_module,将不编译的模块放入其中
image.png
只编译sockert和math
2.在setu.py文件中import这个文件的变量
image.png
3.将not_make_module的内容赋给disabled_module_list
image.png
查看编译出的文件
image.png
umm…再看看动态库文件
image.png
库文件确实是根据我们的配置not_make_module来编译的,也就是只编译sockert和math,但是python文件却是一个都没少
这和我预想的结果有些偏差,我预想的结果是这样的
image.png
那么到底是什么导致的呢?


Makefile文件分析

image.png

解释起来就是这样的
1.先建立一堆文件夹
2.把python路径下Lib/*.py 和文件夹,全部考到install/lib/python3.6
所以它根本就没有关心我们的disabled_module_list里添加了什么,而是全部拷贝!

文件的裁剪

从源头上解决问题不行,只能从尽头上解决问题了。
纵览库文件夹中,占用空间较大的都是文件夹
image.png
所以对文件夹进行裁剪将会是比较可行的办法,其中
1.test测试文件夹占用56M,可以不要
2.__pycache__缓存文件夹占用9.5M,可以不要
3.config-3.6m-arm-linux-gnueabihf文件夹占用14M,编译过程生成的文件夹,可以不要
4.encodings文件夹,是python运行的必备文件夹

使用menuconfig可视化接口生成环境变量


目录结构设计
Kconfig代码

menu "Libs Setting"
	menu "Python Module"
		menu "图形界面相关模块(默认不编译)"					
			config	PYTHON_MODULE_CURSES
				bool "120K    curses		图形函数库"
				default n

			config	PYTHON_MODULE_TURTLEDEMO
				bool "448K    turtledemo		乌龟画图"
				default n

			config	PYTHON_MODULE_TKINTER
				bool "2.5M    tkinter		界面接口"
				default n			

			config	PYTHON_MODULE_IDLELIB
				bool "4.5M    idlelib		python编辑器,不用的话可以不要,依赖tkinter"
				default n
		endmenu 

		menu "网络相关模块"
			config	PYTHON_MODULE_HTTP
				bool "620K    http		http服务模块"
				default y
				
			config	PYTHON_MODULE_XMLRPC
				bool "300K    xmlrpc		使用http协议做为传输协议的rpc机制"
				default y
								
			config	PYTHON_MODULE_XML
				bool "1.2M    xml			xml解析模块"
				default y

			config	PYTHON_MODULE_HTML
				bool "316K    html		html模块"
				default y
				
			config	PYTHON_MODULE_JSON
				bool "188K    json		json解析模块"
				default y

			config	PYTHON_MODULE_URLLIB
				bool "516K    urllib		URL处理模块"
				default y

			config	PYTHON_MODULE_EMAIL
				bool "1.4M    email		邮件模块"
				default n
				
			config	PYTHON_MODULE_WSGIREF
				bool "244K    wsgiref		web服务器网关接口"
				default y			
		endmenu 

		config	PYTHON_MODULE_ENSUREPIP		
			bool "2.0M    ensurepip		启动 pip 安装程序"
			default n
			
		config	PYTHON_MODULE_VENV
			bool "112K    venv		虚拟环境"
			default n
			
		config	PYTHON_MODULE_LIB2TO3
			bool "3.0M    lib2to3		py2转py3自动化工具"
			default n


		config	PYTHON_MODULE_UNITTEST
			bool "2.9M    unittest		单元测试框架"
			default n
			
		config	PYTHON_MODULE_SITE_PACKAGES
			bool "8.0K    site-packages	三方包管理模块"
			default y
			
		config	PYTHON_MODULE_DBM
			bool "112K    dbm			持久字典"
			default y

		config	PYTHON_MODULE_IMPORTLIB
			bool "428K    importlib		动态导入模块"
			default y
			
		config	PYTHON_MODULE_SQLITE3
			bool "636K    sqlite3		sqlite3数据库接口模块"
			default y
			
		config	PYTHON_MODULE_DISTUTILS
			bool "3.5M    distutils		发布工具,默认不选"
			default n
			

			
		config	PYTHON_MODULE_CONCURRENT
			bool "224K    concurrent		并发模块"
			default y
			

			
		config	PYTHON_MODULE_COLLECTIONS
			bool "204K    collections		容器数据类型"
			default y
			

		config	PYTHON_MODULE_MULTIPROCESSING
			bool "940K    multiprocessing	基于进程的并行"
			default y
			
		config	PYTHON_MODULE_ASYNCIO
			bool "1.5M    asyncio		异步io"
			default y
						
		config	PYTHON_MODULE_LOGGING
			bool "508K    logging		日志分级打印"
			default y
			
		config	PYTHON_MODULE_PYDOC_DATA
			bool "1.8M    pydoc_data		Python文档生成工具"
			default n
			
		config	PYTHON_MODULE_CTYPES
			bool "1.6M    ctypes		Python 的外部函数库"
			default y
			

	endmenu 

menuconfig效果:
image.pngimage.png
image.png


查看.config文件
image.png

编写文件拷贝脚本

文件拷贝脚本的思想是:
1.尽量不改变原有的文件,包括Makefile等
2.可根据CONFIG变量进行拷贝


实现过程

#!/bin/bash
PYTHON_INSTALL_LIBS_DIR=`pwd`/install/lib/python3.6
#python临时文件夹
PYTHON_LIBS_TEMP_DIR=${PYTHON_INSTALL_LIBS_DIR}/python


#拷贝bin文件
cp `pwd`/install/bin/* ${LIBBINS_DIR} -rfd

#建立python文件夹
mkdir ${PYTHON_LIBS_TEMP_DIR}

#拷贝python库文件
cp ${PYTHON_INSTALL_LIBS_DIR}/*.py ${PYTHON_LIBS_TEMP_DIR}

#拷贝SO库文件
cp ${PYTHON_INSTALL_LIBS_DIR}/lib-dynload ${PYTHON_LIBS_TEMP_DIR} -R
echo cp ${PYTHON_INSTALL_LIBS_DIR}/lib-dynload ${PYTHON_LIBS_TEMP_DIR} -R

#瘦身SO库文件
${STRIP} ${PYTHON_LIBS_TEMP_DIR}/lib-dynload/*
echo ${STRIP} ${PYTHON_LIBS_TEMP_DIR}/lib-dynload/*

#拷贝文件夹
if [ ${CONFIG_PYTHON_MODULE_ENSUREPIP}x = yx ] ; then
cp ${PYTHON_INSTALL_LIBS_DIR}/ensurepip  ${PYTHON_LIBS_TEMP_DIR} -R	
echo cp ${PYTHON_INSTALL_LIBS_DIR}/ensurepip  ${PYTHON_LIBS_TEMP_DIR} -R
fi

if [ ${CONFIG_PYTHON_MODULE_VENV}x = yx ] ; then
cp ${PYTHON_INSTALL_LIBS_DIR}/venv ${PYTHON_LIBS_TEMP_DIR} -R	
echo cp ${PYTHON_INSTALL_LIBS_DIR}/venv ${PYTHON_LIBS_TEMP_DIR} -R
fi

if [ ${CONFIG_PYTHON_MODULE_LIB2TO3}x = yx ] ; then
cp ${PYTHON_INSTALL_LIBS_DIR}/lib2to3 ${PYTHON_LIBS_TEMP_DIR} -R	
echo cp ${PYTHON_INSTALL_LIBS_DIR}/lib2to3 ${PYTHON_LIBS_TEMP_DIR} -R
fi

if [ ${CONFIG_PYTHON_MODULE_CURSES}x = yx ] ; then
cp ${PYTHON_INSTALL_LIBS_DIR}/curses ${PYTHON_LIBS_TEMP_DIR} -R	
echo cp ${PYTHON_INSTALL_LIBS_DIR}/curses ${PYTHON_LIBS_TEMP_DIR} -R
fi

if [ ${CONFIG_PYTHON_MODULE_HTTP}x = yx ] ; then
cp ${PYTHON_INSTALL_LIBS_DIR}/http ${PYTHON_LIBS_TEMP_DIR} -R	
echo cp ${PYTHON_INSTALL_LIBS_DIR}/http ${PYTHON_LIBS_TEMP_DIR} -R
fi

if [ ${CONFIG_PYTHON_MODULE_XMLRPC}x = yx ] ; then
cp ${PYTHON_INSTALL_LIBS_DIR}/xmlrpc ${PYTHON_LIBS_TEMP_DIR} -R	
echo cp ${PYTHON_INSTALL_LIBS_DIR}/xmlrpc ${PYTHON_LIBS_TEMP_DIR} -R
fi

if [ ${CONFIG_PYTHON_MODULE_UNITTEST}x = yx ] ; then
cp ${PYTHON_INSTALL_LIBS_DIR}/unittest ${PYTHON_LIBS_TEMP_DIR} -R	
echo cp ${PYTHON_INSTALL_LIBS_DIR}/unittest ${PYTHON_LIBS_TEMP_DIR} -R
fi

if [ ${CONFIG_PYTHON_MODULE_SITE_PACKAGES}x = yx ] ; then
cp ${PYTHON_INSTALL_LIBS_DIR}/site-packages ${PYTHON_LIBS_TEMP_DIR} -R	
echo cp ${PYTHON_INSTALL_LIBS_DIR}/site-packages ${PYTHON_LIBS_TEMP_DIR} -R
fi

if [ ${CONFIG_PYTHON_MODULE_DBM}x = yx ] ; then
cp ${PYTHON_INSTALL_LIBS_DIR}/dbm ${PYTHON_LIBS_TEMP_DIR} -R	
echo cp ${PYTHON_INSTALL_LIBS_DIR}/dbm ${PYTHON_LIBS_TEMP_DIR} -R
fi

if [ ${CONFIG_PYTHON_MODULE_TURTLEDEMO}x = yx ] ; then
cp ${PYTHON_INSTALL_LIBS_DIR}/turtledemo ${PYTHON_LIBS_TEMP_DIR} -R	
echo cp ${PYTHON_INSTALL_LIBS_DIR}/turtledemo ${PYTHON_LIBS_TEMP_DIR} -R
fi

if [ ${CONFIG_PYTHON_MODULE_IMPORTLIB}x = yx ] ; then
cp ${PYTHON_INSTALL_LIBS_DIR}/importlib ${PYTHON_LIBS_TEMP_DIR} -R	
echo cp ${PYTHON_INSTALL_LIBS_DIR}/importlib ${PYTHON_LIBS_TEMP_DIR} -R
fi

#if [ ${CONFIG_PYTHON_MODULE_ENCODINGS}x = yx ] ; then
#核心库,缺失python无法运行, 条件判断就大可不必了
cp ${PYTHON_INSTALL_LIBS_DIR}/encodings ${PYTHON_LIBS_TEMP_DIR} -R	
echo cp ${PYTHON_INSTALL_LIBS_DIR}/encodings ${PYTHON_LIBS_TEMP_DIR} -R
#fi

if [ ${CONFIG_PYTHON_MODULE_SQLITE3}x = yx ] ; then
cp ${PYTHON_INSTALL_LIBS_DIR}/sqlite3 ${PYTHON_LIBS_TEMP_DIR} -R	
echo cp ${PYTHON_INSTALL_LIBS_DIR}/sqlite3 ${PYTHON_LIBS_TEMP_DIR} -R
fi

if [ ${CONFIG_PYTHON_MODULE_DISTUTILS}x = yx ] ; then
cp ${PYTHON_INSTALL_LIBS_DIR}/distutils ${PYTHON_LIBS_TEMP_DIR} -R	
echo cp ${PYTHON_INSTALL_LIBS_DIR}/distutils ${PYTHON_LIBS_TEMP_DIR} -R
fi

if [ ${CONFIG_PYTHON_MODULE_XML}x = yx ] ; then
cp ${PYTHON_INSTALL_LIBS_DIR}/xml ${PYTHON_LIBS_TEMP_DIR} -R	
echo cp ${PYTHON_INSTALL_LIBS_DIR}/xml ${PYTHON_LIBS_TEMP_DIR} -R
fi

if [ ${CONFIG_PYTHON_MODULE_CONCURRENT}x = yx ] ; then
cp ${PYTHON_INSTALL_LIBS_DIR}/concurrent ${PYTHON_LIBS_TEMP_DIR} -R	
echo cp ${PYTHON_INSTALL_LIBS_DIR}/concurrent ${PYTHON_LIBS_TEMP_DIR} -R
fi

if [ ${CONFIG_PYTHON_MODULE_HTML}x = yx ] ; then
cp ${PYTHON_INSTALL_LIBS_DIR}/html ${PYTHON_LIBS_TEMP_DIR} -R	
echo cp ${PYTHON_INSTALL_LIBS_DIR}/html ${PYTHON_LIBS_TEMP_DIR} -R
fi

if [ ${CONFIG_PYTHON_MODULE_JSON}x = yx ] ; then
cp ${PYTHON_INSTALL_LIBS_DIR}/json ${PYTHON_LIBS_TEMP_DIR} -R	
echo cp ${PYTHON_INSTALL_LIBS_DIR}/json ${PYTHON_LIBS_TEMP_DIR} -R
fi

if [ ${CONFIG_PYTHON_MODULE_IDLELIB}x = yx ] ; then
cp ${PYTHON_INSTALL_LIBS_DIR}/idlelib ${PYTHON_LIBS_TEMP_DIR} -R	
echo cp ${PYTHON_INSTALL_LIBS_DIR}/idlelib ${PYTHON_LIBS_TEMP_DIR} -R
fi

if [ ${CONFIG_PYTHON_MODULE_COLLECTIONS}x = yx ] ; then
cp ${PYTHON_INSTALL_LIBS_DIR}/collections ${PYTHON_LIBS_TEMP_DIR} -R	
echo cp ${PYTHON_INSTALL_LIBS_DIR}/collections ${PYTHON_LIBS_TEMP_DIR} -R
fi

if [ ${CONFIG_PYTHON_MODULE_URLLIB}x = yx ] ; then
cp ${PYTHON_INSTALL_LIBS_DIR}/urllib ${PYTHON_LIBS_TEMP_DIR} -R	
echo cp ${PYTHON_INSTALL_LIBS_DIR}/urllib ${PYTHON_LIBS_TEMP_DIR} -R
fi

if [ ${CONFIG_PYTHON_MODULE_MULTIPROCESSING}x = yx ] ; then
cp ${PYTHON_INSTALL_LIBS_DIR}/multiprocessing ${PYTHON_LIBS_TEMP_DIR} -R	
echo cp ${PYTHON_INSTALL_LIBS_DIR}/multiprocessing ${PYTHON_LIBS_TEMP_DIR} -R
fi

if [ ${CONFIG_PYTHON_MODULE_ASYNCIO}x = yx ] ; then
cp ${PYTHON_INSTALL_LIBS_DIR}/asyncio ${PYTHON_LIBS_TEMP_DIR} -R	
echo cp ${PYTHON_INSTALL_LIBS_DIR}/asyncio ${PYTHON_LIBS_TEMP_DIR} -R
fi

if [ ${CONFIG_PYTHON_MODULE_TKINTER}x = yx ] ; then
cp ${PYTHON_INSTALL_LIBS_DIR}/tkinter ${PYTHON_LIBS_TEMP_DIR} -R	
echo cp ${PYTHON_INSTALL_LIBS_DIR}/tkinter ${PYTHON_LIBS_TEMP_DIR} -R
fi

if [ ${CONFIG_PYTHON_MODULE_LOGGING}x = yx ] ; then
cp ${PYTHON_INSTALL_LIBS_DIR}/logging ${PYTHON_LIBS_TEMP_DIR} -R	
echo cp ${PYTHON_INSTALL_LIBS_DIR}/logging ${PYTHON_LIBS_TEMP_DIR} -R
fi

if [ ${CONFIG_PYTHON_MODULE_PYDOC_DATA}x = yx ] ; then
cp ${PYTHON_INSTALL_LIBS_DIR}/pydoc_data ${PYTHON_LIBS_TEMP_DIR} -R	
echo cp ${PYTHON_INSTALL_LIBS_DIR}/pydoc_data ${PYTHON_LIBS_TEMP_DIR} -R
fi

if [ ${CONFIG_PYTHON_MODULE_CTYPES}x = yx ] ; then
cp ${PYTHON_INSTALL_LIBS_DIR}/ctypes ${PYTHON_LIBS_TEMP_DIR} -R	
echo cp ${PYTHON_INSTALL_LIBS_DIR}/ctypes ${PYTHON_LIBS_TEMP_DIR} -R
fi

if [ ${CONFIG_PYTHON_MODULE_EMAIL}x = yx ] ; then
cp ${PYTHON_INSTALL_LIBS_DIR}/email ${PYTHON_LIBS_TEMP_DIR} -R	
echo cp ${PYTHON_INSTALL_LIBS_DIR}/email ${PYTHON_LIBS_TEMP_DIR} -R
fi

if [ ${CONFIG_PYTHON_MODULE_WSGIREF}x = yx ] ; then
cp ${PYTHON_INSTALL_LIBS_DIR}/wsgiref ${PYTHON_LIBS_TEMP_DIR} -R	
echo cp ${PYTHON_INSTALL_LIBS_DIR}/wsgiref ${PYTHON_LIBS_TEMP_DIR} -R
fi

#删除python缓存文件夹
#rm ${PYTHON_LIBS_TEMP_DIR}/__pycache__ -R
#拷贝lib文件
cp ${PYTHON_LIBS_TEMP_DIR} ${LIBLIBS_DIR}/ -rfd
#删除lib临时文件夹
rm ${PYTHON_LIBS_TEMP_DIR} -R

实现效果:
1.生成一个拷贝后的文件夹
2.将so库文件strip
3.将bin文件拷贝到libs/install/bin中(之后会自动拷贝到开发板的文件系统中)
4.将lib文件拷贝到libs/install/lib中(之后会自动拷贝到开发板的文件系统中)

将文件拷贝脚本加入makefile

image.png
这个位置是make执行的的最后位置,放在这里等于make完毕后执行此脚本

环境变量的设置

python在开发板中默认的库位置是python编译的位置。
例如我的python库生成位置在home/longguancheng/linuxGatewayPlatform/libs/python-3.6.5/install/lib/python3.6而开发板中不存在这个位置,所以需要重新定向一下这个位置,通过修改PYTHONHOMEPYTHONPATH``这两个环境变量来帮助python找到自己的库。
修改源文件系统的环境变量文件rootfs/node/etc/profile添加两行代码
image.png

裁剪效果

image.png
可见从之前的110m变成了24m左右,效果显著.
我们再看看这个文件夹内部的情况
image.png
和我们设想的效果一致

烧录检验

将生成的文件系统烧录到开发板,测试一下库是否都可以import
image.png
整理成表格:

模块名称menuconfig是否选中import是否成功
ensurepip—Yes——Yes—
venv—Yes——Yes—
lib2to3—Yes——Yes—
curses—Yes—No (不一致)
http—Yes——Yes—
xmlrpc—Yes——Yes—
unittestNoNo
site—Yes——Yes—
dbm—Yes——Yes—
turtledemoNoNo
importlib—Yes——Yes—
sqlite3NoNo
distutilsNoNo
xmlNoNo
concurrent—Yes——Yes—
html—Yes——Yes—
json—Yes——Yes—
idlelibNoNo
collections—Yes——Yes—
urllib—Yes——Yes—
multiprocessing—Yes——Yes—
asyncio—Yes——Yes—
tkinterNoNo
logging—Yes——Yes—
pydoc_dataNoNo
ctypes—Yes——Yes—
emailNoNo
wsgiref—Yes——Yes—

可以发现有一个curses模块是没有对起来的
image.png
curses是一个在Linux/Unix下广泛应用的图形函数库,作用是可以在终端内绘制简单的图形用户界面。
cueses模块没有移植成功主要是因为在编译阶段就因为缺少库而被忽略了
image.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值