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

  1. Launch Xcode
    This tutorial assumes that you are using Xcode 3.1.4 from Apple.
  2. Choose "File->New Project", or type Command-Shift-N.
  3. Select BSD Dynamic Library, and click "Choose".
  4. Choose a project name. For this example, we'll call it "SampleDylib".
  5. Choose "File->New File..."
  6. Choose "C File", from within the "Mac OS" section
  7. 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".
  8. Open the SampleDylib.c source file.
  9. Add the code below:
    #include <string.h>
    
    int addFunction( int a, int b ) {
     return a + b;
    }
    
    int stringLength( char *str ) {
     return strlen(str);
    }
  10. Save the file, then click the Build button, or press Command-B.
  11. 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.
  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 Finder
    You will need to access this location later.

Creating the Realbasic project

  1. Launch Real Studio. Create a new project, and choose "Desktop"
  2. 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"
  3. Double click on "App" in the project window.
  4. Expand the "Events" section, and select "Open"
  5. Add the code below:
    CONST dylibLocation = "@executable_path/../Frameworks/libSampleDylib.dylib"
    
    Declare Function addFunction lib dylibLocation (a as integer, b as Integer) as Integer
    Declare Function stringLength lib dylibLocation (s as CString) as Integer
    
    msgBox "5 + 2 = " + str(AddFunction(5,2))
    
    msgBox "The length of ""asdf"" is " + str(stringLength("asdf"))
  6. Run your application using the "Run Paused" feature of the IDE.
  7. 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)
  8. Now launch the debug application by double clicking it in the Finder. It should then connect to the debugger in the IDE.
  9. 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:

CONST dylibLocation = "/Users/npalardy/Documents/libSampleDylib.dylib"

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:

CONST dylibLocation = "@executable_path/../../../libSampleDylib.dylib"

And you could make the code in your Real Studio project look like:



#if debugBuild
   CONST dylibLocation = "@executable_path/../../../libSampleDylib.dylib" // next to the app bundle
#else
   CONST dylibLocation = "@executable_path/../Frameworks/libSampleDylib.dylib" // inside the app frameworks in the bundle
#endif

Declare Function addFunction lib dylibLocation (a as integer, b as Integer) as Integer
Declare Function stringLength lib dylibLocation (s as CString) as Integer

Document Updated by:
Norman Palardy norman@realsoftware.com
Real Software, Inc.
based on a previous article by Jonathan Johnson
Real Software, Inc.

9 comments:

BobTheCodeBuilder said...

Great tutorial! Thanks for posting.

Thomas said...

Thanks for the helpful article. May I ask that this also gets added to the persistent docs, i.e. the User Manual and maybe the LR (under "declare", perhaps), too?

Also, to pass a ptr to a struct, one can use ByRef. In fact, ByRef can be used for any ref to any variable.

There are issues with passing and returning structs in PPC code, though, so beware if you still use PPC builds.

Frederick W. Roller said...

Where is @executable_path documented? Is it only available in Declares? Are there any others?

Steveorevo said...

Now this is what I'm talking about. Good stuff!

Norman Palardy said...

@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.

More information can be found on

Norman Palardy said...

http://developer.apple.com/library/mac/#documentation/Darwin/Reference/Manpages/man1/dyld.1.html

Norman Palardy said...

Darn thing keeps whacking the URL I'm trying to post.
If you google for "OS X man page dyld" you should see the man page from OS X and it lists & explains how to use the @variables

Frederick W. Roller said...

Thank you Norman!

Norman Palardy said...

For xCode 4 it looks like you need to select either the C/C++ library or STL C++ library project type and make sure the Type is set to "dynamic"

From there most things should be the same as to how you declare into such a dynamic library.