I am going through the Matasano Crypto Challenges right now. Since the challenges seem to be one-input-one-output, question-answer oriented, I’ve setup GNU Autotools to build a binary for each challenge:

mario@bobthebuilder ~ $ ./s1c1 SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t

Recently, however, I have come across Meson, the new-and-improved C/C++ build system. So, I thought I’d describe my experience installing Meson, and building my (albeit simple) project.

Installing Meson

The download page indicates that I might need a dependency, Ninja:

mario@bobthebuilder ~ $ sudo apt-get update mario@bobthebuilder ~ $ sudo apt-get install ninja-build

Installing Meson turned out to be easier than I thought it would be. Although the download page doesn’t mention it, Meson is on PyPi!

mario@bobthebuilder ~ $ virtualenv -p ` which python3 ` meson Running virtualenv with interpreter /usr/bin/python3 Using base prefix '/usr' New python executable in /home/mario/meson/bin/python3 Also creating executable in /home/mario/meson/bin/python Installing setuptools, pip, wheel...done. mario@bobthebuilder ~ $ cd meson/ mario@bobthebuilder ~/meson $ . bin/activate ( meson ) mario@bobthebuilder ~/meson $ pip3 install meson Collecting meson Downloading meson-0.31.0.tar.gz ( 465kB ) 100% |████████████████████████████████| 471kB 1.7MB/s Building wheels for collected packages: meson Running setup.py bdist_wheel for meson ... done Stored in directory: /home/mario/.cache/pip/wheels/9a/53/3f/56f0fedfbc336d525154c8eedd637f7d980bd594ebd01d254a Successfully built meson Installing collected packages: meson Successfully installed meson-0.31.0 ( meson ) mario@bobthebuilder ~/meson $ meson.py --version 0.31.0

Running Meson

My directory layout is pretty simple and flat:

( meson ) mario@bobthebuilder ~/meson/src $ tree . ├── base64.c ├── base64.h ├── hex.c ├── hex.h ├── math.c ├── math.h ├── meson.build ├── s1c1.c ├── s1c1.h ├── s1c2.c ├── s1c3.c ├── xor.c └── xor.h 0 directories, 13 files

Notice that my meson.build file is also very small and simple, since I’m only testing this out on one executable right now:

( meson ) mario@bobthebuilder ~/meson/src $ cat meson.build project ( 'matasano' , 'c' ) executable ( 's1c1' , 's1c1.c' )

Now, all I do is create a build directory and run meson.py on it:

( meson ) mario@bobthebuilder ~/meson/src $ mkdir build ( meson ) mario@bobthebuilder ~/meson/src $ meson.py build The Meson build system Version: 0.31.0 Source dir : /home/mario/meson/src Build dir : /home/mario/meson/src/build Build type : native build Build machine cpu family: x86_64 Build machine cpu: x86_64 Project name: matasano Native c compiler: cc ( gcc 4.8.4-2ubuntu1 ) Build targets in project: 1 ninja: fatal: ninja version ( 1.3.4 ) incompatible with build file ninja_required_version version ( 1.5.1 ) . Traceback ( most recent call last ) : File "/home/mario/meson/lib/python3.4/site-packages/mesonbuild/mesonmain.py" , line 254, in run app.generate () File "/home/mario/meson/lib/python3.4/site-packages/mesonbuild/mesonmain.py" , line 158, in generate g.generate ( intr ) File "/home/mario/meson/lib/python3.4/site-packages/mesonbuild/backend/ninjabackend.py" , line 196, in generate self.generate_compdb () File "/home/mario/meson/lib/python3.4/site-packages/mesonbuild/backend/ninjabackend.py" , line 202, in generate_compdb jsondb = subprocess.check_output ([ ninja_exe, '-t' , 'compdb' , 'c_COMPILER' , 'cpp_COMPILER' ] , cwd = builddir ) File "/usr/lib/python3.4/subprocess.py" , line 620, in check_output raise CalledProcessError ( retcode, process.args, output = output ) subprocess.CalledProcessError: Command '[' ninja ', ' -t ', ' compdb ', ' c_COMPILER ', ' cpp_COMPILER ']' returned non-zero exit status 1

Well, that’s a bummer. The error message is pretty clear, though: I need a newer version of Ninja, at least v1.5.1. After uninstalling Ninja through apt-get , I downloaded the Ninja binary directly from the GitHub release page:

( meson ) mario@bobthebuilder ~ $ wget https://github.com/ninja-build/ninja/releases/download/v1.7.1/ninja-linux.zip --2016-05-02 18:50:34-- https://github.com/ninja-build/ninja/releases/download/v1.7.1/ninja-linux.zip # ... HTTP request sent, awaiting response... 200 OK Length: 74111 ( 72K ) [ application/octet-stream] Saving to: ‘ninja-linux.zip’ 100%[ ======================================>] 74,111 392KB/s in 0.2s 2016-05-02 18:50:34 ( 392 KB/s ) - ‘ninja-linux.zip’ saved [ 74111/74111] ( meson ) mario@bobthebuilder ~ $ unzip -l ninja-linux.zip Archive: ninja-linux.zip Length Date Time Name --------- ---------- ----- ---- 175240 2016-04-28 10:39 ninja --------- ------- 175240 1 file ( meson ) mario@bobthebuilder ~ $ unzip ninja-linux.zip Archive: ninja-linux.zip inflating: ninja ( meson ) mario@bobthebuilder ~ $ sudo mv ninja /usr/local/bin/ ( meson ) mario@bobthebuilder ~ $ which ninja /usr/local/bin/ninja ( meson ) mario@bobthebuilder ~ $ ninja --version 1.7.1

Once I installed Ninja 1.7.1, I re-ran the meson.py build command, but that “failed.” Evidently, since I already ran Meson to produce a Ninja build file, I no longer needed to run Meson again. Meson will “recompile” itself. Or rather, it will self-regenerate the Ninja build file, after editing meson.build. Neat! So, I ran ninja :

( meson ) mario@bobthebuilder ~/meson/src/build $ ninja [ 1/2] Compiling c object 's1c1@exe/s1c1.c.o' FAILED: s1c1@exe/s1c1.c.o cc '-Is1c1@exe' '-I..' '-I.' '-pipe' '-Wall' '-Winvalid-pch' '-O0' '-g' '-MMD' '-MQ' 's1c1@exe/s1c1.c.o' '-MF' 's1c1@exe/s1c1.c.o.d' -o 's1c1@exe/s1c1.c.o' -c ../s1c1.c In file included from ../s1c1.c:4:0: ../hex.h:6:39: error: expected ‘ ; ’, ‘,’ or ‘ ) ’ before ‘hex’ size_t hextobin ( const char * restrict hex, uint8_t * restrict bin ) ; ^ ../hex.h:8:40: error: expected ‘ ; ’, ‘,’ or ‘ ) ’ before ‘bin’ void bintohex ( const uint8_t * restrict bin, const size_t size, ^ In file included from ../s1c1.c:5:0: ../base64.h:6:42: error: expected ‘ ; ’, ‘,’ or ‘ ) ’ before ‘base64’ size_t base64tobin ( const char * restrict base64 , uint8_t * restrict bin ) ; ^ ../base64.h:8:43: error: expected ‘ ; ’, ‘,’ or ‘ ) ’ before ‘bin’ void bintobase64 ( const uint8_t * restrict bin, const size_t size, ^ In file included from ../s1c1.c:6:0: ../s1c1.h:3:40: error: expected ‘ ; ’, ‘,’ or ‘ ) ’ before ‘hex’ void hextobase64 ( const char * restrict hex, char * restrict base64 ) ; ^ ../s1c1.c:8:40: error: expected ‘ ; ’, ‘,’ or ‘ ) ’ before ‘hex’ void hextobase64 ( const char * restrict hex, char * restrict base64 ) { ^ ../s1c1.c: In function ‘main’: ../s1c1.c:19:3: warning: implicit declaration of function ‘hextobase64’ [ -Wimplicit-function-declaration ] hextobase64 ( hex, base64 ) ; ^ ../s1c1.c:21:1: warning: control reaches end of non-void function [ -Wreturn-type ] } ^ ninja: build stopped: subcommand failed.

If you examine this closely, you’ll see I’ve got two general errors in compiling s1c1.c.

First, $CC seems to be choking on restrict , which means I’ve got to set the language standard somewhere. The only reference I could find of that is in Adding Arguments, but only a small blurb:

Note that for setting the C/C++ language standard (the -std=c99 argument in Gcc), you would probably want to use a default option of the project() function.

That wasn’t much to go on, especially since I couldn’t find an API for project() . I initially tried changing the project() line of meson.build to something like, project('matasano', 'c11') , but Meson did not like that. After a few minutes, I gave up and cheated:

( meson ) mario@bobthebuilder ~/meson/src $ cat meson.build project ( 'matasano' , 'c' ) add_global_arguments ( '-std=gnu11' , language : 'c' ) executable ( 's1c1' , 's1c1.c' )

Second, $CC wasn’t finding my other source files or header files (probably because I didn’t tell it about them). After a few trials (and errors), I figured it out:

( meson ) mario@bobthebuilder ~/meson/src $ cat meson.build project ( 'matasano' , 'c' ) add_global_arguments ( '-std=gnu11' , language : 'c' ) src = include_directories ( '.' ) executable ( 's1c1' , [ 's1c1.c' , 'hex.c' , 'base64.c' ] , include_directories : src )

Recall that I keep my header files and source files together, under src/. That’s probably not best practice, but whatever. I had to tell Meson about those header files, by “include”-ing . (the src directory). Then, I listed all of the .c source files as sources of the s1c1 executable. Now, my project compiles:

( meson ) mario@bobthebuilder ~/meson/src/build $ ninja [ 0/1] Regenerating build files The Meson build system Version: 0.31.0 Source dir : /home/mario/meson/src Build dir : /home/mario/meson/src/build Build type : native build Build machine cpu family: x86_64 Build machine cpu: x86_64 Project name: matasano Native c compiler: cc ( gcc 4.8.4-2ubuntu1 ) Build targets in project: 1 [ 2/4] Compiling c object 's1c1@exe/hex.c.o' ../hex.c: In function ‘hexord’: ../hex.c:27:3: warning: array subscript has type ‘char’ [ -Wchar-subscripts ] return lookup[c] ; ^ [ 3/4] Compiling c object 's1c1@exe/base64.c.o' ../base64.c: In function ‘base64ord’: ../base64.c:21:3: warning: array subscript has type ‘char’ [ -Wchar-subscripts ] return lookup[c] ; ^ [ 4/4] Linking target s1c1

SUCCESS!

Conclusion

All in all, it wasn’t a bad experience. I’ve had far worse compiling C before, as–I’m sure–many have. And, truthfully, it was far simpler than first learning GNU Autotools (trying to figure out how autoheader and autoscan and autoconf work together, or whether they all work together). I would have run into one less problem had Ninja been up-to-date on the Debian/Ubuntu repositories, but that wasn’t a major problem, nor the fault of the good people behind Meson + Ninja, as far as I could tell.

This is my first (of many) blog posts. If you’ve got questions, comments, or constructive criticisms, leave them in the comments or contact me.