I Did It! Calling D Library from Objective C in XCode on OSX

Mike McKee via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Mon Dec 14 02:09:29 PST 2015


I finally managed to get it working, using some help from this 
forum and stackoverflow.com, and a little bit of random luck with 
tests.

// test.d
extern (C++) immutable(char)* dfunc(const char *s) {
	import std.string;
	return toStringz(fromStringz(s) ~ "-response");
}

I compiled with "dmd -c test.d" and it created a test.o file. I 
then created a Cocoa Objective C application in XCode7, but could 
have created a console application in XCode just as well.

Next, drag your test.o file into your XCode project at the top, 
underneath the first icon on the left. A dialog will pop open -- 
just go with the defaults.

I renamed the main.m as main.mm in the project. Remember the 
XCode quirk where if you use a .mm file, you may have to go to 
the project settings under Compile Sources and ensure your .mm 
file is there. If not, add it. I know that it won't do that with 
the main.m file as main.mm, but I think it has a problem with 
AppDelegate.m when renamed as AppDelegate.mm. So, keep that in 
mind.

By making it a .mm file, I can now mix Objective C and C++ 
together.

In the main.mm, I had this:

#import <Cocoa/Cocoa.h>

char *dfunc(const char *);
extern "C" int rt_init();
extern "C" int rt_term();

int main(int argc, const char * argv[]) {
	
	const char *sTest = "request";
	rt_init(); // Call D Runtime
	NSLog(@"Result=%s",dfunc(sTest));
	rt_term(); // End D Runtime
	
   return NSApplicationMain(argc, argv);
}

Next, you have to link in AppKit.Framework by going to your 
project settings and choosing Targets > (click your target .app) 
 > Build Phases > Link Binary with Libraries, and choose 
AppKit.Framework. This is a quirk in XCode.

Next, you have to link in libphobos2.a from your /usr/local/lib 
folder in a special way. You have to go into your project 
settings. Instead of clicking on your Targets item, click on your 
Projects item and then click > Build Settings > Linking > Other 
Linker Flags. Click the + on that and simply type the file path 
as /usr/local/lib/libphobos2.a. Do not put a -l in front of that 
or even a single "l" in front of it (without the dash). See, this 
is not g++ or gcc, which support that parameter. Instead, this is 
clang++.

Now, Run your project. When you run it, you'll see your default 
Cocoa form, yes, but in the debugger console you'll see 
"Result=request-response". This is because we passed the string 
"request" to dfunct() and it returned it tacked on "-response" on 
the end.

Okay you could stop there, but let's make this even cooler. Let's 
make it where you can code D code in XCode, have it compile and 
create your .o file, before Objective C is compiled and calls 
that .o file. So, include the actual source of the test.d into 
your project by dragging it under the first little folder icon 
from the top and telling the popup dialog to copy the file there.

Edit that test.d so that we return -response2 instead of 
-response, just to let us know we're calling the latest file.

Now, XCode thinks that .d files are DTrace files. It will 
automatically try to compile this test.d file as a DTrace file. 
So, to turn that off, go to your project settings > Targets > 
(your target) > Compile Sources, and remove test.d out of that 
list.

Next, we need to remove that test.o out of the project so that we 
build this before linktime and include it during linking. So, 
remove test.o out of the project. Next, go your project settings 
 > Targets > (your target) > Build Phases > and click the + and 
choose New Run Script Phase. Simply paste the following into the 
black code area below the shell field:

/usr/local/bin/dmd -c $SRCROOT/test.d

(Note, this may take some finagling. For instance, in my case for 
some strange reason I had a folder called sample with another 
folder inside also called sample, and the test.d was in that. So, 
my line had to be:)

/usr/local/bin/dmd -c $SRCROOT/sample/test.d

Next, here's another quirk in XCode. It won't let you drag and 
drop this Run Script Phase to before the Compile Sources phases. 
However, it will let you drag other items below it. So, it was 
kind of quirky and tricky, but I made the order like so:

1. Target Dependencies
2. Copy Bundle Resources
3. Run Script
4. Link Binary with Libraries
5. Compile Sources

That ensures that when the linker phase runs, it will see a 
test.o file.

Now we need to ensure the linker sees the test.o file. To do 
that, go to your project settings > Targets > (choose your 
target) > Build Settings > Basic > Linking > Other Linker Flags. 
Remember where we added the libphobos2.a file there? Now we need 
to add the test.o file there. So, doubleclick it's value and 
click the + sign. Type in:

$(SRCROOT)/test.o

Note that even though my test.d was in $SRCROOT/sample, for some 
reason it dropped the test.o in $SRCROOT. So, it may take some 
finagling in your case to ensure you have the right path. Note 
also that paths in XCode are in format $(ENV_VAR), but in Bash 
(/bin/sh) are in $ENV_VAR format.

Next, ensure that the libphobos2.a line is above the test.o line 
in the Other Linker Flags. So, drag it there if you have to do so.

At that point, we're set -- let's compile and run. What will 
happen is that it will compile the test.d file first with the dmd 
compiler and make a test.o, then the linker will link 
libphobos2.a, then link test.o, and then start to compile your 
Objective C and C++. XCode will then merge in what it needs from 
Phobos2 and your test.o into the executable binary and will no 
longer require either one. To prove my point, you can run an 
otool -L command on the binary executable that XCode creates and 
you'll see no references to phobos or test.o.

The output you'll see in your debugger console will say 
RESPONSE=request-response2 because it's running through your 
latest source code.

So, from here on out, you can use Objective C minimally, and 
Objective C++ minimally, and have a lot of your code in D. This 
code can then interact with your Cocoa application. Now, as you 
may know (or learn), Cocoa apps are pretty plain -- Apple's 
pretty fascist here and will let you make any kind of app as long 
as the elements are grey or white, lol. This is why I recommend 
using the Cocoa webkit framework -- you'll get far more color and 
style opportunities to make widgets of any sort. I also recommend 
enabling the Objective C embedding into the HTML DOM so that 
Javascript can call that. At that point, your Javascript can call 
ObjC minimally, and then your ObjC can call D, and then D can 
return a response right back through ObjC back to the Javascript.

As for D calling the Apple Foundation Classes, they are, 
evidently, available to C++, so perhaps they can be loaded in D.


More information about the Digitalmars-d-learn mailing list