Max Fierke

never met a *nix system I didn't want to be friends with

So You Want to Compile Crystal on ARM64 (macOS)

Max Fierke

April 13, 2021

Mid-February, I was able to get a PR merged upstream into Crystal for aarch64-apple-darwin support (i.e. Apple Silicon) based on some great work by Romain Franceschini to get the libc bindings and a lot of the foundational stuff ready. This PR also shipped with Crystal 1.0.0, and I was hoping that the cross-compilation story would get a lot easier, so I put off writing a post on targeting arm64. Unfortunately, there's been some speed-bumps I won't get into here, but suffice to say it's not quite possible to brew install crystal via Homebrew yet or run via an official M1 binary build either.

However, I wanted to revisit the process for cross-compiling your copy of the Crystal compiler on the Apple M1 for those that are daring and not willing to wait. My hope is to keep this somewhat updated, so that as things evolve and change, eventually this'll be a pretty simple set of steps. Already, writing this in early/mid April, things have gotten a little bit easier. bdw-gc in Homebrew now ships with the necessary multi-threading patch for Crystal. In addition, I landed another PR to rewrite the Crystal segfault handler in Crystal, instead of C, making it easier to cross-compile (this will ship in 1.1.0.)

Environment preparation

Installing Homebrew(s)

We'll need two Homebrews, one for each CPU architecture, in order to cross-compile Crystal on the same machine. This is because we need to bootstrap the compiler from somewhere, and what better way than to bootstrap it from the x86_64 compiler via Rosetta 2, which we can also run on the machine.

Install x86_64 Homebrew (aka. Intel Homebrew)

Run the following to install Intel Homebrew, which we will occasionally refer to as ibrew throughout this post:

$ arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

Once this finishes, you should have a working Homebrew install in the default x86_64 prefix of /usr/local

ibrew calls specified later will be in reference to this Intel Homebrew install.

Add an alias for Intel Homebrew

In your shell or your shell's config (.bashrc, .zshrc, etc.), add an alias for ibrew:

alias ibrew="arch -x86_64 /usr/local/bin/brew"

Install arm64 Homebrew (aka. ARM Homebrew)

Run the same line again, this time removing the explicit arch call in front of it. It should run under arm64 by default, unless your Terminal is setup to run under Rosetta (Stop doing that now, if you are using your Terminal in Rosetta mode. It'll make the rest so much harder/impossible, and I cannot help you.)

$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

This should prompt for installing into /opt/homebrew, the default prefix for Apple Silicon Homebrew.

Regular brew calls specified later will be in reference to ARM Homebrew.

Add ARM homebrew to your PATH

In your shell or your shell's config, add ARM brew to your path:

$ export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"

This is to ensure ARM software from Homebrew runs before Intel software.

Install Crystal dependencies

LLVM 11+ is required for targeting Apple Silicon, so we need to make sure we install LLVM on both installations of Homebrew, and all the other dependent libraries needed, including build-time dependencies.

$ ibrew install llvm bdw-gc gmp libevent libyaml [email protected] pcre pkg-config
$ brew install llvm bdw-gc gmp libevent libyaml [email protected] pcre pkg-config

Grab an official macOS binary build of Crystal for x86_64

We'll need this for later, when compiling the compiler the first time. You could also install with ibrew as well.

$ mkdir -p ~/crystal-prebuilt
$ curl -fsSL https://github.com/crystal-lang/crystal/releases/download/1.0.0/crystal-1.0.0-1-darwin-x86_64.tar.gz > ~/crystal-prebuilt/crystal-1.0.0-1-darwin-x86_64.tar.gz
$ cd ~/crystal-prebuilt
$ tar -xf crystal-1.0.0-1-darwin-x86_64.tar.gz

Compiling Crystal for ARM64

Setup compile environment variables

Since we'll be targeting two different CPU architectures, we need to be extra careful about what build environment we specify. Intel Homebrew lives in a higher priority directory for most build tools, so it is often checked first and will be used instead of ARM Homebrew's directories unless we specify ARM Homebrew's build tools instead. We'll need to do this a lot, so lets specify some environment variables up-front:

$ export INTEL_BREW_PREFIX="$(ibrew --prefix)"
$ export INTEL_LLVM_ROOT="$INTEL_BREW_PREFIX/opt/llvm"
$ export INTEL_LLVM_CONFIG="$INTEL_LLVM_ROOT/bin/llvm-config"

$ export ARM_BREW_PREFIX="$(brew --prefix)"
$ export ARM_LLVM_ROOT="$ARM_BREW_PREFIX/opt/llvm"
$ export ARM_LLVM_CONFIG="$ARM_LLVM_ROOT/bin/llvm-config"

Compile for x86_64

The first step is to compile for x8664. Yep, x8664. We need to do this, because the official binary builds of the Crystal compiler ship compiled against [email protected] and without the ability to cross-compile for AArch64/ARM64 on Darwin, so we need to re-compile the compiler against LLVM 11 so that we can have the ability to cross-compile for arm64. In the future, this may not be necessary but for now it is.

$ LLVM_CONFIG="$INTEL_LLVM_CONFIG" \
   LDFLAGS="-L$INTEL_LLVM_ROOT/lib" \
   CPPFLAGS="-I$INTEL_LLVM_ROOT/include" \
   CC="$INTEL_LLVM_ROOT/bin/clang" \
   AR="$INTEL_LLVM_ROOT/bin/llvm-ar" \
   CRYSTAL="$HOME/crystal-prebuilt/crystal-1.0.0-1/bin/crystal" \
   arch -x86_64 make

Cross-compile to arm64

The next step is to take our x86_64 compiler and use it to cross-compile for the arm64 target triple (aarch64-apple-darwin).

$ LLVM_CONFIG="$INTEL_LLVM_CONFIG" \
   LDFLAGS="-L$INTEL_LLVM_ROOT/lib" \
   CRYSTAL_CONFIG_TARGET=aarch64-apple-darwin \
   CRYSTAL_CONFIG_LIBRARY_PATH="$ARM_BREW_PREFIX/lib" \
   arch -x86_64 ./bin/crystal build src/compiler/crystal.cr --cross-compile --target aarch64-apple-darwin -Dwithout_playground

Next, we need to compile the LLVM extension libraries for arm64:

$ LLVM_CONFIG="$ARM_LLVM_ROOT/bin/llvm-config" \
   LDFLAGS="-L$ARM_LLVM_ROOT/lib" \
   $ARM_LLVM_ROOT/bin/clang -I$ARM_LLVM_ROOT/include -c -o src/llvm/ext/llvm_ext.o src/llvm/ext/llvm_ext.cc

Finally, we need to link our cross-compiled compiler object (crystal.o) with our arm64 llvm_ext.o and the rest of our arm64 libraries, in order to get a final arm64 executable for the Crystal compiler.

$ LLVM_CONFIG="$ARM_LLVM_ROOT/bin/llvm-config" \
   LDFLAGS="-L$ARM_LLVM_ROOT/lib" \
   CPPFLAGS="-I$ARM_LLVM_ROOT/include" \
   $ARM_LLVM_ROOT/bin/clang crystal.o -o .build/crystal  -rdynamic -L$ARM_BREW_PREFIX/lib src/llvm/ext/llvm_ext.o `"$ARM_LLVM_ROOT/bin/llvm-config" --libs --system-libs --ldflags 2> /dev/null` -lstdc++ -lpcre -lgc -lpthread -L$(brew --prefix libevent)/lib -levent -liconv -ldl

In this final step, it's super easy for x8664 libraries to get picked up instead of or in addition to the arm64 libraries. If you get a working compiler at the end but have a bunch of warnings about the linker trying to link against an x8664 library, you can safely ignore those. If you don't get a working compiler at the end, it's possible you're missing an arm64 version of a required library, and you'll need to specify a more direct path via the linker arguments.

Verifying the compiled compiler

Now, at the end, we should be able to verify that we have a working arm64 crystal compiler:

$ file .build/crystal
.build/crystal: Mach-O 64-bit executable arm64

(Optional): Running compiler and stdlib test suite

This step is probably optional if you just want to compile some of your Crystal programs for arm64, but if you want to be extra sure the compiler works okay or are working on Crystal compiler or standard library development, you may want to run the test suite.

We're doing this directly to avoid triggering unnecessary rebuilds via the Makefile, but it's likely possible to just call make spec with the same environment variables specified and have the right thing happen.

Compiling and running the compiler specs
$ CRYSTAL_PATH=$(pwd)/src \
   CRYSTAL_LIBRARY_PATH=$ARM_BREW_PREFIX/lib \
   CC="$ARM_LLVM_ROOT/bin/clang" \
   LLVM_CONFIG="$ARM_LLVM_ROOT/bin/llvm-config" \
   LDFLAGS="-L$ARM_LLVM_ROOT/lib" \
   CPPFLAGS="-I$ARM_LLVM_ROOT/include" \
   PKG_CONFIG_PATH="$(brew --prefix [email protected])/lib/pkgconfig" \
   ./bin/crystal build -o spec/compiler_spec spec/compiler_spec.cr -Di_know_what_im_doing

$ CRYSTAL_PATH=$(pwd)/src \
   CRYSTAL_LIBRARY_PATH=$ARM_BREW_PREFIX/lib \
   CC="$ARM_LLVM_ROOT/bin/clang" \
   LLVM_CONFIG="$ARM_LLVM_ROOT/bin/llvm-config" \
   LDFLAGS="-L$ARM_LLVM_ROOT/lib" \
   CPPFLAGS="-I$ARM_LLVM_ROOT/include" \
   PKG_CONFIG_PATH="$(brew --prefix [email protected])/lib/pkgconfig" \
   spec/compiler_spec
Compiling and running the stdlib specs
$ CRYSTAL_PATH=$(pwd)/src \
  CRYSTAL_LIBRARY_PATH=$ARM_BREW_PREFIX/lib \
  CC="$ARM_LLVM_ROOT/bin/clang" \
  LLVM_CONFIG="$ARM_LLVM_ROOT/bin/llvm-config" \
  LDFLAGS="-L$ARM_LLVM_ROOT/lib" \
  CPPFLAGS="-I$ARM_LLVM_ROOT/include" \
  PKG_CONFIG_PATH="$(brew --prefix [email protected])/lib/pkgconfig" \
  ./bin/crystal build -o spec/std_spec spec/std_spec.cr --target aarch64-apple-darwin -Di_know_what_im_doing

$ CRYSTAL_PATH=$(pwd)/src \
   CRYSTAL_LIBRARY_PATH=$ARM_BREW_PREFIX/lib \
   CC="$ARM_LLVM_ROOT/bin/clang" \
   LLVM_CONFIG="$ARM_LLVM_ROOT/bin/llvm-config" \
   LDFLAGS="-L$ARM_LLVM_ROOT/lib" \
   CFLAGS="--target aarch64-apple-darwin" \
   CPPFLAGS="-I$ARM_LLVM_ROOT/include" \
   PKG_CONFIG_PATH="$(brew --prefix [email protected])/lib/pkgconfig"
   spec/std_spec

Compiling Crystal programs on arm64

Now that we've got a working Crystal compiler for arm64, we can go about compiling our arm64 binaries.

Or we could. But we probably won't get very far yet. We still need to compile shards for arm64 so that you can install dependencies. However, compiling shards is a fairly good demo for how to compile any Crystal program for arm64, as it is basically just another Crystal program.

Compiling shards

$ git clone [email protected]:crystal-lang/shards.git
$ cd shards
# If $HOME/src/crystal isn't where Crystal is cloned for you, update the path in CRYSTAL_PATH and CRYSTAL
$ CRYSTAL_PATH=lib:$HOME/src/crystal/src \
   CRYSTAL_LIBRARY_PATH=$ARM_BREW_PREFIX/lib \
   CRYSTAL=$HOME/src/crystal/.build/crystal \
   make install

This should install it into /usr/local/bin, which we can verify by running the below command:

$ which shards
/usr/local/bin/shards

$ file $(which shards)
/usr/local/bin/shards: Mach-O 64-bit executable arm64

Compiling other programs

In general, you can probably get most of the way there with your programs by providing CRYSTAL_PATH, CRYSTAL_LIBRARY_PATH, and CRYSTAL as specified above if you have a Makefile-type setup where CRYSTAL can be provided. Otherwise, you can invoke the arm64-compiled crystal binary directly while providing CRYSTAL_PATH and CRYSTAL_LIBRARY_PATH. These two values are needed, because we're not using an omnibus build where these values would already be set by a wrapper script. Instead, we have to provide them and be explicit so that the compiler knows where to look up Crystal code and linkable libraries, respectively.

Depending on the libraries used in your application, you may need to supply other typical environment variables that you might be expected to supply anyone, the most common being for OpenSSL 1.1, e.g. PKG_CONFIG_PATH="$(brew --prefix [email protected])/lib/pkgconfig"

HACK: Mimicking the Omnibus Crystal wrapper

Instead of passing the CRYSTAL_PATH and CRYSTAL_LIBRARY_PATH all over, we can also add a thin shell wrapper to provide those as part of the crystal command. This is similar to what the official omnibus builds do, where there's a top-level crystal shell script which sets the environment properly and passes off to the actual crystal binary.

Add this script to your PATH somewhere (don't forget to make it executable):

#!/bin/sh

# Again, if you installed somewhere other than $HOME/src/crystal, change it:
export CRYSTAL_PATH=lib:$HOME/src/crystal/src
# This should be ARM homebrew's lib
export CRYSTAL_LIBRARY_PATH=$(brew --prefix)/lib
# Mad temporary. You might want to copy this build else where and point it to that less-temporary location
export CRYSTAL=$HOME/src/crystal/.build/crystal

exec "$CRYSTAL" "[email protected]"

This is a bit of a temporary improvement to ergonomics, but should reduce the amount of pain when working in Crystal regularly on an M1.

Future Work

In future, I will be looking at trying to get aarch64/arm64 macOS support into the omnibus setup, but long-term this will require having Apple Silicon in CI, which would require some involvement from the core team.

In the meantime, I'm may try and get an initial version of the omnibus package working, that maybe I can provide builds for in a lagging fashion until it's possible to either cross-compile for arm64 in Homebrew via bootstrapping from the official x86_64 build (will require someone figuring out the LLVM 11 issues in release mode) or the core team is able to provide official builds.

Accomplishments & Challenges in 2018

Max Fierke

December 30, 2018

Hi there. How are you? Good? Good. Glad to see you here. How are your parents? Still trapped in that Winnebago, huh? Have you tried WD-40? Oh, they like it in there? Okay, moving on...

As we go into 2019, one of my resolutions is to use this, my personal site & blog, more and platforms like Twitter and Medium less. I'd like to kick that off by writing up this post on my accomplishments for the year 2018. I don't think I've ever written one of these before, but it seems like something that would be helpful to have for posterity.

Accomplishments

2018 was ultimately a pretty good year for me personally & professionally in a number of ways. I referenced some of these on Twitter.com. There were also of course not-so-great things to happen as well, but here's what I'm proud of:

  1. I finally acquired a puppy. Her name is Tessa. She's a little over a year old now. I'm not 100% sure what breed(s) she is, but my current guess is a Beagle & Rat Terrier mix (which the internet has told me is called a Raggle). She's adorkable and you can see photos of her on my Instagram
  2. In May of 2018, I returned to Germany for the first time in 18 years to see relatives, many of whom who had not seen me since then. It was my first trip there as an adult with an ability to speak some amount of German. My family is all from the former East Germany, so none learned to speak English in school growing up. I was not as good of a conversationalist in German as I was hoping, but I was able to listen and understand most conversations, which was incredibly valuable. I'm hoping to return in 2019.
  3. I was promoted to a "Senior Engineer" at work, which felt very validating. Because I started programming in junior high and did a lot of open source work before I worked professionally, I've always had a hard time with titles and determining where my non-professional & open-source experience fits in with my professional experience. Now it feels like my title more accurately reflects what I feel my capabilities are and that's rad. Mentoring is now a more explicit part of my job, as well, which has been a great so far!
  4. Moved into an apartment for humans, rather than ants. For the last two years, I lived in a <700 sqft 2bd apartment with a roommate. It was small and was difficult to keep all my things in. In July/August, I moved into a ~1200 sqft 2bd apartment, which allowed me to get a puppy! Yay!
  5. Automated a ton of things so I could spend fewer weekends messing with configuration and rebuilding servers and such. I got all of my side-project infrastructure provisioning centralized into one repo with a bunch of Ansible roles & playbooks. It's easy as a few playbook runs for me to recreate any of my side-project infrastructure, and I have done so this year. I also automated installation of my dotfiles across all my machines, so every machine I use works as I expect (It's FANTASTIC.)
  6. Found an artistic outlet in building a video game. Technically, I started this in late 2017, but I did a lot of artwork creation in the early part of 2018. Unfortunately, a lot of this went on hold by the summer, but I'm going to make it more of a priority in 2019.
  7. Caught up in Game of Thrones. I'd been stuck on Season 2 for like 5 years. Finally got HBO Now and binged my way through the rest of it. Very excited to see how it ends in 2019.
  8. Restarted my vinyl collecting in earnest. Another benefit of moving into a new, larger apartment, is that I finally have a place to store my vinyl and a permanent place for my record player. I've picked up a number of really cool things this year, including an 1967 pressing of Sgt. Peppers Lonely Hearts Club Band in very good condition, a translucent green pressing of Green Day's Warning, and finally, after six months of scouring local record shops, a copy of Gil Scott-Heron's Pieces of a Man.
  9. Traveled a ton (mostly for work). I went to Boston a handful of times. I spent two weeks in Germany & Switzerland. I went to Chicago a couple times. I experienced the Cincinnati airport (It's a vegetable-less ghost town.) I spent a week in Denver.
  10. I contributed a bunch of stuff! I wrote a blog post on ember-concurrency with GIFs. I released an ember addon called ember-concurrency-retryable. I became a maintainer for ember-tooltips. I contributed to the crystal compiler. I built a couple things for funsies, like an elevator simulator and a steganography tool. I made progress on porting reptyr to Rust.

Not So Great Stuff About 2018 That I Hope to Improve in 2019

2018 had some downs as well. Here's some of those:

  1. Experienced some health stuff & wasn't drinking coffee for most of 2018. I had some weird gut stuff going on towards the end of 2017, that led to me having stomach pains whenever I consumed coffee, kombucha, and alcohol, among other things. It seems to be all better now. It seemed to be partly due to the side-effects of a medication I'd been taking. And partly because I had developed a small hernia near my navel. I switched medications and had the hernia repaired, and I'm able to consume all those things again.
  2. I was much more of a hermit in 2018. This is a bit of a perennial problem, but I'd made some improvements in years past. This year, it was partly due to health stuff, since I often see people at bars and such, and not really being able to drink made that difficult. More frequent travel also contributed. As did my killer AV setup and PS4 that I acquired in the beginning of 2018. I also have a tendency to be singularly-focused when I'm working on something I'm excited about, so it's easy for weeks to go by before I realize I've not been seeing people as much as I should.
  3. I stopped working out. In 2017, I was probably in the best shape I'd been since high school. My old apartment's rec room was less than 10 feet away from my apartment door, which served as a constant reminder to work out. In my new apartment, it's on the other side of the building. The first few months of living in the new apartment with a new puppy were a bit hectic and I had to be thoughtful about when I left her alone. She's mellowed out a lot recently and can be trusted to be left alone & out of her crate in the apartment for a couple hours, so I'm hoping to get back into working out regularly in the new year.

That's it! I'm going to sit down and plan out some goals for 2019.

A New Concurrency Primitive

Max Fierke

October 14, 2017

No code is more concurrent than code that does not run and then runs all at once, suddenly.

In these modern times, it seems everyone is catching the concurrency bug. At EmberConf 2017, everyone and their elderly friend from down the street named John was talking about ember-concurrency. There were at least 10 talks that were mostly or only about the add-on wunderkind. Furthermore, articles by the author of the add-on have been declaring ember-concurrency as the magic bullet to solve problems you didn’t even know you had. Even Twitter was alight with admiration for this hot new craze.

ember-concurrency is good

Before ember-concurrency, I had to think about things like how to handle failure gracefully and what to do about retrying asynchronous work. I would think about all those things. And then not do them. Because programming is hard and I already spent so much time getting the happy path to work. If the user experienced a network error, that was just their fault for not plugging directly into the Ethernet port outside of Amazon’s data center, like I do. I thought that I shouldn’t have to adapt my applications to workaround every common failure case. But I was wrong. Very wrong. Bad Max. ember-concurrency really saved my bacon. Literally. I used it in the app for my newly-announced startup BaconSaver, which allowed me to launch several requests to grocery APIs, searching for the best bacon prices. Managing all that asynchronous work would have been impossible without ember-concurrency. But it’s still too complex. I have think about concurrency strategies. Should I drop or keepLatest? And then, it has to run code. Ugh. It’s 2017, people. We pretend servers don’t exist now.

But…

I have a Computer Science-related undergraduate degree from an average-ranked public university, so I know what concurrency means. (But I won’t tell you.) Because I definitely know what it means, I am qualified to say that concurrency (and therefore ember-concurrency, a special kind of concurrency) could be a lot better in Ember. As someone who knows what concurrency means, I can tell you that having to run code drastically reduces concurrency, probably. Luckily, I have a solution. I’m announcing in this blog post ember-procrastination. ember-procrastination improves the concurrency story of our Ember apps by optimizing when code gets run.

Did you know?

Did you know that almost no language implements procrastination primitives? Of course, Java offers a ProcrastinationFactory, .NET has System.Enterprise.ProcrastinationServices and Haskell has that powerful Procrastinate monad. However, to date no one has ever implemented anything like this for Ember or JavaScript. Sad.

How to use Ember-Procrastination?

(Check out this demo at Ember-Twiddle)

ember-procrastination introduces a new concurrency primitive called a someday . A someday is a lot like a task from ember-concurrency, but has the special property that it will only schedule and do work when prompted several times. This means that only work the user truly wants done will get completed. But be careful: if you ask too much, it may get mad and cancel work already in-progress. Such truly concurrent code can be finicky. ember-procrastination also leverages the best in lazy code loading technology to ensure that we don’t execute expensive operations until the last possible moment. To do this, ember-procrastination uses an advanced Just-In-Time (JIT) feature present in modern JavaScript: the beforeunload event. When ember-procrastination detects this event, all code that has been previously prompted to run that has not yet been run will run, ensuring that all work is completed. And it all happens concurrently. Amazing.

You can try it today. Simply yarn add --dev ember-procrastination to your project.

Seize The Future, Someday

This revolutionary add-on puts the control back into your user’s hands. Technology does too much for them already anyway. Give your users a chance to go outside and experience nature. Before it all starts on fire.

Max’s Tips for Very Good Air Travel

Max Fierke

March 24, 2017

You may be traveling without even realizing these ten very important things.

These days, I work for a company where I sit in a chair and do complicated brain things. Occasionally, they tell me to go get on a large metal sky bird, or airplane, to sit and not do brain things until I get to another place, where I promptly return to sitting and doing brain things. It’s an exciting life and I am very lucky and happy. I have experienced much of this so-called air travel during the past year or so, so I am sharing some of my tips free of charge, because I am also wonderful and mildly attractive.

Tip #1: Arrive early for the airport.

Airlines are all about timing. To ensure that you are one of the lucky ones who makes it to the plane and does not get eaten by Bernhardt the Senior Flight Attendant, make sure you aim to get there two hours before your flight, but be sure to arrive at a multiple of three and get to the TSA checkpoint on a multiple of five. They are very strict about the timing and will not hesitate to send you to the back of the line. If you’re worried, try to bring your friend Jerry who always ends his sentences with “I am Jerry, I was a Mathsss major”.

Airport

source: upload.wikimedia.org

Tip #2: Stop at the Duty-Free

These days, it’s really rare to find fine products that aren’t covered in shit. The Duty-Free stores at the airport carry only products not covered in shit, so it is a good opportunity to stock up.

Tip #3: Say “Hi” to Sylvester

You will inevitably run into someone from your past on a work flight and it will probably be Sylvester. Be sure to say “Hi” when you see him and then promptly ignore him for the rest of your life. You have given him all you can give. If you acknowledge him later, it will open up a portal to Hell and fill the next 7 years with darkness and despair, so please don’t fuck it up for everyone.

Samsung DVD-C500 DVD Player with HD Upconversion. source: BestBuy.com

Tip #4: Do not bring your Samsung DVD-C500 DVD Player with HD Upconversion onboard

This summer, the FAA banned the Samsung DVD-C500 from all American passenger aircraft, as it poses a serious fire hazard. Do not bring your Samsung DVD-C500 with you when you fly. It will still be at home when you get back and you can return to watching the Season 2 DVD of Reba.

Tip #5: Say no to babies

Sometimes, the airline will offer you to take a baby at the gate. This might seem cool and it might seem fun to get to show all your friends at the bar your cool new baby you got from your Southwest flight, but do not take it. It is another airline trick. Whoever takes the baby has to sit in the baby seat with the baby who also does not like airplanes, probably because it’s parents are the airline. Unfortunately, there’s someone on every flight who takes the free baby and you will be forced to hear their cries “I did not mean to take the baby, waaaaahhhh!”

Tip #6: Do not shout “I LOVE SEAT 5F” from the top of your lungs as soon as you sit down

Obviously, seat 5F is the best of the best when it comes to airline seating and most people know it. However, be really careful and avoid declaring your continuing love for seat 5F at the top of your lungs. It may seem crazy, but some people did not know about seat 5F and not everyone got to be seated in 5F, so shut the fuck up, you lucky little so-and-so, or Bernhardt the Senior Flight Attendant will have your head.

Sky coffee will not look as good, though. source: upload.wikimedia.org

Tip #7: Ask for the hottest coffee available

This is a certified TravelHack™. When the flight attendants come around with beverages, ask for the hottest coffee available on the aircraft. The flight attendants will be impressed with your moxie and will return from the hold with a piping hot pot of coffee, flaming at the top. All of the other passengers will cheer and your legacy will finally be sealed.

Tip #8: Do not eat the peanuts

Do not eat the peanuts, they are a classic airline trap. If you eat the peanuts, you will become Bernhardt’s personal assistant, tasked with playing doubles tennis with him on his private court for eternity, until someone says “Excuse me, do you think it would be okay if I continue using my phone on this flight, I have really important business things to say and I am in first-class”, which will break the spell.

This is what clapping looks like. source: Flickr

Tip #9: Clap Enthusiastically When the Plane Lands

It’s very important that you clap enthusiastically when the plane lands at your destination. It is the only way for the pilot and co-pilot to know that their spell worked and the plane landed successfully. It is also the only way for them to exit their trance without someone obtaining ginger root and four elven toes, placing them directly in the pilot’s mouth, and chanting “Nickelback is a proper musical group and not a community service project”.

Tip #10: Be sure to exit the plane

I know, sometimes it seems hard. The plane is so comfortable, warm, and sponge-like, but you must resist the urge to stay on the plane and take up residence. Only Bernhardt is allowed lodging on the plane and he is not looking for roommates. You must leave the plane.

Favorite Albums of 2015

Max Fierke

January 7, 2016

I know I essentially ended my last blog post with "coming next week/month, part 2 of my top 10 hip-hop albums of all-time" but I'm going to interrupt the flow with a brief list of my favorite albums of 2015. I promise that second part is coming. I have a draft for it sitting unpublished. Next time I'm on a plane, I'll probably finish it. Anyway, here's a list of albums you can use to judge me silently. Or not so, as the case may be.

Favorite Albums of 2015 (fuzzy ordering):

  1. To Pimp A Butterfly by Kendrick Lamar
  2. GO:OD AM by Mac Miller
  3. If You're Reading This Now, It's Too Late by Drake
  4. Summertime '06 by Vince Staples
  5. 90059 by Jay Rock
  6. Couch Potato by Bobby Raps & Corbin
  7. Compton by Dr. Dre
  8. But You Caint Use My Phone by Erykah Badu
  9. B4.DA.$$ by Joey Bada$$
  10. Fetty Wap by Fetty Wap

Honorable Mentions

Most of these were released in 2014 or earlier, but I listened to them a lot in 2015.

  • PRhyme by PRhyme
  • The Grind Date by De La Soul
  • The Pinkprint by Nicki Minaj
  • 2014 Forest Hills Drive by J. Cole
  • Cadillactica by Big K.R.I.T.
  • Oxymoron by ScHoolboy Q

My Hip-Hop Top 5 List

Max Fierke

July 31, 2015

I recently migrated my site over to a new platform I'm building. As part of that, my blog has come with, and by "come with" I mean I deleted my Tumblr(s) and neglected to migrate any of the content because ¯\_(ツ)_/¯. One of the posts I had on my old blog was a post from 2012 or 2013 about my top 5 or top 10 hip-hop albums (my memory is hazy, it wasn't a very well-written post). Needless to say, the past few years have seen a flurry of amazing albums that have definitely knocked off some items from the previous list. I'd like to share an updated list with a few reasons as to why they're on my top 10. These are not necessarily the albums I found most influential, but they are the ones that I will be sharing with any future kids and grand-kids I may have, if a Trump presidency does not leave us in a Fallout-esque post-apocalyptic Hellscape.

1. good kid, m.A.A.d city by Kendrick Lamar

I feel like this is one of those albums that is assumed to be on every hip-hop fan's top 10 list (and for good reason). I've listened to this album an uncountable number of times. My Last.fm profile may have a number, but I guarantee that number is off by at least 50. There was a period from around when the album came out (October 2012) to around September or October 2014, where I had probably listened to the full album at least once or twice each week.

For me, the album is the hip-hop equivalent of the TV series Arrested Development in terms of complexity and nuance. Never before have I heard an album with such a well-constructed & complex narrative comparable to that of a great novel. I believe it will have as much cultural significance in the next 50 years as Sgt. Pepper's Lonely Hearts Club Band had in the last 48. It was the first album that made me think "I want to make my baby-boomer father listen to this" because of the importance I place on both the narrative and the musicality. Furthermore, behind all of the lyrical complexity, the production was truly next-level and somehow made it possible for me to listen to it as many times as I did without getting sick of it. There are lots of albums out there with as good or better production, but somehow none of them captivated me the way GKMC has and continues to.

I've elaborated more on this album than I likely will on any other. If I had to pick one track to represent for the whole album, it would be Sing About Me. I hold this album to be important enough that schools should have it as required listening in junior high or high school.

2. Graduation by Kanye West

Most people will probably place My Beautiful Dark Twisted Fantasy at the top of their lists (which is more than fair). However, Graduation occupies a special place in my heart being one of the first albums I really connected with. Graduation dropped when I was in 8th grade, which is a terrible time for anyone. Self-esteem was at a low, and for me, Graduation was the jerry can I stumbled upon in the desert. To this day, no album gets me feeling better when I'm down than Graduation. And no track boosts my self-confidence like The Glory.

But with my ego
I can stand there in a speedo
And be looked at like a fucking hero

How could that not make you feel better every single time you hear it?

3. Mos Def & Talib Kweli Are Black Star by Black Star

This is a near-perfect album. The lyrics intertwine so well with the production. It's a tremendous feat of sonic balance. The narrative is fantastically well-crafted. It's understated and supremely well-delivered. To extract a collection of lines would, I believe, do a disservice to the cohesion the duo so eloquently instilled within their hallowed words. An album of self-empowerment and Black empowerment, it's truly a cultural masterpiece.

4. To Pimp A Butterfly by Kendrick Lamar

Most, if not all, of the reasons for placing good kid, m.A.A.d city where it is apply to To Pimp A Butterfly as well. However, TPAB is not simply a hip-hop album. It's a smorgasbord of my favorite genres, encompassing hip-hop, R&B, jazz, and funk. It feels eclectic, surreal, and sometimes other-worldly.

5. The Black Album by Jay Z

This is my favorite album from Jay. Yes, Reasonable Doubt is better, but The Black Album has so many iconic tracks that routinely bounce their way into my mind, but never stick around long enough to annoy.

Allow me to re-introduce myself
My name is Hov, OH, H-to-the-O-V
I used to move snowflakes by the O-Z

I've entered many rooms as that verse has dropped and it has always helped as a confidence booster. I mean, I'm not HOV, but I do feel like him for a second when I hear that drop.