Swig创建的Java包Jython导入链接失败

Jython import of Java package created by Swig fails to link

本文关键字:导入 链接 失败 Jython 创建 Java Swig      更新时间:2023-10-16

我正在开发一个应用程序,该应用程序包含用多种语言编写的组件。我正在努力让Jython中的Java功能正常工作。Java通过JNI访问一些本机/C++功能,这些功能由SWIG封装。

每当我试图导入项目中的所有类时,我都会遇到无法链接PROJECTJNI的错误。这是我制作的最低案例:

import sys
sys.path.append('PROJECT.jar')
from com.whatever.project import *

这是执行时的错误消息:

$ jython Bootstrap.py
"my" variable $jythonHome masks earlier declaration in same scope at /usr/bin/jython line 15.
Traceback (most recent call last):
  File "Bootstrap.py", line 9, in <module>
    from com.whatever.project import *
java.lang.UnsatisfiedLinkError: com.whatever.project.PROJECTJNI.swig_module_init()V
        at com.whatever.project.PROJECTJNI.swig_module_init(Native Method)
        at com.whatever.project.PROJECTJNI.<clinit>(PROJECTJNI.java:974)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:278)
        at org.python.core.Py.loadAndInitClass(Py.java:909)
        at org.python.core.Py.findClassInternal(Py.java:844)
        at org.python.core.Py.findClass(Py.java:869)
        at org.python.core.packagecache.PackageManager.basicDoDir(PackageManager.java:107)
        at org.python.core.packagecache.SysPackageManager.doDir(SysPackageManager.java:138)
        at org.python.core.PyJavaPackage.fillDir(PyJavaPackage.java:123)
        at org.python.core.imp.importAll(imp.java:1051)
        at org.python.core.imp.importAll(imp.java:1039)
        at org.python.pycode._pyx0.f$0(Bootstrap.py:9)
        at org.python.pycode._pyx0.call_function(Bootstrap.py)
        at org.python.core.PyTableCode.call(PyTableCode.java:165)
        at org.python.core.PyCode.call(PyCode.java:18)
        at org.python.core.Py.runCode(Py.java:1275)
        at org.python.util.PythonInterpreter.execfile(PythonInterpreter.java:235)
        at org.python.util.jython.run(jython.java:247)
        at org.python.util.jython.main(jython.java:129)
java.lang.UnsatisfiedLinkError: java.lang.UnsatisfiedLinkError: com.whatever.project.PROJECTJNI.swig_module_init()V

当我们调用jython时,第15行的内容就会出现,我们一直在忽略它

我们可以通过简单地一次加载一个类来让Java项目中的类工作:

com.whatever.project import Class1
com.whatever.project import Class2
...
com.whatever.project import Class50

这是非常不切实际的,因为即使是短python脚本,我们也可能需要十几个类。它们中的许多都是我们正在捕捉的具有独特类型的例外。因此,任何健壮地处理错误的东西都可能需要大量的类。

根据jython文档,我应该能够隐藏PROJECTJNI,这样就不会通过这样做来加载它,但我发现文档并不完全清楚。以下是我尝试的:

import com.whatever.project
__all__ = dir(com.whatever.project)
__all__.remove('PROJECTJNI')
from com.whatever.project import *

但这失败了,并且仍然明显地试图加载PROJECTJNI。

我还试图修复本机可执行文件,以便它可以与correctl链接。我了解到另一个使用JRuby的小组在包括所有内容方面都没有问题,所以我决定检查源代码和二进制文件。我在swig创建的Project_wrap.cpp文件中发现了void swig_module_init()。它隐藏在一个宏后面,但它就在那里,objdump证实:

$objdump libPROJECT.so -t |grep PROJECTJNI |grep init
000000000051a900 l     O .data  00000000000009d0              _ZZ59Java_com_whatever_project_PROJECTJNI_swig_1module_1initE7methods
0000000000263f66 g     F .text  00000000000000d1              Java_com_whatever_project_PROJECTJNI_swig_1module_1init

我的任何故障排除步骤是否出错?这是Jython中的一个bug吗?有没有一个简单的Python解决方法可以让它跳过加载PROJECTJNI?

任何让我跳过链接或使此链接正确的操作都将被接受。

在其中一个Java类BinaryLoader上,有一个名为void load_binary()的方法,在该类的静态部分中调用:

public class BinaryLoader
{
    static
        { load_binaries(); }
    public static void load_binaries()
    {
        // Deep inside here is a call to actually load the shared library. Using
        load_shared_library_from_jar("PROJECT");
    }
 ... // more details here
 }

我们使用本机API的Java代码在加载类时调用了此方法。当类被"触摸"时,我们的JRuby代码调用了这个,只使用常量的名称,即类的名称,调用了静态部分。为了澄清,这里有一个来自我们的文档和实时Ruby脚本的示例:

# Tells Jruby to load the Java Interopability layer.
require 'java'
# Loads the PROJECT Java tools.
require 'PROJECT.jar'
# Shorten the PROJECT tool names from their obnoxiously long Java name.
module PROJECT
    include_package "com.whatever.project"
end
# Let Ruby know the BinaryLoader exists and it will have PROJECT load all the system binaries.
PROJECT::BinaryLoader

显然,需要JarFile就足以使Ruby中的所有Native符号和Java类都可用,并且直到最后一行才加载二进制文件,这只是一个解析为类名称并强制运行静态部分的表达式。实际的方法调用看起来像:PROJECT::BinaryLoader.load_binaries

在Jython中,即使在BinaryLoader类上调用其他静态方法时,也从未调用静态部分。我通过导入最少的Java和Native符号来手动调用该方法:

# Tells Jython to load the python system Interopability tools.
import sys
# When searching for python symbols, this Java jar should be search also.
sys.path.append('PROJECT.jar')
# Load just enough the have the JVM Know how to load the native PROJECT libraries
from com.whatever.project import PROJECT
from com.whatever.project import BinaryLoader
# Load any native binaries that are required
BinaryLoader.load_binaries()
# After this line is run in any given script then PROJECT can be used.
from com.whatever.project import *

显然,本机二进制文件并没有像人们想象的那样加载。任何加载这些内容的东西都是可以接受的解决方案。

这可以绕过Jython不调用静态部分,通过尽早完成它应该完成的工作。这似乎是Jython中的一个错误,但可能是Java中没有严格执行静态加载顺序的保证。无论哪种方式,由于这种不兼容性,我们可能会删除静态部分,以防止未来的Java广告Ruby Devs添加可能无法在Python中加载的内容。