Autoconf for Rust Projects

Cargo is an awesome tool for managing Rust projects, it takes care of all the Rust dependencies. But to package and distribute the generated files specific to target distribution is very difficult. For example, if the generated binary to be used by sudo user then we need to copy that to /usr/sbin directory instead of /usr/bin (sbin path may change for example /usr/local/sbin ).

In this blog we will discuss about using Autoconf for Rust projects along with Cargo.

Example: Simple Web server and a systemd service file

Create a new project called myservice using,

cargo new myservice --bin

Update Cargo.toml with required Rust dependencies and other details.

name = "myservice"
version = "0.1.0"
authors = ["NAME <EMAIL>"]

iron = "*"

and add the following example code in src/ (Copied from

extern crate iron;

use iron::prelude::*;
use iron::status;

fn main() {
    fn hello_world(_: &mut Request) -> IronResult<Response> {
        Ok(Response::with((status::Ok, "Hello World!")))

    println!("On 3000");

Create a systemd unit file with the following content, we need Path of bin to add it to service file. We will take help of Autoconf to dynamically generate systemd unit file. Create a systemd unit file as (Note the @SBINDIR@ autoconf variable)


ExecReload=/bin/kill -SIGUSR2 $MAINPID


If we use @sbindir@ then it will not expand completely, introduce a new variable SBINDIR in which will expand completely with default path.

Now we will create file

AC_INIT([myservice], m4_esyscmd([grep version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n"]), [YOUR_EMAIL])

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

# Default value for sbindir

test "${prefix}" = "NONE" && prefix="${ac_default_prefix}"
test "${exec_prefix}" = "NONE" && exec_prefix='${prefix}'

# Initial Value is $exec_prefix/sbin

# Expands to $prefix/sbin
eval sbintemp=\"${sbintemp}\"
# Expands to /usr/local/sbin or /usr/sbin if --prefix is passed
eval sbintemp=\"${sbintemp}\"




and file

CWD := $(shell pwd)
TARDIR := myservice-@VERSION@
RPMBUILD := $(HOME)/rpmbuild

    cargo build

    cargo build --release

    @rm -fr ./dist
    mkdir -p ./dist/$(TARDIR)
    rsync -r --exclude .git/ --exclude dist/ --exclude target/ $(CWD)/ ./dist/$(TARDIR)
    cd ./dist/; tar -zcf $(TARDIR).tar.gz $(TARDIR);

install: build
    install -d $(DESTDIR)@SBINDIR@
    install -d ${DESTDIR}/usr/lib/systemd/system/
    install -m 755 ./target/release/myservice ${DESTDIR}@SBINDIR@/myserviced
    install -m 0644 ./myserviced.service ${DESTDIR}/usr/lib/systemd/system/myserviced.service

rpm: dist
    rm -rf $(RPMBUILD)/SOURCES/myservice*
    rm -rf $(RPMBUILD)/BUILD/myservice*
    mkdir -p $(RPMBUILD)/SOURCES
    cp ./dist/myservice-@VERSION@.tar.gz $(RPMBUILD)/SOURCES; \
    rpmbuild -ba myservice.spec

Now project will looks like,

        - Cargo.toml
            - src/

Run autoconf to generate configure file from file. Then run ./configure, it will generate following files => Makefile => myserviced.service => myservice.spec

Steps to install myservice (Source installation),

sudo make install

make install will run cargo build --release, and copies generated binary to /usr/local/sbin and systemd service file to /usr/lib/systemd/system

Binary can be installed to /usr/sbin by passing --prefix=/usr or --sbindir=/usr/sbin to configure(For example, ./configure --prefix=/usr )

myservice can now be enabled using,

sudo systemctl enable myserviced
sudo systemctl start myserviced

Bonus: Generate RPM for your package

Sample RPM spec file is available in the repo

make rpm

Generated RPM will be available in $HOME/rpmbuild/RPMS/x86_64/

rpm -qlp $HOME/rpmbuild/RPMS/x86_64/myservice-0.1.0-1.fc23.x86_64.rpm

Rust, Cargo and Autoconf Version

rustc 1.8.0 (db2939409 2016-04-11)
cargo 0.9.0-nightly (8fc3fd8 2016-02-29)
autoconf (GNU Autoconf) 2.69

Reference project is available in github

Comments !