Compiling libssh2_sftp-Cocoa-wrapper

November 22, 2012 at 09:13:33

It never ceases to astound me how developers will very generously provide source code and at the same time very shabbily and stingily fail to provide instructions for compiling or using it. I think this is probably a form of misguided intellectual snobbery, a way of saying “I’m smart, and if you’re also smart you’ll also be able to figure this out from the sparse and vague hints I’ve left lying around, and if you’re not smart you don’t deserve to use my software anyway.” A case in point is Mike Adbullah’s libssh2_sftp-Cocoa-wrapper, a set of Cocoa classes and a dynamic library for doing SFTP in a Cocoa application. This is a wonderful library of code, and I certainly couldn’t possibly do SFTP in Cocoa without it. But to get started with it, I had to spend about a week banging my head against the wall, so here, in hopes that it may assist someone else, is what I eventually discovered.

I’m using Snow Leopard and Xcode 4.2 (because I want the resulting Cocoa application to be usable on Snow Leopard, and I don’t want to work in Mountain Lion and have to fuss with backward-compatibility development issues).

Step one is to download the code from github. Do this manually in the Terminal:

$ git clone https://github.com/karelia/libssh2_sftp-Cocoa-wrapper.git

The reason for doing this manually is that the first thing they don’t tell you is that this git repository involves a submodule. So immediately after downloading the code, cd into it and download the submodule:

$ cd libssh2_sftp-Cocoa-wrapper
$ git submodule update --recursive --init

Now you’re ready to open the project in Xcode. You’ve already gotten this far in the Terminal, so you may as well perform this step in the Terminal as well:

$ open libssh2.xcodeproj

You’re now in Xcode, and you’re about to build a dynamic library, libssh2.dylib. The project is currently set to build for PowerPC as well as for Intel 32-bit and 64-bit; I don’t intend to build my application for PowerPC, though, so in the target’s Build Settings I remove ppc from both the Architectures and the Valid Architectures settings. Also, I have no intention of running on anything lower than 10.6, so I change the Mac OS X Deployment Target to Mac OS X 10.6. Now we can build the library:

  • Product > Build For > Build For Archiving

You will get nearly 200 warnings, but the build should succeed. (If it doesn’t, clean the build folder and try again.) The output is in your Derived Data folder, which will probably be a location like this: /Users/[yourUsername]/Library/Developer/Xcode/DerivedData/libssh2-[blahblah]/Build/Products/Release. So locate it now and open that Release folder in the Finder, and leave it open, showing libssh2.dylib inside it.

You’re almost ready to create a test application that uses this dynamic library, libssh2.dylib, along with some Cocoa classes for doing SFTP. But first you need to locate the files for those classes. Back in Terminal, open the folder containing the material you downloaded from github:

$ open .

A folder will open in the Finder, and you’ll see files with names that start with CK2. Keep that folder open; you’ll need it in a moment.

So here’s the situation in the Finder:

  • lissh2_sftp-Cocoa-wrapper is open, showing the CK2 files.
  • The Release folder is open, showing the built libssh2.dylib.

Very well. In Xcode, close the libssh2 project and start a new Cocoa project. Let’s call it SFTPTest. Look at the Summary page for the new project’s target. Notice that Cocoa.framework is naturally among the linked frameworks. However, you’re also going to need the Security framework. So click the Plus button beneath the linked frameworks, select Security.framework, and click Add.

You’re also going to need a copy of the dynamic library, libssh2.dylib, that you just built. So drag it from the Release folder sitting open in the Finder into the SFTPTest group in your project; before clicking Finish, be sure to check “Copy items into destination group’s folder”, and make sure that “Add to targets” is checked too. Observe that libssh2.dylib is automatically added to the list of linked frameworks for your project.

However, although the dynamic library is linked, it is not automatically copied into your built application — and you need it to be. So, in the Build Phases for your target, click Add Build Phase and create a new Copy Files build phase. Open the list of files for this Copy Files build phase, and drag libssh2.dylib from your project files list into it. Also, in the Subpath field, type “Libraries” (without the quotes) and hit Return. Thus, when your application is built, libssh2.dylib will be copied into it, into the Resources/Libraries subfolder.

However, although the dynamic library is linked, and although it will be copied into your application, it won’t automatically be found by the linker! So now you need to tell the linker where it’s going to be. In the Build Settings for your target, find the Runpath Search Paths setting, and set it to @executable_path/../Resources/Libraries. Do not screw this up! Spell everything correctly and exactly.

However, although the dynamic library is linked and will be copied into your application and will be found by the linker, your project doesn’t know how to talk to it: it has no headers for this library. So you need to find those and get them into your project. They are in the folder you downloaded from github (which, you remember, is sitting open in the Finder), in libssh2/include. Grab the include folder and drag it from the Finder into your project, into the SFTPTest group. Check “Copy items into destination group’s folder” and select “Create groups for any added folders” before you click Finish.

However, although you now have the header files, Xcode doesn’t know where to find them. So you have to tell it. In the Build Settings for your project, find the User Header Search Paths setting, and set it to “include” (without the quotes), because that’s the name of the folder you just copied into your project. Also, set Always Search User Paths to Yes (that’s crucial, so don’t forget about it).

At last you are ready to use the code from the github folder! Grab the six CK2 files in the Finder and drag them into your project, into the SFTPTest group (but not into the include group, of course). Check “Copy items into destination group’s folder” and make sure that “Add to targets” is checked, before you click Finish.

You’re now effectively ready to build and run your application, to prove that everything has gone well. Just to be realistic, add this line to the top of AppDelegate.m:

#import "CK2SFTPSession.h"

Now take a deep breath, and choose Product > Run. Your application should build and run without errors. Of course, it isn’t doing any actual SFTP yet; but the point is that the pieces of the puzzle are all in place.

One final word of advice; I wasted an entire day of frustration throwing code at the CK2SFTPSession class before I discovered that we don’t get any DNS resolution. So in constructing the URL to which you want to connect, you’re going to have to use an IP number, not a name. Here’s some sample code that shows that everything is working correctly:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    NSURL* url = [NSURL URLWithString:@"ssh://nnn.nnn.nnn.nnn"];
    // each "n" must be a digit!
    CK2SFTPSession* session = [[CK2SFTPSession alloc] initWithURL:url 
                                                         delegate:self 
                                                 startImmediately:YES];
}

- (void)SFTPSession:(CK2SFTPSession *)session 
   didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
    NSLog(@"challenge");
}

- (void)SFTPSession:(CK2SFTPSession *)session appendStringToTranscript:(NSString *)string 
           received:(BOOL)received
{
    NSLog(@"%@", string);
}

You should see this in the log:

Connecting to nnn.nnn.nnn.nnn
challenge

That’s it! You’re doing SFTP with Cocoa.

Home

This page prepared February 14, 2014 by Matt Neuburg, phd = matt at tidbits dot com, using RubyFrontier. RubyFrontier is a port, written in the Ruby language, of the Web-site-creation features of UserLand Frontier. Works just like Frontier, but written in Ruby!
Download RubyFrontier from GitHub.