Rust Away Mac OS

I recently had to cross-compile my Rust app from macOS to Linux. The process wasn't too hard, but the documentation wasn't as easy to find as I'd like it to be. Hence, I'm documenting the process here.

First, add the appropriate target to your Rust toolchain:

$ rustup target add x86_64-unknown-linux-musl

Rust’s world is harsh. The environment is not kind. Bears and wolves will chase and kill you. Falling from a height will kill you. Being exposed to radiation for an extended period will kill you. Starving will kill you. Being cold will kill you. Other players can find you, kill you, and take your stuff. Fortunately for you, you can kill others and take their stuff. Or maybe you can make. As you can see above, Containers make use of your Host OS and its kernel, and therefore are 'closer to the iron'. For example, in order for a Container to read/write from your Host OS hard drive, it has to: Mount the disk on the Container natively (i.e. It has direct access to the disk on the Host OS thanks to the kernel).

Rust Away Mac Os Catalina

  • Calling all my mac bois, how good does rust play on your mac? Question I have a late 2013 iMac with a 2.9 GHz Intel Core i5 processor, 8Gb of RAM and the NVIDIA GeForce GT 750M 1024 MB, I got rust for free from a friend and I played once on Mediocre graphics and it ran poorly, like 15 fps or so, wondering how it is for the others out there.
  • Toolchain management with rustup. Rust is installed and managed by the rustup tool. Rust has a 6-week rapid release process and supports a great number of platforms, so there are many builds of Rust available at any time. Rustup manages these builds in a consistent way on every platform that Rust supports, enabling installation of Rust from the beta and nightly release channels as well as.
  • I have a rust project including paho-mqtt = '0.7.1' it's building on Linux but when I try to build it on my Macbook (OS 10.15.5) I receive the following error: debug:Target: x8664-apple-darwin.

Now you need to install a linker like musl-cross which you can do with Homebrew:

$ brew install filosottile/musl-cross/musl-cross

Now that you have added the target and installed the linker, make sure your ~/.cargo/config has the following info in it:

Once have completed the previous steps, you should be able to cross-compile your Rust app from macOS to Linux with the following command:

$ cargo build --release --target x86_64-unknown-linux-musl

Personally, I find the previous command too long to remember, and I don't feel like having a Makefile in my apps for it. After reading about how to extend Cargo with new subcommands, I wanted to make a linux subcommand that would do the cross-compilation for me.

For this to work, you need to create a file named cargo-linux and make sure it is in your $PATH. I ended up placing my file in ~/.cargo/bin/ which is already in my $PATH. The file has the following contents:

After doing this, I can now just run cargo linux in the main directory of my Rust projects to cross-compile to Linux.

[Back to top]

First, we have to install Xcode and then set up Xcode build tools. If you already have the build tools installed and they are up to date, you can skip this step.

Next, we need to ensure that Rust is installed and that we can cross compile to the iOS architectures. For this we will be using rustup. If you already have rustup installed, you can skip this step. Rustup installs Rust from the official release channels and enables you to easily switch between different release versions. It will be useful to you for all your future Rust development, not just here.

Add the iOS architectures to rustup so we can use them during cross compilation.

When you installed Rust, it also installed cargo, which is a package manager similar to pip, gems etc. Now we will use cargo to install cargo-lipo. This is a cargo subcommand which automatically creates a universal library for use with iOS. Without this crate, cross compiling Rust to work on iOS is infinitely harder.

Now we’re all setup and we’re ready to start. Let’s create the project directory.

cargo new cargo sets up a brand new Rust project with its default files and directories in a directory called cargo. In this directory is a file called Cargo.toml, which is the package manager descriptor file, and there is be a subdirectory, src, which contains a file called lib.rs. This will contain the Rust code that we will be executing.

Our Rust project here is a super simple Hello World library. It contains a function rust_greeting that takes a string argument and return a string greeting that argument. Therefore, if the argument is “world”, the returned string is “Hello world”.

Open cargo/src/lib.rs and enter the following code.

Let’s take a look at what is going on here.

As we will be calling this library from non-Rust code, we will actually be calling it through a C bridge.#[no_mangle] tells the compiler not to mangle the function name as it usually does by default, ensuring our function name is exported as if it had been written in C.

extern tells the Rust compiler that this function will be called from outside of Rust and to therefore ensure that it is compiled using C calling conventions.

The string that rust_greeting accepts is a pointer to a C char array. We have to then convert the string from a C string to a Rust str. First we create a CStr object from the pointer. We then convert it to a str and check the result. If an error has occurred, then no arg was provided and we substitute there, otherwise we use the value of the provided string. We then append the provided string on the end of our greeting string to create our return string. The return string is then converted into a CString and passed back into C code.

Using CString and returning the raw representation keeps the string in memory and prevents it from being released at the end of the function. If the memory were to be released, the pointer provided back to the caller would now be pointing to empty memory - or possibly something else entirely. But, by ensuring that the string sticks around after the function has finished executing, we have memory allocated and no longer any handle to it. That is a recipe for a memory leak, so we have to provide a second function rust_greeting_free that takes a pointer to a C string and frees that memory. We will have to remember to call rust_greeting_free from our iOS code to ensure we don’t run into problems.

We also need to create our C bridge. Create a new file in cargo/src called greetings.h. Inside that file, let’s define what our C interface will look like. We need to ensure that every rust function that we want to call from iOS is defined here.

Let’s build our code to make sure it works. In order to do this we have to complete our Cargo.toml file. This will tell cargo to create a static library and C dynamic library for our code.

We need to build our library against the iOS architectures using cargo-lipo. The built artifacts of will be placed in cargo/target/. The universal iOS library that we are interested in can be found in cargo/target/universal/release/libgreetings.a.

And that’s it for our Rust library. Let’s get it linked with an iOS project.

Open Xcode and create a new project. Go to FileNewProject… and select the iOSApplicationSingle View Application template. This template is as close to a default iOS app as it gets. Click Next.

Call the project Greetings, make it a Swift project. Click Next to choose the location. We are using the ios directory that we created earlier. If you choose another location you will have to amend some of the paths that we set later. Click Create.

Select the Greetings project from the project navigator, and then ensure the Greetings target is selected. Open the General tab. Scroll down to the Linked Frameworks and Libraries section.

Import your libgreetings.a library by either dragging it in from finder, or clicking the + at the bottom of the list, clicking ‘Add other…’ and navigating to cargo/target/universal/release/. Select libgreetings.a and then click Open.

Link libresolv.tbd. Click the + at the bottom of the Linked Frameworks list and type libresolv into the search box. Select libresolv.tbd and then “Add”.

iOS will need a bridging header to access the C header we created. First, let’s import greetings.h into our Xcode project so we can link to it. Go to FileAdd files to 'Greetings'... Navigate to greetings.h and select Add.

To create our bridging header, go to FileNewFile.... Select iOSSourceHeader File from the provided options and select Next. Name the file Greetings-Bridging-Header.h and select Create.

Open the bridging header and amend it to look like the following:

We need to tell Xcode about the bridging header. Select the Greetings project from the project navigator, and then ensure the Greetings target is selected and open the Build Settings tab. Set the Objective-C Bridging Header option value to $(PROJECT_DIR)/Greetings/Greetings-Bridging-Header.h

We also need to tell Xcode where to look for our libraries for linking. In the same Build Settings pane, amend the Library Search Paths option value to $(PROJECT_DIR)/../../cargo/target/universal/release

Build your xcode project and everything should compile.

Rust away mac os x

Rust Away Mac Os Update

So, now we have imported our Rust library into our iOS project and successfully linked to it. But we still need to call it. go to FileNewFile.... Select iOSSourceSwift File from the provided options and select Next. Name name it RustGreetings.

Add the following code:

This creates a new class that we will use as an interface to call into our Rust library. This will allow us to abstract the nuance away from the main code of the app, including conversion from a C String to a Swift String and ensures that we won’t forget to call our free function and inadvertently introduce a memory leak!

Open ViewController.swift. Inside the viewDidLoad function add the following code:

Now build your project and run. The simulator will open and start running your app. When the view loads, “Hello world” will be output in the console window inside Xcode.

Mac Os Mojave

You can find the code for this on Github

[Back to top]