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_features.h>
21 : #include <config_folders.h>
22 :
23 : #include <pyuno/pyuno.hxx>
24 :
25 : #include <osl/process.h>
26 : #include <osl/file.hxx>
27 : #include <osl/thread.h>
28 :
29 : #include <rtl/ustrbuf.hxx>
30 : #include <rtl/strbuf.hxx>
31 : #include <rtl/bootstrap.hxx>
32 :
33 : #include <cppuhelper/implementationentry.hxx>
34 : #include <cppuhelper/factory.hxx>
35 :
36 : // apparently PATH_MAX is not standard and not defined by MSVC
37 : #ifndef PATH_MAX
38 : #ifdef _MAX_PATH
39 : #define PATH_MAX _MAX_PATH
40 : #else
41 : #ifdef MAX_PATH
42 : #define PATH_MAX MAX_PATH
43 : #else
44 : #error no PATH_MAX
45 : #endif
46 : #endif
47 : #endif
48 :
49 :
50 : using pyuno::PyRef;
51 : using pyuno::NOT_NULL;
52 : using pyuno::Runtime;
53 : using pyuno::PyThreadAttach;
54 :
55 : using com::sun::star::registry::XRegistryKey;
56 : using com::sun::star::uno::Reference;
57 : using com::sun::star::uno::XInterface;
58 : using com::sun::star::uno::Sequence;
59 : using com::sun::star::uno::XComponentContext;
60 : using com::sun::star::uno::RuntimeException;
61 :
62 : namespace pyuno_loader
63 : {
64 :
65 2 : static void raiseRuntimeExceptionWhenNeeded() throw ( RuntimeException )
66 : {
67 2 : if( PyErr_Occurred() )
68 : {
69 0 : PyRef excType, excValue, excTraceback;
70 0 : PyErr_Fetch( (PyObject **)&excType, (PyObject**)&excValue,(PyObject**)&excTraceback);
71 0 : Runtime runtime;
72 0 : com::sun::star::uno::Any a = runtime.extractUnoException( excType, excValue, excTraceback );
73 0 : OUStringBuffer buf;
74 0 : buf.appendAscii( "python-loader:" );
75 0 : if( a.hasValue() )
76 0 : buf.append( ((com::sun::star::uno::Exception *)a.getValue())->Message );
77 0 : throw RuntimeException( buf.makeStringAndClear() );
78 : }
79 2 : }
80 :
81 2 : static PyRef getLoaderModule() throw( RuntimeException )
82 : {
83 : PyRef module(
84 : PyImport_ImportModule( "pythonloader" ),
85 2 : SAL_NO_ACQUIRE );
86 2 : raiseRuntimeExceptionWhenNeeded();
87 2 : if( !module.is() )
88 : {
89 0 : throw RuntimeException( "pythonloader: Couldn't load pythonloader module" );
90 : }
91 2 : return PyRef( PyModule_GetDict( module.get() ));
92 : }
93 :
94 2 : static PyRef getObjectFromLoaderModule( const char * func )
95 : throw ( RuntimeException )
96 : {
97 2 : PyRef object( PyDict_GetItemString(getLoaderModule().get(), (char*)func ) );
98 2 : 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());
104 : }
105 2 : return object;
106 : }
107 :
108 2 : OUString getImplementationName()
109 : {
110 2 : return OUString( "org.openoffice.comp.pyuno.Loader" );
111 : }
112 :
113 2 : Sequence< OUString > getSupportedServiceNames()
114 : {
115 2 : OUString serviceName( "com.sun.star.loader.Python" );
116 2 : 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 2 : static void prependPythonPath( const OUString & pythonPathBootstrap )
146 : {
147 2 : OUStringBuffer bufPYTHONPATH( 256 );
148 2 : sal_Int32 nIndex = 0;
149 : while( true )
150 : {
151 2 : sal_Int32 nNew = pythonPathBootstrap.indexOf( ' ', nIndex );
152 2 : OUString fileUrl;
153 2 : if( nNew == -1 )
154 : {
155 2 : fileUrl = pythonPathBootstrap.copy(nIndex);
156 : }
157 : else
158 : {
159 0 : fileUrl = pythonPathBootstrap.copy(nIndex, nNew - nIndex);
160 : }
161 2 : OUString systemPath;
162 2 : osl_getSystemPathFromFileURL( fileUrl.pData, &(systemPath.pData) );
163 2 : bufPYTHONPATH.append( systemPath );
164 2 : bufPYTHONPATH.append( static_cast<sal_Unicode>(SAL_PATHSEPARATOR) );
165 2 : if( nNew == -1 )
166 2 : break;
167 0 : nIndex = nNew + 1;
168 0 : }
169 2 : const char * oldEnv = getenv( "PYTHONPATH");
170 2 : if( oldEnv )
171 0 : bufPYTHONPATH.append( OUString(oldEnv, strlen(oldEnv), osl_getThreadTextEncoding()) );
172 :
173 4 : OUString envVar("PYTHONPATH");
174 4 : OUString envValue(bufPYTHONPATH.makeStringAndClear());
175 4 : osl_setEnvironment(envVar.pData, envValue.pData);
176 2 : }
177 :
178 2 : Reference< XInterface > CreateInstance( const Reference< XComponentContext > & ctx )
179 : {
180 2 : Reference< XInterface > ret;
181 :
182 2 : if( ! Py_IsInitialized() )
183 : {
184 2 : OUString pythonPath;
185 4 : OUString pythonHome;
186 4 : OUString path( "$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("pythonloader.uno" ));
187 2 : rtl::Bootstrap::expandMacros(path); //TODO: detect failure
188 4 : rtl::Bootstrap bootstrap(path);
189 :
190 : // look for pythonhome
191 2 : bootstrap.getFrom( OUString( "PYUNO_LOADER_PYTHONHOME"), pythonHome );
192 2 : 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 2 : if( !pythonHome.isEmpty() )
197 0 : setPythonHome( pythonHome );
198 :
199 2 : if( !pythonPath.isEmpty() )
200 2 : 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 2 : PyImport_AppendInittab( (char*)"pyuno", PyInit_pyuno );
219 : #else
220 : PyImport_AppendInittab( (char*)"pyuno", initpyuno );
221 : #endif
222 :
223 : #if HAVE_FEATURE_READONLY_INSTALLSET
224 : Py_DontWriteBytecodeFlag = 1;
225 : #endif
226 :
227 : // initialize python
228 2 : Py_Initialize();
229 2 : PyEval_InitThreads();
230 :
231 2 : PyThreadState *tstate = PyThreadState_Get();
232 2 : PyEval_ReleaseThread( tstate );
233 : // This tstate is never used again, so delete it here.
234 : // This prevents an assertion in PyThreadState_Swap on the
235 : // PyThreadAttach below.
236 4 : PyThreadState_Delete(tstate);
237 : }
238 :
239 4 : PyThreadAttach attach( PyInterpreterState_Head() );
240 : {
241 2 : if( ! Runtime::isInitialized() )
242 : {
243 2 : Runtime::initialize( ctx );
244 : }
245 2 : Runtime runtime;
246 :
247 : PyRef pyCtx = runtime.any2PyObject(
248 4 : com::sun::star::uno::makeAny( ctx ) );
249 :
250 4 : PyRef clazz = getObjectFromLoaderModule( "Loader" );
251 4 : PyRef args ( PyTuple_New( 1 ), SAL_NO_ACQUIRE, NOT_NULL );
252 2 : PyTuple_SetItem( args.get(), 0 , pyCtx.getAcquired() );
253 4 : PyRef pyInstance( PyObject_CallObject( clazz.get() , args.get() ), SAL_NO_ACQUIRE );
254 4 : runtime.pyObject2Any( pyInstance ) >>= ret;
255 : }
256 4 : return ret;
257 : }
258 :
259 : }
260 :
261 :
262 : static const struct cppu::ImplementationEntry g_entries[] =
263 : {
264 : {
265 : pyuno_loader::CreateInstance, pyuno_loader::getImplementationName,
266 : pyuno_loader::getSupportedServiceNames, cppu::createSingleComponentFactory,
267 : 0 , 0
268 : },
269 : { 0, 0, 0, 0, 0, 0 }
270 : };
271 :
272 : extern "C"
273 : {
274 :
275 2 : SAL_DLLPUBLIC_EXPORT void * SAL_CALL pythonloader_component_getFactory(
276 : const sal_Char * pImplName, void * pServiceManager, void * pRegistryKey )
277 : {
278 2 : return cppu::component_getFactoryHelper( pImplName, pServiceManager, pRegistryKey , g_entries );
279 : }
280 :
281 : }
282 :
283 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|