neděle 24. listopadu 2013

MacOSX - How to create self contained dynamically linked program which links to the .dylibs bundled with it

Today I had a task - to create version of sox binary (Sound Exchange - swiss knife for sound files) which will be self contained - which works out of the box without installation.

My first finding, there is apple command for detect on which particular executable depends:

otool -L ./sox

its like linux ldd command.


The easiest way how to install sox to mac os x is using homebrew, which is another story of course.

brew install sox

But its not an option because the created binary uses links to dynamic libraries .dylib (like linux shared objects .so) to /usr/local/lib.
bash-3.2$ otool -L /usr/local/Cellar/sox/14.4.1/bin/sox
/usr/local/Cellar/sox/14.4.1/bin/sox:
/usr/local/Cellar/sox/14.4.1/lib/libsox.2.dylib (compatibility version 3.0.0, current version 3.1.0)
/usr/local/lib/libltdl.7.dylib (compatibility version 11.0.0, current version 11.0.0)
/usr/local/lib/libpng15.15.dylib (compatibility version 30.0.0, current version 30.0.0)
/usr/local/lib/libao.4.dylib (compatibility version 5.0.0, current version 5.0.0)
/usr/local/lib/libmad.0.2.1.dylib (compatibility version 3.0.0, current version 3.1.0)
/usr/local/lib/libid3tag.0.3.0.dylib (compatibility version 4.0.0, current version 4.0.0)
/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.5)
/usr/local/lib/libvorbisenc.2.dylib (compatibility version 3.0.0, current version 3.9.0)
/usr/local/lib/libvorbisfile.3.dylib (compatibility version 7.0.0, current version 7.5.0)
/usr/local/lib/libvorbis.0.dylib (compatibility version 5.0.0, current version 5.6.0)
/usr/local/lib/libogg.0.dylib (compatibility version 9.0.0, current version 9.1.0)
/usr/local/lib/libwavpack.1.1.4.dylib (compatibility version 3.0.0, current version 3.4.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1197.1.1)

Downloading official sox binary for Mac OS X is also not an option. Due to licensing issues they don't bundle own sox with lame, vorbis and mad libraries.
bash-3.2$ otool -L ./sox
./sox:
/System/Library/Frameworks/CoreAudio.framework/Versions/A/CoreAudio (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.5)
/usr/lib/libiconv.2.dylib (compatibility version 7.0.0, current version 7.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.1.0)
/usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1094.0.0)

So I must checkout the source code and compile it on my own. First of all I will install sox from homebrew to reuse dependent libraries .dylib and .h for gcc compiler flags - LDFLAGS and CPPFLAGS.
git clone git://git.code.sf.net/p/sox/code sox-code
cd sox-code
./osxbuild
./configure
#...result of configure, we want those libraries enabled
#OPTIONAL FILE FORMATS
#amrnb......................no
#amrwb......................no
#ffmpeg.....................no
#flac.......................no
#gsm........................yes (in-tree)
#lpc10......................yes (in-tree)
#mp2/mp3....................yes
# id3tag....................yes
# lame......................no
# mad.......................yes
# dlopen mad................no
# twolame...................no
#oggvorbis..................yes
#sndfile....................no
#wavpack....................yes
make
I fighted with --enable-static --disable-shared options. I did not make the statically linked sox without needing .dylibs at all. 

But what about the second option, use dynamically linked libraries bundled with executable? I am looking for something like former linux statifier (nowadays does not work but its different story...)

I found this supreme program - Mac dylib bundler which is helping with exactly this. 

It looks to the library descriptors and find all of dependent libraries, copies next to executable to the libs folder and changes their descriptor to be pointed to the new location.

dylibbundler -x ./sox -b -of -od -d ../result

and vua la:
bash-3.2$ otool -L ./sox 
./sox:
/System/Library/Frameworks/CoreAudio.framework/Versions/A/CoreAudio (compatibility version 1.0.0, current version 1.0.0)
@executable_path/../libs/libpng15.15.dylib (compatibility version 30.0.0, current version 30.0.0)
@executable_path/../libs/libao.4.dylib (compatibility version 5.0.0, current version 5.0.0)
@executable_path/../libs/libFLAC.8.dylib (compatibility version 12.0.0, current version 12.0.0)
@executable_path/../libs/libmad.0.2.1.dylib (compatibility version 3.0.0, current version 3.1.0)
@executable_path/../libs/libid3tag.0.3.0.dylib (compatibility version 4.0.0, current version 4.0.0)
/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.5)
@executable_path/../libs/libmp3lame.0.dylib (compatibility version 1.0.0, current version 1.0.0)
@executable_path/../libs/libvorbisenc.2.dylib (compatibility version 3.0.0, current version 3.9.0)
@executable_path/../libs/libvorbisfile.3.dylib (compatibility version 7.0.0, current version 7.5.0)
@executable_path/../libs/libvorbis.0.dylib (compatibility version 5.0.0, current version 5.6.0)
@executable_path/../libs/libogg.0.dylib (compatibility version 9.0.0, current version 9.1.0)
@executable_path/../libs/libwavpack.1.1.4.dylib (compatibility version 3.0.0, current version 3.4.0)
@executable_path/../libs/libsndfile.1.dylib (compatibility version 2.0.0, current version 2.25.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1197.1.1)

středa 16. února 2011

Prestashop OpenGraph module - like "semantic presta"

Hello folks,

nowaday I got worst flu in my life... so what about with a time? I made simple plugin for amazing web 2.0 eshop written in php - prestashop to make deeper integrate with facebook by Facebook OpenGraph API.

This is realy simple peace of code so I decided to give you all by licencing as opensource (GPLv2). Why a Java developer is writing plugin to php e-shop? Well, because of lack significatly similar one in Java space, Konakart is a giant with codebase from 2000, OXiD eshop is not free and Apache OfBiz is anything else but not simple eshop.

Project is hosted on Google Code. Feel free to contribute.

http://code.google.com/p/prestashop-facebook-opengraph-module/

How is this working?

Principle of 'likeness' is nowadays main concept in facebook. Graph API (REST based) is featured API. Whatever on facebook is an object (people, places, events, products) and whatever object is in relation to some other object - in graph relation. With Graph API you select relations you want - transparently.



Do you know Facebook Like button? Standard like button on the web page looks like this:



But when you are using Open Graph META tags, it looks like this:



There are Admin Page and Insights anchors, for normal link facebook create something similar to Facebook Page, with Admin rights, Insights, people who likes it, with Wall and so on...

With OpenGraph META tags, it fill Page default image, page infos - admin email, phone & fax number and so on. Even this link should appear in your Interests in Info tab (dont work for me - I'm using product and website types).

pondělí 2. listopadu 2009

Ubuntu 9.10 64bit and custom 32bit JDK and Eclipse 3.5 pwned

In this short HOWTO, I recap installation of 32bit JDK and Eclipse on 64bit linux platform. Nowadays 64bit linux distribution is not something special. Everything works fine, until is we want 32bit JDK on it - for me, I have one linux developer folder spread out to other team members, why force anybody to use 64bit at all.

OK. We have freshy new Ubuntu 9.10 amd64 with custom 32bit JDK downloaded from Sun. Why Ubuntu supports only 32bit JRE and not JDK???

Shorty mentioned, full how-to may be found here

  1. download JDK from java.sun.com
  2. unzip in /usr/lib/jvm
  3. good behaviour is create symlink pointed to /usr/lib/jvm/java -> /usr/lib/jvm/jdk1.6.0_16
  4. create /etc/profile.d/setup-dev-env.sh, which is setted up for every user, for console and graphical work
  5. export JAVA_HOME=/usr/lib/jvm/java 
    export JDK_HOME=$JAVA_HOME
    
  6. optionally, you may re-write custom create-jvm-alternative script, which sets up additional envirnonment variables and fonts, browser plugins (which on 64bit firefox doen't work of course).

    You are familiar with
    update-java-alternatives -l #list of all alternatives
    update-java-alternatives -s jdk1.6.0_16 #sets to particular one
    
    but its not necessary if you change eclipse.ini file to point -vm argument to your java installation.
    -startup
    plugins/org.eclipse.equinox.launcher_1.0.200.v20090520.jar
    --launcher.library
    plugins/org.eclipse.equinox.launcher.gtk.linux.x86_1.0.200.v20090520
    -product
    org.eclipse.epp.package.jee.product
    -showsplash
    org.eclipse.platform
    --launcher.XXMaxPermSize
    512m
    -vm
    /usr/lib/jvm/java/bin/java
    -vmargs
    -Dosgi.requiredJavaVersion=1.5
    -XX:MaxPermSize=512m
    -Xms80m
    -Xmx768m
    
OK. We have 32bit JDK installed, 32bit Eclipse installed. On Ubuntu 9.10, there is one problem with GTK2 and workaround for bug "Eclipse buttons in Ubuntu 9.10" is known.

We must create custom eclipse launcher, OK.
#!/bin/sh
export GDK_NATIVE_WINDOWS=1
/optdev/eclipse-3.5/eclipse
Everything works well, except internal SWT browser widget, you may found this exceptions on error console in eclipse or like this in Aptana IDE
(Build 1.5.0.25497) [ERROR] Unable to determine current running state of Aptana Studio
org.eclipse.swt.SWTError: No more handles [MOZILLA_FIVE_HOME='/usr/lib/xulrunner-1.9.1.4'] (java.lang.UnsatisfiedLinkError: no swt-mozilla-gtk-3550 or swt-mozilla-gtk in swt.library.path, java.library.path or the jar file)
    at org.eclipse.swt.SWT.error(SWT.java:3910)
    at org.eclipse.swt.browser.Mozilla.create(Mozilla.java:514)
    at org.eclipse.swt.browser.Browser.(Browser.java:119)
    at com.aptana.ide.documentation.views.TutorialView.createPartControl(TutorialView.java:59)
    at org.eclipse.ui.internal.ViewReference.createPartHelper(ViewReference.java:367)
    at org.eclipse.ui.internal.ViewReference.createPart(ViewReference.java:226)
    at org.eclipse.ui.internal.WorkbenchPartReference.getPart(WorkbenchPartReference.java:595)
    at org.eclipse.ui.internal.PartPane.setVisible(PartPane.java:313)
    at org.eclipse.ui.internal.ViewPane.setVisible(ViewPane.java:529)
    at org.eclipse.ui.internal.presentations.PresentablePart.setVisible(PresentablePart.java:180)
    at org.eclipse.ui.internal.presentations.util.PresentablePartFolder.select(PresentablePartFolder.java:270)
    at org.eclipse.ui.internal.presentations.util.LeftToRightTabOrder.select(LeftToRightTabOrder.java:65)
    at org.eclipse.ui.internal.presentations.util.TabbedStackPresentation.selectPart(TabbedStackPresentation.java:473)
    at org.eclipse.ui.internal.PartStack.refreshPresentationSelection(PartStack.java:1256)
    at org.eclipse.ui.internal.PartStack.setSelection(PartStack.java:1209)
    at org.eclipse.ui.internal.PartStack.showPart(PartStack.java:1608)
    at org.eclipse.ui.internal.PartStack.createControl(PartStack.java:649)
    at org.eclipse.ui.internal.PartStack.createControl(PartStack.java:576)
    at org.eclipse.ui.internal.PartSashContainer.createControl(PartSashContainer.java:568)
    at org.eclipse.ui.internal.PerspectiveHelper.activate(PerspectiveHelper.java:272)
    at org.eclipse.ui.internal.Perspective.onActivate(Perspective.java:981)
    at org.eclipse.ui.internal.WorkbenchPage.onActivate(WorkbenchPage.java:2626)
    at org.eclipse.ui.internal.WorkbenchWindow$27.run(WorkbenchWindow.java:2964)
    at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:70)
    at org.eclipse.ui.internal.WorkbenchWindow.setActivePage(WorkbenchWindow.java:2945)
    at org.eclipse.ui.internal.WorkbenchWindow.busyOpenPage(WorkbenchWindow.java:760)
    at org.eclipse.ui.internal.Workbench$21.runWithException(Workbench.java:1045)
    at org.eclipse.ui.internal.StartupThreading$StartupRunnable.run(StartupThreading.java:31)
    at org.eclipse.swt.widgets.RunnableLock.run(RunnableLock.java:35)
    at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Synchronizer.java:134)
    at org.eclipse.swt.widgets.Display.runAsyncMessages(Display.java:3468)
    at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3115)
    at org.eclipse.ui.application.WorkbenchAdvisor.openWindows(WorkbenchAdvisor.java:803)
    at org.eclipse.ui.internal.Workbench$28.runWithException(Workbench.java:1384)
    at org.eclipse.ui.internal.StartupThreading$StartupRunnable.run(StartupThreading.java:31)
    at org.eclipse.swt.widgets.RunnableLock.run(RunnableLock.java:35)
    at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Synchronizer.java:134)
    at org.eclipse.swt.widgets.Display.runAsyncMessages(Display.java:3468)
    at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3115)
    at org.eclipse.ui.internal.Workbench.runUI(Workbench.java:2316)
    at org.eclipse.ui.internal.Workbench.access$4(Workbench.java:2221)
    at org.eclipse.ui.internal.Workbench$5.run(Workbench.java:500)
    at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:332)
    at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:493)
    at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:149)
    at com.aptana.ide.rcp.IDEApplication.start(IDEApplication.java:121)
    at com.aptana.ide.desktop.integration.Application.start(Application.java:56)
    at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:194)
    at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:110)
    at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:79)
    at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:368)
    at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:179)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:559)
    at org.eclipse.equinox.launcher.Main.basicRun(Main.java:514)
    at org.eclipse.equinox.launcher.Main.run(Main.java:1311)
Caused by: java.lang.UnsatisfiedLinkError: no swt-mozilla-gtk-3550 or swt-mozilla-gtk in swt.library.path, java.library.path or the jar file
    at org.eclipse.swt.internal.Library.loadLibrary(Library.java:254)
    at org.eclipse.swt.internal.Library.loadLibrary(Library.java:159)
    at org.eclipse.swt.browser.Mozilla.create(Mozilla.java:499)
    ... 57 more
What to do to solve this strange problem? And this is thing what this article is about :)
  1. First of all, there is magical script called getlibs on Debian and ubuntu, by which you may download 32bit libraries needed by some executable. Install it, it will be invaluable in next steps.
  2. Cleverer may noticed that /usr/lib/xulrunner-1.9.1.4 is not xulrunner you need.

    Download 32bit one by getlibs and set up environmental variable MOZILLA_FIVE_HOME.

    getlibs -i xulrunner-1.9.1.4
    export MOZILLA_FIVE_HOME=/usr/lib32/xulrunner-1.9.1.4
  3. Unfortunately it's not all, we need swt-mozilla-gtk-3550 in 32bits, older version is in 64bit package libswt-mozilla-gtk-3.4-jni, but there is not something like libswt-mozilla-gtk-3.5-jni in 32 or 64bits. So we must download it manually - all -3550.so files - from eclipse subversion repository to /usr/lib32/jni folder
  4. Next thing I had to made, was looking to org.eclipse.swt.browser.Mozilla source code, what is trying to load. I created sample test class, where I simulated eclipse classloader loading behaviour.

    public class Main {
        private static final String JAVA_LIBRARY_PATH = "java.library.path";
    
        public static void main(String[] args) {
            String property = System.getProperty(JAVA_LIBRARY_PATH);
            System.out.println("Original java.library.path=" + property);
            try {
                System.loadLibrary("swt-mozilla-gtk-3550");
            } catch (UnsatisfiedLinkError e) {
                System.err.println("hacking attempt exception :)");
                e.printStackTrace();
            }
        }
    }
    
  5. Executing this piece of code, I figured this out (each time executed getlibs and created symlink to point somewhere in java.library.path). We must download these 32bit libraries to relieve of dependency hell.
    getlibs -l libxcb-atom.so.1
    getlibs -l libxcb-event.so.1
    getlibs -l libxcb-aux.so.0
    getlibs -l libstartup-notification-1.so.0
    getlibs -l libhunspell-1.2.so.0
    getlibs -l libxpcom.so
    
  6. Last but not least is set up environmental variable LD_LIBRARY_PATH to be pointed to 32bit libs. So final /etc/profile.d/setup-dev-env.sh will be:
    export JAVA_HOME=/usr/lib/jvm/java 
    export JDK_HOME=$JAVA_HOME
    export MOZILLA_FIVE_HOME=/usr/lib32/xulrunner-1.9.1.4/
    
    And final eclipse eclipse executable will be (because of this variables are cleared somehow in ubuntu after executing profile script):
    #!/bin/sh
    export GDK_NATIVE_WINDOWS=1
    export LD_LIBRARY_PATH=/usr/lib32/jni:/usr/lib32
    /optdev/eclipse-3.5/eclipse
    
  7. And vuala. We have working custom Eclipse 32bit installation on 32bit JDK and 64bit linux with 32bit xulrunner and his dependencies. We have working SWT browser widget and eclipse welcome screen (BIRT reports preview, GWT less than 2.0-SNAPSHOT hosted mode and so on...). Well done, dude ;)