Stratus3D

Software Engineering, Web Development and 3D Graphics

Lua Version Management With Asdf-lua

I’ve written asdf in a past blog post but I haven’t covered asdf-lua in depth yet. In this blog post I’ll talk about the advantages of asdf-lua over existing version managers and show how it’s used.

Lua version management is a pretty small niche. The most popular Lua version manager on GitHub has only 91 stars. asdf-lua isn’t a self-contained Lua version manager but rather a plugin for asdf, and extendable version manager with support for over a dozen languages. Unless you are already using asdf chances are you have several version managers already installed. Rather than installing yet another version manager you can use asdf to manage all the languages you rely on in development. But asdf will work fine with your existing version managers too so you don’t need to worry about uninstalling anything to use asdf-lua. While it might seem like asdf-lua is more complex than other version managers it’s really not. asdf-lua itself is only two short Bash scripts and both asdf and asdf-lua are easy to install and configure as we will see.

Installation

If you don’t have asdf installed already you will need to install it. It’s easy:

# Clone the repo, currently the latest release is 0.2.1
git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.2.1

# Then add two lines to your bashrc or bash_profile if you are on OSX
echo -e '\n. $HOME/.asdf/asdf.sh' >> ~/.bashrc
echo -e '\n. $HOME/.asdf/completions/asdf.bash' >> ~/.bashrc

That’s it. After reloading your .bashrc you should be able to run asdf in your shell and see the help printed out. Next install asdf-lua:

asdf plugin-add lua https://github.com/Stratus3D/asdf-lua.git

You will also need a C compiler to compile Lua. Refer to the readme for instructions on getting gcc installed on your OS.

Now that we have asdf-lua and it’s dependencies installed using it to install Lua versions is easy.

Usage

To see the Lua versions that are available to install run:

asdf list-all lua

This command will print out a list of all the Lua versions. Find one you want to install and run the installation command. I’ve chosen to install lua 5.3.2:

asdf install lua 5.3.2

After Lua is compiled you can run asdf list lua and you should see the Lua version you just installed. Now that you have a Lua version installed you just need to tell asdf when to use it.

Specifying Lua Versions

asdf looks for a .tool-version file in the current directory and reads it to figure out which version should be used when lua or luac is invoked in that directory. If the file doesn’t exist in the current directory asdf navigates up to the parent directory and checks for it again. This is repeated until a .tool-version file is found on the root directory is reached. Typically you want to set the Lua version on a per-project basis, but you may also want to set a global Lua version so it is available everywhere.

Setting the Lua version for a project

To set a Lua version for a project cd into the project’s root directory and create the tool version file:

echo "lua 5.3.2" > .tool-version

You should now be able to run lua -v in the directory to see the Lua version being set. You can also use asdf current lua to see the Lua version as well as the path to the .tool-version file that set it. Rather than creating a .tool-version file manually for every project you can the asdf local command to create it for you:

asdf local lua 5.3.2

Setting a global Lua version

Since asdf is relying on the presence of a .tool-version file in the current directory or one of the parent directories all you need to do to set a global Lua version is to create .tool-version file in your home directory. There is a command that makes this easy:

asdf global lua 5.3.2

This command works just like asdf local only it creates a .tool-version file in your home directory. You can see what it’s written by running less ~/.tool-version. You should now be able to use Lua in any directory under your home directory.

Conclusion

asdf-lua is comparable to other version managers in many ways. The real benefit to asdf-lua is asdf itself. With asdf there is no need to learn the quirks of yet another version manager just to be able to install multiple versions of Lua. The commands I’ve shown here are the exact same for asdf-ruby, asdf-elixir, and all the other asdf plugins.

Feedback is always welcome so if you have any questions about asdf-lua or this blog post feel free to contact me. I’m always looking for ways of improving asdf-lua and asdf so don’t hesitate to contact me if you have any suggestions.

References

Building ZealDocs for OSX

ZealDocs is an open source Qt app that provides offline documentation browsing. It’s cross-platform but not supported on OSX. I’ve been using ZealDocs for a while now on my Ubuntu machine and wanted to use on OSX too. I figured it was possible to get it compiled on OSX, so after some trial and error I finally figured it out. I was using OSX 10.10 when I came up with these steps, so tweaks to this process may be necessary if you are running the latest version of OSX.

Dependencies

First you will need to install all the ZealDocs dependencies. If your using Homebrew it’s easy:

brew install qt5 qt5-qtwebengine qt5-sqlite-plugin libarchive

If you’re not using Homebrew you will need to either find the equivalent packages in your own package manager or install these packages by hand.

Download ZealDocs

cd to the directory you want to build ZealDocs in (you can move it to /Applications after it’s compiled) and clone it down and enter the project directory:

git clone https://github.com/zealdocs/zeal.git
cd zeal

Configure

You will need to add the include and lib paths for the sqlite and libarchive libraries to the src/libs/core/core.pri file. You can find the versions of each package you have installed by running brew info <package name>. The command I ran looked like this:

cat << EOF >> src/libs/core/core.pri
macx: {
    INCLUDEPATH += /usr/local/Cellar/libarchive/3.2.2/include
    LIBS += -L/usr/local/Cellar/libarchive/3.2.2/lib -larchive
    INCLUDEPATH += /usr/local/Cellar/sqlite/3.15.2/include
    LIBS += -L/usr/local/Cellar/sqlite/3.15.2/lib -lsqlite3

}
EOF

Of course your sqlite and libarchive versions may be different. Note that this is a critical step, if any of these paths are wrong the build will fail.

Build

Now that we have everything configured we can build ZealDocs:

/usr/local/opt/qt5/bin/qmake -makefile
make

If all goes well you should see a Zeal.app/ directory inside of the projects bin/ directory.

Install

Copy the Zeal.app directory into your /Applications directory to install it.

cp bin/Zeal.app /Applications

Once you have it in your /Applications directory you will have to tell OSX to run the app even though it can’t identify the developer. Once you have done this you should be able to open the app and begin using it.

Finally, ZealDocs can be started from the command line with a search term. You could add the appropriate path to your $PATH, but I’ve found an alias to be a little easier. Add this to your .bashrc if you want to make zeal available as an alias in your shell:

alias zeal='/Applications/Zeal.app/Contents/MacOS/Zeal'

When you start a new shell session you should be able to use zeal:

zeal erlang:ping

Conclusion

I’ve been pretty happy we ZealDocs so far, and it’s nice I can now run it on OSX as well as all my other linux distros. Since Zeal is cross platform it doesn’t really integrate with anything OSX-specific so your looking for something a little nicer you should check out Dash. Dash is a proprietary app that has some extra features that make it a little easier to use on OSX.

Update 12/15/2016 - Automation

After writing this post I realized it would be nice to have a Bash script do all this. I wrote one and checked it into my dotfiles repository.

References

The Josephus Problem in Erlang

I saw this video about the Josephus problem yesterday and immediately I thought it might be a fun problem to solve in Erlang. First, here is the video that got me interested.

With Recursion and Pattern Matching

I thought this would be an interesting problem to solve with Erlang’s recursion and pattern matching. I decided to use a list of integers to represent the soldiers in the circle, and a recursive function to loop around the list and remove every second integer until only one remains. Pattern matching makes it easy to pick out every second integer. Here is my first attempt at solving the problem with recursion and pattern matching:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-module(josephus).

-export([survivor_in_circle/1]).

survivor_in_circle(Num) ->
    kill_to_left(lists:seq(1, Num), []).

kill_to_left([Survivor], []) ->
    % One person remaining, they are the survivor
    Survivor;
kill_to_left([], Survivors) ->
    % Start another loop around the circle
    kill_to_left(lists:reverse(Survivors), []);
kill_to_left([Killer], Survivors) ->
    % killer is last person in the loop, start another loop around the circle
    kill_to_left([Killer|lists:reverse(Survivors)], []);
kill_to_left([Killer, _Killed|Rest], Survivors) ->
    % Only the killer survives
    kill_to_left(Rest, [Killer|Survivors]).
1
2
3
4
5
6
7
8
9
10
11
12
1> josephus:survivor_in_circle(1).
1
2> josephus:survivor_in_circle(2).
1
3> josephus:survivor_in_circle(4).
1
4> josephus:survivor_in_circle(5).
3
5> josephus:survivor_in_circle(7).
7
6> josephus:survivor_in_circle(41).
19

While this solution only requires 4 function clauses, it would be nice if it only required 2 clauses. Ideally we should only have one clause for when one number remains in the list, and another clause for when there are two or more numbers in the list. We actually can do this in Erlang with only two clauses by using the ++ operator to concatenate surviving numbers onto the end of the list of remaining numbers, so that it is never empty.

1
2
3
4
5
6
7
8
9
10
11
12
13
-module(josephus).

-export([survivor_in_circle/1]).

survivor_in_circle(Num) ->
    kill_to_left(lists:seq(1, Num)).

kill_to_left([Survivor]) ->
    % One person remaining, they are the survivor
    Survivor;
kill_to_left([Killer, _Killed|Rest]) ->
    % Only the killer survives
    kill_to_left(Rest ++ [Killer]).

I usually try to avoid using ++ operator in Erlang because it creates an entirely new list each time it’s used, so while it’s more elegant than the first solution it’s less efficient. On small lists this won’t matter. The largest list I created above only contained 41 integers, so both solutions are fast enough for this.

With Binary Pattern Matching

I also thought binary method of solving the problem was interesting. All you need to do is move the first bit in the binary representation of the number to the end of the binary. This is also something that can be done easily with Erlang’s pattern matching, and since it only requires moving around a couple bits it should be by far the fastest way to solve the problem.

After a little tinkering, and little help from #erlang IRC channel, I got it working:

1
2
3
survivor(Num) ->
    <<First:8,Rest/binary>> = integer_to_binary(Num, 2),
    binary_to_integer(<<Rest/binary,First:8>>, 2).

Here I’m not actually moving bits around, I’m moving bytes, because the integer_to_binary/2 call returns a binary representing a sequence of ones and zeros, e.g. <<"101">>. Ideally I’d like move bits instead of bytes. This is easy in Erlang, but it took me a while to figure how to how to get this to work. I asked a quite a few questions on this before I finally got it working. Note here that I’m not using Erlang’s integer_to_binary/2 function, I’m using binary:encode_unsigned/1. encode_unsigned converts the number to binary notation, which is the representation required for the bit moving trick to work. After we convert the number to binary notation we remove all leading zero-bits. Then move the first bit to the end, and convert it back to an integer. You can see we have to specify the number of bits that should be used to construct the resulting integer. We count the number of bits in the original binary to get this number.

1
2
3
4
5
6
7
8
9
10
bit_solution(Num) ->
     <<First:1,Rest/bits>> = remove_leading_zero_bits(binary:encode_unsigned(Num)),
     Size = bit_size(Rest) + 1,
     <<Survivor:Size>> = <<Rest/bits,First:1>>,
     Survivor.

remove_leading_zero_bits(<<0:1, Rest/bitstring>>) ->
    remove_leading_zero_bits(Rest);
remove_leading_zero_bits(Bin) ->
    Bin.

Conclusion

When writing this I looked online for solutions to the Josephus problem in Erlang and found several different solutions. Some used recursive functions like I did and others used Erlang processes. Erlang’s pattern matching and recursion makes it an ideal tool for solving this sort of problem. It was a fun problem to solve, and with Erlang I didn’t have to spend any time thinking about how I was going to translate the problem into Erlang, I just jumped in and started coding. The binary solution is by far the most efficient, but all of them are expressive and relatively succinct.

References