If you are working on Objective-C kode in VI the following functions and key mapping will allow you to quickly switch between the header and implementation file.
" Switch editing between" *.c and *.h files (C)" or" *.m and *.h files (ObjC)function! Flip_Extension()ifmatch(expand("%"),'\.m')>0let s:flipname = substitute(expand("%"),'\.m\(.*\)','.h\1',"")call LoadFile(s:flipname)elseifmatch(expand("%"),"\\.h")>0let s:flipname = substitute(expand("%"),'\.h\(.*\)','.m\1',"")if(filereadable(s:flipname))>0call LoadFile(s:flipname)elselet s:flipname = substitute(expand("%"),'\.h\(.*\)','.c\1',"")call LoadFile(s:flipname)endifelseifmatch(expand("%"),"\\.c")>0let s:flipname = substitute(expand("%"),'\.c\(.*\)','.h\1',"")call LoadFile(s:flipname)endif
endfun
" Find the filename in an existing buffer" if it exists open that buffer so you don't" lose your file position.function! LoadFile(filename)let s:bufname = bufname(a:filename)if(strlen(s:bufname))>0exe":buffer" s:bufnameelseexe":find " a:filename
endif
endfun
map<F4>:call Flip_Extension()<CR>
I have a habit of dragging addresses from the Safari address bar to my desktop on my Mac. Eventually these web link files pile up and things become unmanageable. Since I use dropbox, I decided to do a reboot by copying all of these files to a dropbox folder. Then when I had free time and was sitting in front of a Windows machine that was also using dropbox I could do some reading.
“Safari is on Windows, it must read .webloc files”, I naively thought. When I got to my Windows machine and had some free time I discovered that Windows Safari does not read .webloc files. The .webloc file was in a binary format. Opening the webloc file in vi showed some goobly-gook (binary data) and the URL string floating in binary data. I could have easily copied the URL in vi and pasted it into the address bar, but I’d gone this far and a program was demanding to be written.
So a .webloc file is stored as a binary plist, and reading a plist file is simple in Cocoa. This was the perfect excuse for writing an Objective-C program in Windows.
Be warned. Unlike my regular posts which I try to keep focused and to the point this one does go off on a tangent.
If you want to just skip ahead and read the kode then take a look at the following two gitHub repositories:
In a previous posting I explained how to write a simple Objective-C program in Windows. The main benefits of using Objective-C on the Macintosh are:
It’s object oriented
The frameworks/libraries provided
It’s not as ugly as C++
The frameworks and libraries are particularly important. Learning new frameworks takes time, and being able to leverage a familiar framework can save time. Frameworks can also be designed poorly making them hard to use. Lots of methods with too many parameters for example.
Even if a framework is designed well, if the documentation for that framework is poor then learning how to use that framework will still be tedious. Without proper documentation, you will need to experiment with method calls more which will take more time.
The framework that we’re talking about here is obviously Cocoa. All those classes that make Objective-C programming fun like NSString and NSData. So what options do we have for using Cocoa on Windows?
I decided to first start with Cocotron so I downloaded the latest source code. However, Cocotron can only be built through XCode on Mac OS X. Since I’m trying to do the build directly from Windows this was a non-starter but I figured having the source code handy would be a good idea.
Initially I stayed away from GNUStep because I was unsure of the licensing. I also already have a highly customized Cygwin environment so was hesitant in using GNUStep because it uses minGW and that would be another environment to customize and maintain. Later I did look into GNUStep and ported my implementation to GNUStep easily.
As far as GNUStep licensing is concerned it uses GPL and LGPL so it should be safe to link to and distribute a binary. However, I am not a lawyer and have yet to find a good explanation of the licensing terms. There is no clear document that I could find in the GNUStep installation or website that states that GNUStep uses LGPL. I determined that GNUStep is LGPL by the description on wikipedia and by looking at the license inside the header files (/GNUstep/System/Library/Headers). Documentation is not fun work, but the organization of the GNUStep documentation is particularly scatterbrained.
So with the constraint of doing the build purely in Windows, Cocotron was not an option. I decided to stay away from GNUStep temporarily so what option did that leave me with? Surely I wasn’t going to build my own Cocoa framework. That would be pure folly.
Core Foundation
Well there was another option I didn’t mention, Core Foundation. Like the Objective-C runtime, Apple has been releasing their Core Foundation library as open source for some time now. Core Foundation is not Objective-C kode, it is pure C, however the names of the C structures are eerily similar to some Objective-C foundation classes. Core Foundation has a CFStringRef (NSString), CFArrayRef(NSArray), etc. So it didn’t seem far fetched to write Objective-C wrappers for these C data structures.
I downloaded and compiled Core Foundation, which was a little bit more difficult than I expected. Like the Objective-C runtime the build is not always verified to see if it works in Windows so some massaging is required. If you are interested in doing this then check out the links below this paragraph. Building Core Foundation is a good exercise, because it will help familiarize you with the library if you read some of the kode. Now that I’ve built it however, I will just grab a copy out of a Safari installation because it’s just plain easier.
Once I decided to write Objective-C wrappers for Core Foundation I wondered if that was what Cocotron did. So I looked at the CocoTron kode and discovered that CocoTron does not rely on Core Foundation. This surprised me because it went completely against my lazy programmer instincts. Why reinvent the wheel? You have this highly tested kode base that you can use but you’re going to rewrite it?
Eventually Christopher Lloyd did explain that CocoTron does not use Core Foundation because of some philosophical issues with regard to the APSL (Apple Software License Agreement).
If you modify any APSL kode you must clearly mark what you modified.
Apple has the right to terminate the APSL.
Christopher believes that issue #1 would be a long term hassle. Personally I just wouldn’t modify any APSL kode unless there were no other options. Fix any issues with the kode in the wrapper layer, or provide an alternative implementation if the APSL kode doesn’t work properly.
Issue #2 is nothing to get your feathers ruffled about. Sure it sounds pretty nasty but this is a corporate cover your ass clause. Apple has been releasing stuff under APSL for 10 years now and I have not heard of them terminating the license on anything. I can see this clause only being used in exceptional circumstances because the PR backlash it would generate in the programmer world (general consumers couldn’t care less) could be big.
Here are some interesting links. If you’re smart enough to read then you’re smart enough to think. Make up your own mind about the APSL.
So the stage has been set and now you know the actors. I decided to call the program lilac for lack of a better name. I first wrote lilac in pure C with Core Foundation to read the .webloc file as a plist and print out the URL value to the console. This was very easy as Apple has the kode to read a plist file with Core Foundation here. Then I figured out how to read the default web browser from the registry (fun) and open a link in the default browser (see mondoWin32/WinHelper.m).
After the core logic was built I started implementing it in Objective-C. I created only the Cocoa objects that I needed, with the methods that I needed and put these source files in a directory called cocoaLite. I copied what code I could from CocoTron but some of the logic is obviously different since I was plugging into Core Foundation.
In the end I really only created NSString and NSArray, but this was enough for me to prove that it could be done. Since the application I was creating is very short-lived (it reads a file and passes an URL to a browser and then dies), there was no need to support memory management (retain, release, pools).
How NSString Works
The trickiest part of the implementation was getting NSString working, and the Cocotron kode was very helpful in this regard. Actually the trickiest part was sitting down and learning how to write a makefile but that was more patience and reading than anything else; I had been avoiding that for years anyway.
To get @”" string constants to work you need to do the following:
@implementationNSString// NSString is a stub class that creates another class// that extends NSString. We need to do this// so that the variable signature does not interfere// with NSConstantString which allows us to use @"blah"// to construct strings.+(id)alloc {return(id)class_create_instance([NSStringCF class]);
}
Create an NSStringCF class that extends NSString and works as a wrapper for a Core Foundation CFStringRef.Because you’re going to need a non-constant implementation of NSString.
So I defined NSString, and NSArray, and I could have implemented NSData but I didn’t. I felt I had gotten a good feeling as to how much work was required to create Objective-C wrappers for all the Core Foundation objects (alot).
Later I followed a tutorial on how to install GNUStep and create a HelloWorld application. The installation for GNUStep strikes me as odd. You have to run three installers to use GNUStep, and it is this type of disorganization that makes people shy away from things. However, once you go through all those hoops, like an obedient show dog, you get the benefit of an Objective-C API (OpenStep/Cocoa) that you can program against.
Converting lilac to use GNUStep (lilacStep) was quick and easy. The biggest stumbling block that I had was accidently typing “make” from a Cygwin window instead of the minGW. Because I had access to a framework almost identical to Cocoa I could delete the files from src/CocoaLite and simplify the makefile. I could also greatly simplify the implementation of getURLFromWeblocFilename since I had access to NSData and NSDictionary.
So for some reason or another you need to get something from a DMG file and there isn’t a Mac in the vicinity. It is possible, and you don’t need to pay $50 for something like MacDrive.
For the super lazy and non-koder types you can use dmg2iso to create an ISO from the DMG file. Then mount the ISO with some windows ISO mounting software.
I didn’t do this. I downloaded the source to dmg2img and read the README. Yes, I actually read README files, why do you think they’re named so? This pointed me in the direction of libdmg-hfsplus.
People looking for a more professional tool should take a look to the libdmg library and related utilities at
libdmg-hfsplus uses CMake so make sure you have cmake installed. You can either install the cygwin package or get the latest Windows version. I recommend the cygwin version since that’s what I used.
Execute these commands in the root libdmg-hfsplus directory:
cmake ./
make
Now you’ve built the tools you need to cut through DMG files like butter.
dmg.exe
hdutil.exe
hfsplus.exe
Copy the exe files somewhere that is in your path (~/bin for example).
Use the Tools
To test this out you’re gonna need a DMG file. You can download the DMG file I used here. It’s zipped up so you should probably unzip it.
Of the three executable files you built the only one you really need to use is hdutil. Here is the command to list the contents of a DMG file.
hdutil SunFlowerPublic-0.7.dmg ls
The ls command will also take a parameter which will let you explore the contents of the DMG file system.
hdutil SunFlowerPublic-0.7.dmg ls /SunFlower.app/Contents
The extract command wants two arguments. The first is the location of the file in the DMG file. The second is where to put the file on your machine.
Perplexingly, nowhere on the Bluebird page does it state what Bluebird is. I had to download it and run it to find out. Quite forgivable though since they are in beta.
If you look at the Safari installation directory on Windows you probably noticed the objc.dll file. Are you curious if Windows Safari is using Objective-C version 1.0 or 2.0? I was, and this is my little adventure on trying to find that out. The short answer is NO. Safari on Windows still uses Objective-C 1.0. In this article I’m going to build the Objective-C DLL and show you why I don’t think Safari for Windows is using version 2.0.
The Objective-C Runtime Environment
In my last article I showed you how to build a bare bones Objective-C program in Windows with Cygwin. In that case, the Objective-C runtime used was installed with Cygwin. You can execute the following command to take a peek at the Objective-C header and library files that Cygwin uses.
find /lib/gcc | grep objc
This is not the same Objective-C runtime that Apple uses when compiling Safari for Windows. In fact there are three Objective-C runtime environments that I know of:
Cygwin
Apple
GnuStep
We used Cygwin in the last article and now we’re going to build Apple’s implementation on Windows. I may discuss GnuStep at a later date.
Once you unpack the tarball it’s time to find out how to build this sucker. You won’t find a Makefile but you will find objc.vcproj. This is a Microsoft Visual Studio project file.
If you don’t have Visual Studio installed you can download the free version here. Why Microsoft insists on on continuing to charge for an IDE is beyond me. Must be a cultural thing. What you want is Visual C++ Expression Edition 2008. It may or may not work with the new 2010 Beta; I have no idea. I ran 2008 so I’m telling you to do the same.
Once you have Visual Studio installed, open up the project file and choose build –> build solution. You’re going to get some errors about AvailablityMacros.h and TargetConditionals.h not being found.
Did you really think it was going to be that easy?
Error #1 TargetConditionals.h
If you have XCode installed on a Mac you can easily find TargetConditionals.h. However, that file doesn’t support the Microsoft compiler. This is evident by the comment at the top of the file. It’s good to read the source no?
/*
File: TargetConditionals.h
Contains: Autoconfiguration of TARGET_ conditionals for Mac OS X and iPhone
Note: TargetConditionals.h in 3.4 Universal Interfaces works
with all compilers. This header only recognizes compilers
known to run on Mac OS X.
*/#ifndef __TARGETCONDITIONALS__#define __TARGETCONDITIONALS__
So with a little Googly magic you’ll find the TargetConditionals.h that you’re looking for here under Universal Interfaces 3.4.2. Of course, a *.img.bin file isn’t much help on a Windows operating system so you’re going to have to extract the files on a Mac and then copy them to the Windows Machine.
Oh, and the files are going to have those classic Mac line breaks instead of Unix line breaks so you’re going to have to convert them.
Put those UniversalHeaders files you worked so hard to get somewhere logical, and include the header files in your project search path. What’s that? You can’t figure out where to do that? Confusing Microsoft UI got you down?
Tools –> Options –> VC++ Directories
Then set the “Show directories for” combo box to “Include files”.
If you do another build then you’ll notice there are no complaints about TargetConditionals.h but Visual Studio still can’t find AvailabilityMacros.h.
Error #2 AvailabilityMacros.h
To get rid of the AvailabilityMacros.h error grab all the header files in the latest SDK you installed on your Mac. We are trying to compile the version of Objective-C that is compiled on Snow Leopard so you should probably get the 10.6 *.h files.
Create another directory for some extra includes. I called mine /cygdrive/c/Dev/usr/include/VisualStudio, but you can call it what you like. Now copy these three files to that new directory, and add the directory to the list of include directories in Visual Studio.
Availability.h
AvailabilityInternal.h
AvailabilityMacros.h
Error #3 stdint.h
Stdint.h is used for making C code more portable and you need a copy.
Basically you need to comment out three functions so that it will compile. The explanation on the mailing list is that the source is out of sync which means nobody at Apple has probably tried to build objc on Windows for a while.
unmap_image
map_images
load_images
Add #if !TARGET_OS_WIN32 … #endif around those three functions.
#if !TARGET_OS_WIN32/***********************************************************************
* unmap_image
* Process the given image which is about to be unmapped by dyld.
* mh is mach_header instead of headerType because that's what
* dyld_priv.h says even for 64-bit.
**********************************************************************/
__private_extern__ void
unmap_image(conststruct mach_header *mh, intptr_t vmaddr_slide){
recursive_mutex_lock(&loadMethodLock);
unmap_image_nolock(mh, vmaddr_slide);
recursive_mutex_unlock(&loadMethodLock);}/***********************************************************************
* map_images
* Process the given images which are being mapped in by dyld.
* Calls ABI-agnostic code after taking ABI-specific locks.
**********************************************************************/
__private_extern__ constchar*
map_images(enum dyld_image_states state, uint32_t infoCount,conststruct dyld_image_info infoList[]){constchar*err;
recursive_mutex_lock(&loadMethodLock);
err = map_images_nolock(state, infoCount, infoList);
recursive_mutex_unlock(&loadMethodLock);return err;}/***********************************************************************
* load_images
* Process +load in the given images which are being mapped in by dyld.
* Calls ABI-agnostic code after taking ABI-specific locks.
*
* Locking: acquires classLock and loadMethodLock
**********************************************************************/
__private_extern__ constchar*
load_images(enum dyld_image_states state, uint32_t infoCount,conststruct dyld_image_info infoList[]){
BOOL found;
recursive_mutex_lock(&loadMethodLock);// Discover +load methods
found = load_images_nolock(state, infoCount, infoList);// Call +load methods (without classLock - re-entrant)if(found){
call_load_methods();}
recursive_mutex_unlock(&loadMethodLock);return NULL;}#endif
Error #5 SRCROOT & DSTROOT
We’ve almost got this DLL built, we just need to do one last thing. Several places in the project properties the variables $(SRCROOT) and $(DSTROOT) are referenced.
Created a batch file to define those variables right before opening up the Visual Studio Project file.
Now you should be able to successfully build the objc.dll library. It can be found under $DSTROOT\AppleInternal\bin\objc.dll.
Attempt to Build Objective-C 2.0
So far you’ve built Objective-C 1.0. Now let’s try to build version 2.0. If you look at the source code you see __OBJC2__ littered all over the kode. To build version 2.0 you need to define it.
Add /D “__OBJC2__” to the C/C++ command line arguments in the project properties and do a build. You will get lots of errors.
If you’re building on 32-bit windows like myself you’ll get an error about an unknown preprocessor command in objc-runtime-new.m. Apparently the Microsoft C++ compiler does not recognize the #warning directive, must be a cultural thing.
If Objective-C 2.0 can be built on 32-bit Windows I don’t know how to do it. Garbage collection is one of the staple features of Objective-C 2.0 (properties and fast enumeration are just syntactic sugar) and if it’s not supported, is there really any point of building it? Therefore we can firmly conclude that Safari for Windows is not using Objective-C 2.0, at least the 32-bit version anyway.
Matthieu Cormier has been developing and releasing Macintosh software since 2003. He is pursuing a full-time career as a Macintosh developer but keeps getting roped into bland web-based enterprise projects.