平时经常用用到python脚本来实现一些小功能,可以通过pyinstaller之类的工具把py文件编译成exe程序,可是编译出来的程序动辄3mb以上,太大了很不爽。、、
我就想能不能把py编译成更小一点的exe。首先想到的是把py代码转换为c++代码,网上搜了很久也木有这样的工具。又一想,有没有python精简版之类的python库,又搜索很久没找到。
后来想到,自己编译python核心库,通过核心库来解析python代码不就好了嘛。于是有了下文方法:
(1)编译一个python解释器,单独一个exe,不依赖其他文件。
(2)把py文件附件到解释器的末尾,解释器读取自身py数据加载。
(3)用exe压缩工具压缩python解释器,把解释器程序压缩到最小。
一番捣鼓之下,成功了,有了一个1mb大小的python解释器,可以运行那些只import了内置库的py脚本,爽极了。详细操作步骤如下:
1、静态编译python为静态库嵌入到C++中
参照下文编译python静态库以及测试程序
1、下载 python2.7.6版本源代码(http://www.python.org/ftp/python/2.7.6/)
2、解压到Python-2.7.6文件夹
3、进入Python-2.7.6\PC\VS8.0文件夹,用Microsoft Visual Studio 2010打开解决方案 pcbuild.sln
4、切换到Release模式
5、更改C/C++—代码生成—运行库:多线程(/MT)
6、更改配置属性—配置类型:静态库(.lib)
7、更改配置属性—目标文件名:bin\python27.lib
8、在pythoncore中添加Modules文件夹中的getbuildinfo.c文件
9、打开PC文件夹中dl_nt.c文件,删除第14行的#ifdef Py_ENABLE_SHARED和第106行的#endif /* Py_ENABLE_SHARED */
10、先分别编译make_buildinfo和make_versioninfo,再编译pythoncore生成python27.lib
11、在Python-2.7.6文件夹下建立一个空的新解决方案pythonTest.sln
12、添加一个文件main.cpp
内容如下
#include <python.h>
#include <stdio.h>
int main()
{
Py_Initialize();
PyRun_SimpleString("print '\\nPython静态库编译成功!'");
Py_Finalize();
getchar();
return 0;
}
13、配置pythonTest工程
① 在C/C++项的附加包含目录中添加:..\..\Include和..\..\PC两个目录
② C/C++—预处理器—预处理器定义中添加一个宏: Py_NO_ENABLE_SHARED
③ 更改C/C++—代码生成—运行库:多线程(/MT)
④ 设置链接器—附加库目录中添加:..\..\\PC\VS8.0\bin目录
⑤ 设置链接器—输入—附加依赖项:python27.lib
14、执行编译,生成 exe文件为:pythonTest.exe
成功!!!!!!!!!!!!!!!!!!!!!
来源: https://emonkey.iteye.com/blog/2012822
编译出来的库大小
exe
挺大的嘛。
2、修改exe,添加py数据load,运行功能。
(1)exe添加自身py数据加载代码
#include <python.h>
#include <Windows.h>
#include <stdio.h>
unsigned char gBuf[1024*1024];
void PyRunExeFile()
{
int offset = 0;
char szPath[MAX_PATH] = {0};
::GetModuleFileName(NULL, szPath, MAX_PATH);
FILE *fp = fopen(szPath, "rb");
if (NULL != fp)
{
fseek(fp, 0x20, SEEK_SET);
fread(&offset, 1, sizeof(offset), fp);
if (0 == offset)
{
printf("PyData Offset = 0, Error!\n");
fclose(fp);
}
else
{
fseek(fp, offset, SEEK_SET);
fread(gBuf, 1, sizeof(gBuf), fp);
fclose(fp);
if (0 == strlen((char *)gBuf))
{
printf("PyData Offset = 0x%x, Size = 0!\n", offset);
}
else
{
PyRun_SimpleString((const char *)gBuf);
}
}
//PyRun_SimpleString((const char *)gBuf);
}
}
int main(int argc, char **argv)
{
Py_Initialize();
PySys_SetArgv(argc, argv);
PyRunExeFile();
Py_Finalize();
//getchar();
return 0;
}
py数据存放在exe文件头偏移0x20指向的位置,exe头是一个_IMAGE_DOS_HEADER结构,偏移0x20的地方,刚好是e_res保留数据,可供我们利用。
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
WORD e_magic; // Magic number
WORD e_cblp; // Bytes on last page of file
WORD e_cp; // Pages in file
WORD e_crlc; // Relocations
WORD e_cparhdr; // Size of header in paragraphs
WORD e_minalloc; // Minimum extra paragraphs needed
WORD e_maxalloc; // Maximum extra paragraphs needed
WORD e_ss; // Initial (relative) SS value
WORD e_sp; // Initial SP value
WORD e_csum; // Checksum
WORD e_ip; // Initial IP value
WORD e_cs; // Initial (relative) CS value
WORD e_lfarlc; // File address of relocation table
WORD e_ovno; // Overlay number
WORD e_res[4]; // Reserved words
WORD e_oemid; // OEM identifier (for e_oeminfo)
WORD e_oeminfo; // OEM information; e_oemid specific
WORD e_res2[10]; // Reserved words
LONG e_lfanew; // File address of new exe header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
3、压缩exe
upx是一款优秀的exe加壳程序,可以通过它来对exe进行加壳压缩。
执行命令:
upx -k -9 test.exe
压缩后的exe大小:
小了很多,只有1MB了。
4、py文件附加到exe
(1)exe长度扩展,1kb长度对齐,添加py数据指针。
infile = 'Test.exe'
outfile = 'TestPy.exe'
# print (padto)
def uin32_to_array_pos(arrData, pos, data):
arrData[pos+0] = (data>>0)&0xff
arrData[pos+1] = (data>>8)&0xff
arrData[pos+2] = (data>>16)&0xff
arrData[pos+3] = (data>>24)&0xff
def main():
fin = open(infile,'rb')
fout = open(outfile, 'wb')
d8 = array.array('B')
size = os.path.getsize(infile)
print (size)
d8.fromfile(fin,size)
fin.close()
padto = size + 1024 - (size % 1024)
print ("padto size = 0x%x, %d bytes, %d kb"% (padto, padto, padto/1024))
uin32_to_array_pos(d8, 0x20, padto)
padcnt = padto - size
for i in range(padcnt):
d8.append(0)
d8.tofile(fout)
fout.close()
(2)合并py文件到exe
copy /b testpy.exe+test.py my.exe
至此,py解释器程序整合完毕,可以把那些只import了内置库的py文件合并到testpy.exe,把py文件变成一个exe程序。该exe只有1mb多一点,远比其他py编译exe程序编译出来的exe文件体积小,太爽了,哇咔咔!