Monday, December 12, 2011

How to create a Dylib on Mac OS X and create declares in Realbasic to use it.

This tutorial explains how to use Xcode to create a dylib, and then use Realbasic to declare into it. Dylib is short for Dynamic Library, which is a file that contains code that can be linked against at runtime. This means that it isn't built directly into your application, like a static library is, but you can still load it up and call methods in it.

This is mainly useful for C/C++ or Objective-C programmers to create a library that may do something not possible from Realbasic.

Creating the dylib

Launch XcodeThis tutorial assumes that you are using Xcode 3.1.4 from Apple.

Choose "File->New Project", or type Command-Shift-N.

Select BSD Dynamic Library, and click "Choose".

Choose a project name. For this example, we'll call it "SampleDylib".

Choose "File->New File..."

Choose "C File", from within the "Mac OS" section

Name the file. For this example, we'll call it "SampleDylib.c". The default settings are generally correct, but if you have multiple projects open with multiple targets, make sure the proper project and target is selected. When done, click "Finish".

If there were build errors, ensure that the code you entered looks exactly like it does above. Once the build is successful, continue to step 12.

Find where the build was saved. If the builds folder location hasn't been changed, it will be located in a "build" folder, next to your project file. It will be named libSampleDylib.dylib. (This is bceuase we've not altered the default install name in the build target.)Another way to locate the file is to locate the target by going to the project window, expanding libSampleDylib, then expanding Products. Select libSampleDylib.dylib, and control-click (or right-click, for those with two-button mice), and choose Reveal in FinderYou will need to access this location later.

Creating the Realbasic project

Launch Real Studio. Create a new project, and choose "Desktop"

Save the project to your Documents folder (so that it is next to the dylib file). For this example, we'll name it, "Test Dylib.rb"

Copy the dylib from the Xcode build into the application bundle. You can do this by right clicking on the debug app & selecting "Show Package Contents".Navigate to Contents > Mac OS & copy the dynamic library created by Xcode into the Frameworks directory in the bundle. (If the edition of Real Studio you use supports this you could use a Build Automation Step to do this for you on every compile)

Now launch the debug application by double clicking it in the Finder. It should then connect to the debugger in the IDE.

Notice that it correctly adds, and also correctly computes the length of the string.

What is it doing?

The loader (aka Dynamic Linker) doesn't search for a dynamic library very well. It relies on either having a full path to the library, or having it relative to the executable path. Since we want the dylib to be installed in the bundle, we use "@executable_path/../Frameworks/libSampleDylib.dylib" as the path for non-debug builds. This means that when you build your application you will need to copy the SampleDylib.dylib file into your Contents/Frameworks directory next to your actual executable file.However, since the application is regenerated every time Realbasic builds it, the same path won't work for debug builds. (Suggestions about how to deal with this are included later)

What about the native types in Realbasic?

Using Realbasic, you can do almost anything through declares. Here are a few tips on how to handle different types in Realbasic:

Passing a float into your dylib, or returning a float: In Realbasic, use "Single" as the data type. Singles are the exact same as a float -- a single precision floating point, to be specific.

Passing a double into your dylib, or returning a double: Doubles are the exact same in both languages.

Passing a c-string (null-terminated string) into your dylib: In Realbasic, declare the type for the parameter as CString.

Passing a pascal-string (one byte length specifier) into your dylib: In Realbasic declare the type for the parameter as PString.

Passing a void* (arbitrary data) into your dylib, or returning it: In Realbasic, declare the type for the parameter as Ptr, and pass in a MemoryBlock. Also declare the return type as Ptr, and it will automatically be converted to a MemoryBlock on return.

Passing a pointer to a struct into your dylib: The same as above. Set up the memoryblock to contain the different fields, and pass it in as a Ptr.

Limitation: It isn't currently possible to pass in a struct inline.

Known issues

By default all symbols (functions & subroutines in C) are exported. This is usually what you want.If you happen to preface one with the C keyword "static" then it won't be exported & so it will not be usable directly in Real Studio code like we've shown.

If you name your file with a ".cpp" extension then Xcode assumes that it is a C++ file & names will get mangled by the Xcode compiler. This means they would not be directly usable in Real Studio.You will need to preface them with an "extern "C"" declaration to make sure the names get exported unmangled.

Because Real Studio recompiles your application every time you run it under the debugger you can't use the same path for the dylib while debugging as you can in the final build. You can deal with this by using an absolute path to the dylib when debugging. In my case this would be:

Another way to deal with this is to put the dylib next to the application bundle where Real Studio builds the application (right next to the project file on OS X). Then you can use a relative path like:

@executable_path is actually not tied to Real Studio.It's part of Mac OS X and how it locates & loads dynamic libraries. On many other OS's there's a search path that is used to locate dynamic libraries.Not so on OS X. So dyld, the dynamic link editor requires some means to know where to locate dynamic libraries. But this is often an absolute path which would be useless for a bundled application that has it's own frameworks & libraries included with it. So there are three @variables that can be used@executable_path, @loader_path & @rpath.