tccinpy  -  Tiny C Compiler In PYthon   


Le but est de créer des extensions en langage C à l'interieur de Python, sous Windows.
Comme je ne connais pas le langage C, j'ai dû faire simple.

Petit détail : toute fonction publique doit commencer par "PUBLIQUE ", sauf s'il y a une seule fonction, auquel cas le programme ajouter cela automatiquement.

Le principe est simple : on compile avec TCC, et on crée à la volée une DLL, qui est immédiatement chargée, et accédée avec ctypes.

Détail important : comme ctypes ne permet pas de décharger une DLL, s'il y en a plusieurs, il faudra changer de nom, si vous en définissez plusieurs dans le même script...
Et aussi : adaptez le chemin où vous avez installé TCC (voir commentaire dans le début du code de la classe).

Voici le code (en Python), avec les exemples dans le __main__ :

# -*- coding: utf-8 -*-

import os,os.path,sys,ctypes,time

class tcc(object):
   
    # Valeur par défaut à adapter à l'installation
    tccplace="tcc"

    def __init__(self, nom="tccinpy", source="", tccplace=None):
        import ctypes
        self.source=source
        self.dllname=nom
        self.dll=None
        if tccplace is None:
            self.tccplace=tcc.tccplace
        else:
            self.tccplace=tccplace
        if not os.path.isfile(self.tccplace+'\\dll_squ.c'):
            open(self.tccplace+'\\dll_squ.c',"w").write('''
#include <windows.h>
#include <memory.h>
#include <string.h>
#include <wchar.h>
#include <math.h>
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>

#define DLL_EXPORT __declspec(dllexport)

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {

   switch (fdwReason) {
    // chargement de la DLL
    case DLL_PROCESS_ATTACH:
     break;
    // attachement d'un processus
    case DLL_THREAD_ATTACH:
     break;
    // détachement d'un processus
    case DLL_THREAD_DETACH:
     break;
    // déchargement de la DLL
    case DLL_PROCESS_DETACH:
    //détachement du processus
        break;

    default:
     break;
 }
   return TRUE;
   UNREFERENCED_PARAMETER(hinstDLL);
   UNREFERENCED_PARAMETER(lpvReserved);
}

%SOURCE%
''')

        if source != "":
            self.setsource(source)

    def setsource(self, source=""):
        self.source=source
        self.compile()

    def compile(self):
        import ctypes,os,os.path,shutil,time,subprocess

        if os.path.isfile(self.tccplace+'\\TEMP_INTERNE.dll'):
            os.remove(self.tccplace+'\\TEMP_INTERNE.dll')

        scode=open(self.tccplace+"\\dll_squ.c","rb").read()
        self.source = self.source.replace("PUBLIQUE","DLL_EXPORT __stdcall ")
       
        if self.source.find("DLL_EXPORT __stdcall ")>-1:
            scode = scode.replace("%SOURCE%",self.source)
        else:
            scode = scode.replace("%SOURCE%","DLL_EXPORT __stdcall "+self.source)
        open(self.tccplace+"\\TEMP_INTERNE.c","wb").write(scode)

        bufdir=os.getcwd()
        os.chdir(self.tccplace)
       
        pipe = subprocess.Popen("tcc -shared -o TEMP_INTERNE.dll TEMP_INTERNE.c",  shell=True, stderr=subprocess.PIPE).stderr
        result=pipe.readlines()
        os.chdir(bufdir)
        if len(result)>0:
            print "\r\n".join(result)
            print u"ERREUR ; la DLL n'a pas été créée."
            raise WindowsError
            return
       
        if os.path.isfile(self.tccplace+'\\TEMP_INTERNE.dll'):
            shutil.copyfile(self.tccplace+'\\TEMP_INTERNE.dll', os.getcwd()+'\\'+self.dllname+'.dll')
            self.dll=ctypes.windll.LoadLibrary(self.dllname)
        else:
            self.dll=None
            print u"ERREUR ; la DLL n'a pas été créée."
            raise WindowsError

 

 

 

if __name__=='__main__':


    c=tcc(nom='test1', source='''PUBLIQUE int test(int a,int b) {
       int r;
       r=a*b;
       return r;
    }

    PUBLIQUE int testadd(int a,int b) {
       int r;
       r=a+b;
       return r;
    }
    ''')

    print c.dll.test(3,4)
    print c.dll.test(33,44)
    print c.dll.testadd(33333,44444)
    print

 

 

    c=tcc(nom='test2', source='''int stest(char *st) {
       st[2]=(char)'-';
       st[3]=(char)'x';
       st[4]=(char)'y';
       st[5]=(char)'z';
       st[6]=(char)'-';
       return strlen(st);
    }
    ''')
    ss="ABCDEFGHI"
    stemp=ctypes.create_string_buffer(ss)
    print c.dll.stest( stemp)
    print ss
    print stemp.value
    print

 


    c=tcc(nom='test3', source='''int longuni(wchar_t *t) {
       int i = 0;
       while(t[i] != '\\0') {
          i++;
       }
       return i;
    }


    PUBLIQUE int utest(wchar_t *st) {
       st[5] = (wchar_t) '_';
       st[6] = 0x20AC;   //caractère Euro

       st[7] = (wchar_t) '_';
       return longuni(st);
    }
    ''')
    ss=u"ééABCDEFGHIèè"
    stemp=ctypes.create_unicode_buffer(ss)
    print c.dll.utest(stemp)
    print ss
    print stemp.value.encode('cp1252','replace')
    print

 


    c=tcc(nom='test4', source='''float dtest(float i) {
       float j=i*2.0;
       return j;
    }
    ''')
    c.dll.dtest.restype = ctypes.c_float
    ctemp=c.dll.dtest(ctypes.c_float(222.333))
    print ctemp
    print

 

 

    c=tcc(nom='test5', source='''char* sttest(char *s) {
       char *toto=s;
       strcat(toto,"1234");
       return toto;
    }
    ''')
    c.dll.sttest.restype = ctypes.c_char_p
    ctemp=c.dll.sttest("ABCD")
    print ctemp
    print

 

 C'est tout !