Larry Price

And The Endless Cup Of Coffee

Copy Data Using Qmake

| Comments

I’ll take things that should be really easy but are in fact impossible for $1000, Mr Trebek.

I have a Qt desktop app that uses qmake. I have tests. I have test data that I need to copy to my build directory. Not only that, but I want to copy a whole directory recursively. In Linux and Windows. You must be thinking to yourself, ‘There’s no way that’s possible, leave me out of your crazy plans!’ Well to that I say nay. Nay indeed.

I originally copied my test data using the INSTALLS variable. My data folder is just a directory sitting in the test source called “TestData.” This solution is fantastic because it works cross-platform.

MyBAProjectTest.pro
1
2
3
4
install_it.path = $${OUT_PWD}
install_it.files += TestData/

INSTALLS += install_it

However, this means that if I ever have a clean directory, I have to run make install to install my test data. What a pain! Why bother! Why even write tests when life is this unjust!

BUT WAIT! After combing the depths of the internets for much of the evening, I was able to find many halfway solutions to my original problem. The concept is to add commands to the QMAKE_POST_LINK variable.

For Linux, I use cp:

MyBAProjectTest.pro
1
QMAKE_POST_LINK += $$quote(cp -rf $${PWD}/TestData $${OUT_PWD})

For Windows, I use xcopy:

MyBAProjectTest.pro
1
2
3
4
5
PWD_WIN = $${PWD}
PWD_WIN ~= s,/,\\,g

QMAKE_POST_LINK += $$quote(mkdir DestFolder)
QMAKE_POST_LINK += $$quote(xcopy $${PWD_WIN}\\TestData $${OUT_PWD_WIN}\\TestData /E)

I also want that directory to be deleted when I run make clean. Cleaning up just means adding some commands to the QMAKE_CLEAN directive. I also want to run the appropriate commands whether I’m on Unix or Windows without having to modify my .pro file. Wrapping the previous commands in what I call an “OS-space” will cause those commands to only run in the specified operating system.

MyBAProjectTest.pro
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
win32 {
    PWD_WIN = $${PWD}
    PWD_WIN ~= s,/,\\,g

    QMAKE_POST_LINK += $$quote(mkdir DestFolder)
    QMAKE_POST_LINK += $$quote(xcopy $${PWD_WIN}\\TestData $${OUT_PWD_WIN}\\TestData /E)

    QMAKE_CLEAN += /s /f /q TestData && rd /s /q TestData
}

unix {
    QMAKE_POST_LINK += $$quote(cp -rf $${PWD}/TestData $${OUT_PWD})

    QMAKE_CLEAN += -r TestData
}

Data copied, tests working again. Take that, Nokia; no matter how difficult you make your build-processing tool, I’ll figure out how to contort it to my whims.

Installing GTest and GMock Libs in Ubuntu 13.04

| Comments

I started trying to work on an open-source project and found that I needed to install googletest and googlemock on my home machine. Seemed easy enough, I found a package called google-mock in the Raring repositories which depends on a package called libgtest-dev. So I install it:

1
$ sudo apt-get install -y google-mock

And the CMake file I was running before tells me that I have gmock, but I’m still missing gtest. What’s going on here?

Well, there was a changeset applied in Ubuntu 12.04 (Precise) with the following text (source):

Stop distributing static library (although still build it, to ensure gtest works). Upstream recommends against shipping the libary at all, just the source. (See: http://code.google.com/p/googletest/wiki/FAQ) The Debian maintainer plans to do this also (see BTS: 639795); do it in Ubuntu now to fulfil MIR requirements.

– Christopher James Halse Rogers Thu, 08 Mar 2012 17:45:29 +1100

What does that mean? That means we get to build and “install” the gtest libs ourselves. The source is conveniently installed in /usr/src after installing libgtest-dev (which we automatically got when we installed google-mock).

Installing gtest libs
1
2
3
4
5
6
7
8
9
$ sudo apt-get install -y cmake --quiet
$ cd /usr/src/gtest
$ sudo cmake -E make_directory build
$ sudo cmake -E chdir build cmake .. >> /dev/null
$ sudo cmake --build build >> /dev/null
$ ls build/libgtest*
build/libgtest.a  build/libgtest_main.a
$ sudo cp build/libgtest* /usr/local/lib/
$ sudo rm -rf build

The >> /dev/null can be dropped if you would like to see the output of these commands when successful, any error text will still show up with this redirect in place. I like to move all my personally-compiled libraries (and includes) to /usr/local, but you could just as easily copy them over to /usr/lib. All of this could also be done in /tmp if you’re so inclined.

RVM Quick Start

| Comments

When I was working on a few different projects at once, I started running into issues where my Ruby gem versions would start to mismatch. How did I fix that issue? Naively. I adjusted the versions as necessary so my gems were always the same version. That was… pretty dumb. To make up for my past ignorance, I’ve been exploring ways to manage my Ruby versions and my gems intelligently. Enter RVM.

RVM is a simple tool to solve just problems. And it works pretty well. I hit some kinks along the way, but my installation pleases me well enough. Getting RVM is not for the faint of heart. Maybe there are better ways to do it, but the website says to execute the following:

/home/lrp/my_project
1
> curl -L https://get.rvm.io | bash -s stable

This command uses curl to fetch the data that lives at https://get.rvm.io, which happens to be a big bash script. It executes the bash script with the args -s stable. ‘Stable’ means the latest stable release of RVM. I could just as easily give it ‘dev’ and get the latest developer release instead, but I really don’t want that. So that command does a lot of stuff and may give you some further instructions to run before you can continue. Use that big head of yours and follow the instructions. You may also need to restart a terminal to get RVM to be recognized as a command. Since I was using gnome-terminal, I had to follow these instructions.

Now I want RVM to know about some Ruby versions. For a full list of possible Ruby versions to install, run rvm list known. I just want 1.9.3. I found that I needed to do this even though I had 1.9.3 installed on my system previously.

/home/lrp/my_project
1
> rvm install ruby-1.9.3

In Ubuntu 13.04, this command installs ruby-1.9.3 in ~/.rvm/rubies/ruby-1.9.3-p448/bin/ruby. I’ve found that this also sets my default Ruby to the RVM version of Ruby, which I don’t want. To verify and undo this, I executed the following commands outside of my project directory.

/home/lrp
1
2
3
4
5
6
7
8
9
> which ruby
/home/lrp/.rvm/rubies/ruby-1.9.3-p448/bin/ruby
> echo "D'oh"
D'oh
> rvm use system
Now using system ruby.
> which ruby
/usr/bin/ruby
>

Now that that’s settled, I want to tell RVM to use the local version of Ruby for my project and to install any gems in a special location.

/home/lrp/my_project
1
> rvm 1.9.3@my-project --create --ruby-version

This creates a gemset and ruby-version file (.ruby-gemset and .ruby-version files) using the Ruby installation 1.9.3 created above. I specifiy to use --ruby-version instead of --rvmrc because RVM told me that I should. After some research, the .ruby-version file is used by several other tools, so this will keep my potential number of config files low. Now I check that all my Ruby versions are okay.

/home/lrp/my_project
1
2
3
4
5
6
> which ruby
/home/lrp/.rvm/rubies/ruby-1.9.3-p448/bin/ruby
> cd ..
> which ruby
/usr/bin/ruby
>

When in my project directory, all the gems I install will be installed to my specified gemset, which means they are no longer cluttering my global gemspace, even when I install them using Bundler. It also means that I can use ruby-1.9.3 for this project and 2.0 for another project with minimal mental overhead. This makes me a happy developer.

I only glazed over the installation process and documentation for RVM. Go to the web site for more.

The Pragmatic Programmer

| Comments

Don’t be a slave to history. Don’t let existing code dictate future code. All code can be replaced if it is no longer appropriate

The Gist

The Pragmatic Programmer written by Andrew Hunt and David Thomas is a book about being an above-average software developer.

My Opinion

There are many good concepts in this book. However, I’ve been reading a bit much lately and found that most of the topics covered have been rehashed in other books. As it turns out, re-reading this kind of motivational book is less exciting when you’ve read similar texts earlier in the year.

I did enjoy the section on Refactoring; there has been some discussion on my current project team of what to do when you see “gnarly” code. This section reiterates the fact that poorly-written code should be updated. The authors talk about treating code as organic. Refactoring your code as it grows is the same as reevaluating oneself as one grows older. Changing one’s hair or clothes, changing what one eats or reads or watches (inputs), or changing a daily routine is just refactoring of your daily life: the person is the same and accomplishes largely the same tasks, but in a slightly different manner.

One tip in this book is “Use one editor well.” I’ve found that I love to use Sublime Text for my work in Ruby, Python, and markup languages. Having said that, I find Sublime an inappropriate tool for doing C++ (Qt Creator), Java (Eclipse or even NetBeans), or anything .NET (duh Visual Studio). Considering my job could involve any programming language, I would revise this tip to “Use the right editor for the job, and learn to use it well.”

The chapter introductions were largely worthless to me: long-winded summaries of EVERYTHING in the chapter. I eventually trained myself to automatically skip the chapter intros in favor of reading the details. It seems that I could have just as easily gone the other way and gotten away with only reading the intros, but that hardly seems like any fun.

Who Would Like This

Fresh-outs should read this book. It’s not too long and it’s split into manageable chapters. Great for fresh-out book clubs, in which I probably should have participated.

Who would not like this? People who have read books with similar premeses, such as Code Complete, within recent memory.

Using Foreman to Create an Upstart Service

| Comments

I just finished my first attempt at deploying a web app to run automatically in the background on a friend’s server. Pretty easy, really. The first thing I did was install foreman. Assuming you have ruby and rubygems installed:

1
$ sudo gem install foreman

Next I needed to give foreman the commands to start my app. I created a file in the root of my project directory called ‘Procfile’ and gave it the steps I would run to start my app manually. For the sake of simplicity, let’s say I run my app pretty barebones:

Procfile
1
web: bundle exec rackup

Now when I run foreman start, foreman will use Bundler to execute rackup with the correct gems in my Gemfile. Now exporting upstart config files is pretty easy.

1
$ sudo foreman export upstart --app=MyApp --user=root /etc/init

That command creates the .conf files needed for upstart to control the service called ‘MyApp’ as the user ‘root.’ It puts all the .conf files in /etc/init (which is where Ubuntu puts such things) and will create a default log directory in /var/log/MyApp. Now I can control my service by running service MyApp start, service MyApp stop, service MyApp restart, and service MyApp status. Hooray for me.

But I need to run my app in two ways: in dev mode on a local port with my dev database, and I need to run it in production mode using port 80 and the production database. I’ve also heard that using webrick (the default server installed with rackup) is great for develpment, but I should be using something else for my production server. So I made some config files for foreman:

development.env
1
2
3
RACK_ENV=development
PORT=9292
SERVER=rackup
production.env
1
2
3
RACK_ENV=production
PORT=80
SERVER=unicorn

And I change my Procfile to:

Procfile
1
web: bundle exec $SERVER -p $PORT -E $RACK_ENV

Ridiculously configured. Now when I run foreman start, it will error out. I need to specify my environment file:

1
$ foreman start -e production.env

Now foreman will use Bundler to startup the server specified in $SERVER, run the app on port $PORT (-p), and will pass through the environment listed as $RACK_ENV to my application (-E), allowing my app to do whatever configuration it does given the current environment. Power to the people.

I found that this guy does a lot more complicated stuff with Foreman, if you need more.