Remote Compilation Using SSH and Make Issue 21

#### "Linux Gazette...making Linux just a little more fun!"

## Remote Compilation Using SSH and Make

#### By John R. Daily, jdaily@bbn.com

### Problem

Occasionally I use my Linux machine at home to write code that I intend to compile on a remote machine.

While maintaining open `ftp` and `telnet` connections to the remote machine to handle the transfer and compilation steps is a manageable solution, I decided to explore `ssh` and `make` to develop a more automated method.

The benefits of my solution:

- No need to remember which files have been modified.
- Ability to use Emacs' compilation capabilities to move to errors in the source.
- As mentioned above, no need to use `ftp` and `telnet`, and hence no benefit to keep an open dialup connection when not compiling.
- Automate, automate, automate. Laziness is a virtue.

### Overview of Solution

My first step was to set up `ssh` and related tools in order to allow secure copying of files to my remote system. While setting up a `.rhosts` file is a (barely) acceptable solution, my IP address and name is different each time I dial in, and it would be rather awkward to change the remote system's `.rhosts` file each time I dialed in.

`ssh` allows me to use a much more secure form of authentication to copy files and execute remote commands.

Once I had `ssh` behaving properly, I used Emacs' `info` facility to explore implicit rules in makefiles, and wrote a simple makefile to handle the file transfers and remote compilation.

As an example of the intended effect, assume my remote machine is called "remote" (and my local machine "local"), and I've just modified a source file called `daemon.c`. I would like to execute the following commands in an automated fashion (note that `scp` is a secure copy command packaged with `ssh`, and that the `-C` option specifies compression, useful for dialup connections):

    scp -C daemon.c jdaily@remote:~/source-directory
    ssh -C remote "cd source-directory && make"

### Implementation

First, I needed `sshd` running on the remote system to handle my secure connections. Fortunately, `sshd` was already running on the remote system in question, but according to the man pages, it can be run as any user, and is restricted to handling connections for that user (which should be quite sufficient for our needs).

Then, I needed to install the `ssh` toolset on my local machine. Again, ideally these would be installed in a public binary directory such as `/usr/local/bin`, but any user can install them in his/her home directory.

I also wanted a key which would allow me to authenticate myself between systems, and which would eliminate the need to type my password each time I tried to run one of the `ssh` commands. For this, I just ran `ssh-keygen`, and made sure to not give a pass phrase, so that none would be needed to use my private key to establish the connection.

    [jdaily@local ~]$ ssh-keygen
    Initializing random number generator...
    Generating p:  ............++ (distance 186)
    Generating q:  ......................................++ (distance 498)
    Computing the keys...
    Testing the keys...
    Key generation complete.
    Enter file in which to save the key (/home/jdaily/.ssh/identity): <CR>
    Enter passphrase: <CR>
    Enter the same passphrase again: <CR>
    Your identification has been saved in /home/jdaily/.ssh/identity.
    Your public key is:
    1024 35 718535638573954[...] jdaily@local
    Your public key has been saved in /home/jdaily/.ssh/identity.pub

Once I had a public key, I used `scp` to copy it to the remote machine.

    [jdaily@local ~]$ scp -C ~/.ssh/identity.pub jdaily@remote:~/.ssh/key
    jdaily's password: <entered my remote password>

Then I logged into the remote host and copied the key file into `~/.ssh/authorized_hosts`. If that file already existed, I would have appended the key file.

Following all this, I could run `ssh` and `scp` without needing either a password or a pass phrase to connect to `remote`.

Now I needed a makefile to automate my system. Ideally, the files on the remote machine would be checked to see if they were older than the files on my local machine, and if so, they would be copied over. To simplify matters, I decided to keep a record of the "last transferred date" for each file by touching a corresponding file each time I copied a source file over.

As an example, when I transferred a newer copy of `daemon.c` over, I touched `daemon.ct` in the same directory. Any transfer of a `.h` file would be marked by the creation of a file with a `.ht` suffix.

After poking around the `info` file for `make`, I came up with the following makefile.

    TRANSFER=scp
    REXEC=ssh
    SSHFLAGS=-C # Compress data
    REMOTE=jdaily@remote:~/source-directory
    FILES=debug.ht messages.ht client.ct daemon.ct queue.ct queue.ht

    %.ht : %.h
    	$(TRANSFER) $(SSHFLAGS) $< $(REMOTE)
    	touch $@

    %.ct : %.c
    	$(TRANSFER) $(SSHFLAGS) $< $(REMOTE)
    	touch $@

    all-done: $(FILES)
    	$(REXEC) $(SSHFLAGS) remote "cd source-directory && make"
    	touch all-done

This had one limitation in particular; I was unable to specify command-line arguments for `make` on the remote machine without writing them directly into the makefile on my local system. While this was fine for the current application, I decided to generalize it by creating a `run-make` shell script, which would handle the remote execution of make after calling make on the local system.

Here is my `run-make` shell script:

    #!/bin/sh

    make
    echo ssh -C remote \"cd source-directory \&\& make $*\"
    ssh -C remote "cd source-directory && make $*"

I then removed the line from my makefile which remotely ran `make`.

Here's the output from a successful compilation sequence.

    cd ~/source-directory/
    ./run-make
    scp -C debug.h jdaily@remote:~/source-directory
    touch debug.ht
    scp -C messages.h jdaily@remote:~/source-directory
    touch messages.ht
    scp -C client.c jdaily@remote:~/source-directory
    touch client.ct
    scp -C daemon.c jdaily@remote:~/source-directory
    touch daemon.ct
    scp -C queue.c jdaily@remote:~/source-directory
    touch queue.ct
    scp -C queue.h jdaily@remote:~/source-directory
    touch queue.ht
    touch all-done
    ssh -C remote "cd source-directory && make "
    gcc -Wall -Wstrict-prototypes -Wmissing-prototypes -g -c queue.c
    gcc -Wall -Wstrict-prototypes -Wmissing-prototypes -g -DPORT=3000 -o daemon daemon.c queue.o -lsocket -lthread
    gcc -Wall -Wstrict-prototypes -Wmissing-prototypes -g -DPORT=3000 -o client client.c -lsocket

    Compilation finished at Sat Aug  9 01:22:19

### Tools

`ssh` is a secure replacement for such tools as `rsh`, `rlogin`, and `rcp`. It can be found at http://www.cs.hut.fi/ssh.

`make` is a standard Unix utility. GNU's `make` comes with most, if not all, Linux distributions.

##### Copyright © 1997, John R. Daily
Published in Issue 21 of the Linux Gazette, September 1997