A Python pode ser compilada?

Olá! Muitas pessoas perguntam: Posso compilar Python? A resposta é não, e dirá, porque está a escrever um artigo se não pode, explicarei porque digo que não pode compilar Python:

¿Se puede compilar python?

Na verdade, a pitão é uma linguagem interpretada, pelo que não pode ser compilada, mas existem vários usos que podem gerar um executável a partir do seu código python, seja isolado ou dependente da biblioteca:

1- A primeira maneira de compilar a python: cython

A primeira forma de criar um executável a partir do nosso script python é com o utilitário Cython, que converte o código para a linguagem C e depois o compila com um compilador C como o GCC:

Script “test.py

def testPython():
   toprint = "Python mola"
   print(toprint)

if __name__=="__main__":
   testPython()

Para fazer o seguinte é necessário instalar o cython, pode instalá-lo com easy_install ou pip:

root@host:~# pip install cython
root@host:~# 

Depois transformamos o código python em C e compilamo-lo com GCC:

root@host:~# cython --embed -o test.c test.py
root@host:~# gcc -I/usr/include/python2.7 -lpython2.7 -o test test.c
root@host:~# 

Como se pode ver, o executável é criado e comporta-se exactamente como o guião:

root@host:~# ./test 
Python mola
root@host:~# python test.py 
Python mola

Como pode ver, foi gerado um ficheiro executável de elfo linux (exe no windows), depende das bibliotecas python (e como em qualquer binário tem de se ter cuidado com o que se deixa nas cordas como com o comando strings pode obter texto plaintext):

root@host:~# file test
test: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=e54070acae5bb3d0c751b495919680933e43955f, not stripped
root@host:~# ldd test
	linux-vdso.so.1 (0x00007ffea39cc000)
	libpython2.7.so.1.0 => /usr/lib/x86_64-linux-gnu/libpython2.7.so.1.0 (0x00007fdf70a44000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fdf70699000)
	libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007fdf7047e000)
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fdf70261000)
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fdf7005d000)
	libutil.so.1 => /lib/x86_64-linux-gnu/libutil.so.1 (0x00007fdf6fe5a000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fdf6fb59000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fdf70fc9000)
root@host:~# strings test|grep "Python"
testPython
Python mola
testPython
test.testPython
 while calling a Python object
__pyx_k_testPython
__pyx_k_Python_mola
__pyx_kp_s_Python_mola
__pyx_n_s_testPython
__pyx_mdef_4test_1testPython
__pyx_pw_4test_1testPython
__pyx_pf_4test_testPython

2- Segunda maneira de compilar a pitão: pyinstaller

Podemos também criar um executável com instalador pyinstaller, embora desta forma possamos realmente empacotar o script python e as suas bibliotecas num binário, pelo que nem o python nem as suas bibliotecas são obrigados a ser instalados no sistema para executar o binário gerado:

A primeira coisa é instalar o pyinstaller, pode ser feito com pip ou com easy_install

root@host:~# pip install pyinstaller
root@host:~# 

Geramos então o binário com:

root@host:~# pyinstaller -F test.py 
23 INFO: PyInstaller: 3.2.1
23 INFO: Python: 2.7.9
24 INFO: Platform: Linux-3.16.0-4-amd64-x86_64-with-debian-8.10
24 INFO: wrote /tmp/test.spec
27 INFO: UPX is not available.
28 INFO: Extending PYTHONPATH with paths
['/tmp', '/tmp']
28 INFO: checking Analysis
28 INFO: Building Analysis because out00-Analysis.toc is non existent
28 INFO: Initializing module dependency graph...
29 INFO: Initializing module graph hooks...
61 INFO: running Analysis out00-Analysis.toc
74 INFO: Caching module hooks...
76 INFO: Analyzing /tmp/test.py
77 INFO: Loading module hooks...
77 INFO: Loading module hook "hook-encodings.py"...
1121 INFO: Looking for ctypes DLLs
1122 INFO: Analyzing run-time hooks ...
1125 INFO: Looking for dynamic libraries
1231 INFO: Looking for eggs
1231 INFO: Python library not in binary depedencies. Doing additional searching...
1260 INFO: Using Python library /usr/lib/x86_64-linux-gnu/libpython2.7.so.1.0
1262 INFO: Warnings written to /tmp/build/test/warntest.txt
1290 INFO: checking PYZ
1290 INFO: Building PYZ because out00-PYZ.toc is non existent
1290 INFO: Building PYZ (ZlibArchive) /tmp/build/test/out00-PYZ.pyz
1440 INFO: Building PYZ (ZlibArchive) /tmp/build/test/out00-PYZ.pyz completed successfully.
1465 INFO: checking PKG
1465 INFO: Building PKG because out00-PKG.toc is non existent
1465 INFO: Building PKG (CArchive) out00-PKG.pkg
3070 INFO: Building PKG (CArchive) out00-PKG.pkg completed successfully.
3074 INFO: Bootloader /usr/local/lib/python2.7/dist-packages/PyInstaller/bootloader/Linux-64bit/run
3074 INFO: checking EXE
3074 INFO: Building EXE because out00-EXE.toc is non existent
3075 INFO: Building EXE from out00-EXE.toc
3075 INFO: Appending archive to ELF section in EXE /tmp/dist/test
3090 INFO: Building EXE from out00-EXE.toc completed successfully.

O ficheiro binário foi gerado no directório “dist/” e, como se pode ver abaixo, comporta-se tal como o script como o outro binário que gerámos anteriormente:

root@host:~# dist/test 
Python mola

Como se pode ver, também foi gerado um ficheiro binário ELF (no windows um EXE), tem menos dependências de biblioteca e nada do que está escrito nas cordas aparece quando executamos o comando “strings”:

root@host:~# file dist/test 
dist/test: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=373ec5dee826653796e927ac3d65c9a8ec7db9da, stripped
root@host:~# ldd dist/test 
	linux-vdso.so.1 (0x00007ffc22787000)
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fc7ada4b000)
	libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007fc7ad830000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc7ad485000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fc7adc4f000)
root@host:~# strings dist/test |grep Python
Py_SetPythonHome
Cannot dlsym for Py_SetPythonHome
Error loading Python lib '%s': %s
Error detected starting Python VM.

Outra coisa a ter em conta é que o tamanho do ficheiro executável será maior com este utilitário porque foi gerado um ficheiro autónomo e por essa razão as bibliotecas necessárias foram incluídas no código:

root@host:~# ls -alh test
-rwxr-xr-x 1 root root 48K feb 15 01:24 test
root@host:~# ls -alh dist/test
-rwxr-xr-x 1 root root 3,6M feb 15 01:35 dist/test

Deixe uma resposta