1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 #include <pyuno/pyuno.hxx> 25 26 #include <osl/process.h> 27 #include <osl/file.h> 28 #include <osl/thread.h> 29 30 #include <rtl/ustrbuf.hxx> 31 #include <rtl/strbuf.hxx> 32 #include <rtl/bootstrap.hxx> 33 34 #include <cppuhelper/implementationentry.hxx> 35 #include <cppuhelper/factory.hxx> 36 37 using rtl::OUString; 38 using rtl::OUStringBuffer; 39 using rtl::OString; 40 41 using pyuno::PyRef; 42 using pyuno::Runtime; 43 using pyuno::PyThreadAttach; 44 45 using com::sun::star::registry::XRegistryKey; 46 using com::sun::star::uno::Reference; 47 using com::sun::star::uno::XInterface; 48 using com::sun::star::uno::Sequence; 49 using com::sun::star::uno::XComponentContext; 50 using com::sun::star::uno::RuntimeException; 51 52 namespace pyuno_loader 53 { 54 55 static void raiseRuntimeExceptionWhenNeeded() throw ( RuntimeException ) 56 { 57 if( PyErr_Occurred() ) 58 { 59 PyRef excType, excValue, excTraceback; 60 PyErr_Fetch( (PyObject **)&excType, (PyObject**)&excValue,(PyObject**)&excTraceback); 61 Runtime runtime; 62 com::sun::star::uno::Any a = runtime.extractUnoException( excType, excValue, excTraceback ); 63 OUStringBuffer buf; 64 buf.appendAscii( "python-loader:" ); 65 if( a.hasValue() ) 66 buf.append( ((com::sun::star::uno::Exception *)a.getValue())->Message ); 67 throw RuntimeException( buf.makeStringAndClear(), Reference< XInterface> () ); 68 } 69 } 70 71 static PyRef getLoaderModule() throw( RuntimeException ) 72 { 73 PyRef module( 74 PyImport_ImportModule( const_cast< char * >("pythonloader") ), 75 SAL_NO_ACQUIRE ); 76 raiseRuntimeExceptionWhenNeeded(); 77 if( !module.is() ) 78 { 79 throw RuntimeException( 80 OUString( RTL_CONSTASCII_USTRINGPARAM( "pythonloader: Couldn't load pythonloader module" ) ), 81 Reference< XInterface > () ); 82 } 83 return PyRef( PyModule_GetDict( module.get() )); 84 } 85 86 static PyRef getObjectFromLoaderModule( const char * func ) 87 throw ( RuntimeException ) 88 { 89 PyRef object( PyDict_GetItemString(getLoaderModule().get(), (char*)func ) ); 90 if( !object.is() ) 91 { 92 OUStringBuffer buf; 93 buf.appendAscii( "pythonloader: couldn't find core element pythonloader." ); 94 buf.appendAscii( func ); 95 throw RuntimeException(buf.makeStringAndClear(),Reference< XInterface >()); 96 } 97 return object; 98 } 99 100 OUString getImplementationName() 101 { 102 return OUString( RTL_CONSTASCII_USTRINGPARAM( "org.openoffice.comp.pyuno.Loader" ) ); 103 } 104 105 Sequence< OUString > getSupportedServiceNames() 106 { 107 OUString serviceName( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.loader.Python" ) ); 108 return Sequence< OUString > ( &serviceName, 1 ); 109 } 110 111 static void setPythonHome ( const OUString & pythonHome ) 112 { 113 OUString systemPythonHome; 114 osl_getSystemPathFromFileURL( pythonHome.pData, &(systemPythonHome.pData) ); 115 OString o = rtl::OUStringToOString( systemPythonHome, osl_getThreadTextEncoding() ); 116 #if PY_MAJOR_VERSION < 3 || PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION <= 4 117 rtl_string_acquire(o.pData); // leak this string (thats the api!) 118 Py_SetPythonHome( o.pData->buffer); 119 #else 120 static wchar_t *wpath = Py_DecodeLocale(o.pData->buffer, NULL); 121 if (wpath == NULL) { 122 PyErr_SetString(PyExc_SystemError, "Cannot decode python home path"); 123 return; 124 } 125 Py_SetPythonHome(wpath); 126 #endif 127 } 128 129 static void prependPythonPath( const OUString & pythonPathBootstrap ) 130 { 131 rtl::OUStringBuffer bufPYTHONPATH( 256 ); 132 sal_Int32 nIndex = 0; 133 while( 1 ) 134 { 135 sal_Int32 nNew = pythonPathBootstrap.indexOf( ' ', nIndex ); 136 OUString fileUrl; 137 if( nNew == -1 ) 138 { 139 fileUrl = OUString( &( pythonPathBootstrap[nIndex] ) ); 140 } 141 else 142 { 143 fileUrl = OUString( &(pythonPathBootstrap[nIndex]) , nNew - nIndex ); 144 } 145 OUString systemPath; 146 osl_getSystemPathFromFileURL( fileUrl.pData, &(systemPath.pData) ); 147 bufPYTHONPATH.append( systemPath ); 148 bufPYTHONPATH.append( static_cast<sal_Unicode>(SAL_PATHSEPARATOR) ); 149 if( nNew == -1 ) 150 break; 151 nIndex = nNew + 1; 152 } 153 const char * oldEnv = getenv( "PYTHONPATH"); 154 if( oldEnv ) 155 bufPYTHONPATH.append( rtl::OUString(oldEnv, strlen(oldEnv), osl_getThreadTextEncoding()) ); 156 157 rtl::OUString envVar(RTL_CONSTASCII_USTRINGPARAM("PYTHONPATH")); 158 rtl::OUString envValue(bufPYTHONPATH.makeStringAndClear()); 159 osl_setEnvironment(envVar.pData, envValue.pData); 160 } 161 162 Reference< XInterface > CreateInstance( const Reference< XComponentContext > & ctx ) 163 { 164 Reference< XInterface > ret; 165 166 if( ! Py_IsInitialized() ) 167 { 168 OUString pythonPath; 169 OUString pythonHome; 170 OUString path( RTL_CONSTASCII_USTRINGPARAM( "$OOO_BASE_DIR/program/" SAL_CONFIGFILE("pythonloader.uno" ))); 171 rtl::Bootstrap::expandMacros(path); //TODO: detect failure 172 rtl::Bootstrap bootstrap(path); 173 174 // look for pythonhome 175 bootstrap.getFrom( OUString( RTL_CONSTASCII_USTRINGPARAM( "PYUNO_LOADER_PYTHONHOME") ), pythonHome ); 176 bootstrap.getFrom( OUString( RTL_CONSTASCII_USTRINGPARAM( "PYUNO_LOADER_PYTHONPATH" ) ) , pythonPath ); 177 178 // pythonhome+pythonpath must be set before Py_Initialize(), otherwise there appear warning on the console 179 // sadly, there is no api for setting the pythonpath, we have to use the environment variable 180 if( pythonHome.getLength() ) 181 setPythonHome( pythonHome ); 182 183 if( pythonPath.getLength() ) 184 prependPythonPath( pythonPath ); 185 186 // initialize python 187 Py_Initialize(); 188 PyEval_InitThreads(); 189 190 PyThreadState *tstate = PyThreadState_Get(); 191 PyEval_ReleaseThread( tstate ); 192 } 193 194 PyThreadAttach attach( PyInterpreterState_Head() ); 195 { 196 if( ! Runtime::isInitialized() ) 197 { 198 Runtime::initialize( ctx ); 199 } 200 Runtime runtime; 201 202 PyRef pyCtx = runtime.any2PyObject( 203 com::sun::star::uno::makeAny( ctx ) ); 204 205 PyRef clazz = getObjectFromLoaderModule( "Loader" ); 206 PyRef args ( PyTuple_New( 1 ), SAL_NO_ACQUIRE ); 207 PyTuple_SetItem( args.get(), 0 , pyCtx.getAcquired() ); 208 PyRef pyInstance( PyObject_CallObject( clazz.get() , args.get() ), SAL_NO_ACQUIRE ); 209 runtime.pyObject2Any( pyInstance ) >>= ret; 210 } 211 return ret; 212 } 213 214 } 215 216 217 static struct cppu::ImplementationEntry g_entries[] = 218 { 219 { 220 pyuno_loader::CreateInstance, pyuno_loader::getImplementationName, 221 pyuno_loader::getSupportedServiceNames, cppu::createSingleComponentFactory, 222 0 , 0 223 }, 224 { 0, 0, 0, 0, 0, 0 } 225 }; 226 227 extern "C" 228 { 229 230 //================================================================================================== 231 SAL_DLLPUBLIC_EXPORT void SAL_CALL component_getImplementationEnvironment( 232 const sal_Char ** ppEnvTypeName, uno_Environment ** ) 233 { 234 *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME; 235 } 236 //================================================================================================== 237 SAL_DLLPUBLIC_EXPORT void * SAL_CALL component_getFactory( 238 const sal_Char * pImplName, void * pServiceManager, void * pRegistryKey ) 239 { 240 return cppu::component_getFactoryHelper( pImplName, pServiceManager, pRegistryKey , g_entries ); 241 } 242 243 } 244 245