Loud ramblings of a Software Artisan

Tuesday 13 December 2016

Rust and Automake - Part 2

I already talked about Rust and Automake. This is a follow-up.

One of the caveat of the previous implementation is that it doesn't pass make distcheck as it doesn't work when srcdir != builddir.

The main problem is that we need to tell cargo to build in a different directory. Fortunately Cargo PR #1657 has taken care of this. So all we need is a carefully crafted CARGO_TARGET_DIR environment variable to set before calling cargo. You can substitute abs_top_builddir for the top build directory.

But let's show the patch. Nothing complicated.

Sunday 6 November 2016

Introducing GPS Ami

Once upon a time, I started geotagging my photos. For that I bought a GPS logger, an Holux M-1200E. The device works great with gpsbabel, and since my photography workflow was stuck on MacOS, I used Houdah GPS (which uses gpsbabel behind the scene, BTW). Also I have been working for too long on moving that workflow to Linux and GNOME. At one point I even started to write an app I called "Magellan" to do what that MacOS tool did, as a part of my other project, Niepce. I didn't really get motivated so it went nowhere. It was written in C++ like the rest of Niepce. The technology isn't the problem here.

Fast forward, my photography machine got upgraded to a more recent version of its operating system after the one I was using was abandonned by browser vendors, and it happens that in that upgrade, the GPS logger stopped working because MacOS 10.11 stopped providing the USB->Serial driver needed. I could install some random driver, but given how much trust I have, I decided to pass. On Linux, it still works.

I had already started rewriting Magellan in Rust using gtk-rs ; I did that as just another Rust learning project. This breakage came right as a good motivator to actually push the development of that application and make it work. And it does.

The name "Magellan" was already used for some GPS related product (not surprising), so my app became GPS Ami ("Ami" means "friend" in French).

The design

I basically reimplemented Houdah GPS, UI and such. It works OK, but I think it will act as a transitionary state. I have bigger plans.

Notably, I want to allow a better control of the device, like what bt747 can do - my logger is based on that chipset, and other automation feature so I can use GPS Ami from Niepce to download the tracks. I currently only tested with the device I have.

The implementation

As explained before GPS Ami is written in Rust and uses gtk-rs for the UI. I have to be honest, gtk-rs is not ready for prime-time, but it looks very promising and I'm very happy to be able to contribute as needed. Not surprising, but you should be ready to have to put your hands in it if you want to use it. I did just that: provided more APIs, filed some bugs, sometime fixing them. I also had to implement gudev-rs to be able to have gudev functionnality for device hotplug — to plug into the mainloop. This was a learning experience.

Rust tooling is a lot about generating a monolithic binary, without data files. This is not bad per see, but when you need data files like .ui from glade to load the UI in Gtk (albeit this not required on Gtk side, it is more convenient), you are a bit stuck. Fortunately there is the includestr!() macro in Rust which mean "load this file at compile time and put it in this string".

Another problem I had was installing the rest of the files, like the .desktop or icons, problem I solved by wrapping cargo into an automake build system.

On overall, I'm just calling gpsbabel from a UI to download file.

The future

So what should come into the future?

  • polish the UI more, possibly tweaking it. This will probably require more gtk-rs work. IMHO there are a few show stopper for a first version.
  • implement a driver for this GPS, in Rust so that the actual driver be written in memory safe language.
  • see about adding more control feature: we should be able to read the GPS values like it is done in bt747.
  • support other devices officially (I don't have any other though)

Help wanted

  • I don't have an icon
  • I only have one device to test with

Friday 7 October 2016

Rust and Automake

But why automake? Cargo is nice.

Yes it is. But it is also limited to build the Rust crate. It does one thing, very well, and easily.

Although I'm writing a GNOME application and this needs more than building the code. So I decided I need to wrap the build process into automake.

Let's start with Autoconf for Rust Project. This post is a great introduction to solving the problem and give an actual example on doing it even though the author just uses autoconf. I need automake too, but this is a good start.

We'll basically write a configure.ac and a Makefile.am in the top level Rust crate directory.

configure.ac:

AC_INIT([gpsami], m4_esyscmd([grep ^version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n"]), [hub@figuiere.net])
AM_INIT_AUTOMAKE([1.11 foreign no-dependencies no-dist-gzip dist-xz subdir-objects])

Let's init autoconf and automake. We use the options: foreign to not require all the GNU files, no-dependencies because we don't have dependency tracking done by make (cargo do that for us) and subdir-objects because we have one Makefile.am and don't want recursive mode.

The m4_esyscmd macro is a shell command to extract the version out of the Cargo.toml.

VERSION=$(grep ^version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")

This does the same as above, but put it into VERSION

This shell command was adapted from Autoconf for Rust Project but fixed as it was being greedy and also captured the "version" strings from the dependencies.

AC_CHECK_PROG(CARGO, [cargo], [yes], [no])
AS_IF(test x$CARGO = xno,
    AC_MSG_ERROR([cargo is required])
)
AC_CHECK_PROG(RUSTC, [rustc], [yes], [no])
AS_IF(test x$RUSTC = xno,
    AC_MSG_ERROR([rustc is required])
)

Check for cargo and rustc. I'm pretty sure without rustc you don't have cargo, but better be safe than sorry. Note that this is considered a fatal error at configure time.

dnl Release build we do.
CARGO_TARGET_DIR=release
AC_SUBST(CARGO_TARGET_DIR)

This is a trick: we need the cargo target directory. We hardcode to release as that's what we want to build.

The end is pretty much standard.

So far just a few tricks.

Makefile.am

desktop_files = data/gpsami.desktop

desktopdir   = $(datadir)/applications
desktop_DATA = $(desktop_files)
ui_files = src/mgwindow.ui \
	$(null)

Just some basic declarations in the Makefile.am. The desktop file with installation target and the ui_files. Note that at the moment the ui files are not installed because we inline them in Rust.

EXTRA_DIST = Cargo.toml \
	src/devices.json  \
	src/devices.rs  \
	src/drivers.rs  \
	src/gpsbabel.rs  \
	src/main.rs \
	src/mgapplication.rs \
	src/utils.rs \
	$(ui_files) \
	$(desktop_in_files) \
	$(null)

We want to distribute the source files and the desktop files. This will get more complex when the crate grows as we'll need to add more files to here.

all-local:
	cargo build --release

clean-local:
	-cargo clean

Drive build and clean targets with cargo.

install-exec-local:
	$(MKDIR_P) $(DESTDIR)$(bindir)
	$(INSTALL) -c -m 755 target/@CARGO_TARGET_DIR@/gpsami $(DESTDIR)$(bindir)

We have to install the binary by hand. That's one of the drawback of cargo.

We this, we do

$ autoreconf -si
$ ./configure
$ make
# make install

This build in release and install it in the prefix. You can even make dist, which is another of the reason why I wanted to do that.

Caveats: I know this will not work if we build in a different directory than the source directory. make distcheck fails for that reason.

I'm sure there are ways to improve this, and I will probably, but I wanted to give a recipe for something I wanted to do.

Wednesday 28 September 2016

Introducing gudev-rs

A couple of weeks ago, I released gudev-rs, Rust wrappers for gudev. The goal was to be able to receive events from udev into a Gtk application written in Rust. I had a need for it, so I made it and shared it.

It is mostly auto-generated using gir-rs from the gtk-rs project. The license is MIT.

Source code

If you have problems, suggestion, patches, please feel free to submit them.

Monday 21 March 2016

Rust...

Over the last holidays I plunged and started learning Rust in a practical way. Coming from a C++ background, and having a strong dislike of the whole concept of checking the correctness at runtime, like in, say, JavaScript, Rust is really promising.

With Rust, you get:

  • raw performance since it is compiled to native code, and no garbage collection to introduce a pause.
  • RAII (Resource Acquisition Is Initialisation) that allow a clear release of resources when going out of scope, one of the major feature I like in C++.
  • Strong typing, with inference (C++ finally got the auto keyword for that purpose) the right balance between over declaration and none.
  • Ownership strictly enforced, and this is where C++ lacks: the compiler strictly enforce ownership of data, making "moveable" and "immutable" the defaults, enforcing lifetime of reference (no pointers !).
  • Relative easiness to interface foreign function (like C) with Rust, offering clear unsafe code blocks.
  • Proper tooling for dependency management, building, documentation and built-in test support.
  • A clean macro syntax, unlike the C preprocessor.
  • Concurrent programming built into a the standard library and language.

Rust is not an object oriented language. Since it doesn't have inheritance it can only do polymorphism through traits, and it has generic types. Just a design choice that force us to rethink a bit and this is checked by the compiler. And many of these design choices are here to write safer code, code less subject to causing security issues like we find everyday lately, like very recently in libotr, git and a few others.

In short, I really see myself doing more stuff with Rust.