Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include <pyuno/pyuno.hxx>
21 :
22 : #include <osl/process.h>
23 : #include <osl/file.hxx>
24 : #include <osl/thread.h>
25 :
26 : #include <rtl/ustrbuf.hxx>
27 : #include <rtl/strbuf.hxx>
28 : #include <rtl/bootstrap.hxx>
29 :
30 : #include <cppuhelper/implementationentry.hxx>
31 : #include <cppuhelper/factory.hxx>
32 :
33 : // apparently PATH_MAX is not standard and not defined by MSVC
34 : #ifndef PATH_MAX
35 : #ifdef _MAX_PATH
36 : #define PATH_MAX _MAX_PATH
37 : #else
38 : #ifdef MAX_PATH
39 : #define PATH_MAX MAX_PATH
40 : #else
41 : #error no PATH_MAX
42 : #endif
43 : #endif
44 : #endif
45 :
46 :
47 : using pyuno::PyRef;
48 : using pyuno::Runtime;
49 : using pyuno::PyThreadAttach;
50 :
51 : using com::sun::star::registry::XRegistryKey;
52 : using com::sun::star::uno::Reference;
53 : using com::sun::star::uno::XInterface;
54 : using com::sun::star::uno::Sequence;
55 : using com::sun::star::uno::XComponentContext;
56 : using com::sun::star::uno::RuntimeException;
57 :
58 : namespace pyuno_loader
59 : {
60 :
61 0 : static void raiseRuntimeExceptionWhenNeeded() throw ( RuntimeException )
62 : {
63 0 : if( PyErr_Occurred() )
64 : {
65 0 : PyRef excType, excValue, excTraceback;
66 0 : PyErr_Fetch( (PyObject **)&excType, (PyObject**)&excValue,(PyObject**)&excTraceback);
67 0 : Runtime runtime;
68 0 : com::sun::star::uno::Any a = runtime.extractUnoException( excType, excValue, excTraceback );
69 0 : OUStringBuffer buf;
70 0 : buf.appendAscii( "python-loader:" );
71 0 : if( a.hasValue() )
72 0 : buf.append( ((com::sun::star::uno::Exception *)a.getValue())->Message );
73 0 : throw RuntimeException( buf.makeStringAndClear(), Reference< XInterface> () );
74 : }
75 0 : }
76 :
77 0 : static PyRef getLoaderModule() throw( RuntimeException )
78 : {
79 : PyRef module(
80 : PyImport_ImportModule( "pythonloader" ),
81 0 : SAL_NO_ACQUIRE );
82 0 : raiseRuntimeExceptionWhenNeeded();
83 0 : if( !module.is() )
84 : {
85 : throw RuntimeException(
86 : OUString( "pythonloader: Couldn't load pythonloader module" ),
87 0 : Reference< XInterface > () );
88 : }
89 0 : return PyRef( PyModule_GetDict( module.get() ));
90 : }
91 :
92 0 : static PyRef getObjectFromLoaderModule( const char * func )
93 : throw ( RuntimeException )
94 : {
95 0 : PyRef object( PyDict_GetItemString(getLoaderModule().get(), (char*)func ) );
96 0 : if( !object.is() )
97 : {
98 0 : OUStringBuffer buf;
99 0 : buf.appendAscii( "pythonloader: couldn't find core element pythonloader." );
100 0 : buf.appendAscii( func );
101 0 : throw RuntimeException(buf.makeStringAndClear(),Reference< XInterface >());
102 : }
103 0 : return object;
104 : }
105 :
106 0 : OUString getImplementationName()
107 : {
108 0 : return OUString( "org.openoffice.comp.pyuno.Loader" );
109 : }
110 :
111 0 : Sequence< OUString > getSupportedServiceNames()
112 : {
113 0 : OUString serviceName( "com.sun.star.loader.Python" );
114 0 : return Sequence< OUString > ( &serviceName, 1 );
115 : }
116 :
117 0 : static void setPythonHome ( const OUString & pythonHome )
118 : {
119 0 : OUString systemPythonHome;
120 0 : osl_getSystemPathFromFileURL( pythonHome.pData, &(systemPythonHome.pData) );
121 0 : OString o = OUStringToOString( systemPythonHome, osl_getThreadTextEncoding() );
122 : #if PY_MAJOR_VERSION >= 3
123 : // static because Py_SetPythonHome just copies the "wide" pointer
124 : static wchar_t wide[PATH_MAX + 1];
125 0 : size_t len = mbstowcs(wide, o.pData->buffer, PATH_MAX + 1);
126 0 : if(len == (size_t)-1)
127 : {
128 0 : PyErr_SetString(PyExc_SystemError, "invalid multibyte sequence in python home path");
129 0 : return;
130 : }
131 0 : if(len == PATH_MAX + 1)
132 : {
133 0 : PyErr_SetString(PyExc_SystemError, "python home path is too long");
134 0 : return;
135 : }
136 0 : Py_SetPythonHome(wide);
137 : #else
138 : rtl_string_acquire(o.pData); // increase reference count
139 : Py_SetPythonHome(o.pData->buffer);
140 : #endif
141 : }
142 :
143 0 : static void prependPythonPath( const OUString & pythonPathBootstrap )
144 : {
145 0 : OUStringBuffer bufPYTHONPATH( 256 );
146 0 : sal_Int32 nIndex = 0;
147 : while( 1 )
148 : {
149 0 : sal_Int32 nNew = pythonPathBootstrap.indexOf( ' ', nIndex );
150 0 : OUString fileUrl;
151 0 : if( nNew == -1 )
152 : {
153 0 : fileUrl = pythonPathBootstrap.copy(nIndex);
154 : }
155 : else
156 : {
157 0 : fileUrl = pythonPathBootstrap.copy(nIndex, nNew - nIndex);
158 : }
159 0 : OUString systemPath;
160 0 : osl_getSystemPathFromFileURL( fileUrl.pData, &(systemPath.pData) );
161 0 : bufPYTHONPATH.append( systemPath );
162 0 : bufPYTHONPATH.append( static_cast<sal_Unicode>(SAL_PATHSEPARATOR) );
163 0 : if( nNew == -1 )
164 0 : break;
165 0 : nIndex = nNew + 1;
166 0 : }
167 0 : const char * oldEnv = getenv( "PYTHONPATH");
168 0 : if( oldEnv )
169 0 : bufPYTHONPATH.append( OUString(oldEnv, strlen(oldEnv), osl_getThreadTextEncoding()) );
170 :
171 0 : OUString envVar("PYTHONPATH");
172 0 : OUString envValue(bufPYTHONPATH.makeStringAndClear());
173 0 : osl_setEnvironment(envVar.pData, envValue.pData);
174 0 : }
175 :
176 0 : Reference< XInterface > CreateInstance( const Reference< XComponentContext > & ctx )
177 : {
178 0 : Reference< XInterface > ret;
179 :
180 0 : if( ! Py_IsInitialized() )
181 : {
182 0 : OUString pythonPath;
183 0 : OUString pythonHome;
184 0 : OUString path( "$BRAND_BASE_DIR/program/" SAL_CONFIGFILE("pythonloader.uno" ));
185 0 : rtl::Bootstrap::expandMacros(path); //TODO: detect failure
186 0 : rtl::Bootstrap bootstrap(path);
187 :
188 : // look for pythonhome
189 0 : bootstrap.getFrom( OUString( "PYUNO_LOADER_PYTHONHOME"), pythonHome );
190 0 : bootstrap.getFrom( OUString( "PYUNO_LOADER_PYTHONPATH" ) , pythonPath );
191 :
192 : // pythonhome+pythonpath must be set before Py_Initialize(), otherwise there appear warning on the console
193 : // sadly, there is no api for setting the pythonpath, we have to use the environment variable
194 0 : if( !pythonHome.isEmpty() )
195 0 : setPythonHome( pythonHome );
196 :
197 0 : if( !pythonPath.isEmpty() )
198 0 : prependPythonPath( pythonPath );
199 :
200 : #ifdef WNT
201 : //extend PATH under windows to include the branddir/program so ssl libs will be found
202 : //for use by terminal mailmerge dependency _ssl.pyd
203 : OUString sEnvName("PATH");
204 : OUString sPath;
205 : osl_getEnvironment(sEnvName.pData, &sPath.pData);
206 : OUString sBrandLocation("$BRAND_BASE_DIR/program");
207 : rtl::Bootstrap::expandMacros(sBrandLocation);
208 : osl::FileBase::getSystemPathFromFileURL(sBrandLocation, sBrandLocation);
209 : sPath = OUStringBuffer(sPath).
210 : append(static_cast<sal_Unicode>(SAL_PATHSEPARATOR)).
211 : append(sBrandLocation).makeStringAndClear();
212 : osl_setEnvironment(sEnvName.pData, sPath.pData);
213 : #endif
214 :
215 : #if PY_MAJOR_VERSION >= 3
216 0 : PyImport_AppendInittab( (char*)"pyuno", PyInit_pyuno );
217 : #else
218 : PyImport_AppendInittab( (char*)"pyuno", initpyuno );
219 : #endif
220 : // initialize python
221 0 : Py_Initialize();
222 0 : PyEval_InitThreads();
223 :
224 0 : PyThreadState *tstate = PyThreadState_Get();
225 0 : PyEval_ReleaseThread( tstate );
226 : // This tstate is never used again, so delete it here.
227 : // This prevents an assertion in PyThreadState_Swap on the
228 : // PyThreadAttach below.
229 0 : PyThreadState_Delete(tstate);
230 : }
231 :
232 0 : PyThreadAttach attach( PyInterpreterState_Head() );
233 : {
234 0 : if( ! Runtime::isInitialized() )
235 : {
236 0 : Runtime::initialize( ctx );
237 : }
238 0 : Runtime runtime;
239 :
240 : PyRef pyCtx = runtime.any2PyObject(
241 0 : com::sun::star::uno::makeAny( ctx ) );
242 :
243 0 : PyRef clazz = getObjectFromLoaderModule( "Loader" );
244 0 : PyRef args ( PyTuple_New( 1 ), SAL_NO_ACQUIRE );
245 0 : PyTuple_SetItem( args.get(), 0 , pyCtx.getAcquired() );
246 0 : PyRef pyInstance( PyObject_CallObject( clazz.get() , args.get() ), SAL_NO_ACQUIRE );
247 0 : runtime.pyObject2Any( pyInstance ) >>= ret;
248 : }
249 0 : return ret;
250 : }
251 :
252 : }
253 :
254 :
255 : static struct cppu::ImplementationEntry g_entries[] =
256 : {
257 : {
258 : pyuno_loader::CreateInstance, pyuno_loader::getImplementationName,
259 : pyuno_loader::getSupportedServiceNames, cppu::createSingleComponentFactory,
260 : 0 , 0
261 : },
262 : { 0, 0, 0, 0, 0, 0 }
263 : };
264 :
265 : extern "C"
266 : {
267 :
268 0 : SAL_DLLPUBLIC_EXPORT void * SAL_CALL pythonloader_component_getFactory(
269 : const sal_Char * pImplName, void * pServiceManager, void * pRegistryKey )
270 : {
271 0 : return cppu::component_getFactoryHelper( pImplName, pServiceManager, pRegistryKey , g_entries );
272 : }
273 :
274 : }
275 :
276 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|