pos*_*def 5 java swt cross-platform build
这是潜在问题的背景,我正在与一个使用Swt库的项目小组合作,我正在尝试打包软件进行部署.事实证明,SWT它非常依赖于平台/架构.我希望能够将所有六个jar(linux,mac,win和32/64位)打包到同一个包中,并根据系统使用相应的库.我意识到这是一个艰难的挑战,但转换到Swing(或其他任何东西)现在并不是一个真正的选择.
我找到了许多相关主题(@Aaron Digulla的主题和@ mchr的主题),它为我提供了有关手头问题的宝贵见解.我试图落实@Alexey罗曼诺夫提出的解决方案在这里.有一点不同,因为loadSwtJar()他提出的方法不是静态的,我实例化对象,然后紧接着,在对对象做任何其他事情之前运行方法.
看起来装载过程无法正常工作.我对此声明的推理如下:
Swt从可执行jar文件的类路径中删除所有jar,则Exception in thread "main" java.lang.NoClassDefFoundError: org/eclipse/swt/events/MouseListener抛出由以下原因引起的:java.lang.ClassNotFoundException: org.eclipse.swt.events.MouseListener 对我来说,这意味着在类路径中找不到库,我错了吗?
swtjar类保留在类路径上,则系统在执行期间使用第一个jar文件.这意味着如果gtk-linux-x86_64恰好是jar列表中的第一个swt jar,那么无论系统是win32还是Mac OSX,系统都会尝试使用它.我试图添加一些输出来查看该loadSwtJar()方法是否选择了正确的jar,并且输出似乎在我尝试的所有平台上都正确,因为选择了正确的包(并且文件确实存在于runnable jar中).但是仍然没有加载正确的库因此导致执行错误:
Exception in thread "main" java.lang.reflect.InvocationTargetException由ex引起:( Caused by: java.lang.UnsatisfiedLinkError: Cannot load 32-bit SWT libraries on 64-bit JVM
请注意,如果我更改build.xml文件上64位和32位swt库的出现顺序,这是我在Linux机器上出现的错误)
那么,这里的问题似乎是什么?我是否错过了一些细节,或者根本无法检查系统属性并相应地加载适当的库?
下面是我的构建文件的摘录,认为它可能有助于找到问题的根源.
提前致谢,
编辑:经过与同事的长时间调试,问题得到解决(除了我在这里提到的关于MacOS上的线程管理的烦人的错误).它涉及到使用ANT构建进行调整以及编写主类的方式.(事实证明,主类是从SWT库扩展和实现引用,这意味着代码根本不会编译,用另一个类包装主类并从那里加载SWT jar似乎已经足够了解决这个问题)
感谢所有贡献的人,特别是@Aaron.非常感谢!
这是我的Main类的最新版本的副本.如果这对您有用,请告诉我.我在Linux(32/64bit)和Windows(32bit)上测试过它.
package de.pdark.epen.editor;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import org.apache.commons.lang.SystemUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.core.util.StatusPrinter;
import de.pdark.epen.exceptions.WikiException;
public class Main
{
public final static String VERSION = "V0.9 (13.05.2010)"; //$NON-NLS-1$
private final static Logger log = LoggerFactory.getLogger (Main.class);
private static final String ORG_ECLIPSE_SWT_WIDGETS_SHELL = "org.eclipse.swt.widgets.Shell"; //$NON-NLS-1$
/**
* @param args
*/
@SuppressWarnings({"nls", "PMD.SystemPrintln"})
public static void main (String[] args)
{
String msg = "Starting ePen "+VERSION;
System.out.println (msg);
log.info (msg);
LoggerContext lc = (LoggerContext)LoggerFactory.getILoggerFactory ();
StatusPrinter.print (lc);
int rc = 1;
try
{
Main main = new Main ();
main.run (args);
rc = 0;
}
catch (Throwable t) //NOPMD
{
ExceptionUtils.printRootCauseStackTrace (t);
}
finally
{
System.out.println ("Done.");
log.info ("Exit {}", rc);
System.exit (rc); //NOPMD
}
}
@SuppressWarnings({"nls", "PMD.SystemPrintln", "PMD.SignatureDeclareThrowsException"})
private void run (String[] args) throws Exception
{
if (!SystemUtils.isJavaVersionAtLeast (150))
{
System.out.println ("Version="+SystemUtils.JAVA_VERSION_INT);
throw new WikiException ("Need at least Java 5 but this Java is only "+SystemUtils.JAVA_VERSION);
}
loadSwtJar ();
URLClassLoader cl = (URLClassLoader) getClass().getClassLoader(); //NOPMD
Class<?> c = cl.loadClass ("de.pdark.epen.editor.EPenEditor");
Class<?> shellClass = cl.loadClass (ORG_ECLIPSE_SWT_WIDGETS_SHELL);
Constructor<?> ctor = c.getConstructor (shellClass);
Object obj = ctor.newInstance (new Object[] { null });
Method run = c.getMethod ("run", args.getClass ()); //$NON-NLS-1$
run.invoke (obj, new Object[] { args });
}
@SuppressWarnings({"nls", "PMD"})
private void loadSwtJar ()
{
try {
Class.forName (ORG_ECLIPSE_SWT_WIDGETS_SHELL);
// Already on classpath
return;
} catch (ClassNotFoundException e) {
// Add the JAR
}
String osName = SystemUtils.OS_NAME.toLowerCase ();
String osArch = SystemUtils.OS_ARCH.toLowerCase ();
String swtFileNameOsPart =
osName.contains("win") ? "win32" :
osName.contains("mac") ? "macosx" :
osName.contains("linux") || osName.contains("nix") ? "linux" :
null;
String swtFileNameUiPart =
osName.contains("win") ? "win32" :
osName.contains("mac") ? "cocoa" :
osName.contains("linux") || osName.contains("nix") ? "gtk" :
null;
if (null == swtFileNameOsPart)
{
throw new RuntimeException ("Can't determine name of SWT Jar from os.name=[" + osName + "] and os.arch=["
+ osArch + "]");
}
String swtFileNameArchPart = osArch.contains ("64") ? ".x86_64" : ".x86";
if(".x86".equals(swtFileNameArchPart) && "macosx".equals(swtFileNameOsPart)) {
swtFileNameArchPart = "";
}
String swtFileName = "org.eclipse.swt." + swtFileNameUiPart + "." + swtFileNameOsPart + swtFileNameArchPart + "-3.6.0.jar";
File file = new File ("swt", swtFileName);
if (!file.exists ())
{
throw new RuntimeException ("Can't locate SWT Jar " + file.getAbsolutePath ());
}
try
{
URLClassLoader classLoader = (URLClassLoader) getClass ().getClassLoader ();
Method addUrlMethod = URLClassLoader.class.getDeclaredMethod ("addURL", URL.class);
addUrlMethod.setAccessible (true);
URL swtFileUrl = file.toURI ().toURL ();
log.info ("Adding {} to the classpath", swtFileUrl);
addUrlMethod.invoke (classLoader, swtFileUrl);
}
catch (Exception e)
{
throw new RuntimeException ("Unable to add the swt jar to the class path: " + file.getAbsoluteFile (), e);
}
}
}
Run Code Online (Sandbox Code Playgroud)