I haven't really thought through where this blog post is going, but it will probably just be a duplicate of 2,000 others out there (including the official documentation) which say "get started with Rust by …". But this is my living memory of doing it.
I am working from The Rust Programming Language (2nd Edition), which I will hereafter refer to as "the book".
I've just acquired a new Linux box, and apart from the obvious setup that you always need to do on a new box (installing Chrome, logging in, etc [on this occasion installing a new kernel to get the WiFi to work]), I've not doing anything with it yet. So the first thing I'm going to do is install Rust.
According to the book, I need to type this:
% curl --proto '=https' --tlsv1.3 https://sh.rustup.rs -sSf | shI have never used the --proto option before, but both that and the --tlsv1.3 options seem like overkill to me. Maybe they offer some level of protection against MITM attacks; I don't know. Curl usually seems pretty good to me about mismatched certificates and the like. Before passing it directly to sh, I did decide to look at the script. Hmm, pretty hairy.
So, let's go and run it.
info: downloading installerI'd been advised that it may ask for my password, but actually the first thing it does is ask if I want to proceed, customize or cancel the installation. I presume I want to proceed since I don't know (yet) what customizations I might want and cancelling seems somewhat self-defeating.
Welcome to Rust!
This will download and install the official compiler for the Rust
programming language, and its package manager, Cargo.
Rustup metadata and toolchains will be installed into the Rustup
home directory, located at:
/home/gareth/.rustup
This can be modified with the RUSTUP_HOME environment variable.
The Cargo home directory is located at:
/home/gareth/.cargo
This can be modified with the CARGO_HOME environment variable.
The cargo, rustc, rustup and other commands will be added to
Cargo's bin directory, located at:
/home/gareth/.cargo/bin
This path will then be added to your PATH environment variable by
modifying the profile files located at:
/home/gareth/.profile
/home/gareth/.bashrc
You can uninstall at any time with rustup self uninstall and
these changes will be reverted.
Current installation options:
default host triple: x86_64-unknown-linux-gnu
default toolchain: stable (default)
profile: default
modify PATH variable: yes
1) Proceed with installation (default)
2) Customize installation
3) Cancel installation
It then goes off and downloads a lot of things
>1Very good, so let's run the .cargo/env script ... seems fine.
info: profile set to 'default'
info: default host triple is x86_64-unknown-linux-gnu
info: syncing channel updates for 'stable-x86_64-unknown-linux-gnu'
info: latest update on 2023-10-05, rust version 1.73.0 (cc66ad468 2023-10-03)
info: downloading component 'cargo'
7.8 MiB / 7.8 MiB (100 %) 2.5 MiB/s in 2s ETA: 0s
info: downloading component 'clippy'
info: downloading component 'rust-docs'
13.8 MiB / 13.8 MiB (100 %) 3.2 MiB/s in 4s ETA: 0s
info: downloading component 'rust-std'
24.7 MiB / 24.7 MiB (100 %) 3.8 MiB/s in 7s ETA: 0s
info: downloading component 'rustc'
61.6 MiB / 61.6 MiB (100 %) 3.4 MiB/s in 21s ETA: 0s
info: downloading component 'rustfmt'
info: installing component 'cargo'
info: installing component 'clippy'
info: installing component 'rust-docs'
13.8 MiB / 13.8 MiB (100 %) 6.5 MiB/s in 1s ETA: 0s
info: installing component 'rust-std'
24.7 MiB / 24.7 MiB (100 %) 12.4 MiB/s in 1s ETA: 0s
info: installing component 'rustc'
61.6 MiB / 61.6 MiB (100 %) 14.2 MiB/s in 4s ETA: 0s
info: installing component 'rustfmt'
info: default toolchain set to 'stable-x86_64-unknown-linux-gnu'
stable-x86_64-unknown-linux-gnu installed - rustc 1.73.0 (cc66ad468 2023-10-03)
Rust is installed now. Great!
To get started you may need to restart your current shell.
This would reload your PATH environment variable to include
Cargo's bin directory ($HOME/.cargo/bin).
To configure your current shell, run:
source "$HOME/.cargo/env"
We can now check that we have a rust compiler installed.
$ rustc --versionOK, so let's try creating a project. I tend to put personal projects in a "Projects" directory, but that isn't there yet because (did I mention this?) this is a brand new machine.
rustc 1.73.0 (cc66ad468 2023-10-03)
$ mkdir ProjectsSo far, so good. We have a new project with a git repo, a cargo file and a main source file. We should be able to compile this, right?
$ cd Projects
$ cargo new hello_rust
Created binary (application) `hello_rust` package
$ find hello_rust -type f -name \* -print -o -name .git -prune
hello_rust/Cargo.toml
hello_rust/src/main.rs
hello_rust/.gitignore
$ cd hello_rust/I'm not wild about chatty compilers. Get on with your job, I'm a client, not your manager. But otherwise, that's fine.
$ cargo build
Compiling hellorust v0.1.0 (/home/gareth/Projects/hellorust)
Finished dev [unoptimized + debuginfo] target(s) in 0.17s
$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/hello_rust`
Hello, world!
VSCode
OK. I used to develop code with just vi or Emacs (looking back, I realize that I was actually a fairly early adopter of Emacs, but it didn't seem that way at the time), but this is the 21st Century, and we use IDEs. The book suggests that I should be able to use VSCode with Rust, so let's try that.First, I need to install VSCode on this new machine. I'm not even sure how to do that on a Linux box. Is it in a repo, or do I need to download something? Going to the VisualStudio site, it offers me a .deb download, but doesn't suggest adding a repo and linking to that. So I will download that and try and install it.
$ sudo apt install ~/Downloads/code*So that then seems to imply that it has installed the package code instead of my file, but maybe that just means it's choosing the name. Either way, it has installed the version I downloaded:
[sudo] password for gareth:
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
Note, selecting 'code' instead of '/home/gareth/Downloads/code1.83.1-1696982868amd64.deb'
The following NEW packages will be installed
code
0 to upgrade, 1 to newly install, 0 to remove and 4 not to upgrade.
Need to get 0 B/95.7 MB of archives.
After this operation, 386 MB of additional disk space will be used.
Get:1 /home/gareth/Downloads/code1.83.1-1696982868amd64.deb code amd64 1.83.1-1696982868 [95.7 MB]
Selecting previously unselected package code.
(Reading database ... 607240 files and directories currently installed.)
Preparing to unpack .../code1.83.1-1696982868amd64.deb ...
Unpacking code (1.83.1-1696982868) ...
Setting up code (1.83.1-1696982868) ...
Processing triggers for gnome-menus (3.36.0-1ubuntu3) ...
Processing triggers for shared-mime-info (2.1-2) ...
Processing triggers for mailcap (3.70+nmu1ubuntu1) ...
Processing triggers for desktop-file-utils (0.26+mint3+victoria) ...
N: Download is performed unsandboxed as root, as file '/home/gareth/Downloads/code1.83.1-1696982868amd64.deb' couldn't be accessed by user '_apt'. - pkgAcquire::Run (13: Permission denied)
$ code --versionSo, let's start it up:
1.83.1
f1b07bd25dfad64b0167beb15359ae573aecd2cc
x64
codeAs I do this, the "Welcome" window pops up and offers me a suite of options to configure, one of which is recommended language support. Lo and behold, in there is rust-analyzer, just what I was looking for. Let me install that.
And now I can clear all that off, and open a new folder (~/Projects/hello_rust). And there it all is. I can now press C-F5 and it will compile and run, right. Uh, no. A menu pops up inviting me to "select debugger" (odd, when I wanted to run without debugging...). It does have an option to "install an extension for Rust". Let me see what I can find out.
From reading this page, it seems that what you need to do is to run the cargo build and cargo run commands in a terminal, including the integrated terminal. OK, seems lame, but yes it works.
It also says that you can install a debugger for runtime debugging called LLDB which is a good, old-fashioned binary debugger. So let's install that. We then need to enable the option "Debug: Allow breakpoints everywhere". OK, done. And add a breakpoint. Yeah, makes sense. And then use the Debug command. Now when I do this, one of the options is LLDB, which, since I've just installed it, makes a lot of sense to choose. This then complains that there isn't a target to run, but then offers to create one from my Cargo.toml file. Sounds good. And then everything works just the way I'd expect it to. C-F5 works too, and doesn't obviously put out any of the chatter about compiling and running. Maybe that's just gone in another terminal window.
And just to be absolutely sure I've understood what's happening, I'll make a change to the output and check that it comes out as expected when I run it again.
Hello there, world!Most excellent.
Cross compiling
Now, while it may seem I am just playing with Rust for the sake of it, there is a bigger picture here (to be revealed in a later post), but I want to be able to compile on my Linux box and run on a Raspberry Pi with an ARM processor. So I need to be able to cross-compile for ARM and then check if that works. I don't think that's covered in the book, so we need a different resource.First off, we apparently need to install a cross-compiling version of gcc. I think this is the one I want, and can be installed from the debian repository:
$ sudo apt install gcc-arm-linux-gnueabihfNext up, we want to install what is called a "target" in Rust-speak, that is an environment which we can compile for from our current environment. The full command for this appears to be:
$ rustup target add armv7-unknown-linux-gnueabihfAnd, lo and behold, we have a target environment in place. So can we now compile "hello, world" for this target, install it and run it successfully? Yes, I doubt it, too, but let's give it a try.
info: downloading component 'rust-std' for 'armv7-unknown-linux-gnueabihf'
info: installing component 'rust-std' for 'armv7-unknown-linux-gnueabihf'
23.8 MiB / 23.8 MiB (100 %) 13.1 MiB/s in 1s ETA: 0s
We need to add a --target option to the cargo build command to tell it to use the appropriate libraries and tools for this target environment; furthermore, we need to use the cross-compiled linker we just installed when building for this target environment. So, first we create a directory .cargo for our project, and add a file config with the following contents:
[target.armv7-unknown-linux-gnueabihf]The first line here says "use these options when you are compiling for the target armv7-unknown-linux-gnueabihf". The second line specifies a specific linker (the ARM one) that we should use for this job. Now we can run cargo from the command line:
linker = "arm-linux-gnueabihf-gcc"
$ cargo build --release --target=armv7-unknown-linux-gnueabihfAnd we have a binary file generated in an appropriate subdirectory of our project directory:
Compiling hellorust v0.1.0 (/home/gareth/Projects/hellorust)
Finished release [optimized] target(s) in 0.21s
$ file target/armv7-unknown-linux-gnueabihf/release/hello_rustPersonally, I'm a little surprised to see that a "release" build ends up with debug_info and not being stripped, but maybe I'm just not really understanding what "release" builds are in this context, or maybe that is something I can configure.
target/armv7-unknown-linux-gnueabihf/release/hellorust: ELF 32-bit LSB pie executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, BuildID[sha1]=0a43f1c8602c9423d269bee77f131676dce1236b, for GNU/Linux 3.2.0, with debuginfo, not stripped
So, now I can copy that across to my Raspberry Pi and everything will just work:
$ scp target/armv7-unknown-linux-gnueabihf/release/hellorust pi@testpi:hellorustAnd then on the Pi:
hello_rust 100% 4278KB 6.5MB/s 00:00
$ ./hello_rustThat would be a no, then. I'm not quite sure why. The file is definitely there. Is it some other file it's not able to pull in? Let's use strace to find out:
-bash: ./hello_rust: cannot execute: required file not found
$ strace ./hello_rustUh, well, whatever is going wrong is going wrong down in the kernel where we can't see it. Let's see if file has any insights to offer:
execve("./hello_rust", ["./hello_rust"], 0x7fd7eae660 / 22 vars /) = -1 ENOENT (No such file or directory)
strace: exec: No such file or directory
+++ exited with 1 +++
$ file hello_rustOK, well, that says "32-bit" and I think I'm running a 64-bit OS:
hello_rust: ELF 32-bit LSB pie executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, BuildID[sha1]=0a43f1c8602c9423d269bee77f131676dce1236b, for GNU/Linux 3.2.0, with debug_info, not stripped
$ uname -aI certainly want to be running 64-bit (who wants to develop with old stuff?) but just to be sure there's nothing weird going on let's just check, say, cat:
Linux testpi-61 6.1.0-rpi4-rpi-v8 #1 SMP PREEMPT Debian 1:6.1.54-1+rpt2 (2023-10-05) aarch64 GNU/Linux
$ file /bin/catSo, that's 64 bit, and aarch64 with a different linker and for a different Linux (3.7.0). In short, it's a different target. So, basically, I've followed some out of date instructions, but it's reasonable to assume that they were along the right lines, so let's go and find a different target and possibly a different set of development tools and wire everything up slightly differently.
/bin/cat: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, BuildID[sha1]=67c0f2beecec29f5643100ff22b6eef7af5c1ad1, for GNU/Linux 3.7.0, stripped
Looking at the official list of Rust targets it would seem that what we are most likely to want is aarch64-unknown-linux-gnu, so let's try that:
$ rustup target add aarch64-unknown-linux-gnuAnd likewise update our .cargo/config file to specify the linker for this target:
info: downloading component 'rust-std' for 'aarch64-unknown-linux-gnu'
info: installing component 'rust-std' for 'aarch64-unknown-linux-gnu'
29.9 MiB / 29.9 MiB (100 %) 12.5 MiB/s in 2s ETA: 0s
$ apt install gcc-aarch64-linux-gnu
[target.aarch64-unknown-linux-gnu]Now we can build and copy it again, being sure to update all our references to the target:
linker = "aarch64-linux-gnu-gcc"
$ cargo build --release --target=aarch64-unknown-linux-gnuAnd then on the Pi we can run it to our heart's content:
Compiling hellorust v0.1.0 (/home/gareth/Projects/hellorust)
Finished release [optimized] target(s) in 0.17s
$ scp target/aarch64-unknown-linux-gnu/release/hellorust pi@testpi:hellorust
hello_rust 100% 4751KB 5.5MB/s 00:00
$ ./hello_rustWow.
Hello there, world!
(Editor's note: this was, in fact, a lot more of a struggle than it looked, because apart from everything else, I was using a Pi that I hadn't updated in a couple of years. During my attempts to bring it up to date, it died horribly and I had to reinstall from scratch. A whole Sunday afternoon disappeared while I was doing this. Compared to that, the Rust portion was relatively trivial, even though I couldn't find a definitive reference of which targets and linkers I should use for the exact combination of hardware and software I was using. If you are using a Pi 3B+ and the "latest" version of RaspberryPiOS (64-bit), you now do!).
This code is checked in and tagged HELLO_RUST.
No comments:
Post a Comment