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