Embedding Application Data in Racket Programs

Written by Dominik Joe Pantůček on July 25, 2019.

As we have started Cryptoucan™ mass-production, we found out we need more and more supporting software. Little – or slightly larger – programs for testing the device during various stages of manufacturing, software for managing the logistics data, programs for engraving or something more ordinary like tools for flashing the firmware. All these programs typically require some data which is not included in the code and we wanted to embed this data in the compiled binaries. Read on to learn more about our neat solution in our favorite programming language.

As you might remember, we are using Racket[1] programming language for most of our supporting software. Racket allows you to compile standalone binaries for various systems. For Linux[2], it is rather simple:

raco pkg exe --embed-dlls --gui --ico icon.ico main-program.rkt

The result will be a file called main-program which can be executed by the operating system. When cross-compiling[3] for Windows[4] platform, we use Racket under WINE[5] to create Portable Executable[6] files:

wine ~/.wine/drive_c/Program\ Files\ $x86$/Racket/raco.exe exe --embed-dlls --gui --ico icon.ico main-program.rkt


Of course, the result will be called main-program.exe which can – again – be directly run by the operating system.

In both cases this creates an executable binary that contains all the required program code. But what about application data? Some of the programs require PCB[7] layout files, others require images or the firmware to be flashed. Including these in the resulting program would remove the need for distributing the programs with other files in compressed archives.

Good news, in Racket you can easily program this. No, the language does not have any direct support for such embedding. It just allows you to execute regular code during the compilation stage. Actually there are more compilation stages and we want to execute our code at what is called the syntax stage. It is possible to write a function that loads given file during syntax stage and injects its contents into the code as if it was a regular string or byte string.

Our implementation is very simple – just a few lines of Racket code:

#lang racket

(require (for-syntax racket))

(let* ((lst (syntax->datum stx))
(syntax-fname (syntax-source stx))
(syntax-dir (path-only syntax-fname))
(filename (build-path syntax-dir base-name)))
(datum->syntax
stx
(with-input-from-file filename port->bytes))))


Yes, that is all. Just load the file at the syntax stage and convert it to syntax object at the same scope. Now to use it, just require this file and embed whatever files you need to embed:

#lang racket

...

It could not be any easier.

Thank you for following our quest for bringing Cryptoucan™ to you. Come back next week for our firmware testing machine with robotic hand!

References

2. Wikipedia contributors. (2019, July 20). Linux. In Wikipedia, The Free Encyclopedia. Retrieved 21:16, July 24, 2019, from https://en.wikipedia.org/w/index.php?title=Linux&oldid=907087292

3. Wikipedia contributors. (2019, July 12). Cross compiler. In Wikipedia, The Free Encyclopedia. Retrieved 21:17, July 24, 2019, from https://en.wikipedia.org/w/index.php?title=Cross_compiler&oldid=905889718

4. Wikipedia contributors. (2019, July 23). Microsoft Windows. In Wikipedia, The Free Encyclopedia. Retrieved 21:17, July 24, 2019, from https://en.wikipedia.org/w/index.php?title=Microsoft_Windows&oldid=907553153

6. Wikipedia contributors. (2019, July 9). Portable Executable. In Wikipedia, The Free Encyclopedia. Retrieved 21:18, July 24, 2019, from https://en.wikipedia.org/w/index.php?title=Portable_Executable&oldid=905533093

7. Wikipedia contributors. (2019, July 17). Printed circuit board. In Wikipedia, The Free Encyclopedia. Retrieved 21:19, July 24, 2019, from https://en.wikipedia.org/w/index.php?title=Printed_circuit_board&oldid=906675253