嵌入式python的裁剪

CPython的裁剪

为什么要裁剪

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

目标

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
_testbuffer fcntl resource
_testcapi grp select
_testimportmultiple math spwd
_testmultiphase mmap syslog
_tkinter nis termios
array ossaudiodev time
atexit parser unicodedata
audioop pwd xxlimited
binascii pyexpat zlib
cmath readline _struct

裁剪工作

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

通过配置不编译项裁剪

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


Makefile文件分析

嵌入式python的裁剪_第6张图片

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

文件的裁剪

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

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


目录结构设计
嵌入式python的裁剪_第8张图片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效果:
嵌入式python的裁剪_第9张图片嵌入式python的裁剪_第10张图片
嵌入式python的裁剪_第11张图片


查看.config文件
嵌入式python的裁剪_第12张图片

编写文件拷贝脚本

文件拷贝脚本的思想是:
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

嵌入式python的裁剪_第13张图片
这个位置是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左右,效果显著.
我们再看看这个文件夹内部的情况
嵌入式python的裁剪_第14张图片
和我们设想的效果一致

烧录检验

将生成的文件系统烧录到开发板,测试一下库是否都可以import
嵌入式python的裁剪_第15张图片
整理成表格:

模块名称 menuconfig是否选中 import是否成功
ensurepip —Yes— —Yes—
venv —Yes— —Yes—
lib2to3 —Yes— —Yes—
curses —Yes— No (不一致)
http —Yes— —Yes—
xmlrpc —Yes— —Yes—
unittest No No
site —Yes— —Yes—
dbm —Yes— —Yes—
turtledemo No No
importlib —Yes— —Yes—
sqlite3 No No
distutils No No
xml No No
concurrent —Yes— —Yes—
html —Yes— —Yes—
json —Yes— —Yes—
idlelib No No
collections —Yes— —Yes—
urllib —Yes— —Yes—
multiprocessing —Yes— —Yes—
asyncio —Yes— —Yes—
tkinter No No
logging —Yes— —Yes—
pydoc_data No No
ctypes —Yes— —Yes—
email No No
wsgiref —Yes— —Yes—

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

你可能感兴趣的:(嵌入式python,python,linux,makefile,交叉编译,shell)