Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Compiling Python for Windows Phone 8

0.00/5 (No votes)
14 Mar 2013 1  
This article talks about how to compile Python source code on WP8, the changes made on the source code, and an example using Python in a WP8 native app.

Introduction

Windows Phone 8 (WP8) supports native code. Developers can write applications using C/C++ directly, and port existing achievements to Windows Phone. But there are many differences between APIs on WP8 and APIs on Win32. These differences exist in many categories, such as native thread, synchronization method, file find, library load, etc., which brings a lot of workload for code porting.

Python is a dynamic script language and has many function modules. It is easy to learn and use. Python can improve the flexibility of the application and existing Python modules can also be used to speed up the development procedure. As Python is a dynamic language, apps can use this feature to create logic or control some application functions dynamically. The Python interpreter is written in C. Because WP8 supports native code, we can compile Python source code on WP8. Thus, it makes it possible to use Python on WP8.

Because of the limitations of APIs on WP8, changes have to be made in some source code of Python to be compiled successfully. Furthermore, some APIs such as thread, Windows registry are not supported, not all modules or features can be ported to WP8.

This article talks about how to compile Python source code on WP8, the changes made on the source code, and gives an example of using Python in a WP8 native app.

Main Changes in Source Code

Thread Related Functions

As WP8 does not support native threading, thread related functions cannot be compiled on WP8. So we remove these source code files from the project and change pyconfig.h.

/* Define if you want to compile in rudimentary thread support */
#undef WITH_THREAD 

These source files are removed from the project:

modules\posixmodule
modules\mmapmodule 
modules\threadmodule
Python\thread.c 

Changes in timemodule.c:

#if !defined(ENV_WP)
static PyObject *
time_sleep(PyObject *self, PyObject *args)
{
    double secs;
    if (!PyArg_ParseTuple(args, "d:sleep", &secs))
        return NULL;
    if (floatsleep(secs) != 0)
        return NULL;
    Py_INCREF(Py_None);
    return Py_None;
}
#endif
#if !defined(ENV_WP)
    {"sleep",           time_sleep, METH_VARARGS, sleep_doc},
#endif 

Changes in config.c:

#if !defined(ENV_WP)
    {"nt", initnt}, /* Use the NT os functions, not posix */
#endif
#if !defined(ENV_WP)
    {"mmap", initmmap},
#endif
#if !defined(ENV_WP)
    {"_winreg", init_winreg},
#endif
#if !defined(ENV_WP)
    {"_subprocess", init_subprocess},
#endif 

Because threads are not supported, the OS Python module cannot work correctly, which causes site.py to not import correctly. So site.py should be imported by default.

In pythonrun.c:

#if !defined(ENV_WP)
    if (!Py_NoSiteFlag)
        initsite(); /* Module site */
#endif 

Changes to the getenv Function

The getenv function on WP8 returns no environment value, so we replace this function with a new one called getenv_win8 which returns variables for PYTHONPATH and PYTHONHOME.

First, change pydebug.h:

extern char *getenv_win8(char *name);
#define Py_GETENV(s) (Py_IgnoreEnvironmentFlag ? NULL : getenv_win8(s)) 

Create a new source file named Python_wp8.cpp, and add the function getenv_win8.

bool GetInstall_Dir_win8(char *Buf,int BufSize)
{
    if( BufSize == 0 || Buf == NULL )
        return false;
    WideCharToMultiByte( CP_ACP, 0, Windows::ApplicationModel::Package::Current->InstalledLocation -> 
                         Path->Data(), -1, Buf, BufSize-1, NULL, NULL );
    Buf[BufSize-1] = 0;
    return true;
}
char *getenv_win8(char *name)
{
    char Buf[1024];
    static char RetBuf[1024];
    GetInstall_Dir_win8(Buf,1024);
    if( stricmp(name,"PYTHONPATH") == 0 ){
        sprintf(RetBuf,"%s\\python\\lib",Buf);
        return RetBuf;
    }else if( stricmp(name,"PYTHONHOME") == 0 ){
        sprintf(RetBuf,"%s\\python",Buf);
        return RetBuf;
    }else
        return NULL;
}  

Note: The Python_wp8.cpp file should be compiled with the Windows Runtime.

Changes to the loadlibraryex Function

First, LoadLibraryEx cannot load the library with an absolute path, so GetFullPathName should not be called before the LoadLibraryEx function. Second, LoadPackagedLibrary should be used other than LoadLibraryEx.

Changes in dynload_win.c.

#if !defined(ENV_WP)
    old_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
    if (GetFullPathName(pathname,
                        sizeof(pathbuf),
                        pathbuf,
                        &dummy)) {
        ULONG_PTR cookie = _Py_ActivateActCtx();
        /* XXX This call doesn't exist in Windows CE */
        hDLL = LoadLibraryEx(pathname, NULL,
                             LOAD_WITH_ALTERED_SEARCH_PATH);
        _Py_DeactivateActCtx(cookie);
    }
    /* restore old error mode settings */
    SetErrorMode(old_mode);
#endif
#if defined(ENV_WP)
    {
        ULONG_PTR cookie = _Py_ActivateActCtx();
        /* XXX This call doesn't exist in Windows CE */
        MultiByteToWideChar( CP_ACP, MB_ERR_INVALID_CHARS, pathname, -1,wacModuleName, 512 );
        //hDLL = (HINSTANCE)LoadPackagedLibrary(wacModuleName,0);
        hDLL = (HINSTANCE)SRP_LoadPackage(wacModuleName);
        _Py_DeactivateActCtx(cookie);
    }
#endif

void  *SRP_LoadPackage(wchar_t *wacModuleName)
{
    return (void *)LoadPackagedLibrary(wacModuleName,0);
}   

Changes to the FindFirstFile Function

FindFirstFile is not supported, FindFirstFileEx should be used with a Unicode string. Therefore the function in import.c has to be changed.

static int
case_ok(char *buf, Py_ssize_t len, Py_ssize_t namelen, char *name)
{
/* Pick a platform-specific implementation; the sequence of #if's here should
 * match the sequence just above.
 */
/* MS_WINDOWS */
#if defined(MS_WINDOWS)
#if !defined(ENV_WP)
    WIN32_FIND_DATA data;
#else
    wchar_t wacFileName[512] ;
    char acFileName[512] ;
    WIN32_FIND_DATAW data;
#endif
    HANDLE h;
    if (Py_GETENV("PYTHONCASEOK") != NULL)
        return 1;
#if !defined(ENV_WP)
    h = FindFirstFile(buf, &data);
#else
    MultiByteToWideChar( CP_ACP, MB_ERR_INVALID_CHARS, buf, -1,wacFileName, 512 );
    h = FindFirstFileExW(wacFileName,FindExInfoStandard,&data,FindExSearchNameMatch,NULL, 0);
#endif
    if (h == INVALID_HANDLE_VALUE) {
        PyErr_Format(PyExc_NameError,
          "Can't find file for module %.100s\n(filename %.300s)",
          name, buf);
        return 0;
    }
    FindClose(h);
#if !defined(ENV_WP)
    return strncmp(data.cFileName, name, namelen) == 0;
#else
    WideCharToMultiByte( CP_ACP, 0, data.cFileName, -1, acFileName, 511, NULL, NULL );
    return strncmp(acFileName, name, namelen) == 0;
#endif 

Changes to Other Files or Functions

There are some other changes in the source code, please check the source code.

The full source change with the WP8 project can be downloaded from http://code.google.com/p/c-python-for-windows-phone8.

Example of Using Python in Native Apps on WP8

1. Create Project

Open VS2012 and create a native project for Windows Phone 8 as follows:

2. Add python27.dll to the Project, Set its Property "content" to Yes

3. Set Include Directories and Library Search Path

4. Add python27.lib

5. Create pytest.py, and Add It to the Project, Set its Property Content to Yes

def add(a,b):  
    return a + b 

The Python code to be tested is simple. It counts the sum of two numbers.

6. Create C++ Code to Call Python

#include <windows.h>
#if defined(_DEBUG)
#undef _DEBUG
#include "python.h"
#define _DEBUG
#else
#include "python.h"
#endif
void test()
{
    PyObject *pName, *pModule, *pDict, *pFunc, *pArgs, *pRetVal; 
    Py_Initialize();  
    if ( !Py_IsInitialized() ){  
        return;  
    }  
    pName = PyString_FromString("pytest");  
    pModule = PyImport_Import(pName);  
    if ( !pModule ){  
        OutputDebugString(L"can't find pytest.py\n");
        return;  
    }  
    pDict = PyModule_GetDict(pModule);  
    if ( !pDict ){  
        return;  
    }  
    pFunc = PyDict_GetItemString(pDict, "add");  
    if ( !pFunc || !PyCallable_Check(pFunc) ) {  
        OutputDebugString(L"can't find function [add]\n");  
        return;  
    }  
    pArgs = PyTuple_New(2);  
    PyTuple_SetItem(pArgs, 0, Py_BuildValue("l",3));   
    PyTuple_SetItem(pArgs, 1, Py_BuildValue("l",4));  
    pRetVal = PyObject_CallObject(pFunc, pArgs);  
    char ResultBuf[512];
    wchar_t ResultBufW[512];
    sprintf_s(ResultBuf,512,"function return value : %ld\r\n", PyInt_AsLong(pRetVal));
    MultiByteToWideChar( CP_ACP, MB_ERR_INVALID_CHARS, ResultBuf, -1,ResultBufW, 512 );
    OutputDebugString(ResultBufW);  
    Py_DECREF(pName);  
    Py_DECREF(pArgs);  
    Py_DECREF(pModule);  
    Py_DECREF(pRetVal);  
    Py_Finalize();  
    return;  
} 

Compile and run, the result will be printed in the output window.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here