Introduction
Download
Screenshots
Wiki
User's Guide
Developer's Guide
FAQ
Wish List
License
Contributors
Search
Mailing List
Bug Tracker

Golden Cheetah Cycling Performance Software for Linux, Mac OS X, and Windows
Developer's Guide

This guide will teach you how to download the GoldenCheetah source code, build it, modify it, and submit your changes to be included in the next release. If you're just looking to use GoldenCheetah, please check out the Users Guide or the Download Page.

Installing dependencies

GC requires a number of libraries. On Mac OS X, you can get most of these through Mac Ports or Homebrew. On Linux and other Unixes, you can use whatever package manager your distribution provides. (We list the package names for MacPorts and Ubuntu below.) On Windows, you'll probably need to download and install everything by hand. You might also want to read this mailing list message or check the INSTALL documents on GitHub.

You'll need the following:

Package | Version | MacPorts | Ubuntu
Qt | 4.8.0 or later | qt4-mac | libqt4-dev
git | any | git-core | git

If you're going to download directly from a PowerTap, you might need to install the FTDI USB driver. If you're going to download directly from an SRM, you need to download and install libsrmio. Neither of these libraries is required if you just want to import data you've already downloaded with another program.

Checking out the code

Once you've downloaded and installed the above dependencies, you need to check out the GC source code. GC uses git for version control. To checkout the code, execute this command:

>     git clone git://github.com/GoldenCheetah/GoldenCheetah.git

That should create a new directory, `GoldenCheetah`, in your current working directory. In the rest of this document, we'll reference paths relative to that directory. You can find the source code in `GoldenCheetah/src`, for instance. Likewise, this document is in `GoldenCheetah/doc/developers-guide.content`.

Building an executable

To build GC, we currently use qmake, which comes with the Qt libraries referenced above. All local configuration is stored in the file `gcconfig.pri`, which you create by copying `gcconfig.pri.in`, both in the `GoldenCheetah/src` directory. Additionally, GoldenCheetah uses a patched version of Qwt which is located in the `GoldenCheetah/qwt` directory. You'll need to copy the configuration file `qwtconfig.pri.in` to `qwtconfig.pri` and edit that file if needed. The steps you'll take are as follows:

> In the top level GoldenCheetah directory

>     cp src/gcconfig.pri.in src/gcconfig.pri
>     cp qwt/qwtconfig.pri.in qwt/qwtconfig.pri
>     vi src/gcconfig.pri # Follow the directions at the top of the file.
>     vi qwt/qwtconfig.pri # This may not need to be edited to successfully build.
>     qmake build.pro # called qmake-mac in MacPorts
>     make

We're aware that a lot of people would rather use a configure-like script for the build process. We would too, but none of us know autoconf well enough to integrate it with Qt on Mac, Linux, and Windows. If you can help us out with that, please post a message on the GoldenCheetah User's Google Group.

Making changes

Now that you've got GC up and running, you can add whatever features you want. We generally frown on dogmatic coding conventions, and we're big fans of the "rough consensus and running code" philosophy. That said, please do your best to adhere to the following style guidelines:

- Use spaces instead of tabs.
- Do not end lines with whitespace. End every file with a newline. Otherwise git becomes angry. This command will highlight any whitespace problems in commit abcd0123 in red:

  >     git show --color abcd0123

- Avoid "using namespace ..." in header files.
- Don't declare global variables in header files. If you must use a global variable, declare it `static` within a .cpp file.
- Only call C++'s operator `new` within the constructors and `reset` functions of `std::auto_ptr` etc. or when passing a parent pointer to a Qt class (so that the parent deletes the child). Never call `delete` explicitly.
- Do not use `malloc` or `free` unless forced to by an external C library.
- Allocate large buffers on the heap, not on the stack.
- When the Qt or C++ standard library has an appropriate function, use it.
- Only use external libraries with GPL-compatible licenses.
- Avoid C-style casts. Learn and use C++'s `static_cast`, `reinterpret_cast`, etc.

Not all of the GoldenCheetah code follows these guidelines today, but we're working on it. You can help out by adhering to them in new code.

At some point, you'll probably decide that a change you've made is worth sharing with others. You'll use git again to share your changes, and the following sections will show you how. A warning: git is pretty hard to learn, but it's worth it. Once you get used to it, you'll be surprised you ever put up with another revision control system.

Committing changes to git

An example will make this section more concrete. Since my SRM doesn't record altitude, let's say that I get annoyed that the Ride Summary always shows "Elevation Gain (feet): 0.0", so I change the code not to show any ride metric whose value is zero. git-diff shows exactly what I've changed:

>     $ cd GoldenCheetah/src
>     $ git diff
>     diff --git a/src/RideItem.cpp b/src/RideItem.cpp
>     index 6971b9b..c368725 100644
>     --- a/src/RideItem.cpp
>     +++ b/src/RideItem.cpp
>     @@ -362,6 +362,8 @@ RideItem::htmlSummary()
>                      assert(displayName.length() > 0);
>                      const RideMetric *m = metrics.value(name);
>                      assert(m);
>     +                if (m->value(false) == 0.0)
>     +                    continue;
>                      if (m->units(metricUnits) == "seconds") {
>                          QString s("<tr><td>%1:</td><td "
>                                    "align=\"right\">%2</td></tr>");

In order to share this change, I need to use git-commit:

>     $ git commit RideItem.cpp

git will open up an editor for me to type a commit message. It's important to take the time to write good commit messages, as they form a history of who has changed which lines of code and for what purpose. The first line of every commit message should be a short description of 50 characters or less. The second line should be blank. Subsequent lines should be less than 80 characters long and should describe the change in detail. If your commit addresses an existing bug or feature please add a tag to the body of your commit message. Allowable tags are "fixes" which is used to close an issue and "refs" to reference an issue. For example, adding the text "fixes #2" will close issue #2. Once I write the file and exit the editor, git-log will show the result:

>     $ git log -p -1
>     commit 30303ef2d11f4bead0860b969b4b74814053b76b
>     Author: Sean Rhea <sean.c.rhea@gmail.com>
>     Date:   Wed Sep 2 21:04:33 2009 -0400

>         don't include zero metrics in ride summary

>         When a device doesn't have altitude, there's no reason to show it.  Likewise
>         with heart rate if the user wasn't wearing a heart rate monitor during a ride.
>         Maybe in the future this behavior could be enabled on a per-metric basis.

>     diff --git a/src/RideItem.cpp b/src/RideItem.cpp
>     index 6971b9b..c368725 100644
>     --- a/src/RideItem.cpp
>     +++ b/src/RideItem.cpp
>     @@ -362,6 +362,8 @@ RideItem::htmlSummary()
>                      assert(displayName.length() > 0);
>                      const RideMetric *m = metrics.value(name);
>                      assert(m);
>     +                if (m->value(false) == 0.0)
>     +                    continue;
>                      if (m->units(metricUnits) == "seconds") {
>                          QString s("<tr><td>%1:</td><td "
>                                    "align=\"right\">%2</td></tr>");

Note that had I changed more than one file, I would have just listed them all when I ran git-commit. For example,

>     $ git commit file1.cpp file2.cpp

I can also commit everything I've changed all at once via

>     $ git commit . # note the 'dot'

Managing commits

git works best if you commit early and often. For example, I usually commit a few times as I'm writing a new feature. Once I get my code to compile, I commit it again. Then if I fix any bugs that turn up during runtime, I commit the bug fixes. Then maybe I go back and clean up the new code, now that I understand the problem better, and I commit those changes, too.

The reasoning behind all of these commits is that commits are like save points in a video game. If at any point I decide I'm messing things up, I can just go back to the previous commit. git-diff shows me my uncommitted changes. Let's say that I've decided I should also change the text in the Ride Summary to reflect the fact that I'm only showing non-zero metrics now. Here's my change:

>     $ git diff
>     diff --git a/src/RideItem.cpp b/src/RideItem.cpp
>     index c368725..2ff9c49 100644
>     --- a/src/RideItem.cpp
>     +++ b/src/RideItem.cpp
>     @@ -159,13 +159,13 @@ static const char *metricsXml =
>          "            precision=\"1\"/>\n"
>          "  </metric_group>\n"
>          "  <metric_group name=\"Averages\">\n"
>     -    "    <metric name=\"average_speed\" display_name=\"Speed\"\n"
>     +    "    <metric name=\"average_speed\" display_name=\"(Non-zero) Speed\"\n"
>          "            precision=\"1\"/>\n"
>     -    "    <metric name=\"average_power\" display_name=\"Power\"\n"
>     +    "    <metric name=\"average_power\" display_name=\"(Non-zero) Power\"\n"
>          "            precision=\"0\"/>\n"
>     -    "    <metric name=\"average_hr\" display_name=\"Heart rate\"\n"
>     +    "    <metric name=\"average_hr\" display_name=\"(Non-zero) Heart rate\"\n"
>          "            precision=\"0\"/>\n"
>     -    "    <metric name=\"average_cad\" display_name=\"Cadence\"\n"
>     +    "    <metric name=\"average_cad\" display_name=\"(Non-zero) Cadence\"\n"
>          "            precision=\"0\"/>\n"
>          "  </metric_group>\n"
>          "  <metric_group name=\"BikeScore™\" note=\"BikeScore is a trademark

But now I decide I don't like that change--I'd rather do it another way. No problem. git-checkout will restore the previous version committed:

>     $ git checkout src/RideItem.cpp

If I want to restore the entire directory to the state of the last commit, I checkout the whole directory:

>     $ git checkout . # note the 'dot'

Alternatively, if I had already committed this change, I can use git-reset to throw away my latest commit like this:

>     $ git reset --hard HEAD^

Be careful with that one, though--it's irreversible.

Combining commits

Coming back to our example, let's say I instead decide to change the ride summary a little differently and commit it:

>     $ git log -p -1
>     commit 225f3093a206cbcc296ed1c8a25996ce1968bda6
>     Author: Sean Rhea <sean.c.rhea@gmail.com>
>     Date:   Sat Sep 5 16:21:33 2009 -0400

>         include "non-zero" in metric group titles

>     diff --git a/src/RideItem.cpp b/src/RideItem.cpp
>     index c368725..449e19e 100644
>     --- a/src/RideItem.cpp
>     +++ b/src/RideItem.cpp
>     @@ -146,7 +146,7 @@ double RideItem::timeInZone(int zone)

>      static const char *metricsXml =
>          "<metrics>\n"
>     -    "  <metric_group name=\"Totals\">\n"
>     +    "  <metric_group name=\"Non-zero Totals\">\n"
>          "    <metric name=\"workout_time\" display_name=\"Workout time\"\n"
>          "            precision=\"0\"/>\n"
>          "    <metric name=\"time_riding\" display_name=\"Time riding\"\n"
>     @@ -158,7 +158,7 @@ static const char *metricsXml =
>          "    <metric name=\"elevation_gain\" display_name=\"Elevation Gain\"\n"
>          "            precision=\"1\"/>\n"
>          "  </metric_group>\n"
>     -    "  <metric_group name=\"Averages\">\n"
>     +    "  <metric_group name=\"Non-zero Averages\">\n"
>          "    <metric name=\"average_speed\" display_name=\"Speed\"\n"
>          "            precision=\"1\"/>\n"
>          "    <metric name=\"average_power\" display_name=\"Power\"\n"

Now I have two commits, which I can see with git-log:

>     $ git log origin/master..devel-guide
>     commit 225f3093a206cbcc296ed1c8a25996ce1968bda6
>     Author: Sean Rhea <sean.c.rhea@gmail.com>
>     Date:   Sat Sep 5 16:21:33 2009 -0400

>         include "non-zero" in metric group titles

>     commit df657cd3f0dcb8484a468c2efb04da77ee0472e0
>     Author: Sean Rhea <sean.c.rhea@gmail.com>
>     Date:   Wed Sep 2 13:42:33 2009 -0400

>         don't include zero metrics in ride summary

>         When a device doesn't have altitude, there's no reason to show it.  Likewise
>         with heart rate if the user wasn't wearing a heart rate monitor during a ride.
>         Maybe in the future this behavior could be enabled on a per-metric basis.

If I'm happy with my changes, I can share them with the world just like they are using git-format-patch. In this case, however, these two changes should really be combined into one: the second change was something I should have done along with the first, I just didn't think of it at the time. I can use git-rebase -i to combine them:

>     $ git rebase -i origin/master

That will bring up an editor window with a list of my changes, like this:

>     pick df657cd don't include zero metrics in ride summary
>     pick 225f309 include "non-zero" in metric group titles

>     # Rebase df33fe2..920643f onto df33fe2
>     #
>     # Commands:
>     #  p, pick = use commit
>     #  e, edit = use commit, but stop for amending
>     #  s, squash = use commit, but meld into previous commit
>     #
>     # If you remove a line here THAT COMMIT WILL BE LOST.
>     # However, if you remove everything, the rebase will be aborted.
>     #

The directions are pretty self explanatory. You can reorder commits by reordering their lines in this file. You can drop a commit by removing it from the file entirely. You can also change the first word on a line from "pick" to "squash", and git will combine that commit with the one that comes before it. That's what I want to do here. I change line 2 of this file so that the first two lines are:

>     pick df657cd don't include zero metrics in ride summary
>     squash 225f309 include "non-zero" in metric group titles

Then I write the file and quit the editor. Git does a little work saying:

>     Rebasing (1/2)

And then it brings up another editor window that shows both of my commit messages. I edit the two message to combine them into one, write the file, and exit the editor. git says:

>     Successfully rebased and updated refs/heads/master.

And I can see the result with `git log -p`.

Submitting a patch

Okay, now I'm ready to share my change. I'll use git-format-patch:

>     $ git format-patch HEAD^
>     0001-don-t-include-zero-metrics-in-ride-summary.patch

In the `GoldenCheetah/src` directory I'll now find a patch file, `0001-don-t-include-zero-metrics-in-ride-summary.patch`, that other people can use to include my change in their own local git repositories.

If you have a patch you'd like to share with others, we recommend that you fork the main GIT repository and submit a pull request with your patch. Alternatively you can join the Golden Cheetah Developer's Group and post a patch there.

Applying patches

Let's say I email the patch above to the mailing list, and it sounds like a useful feature to you. To test it yourself, you can download the patch file to your `GoldenCheetah/src` directory and apply it to your repository using git-am:

>     $ git am 0001-don-t-include-zero-metrics-in-ride-summary.patch
>     Applying: don't include zero metrics in ride summary

If you now type, "git log", you'll see that "don't include zero metrics in ride summary" has been added to your repository.

If enough people like a patch, and it doesn't introduce any new bugs, one of the GoldenCheetah maintainers will probably commit it to the official GC repository on github.

Staying up to date

In order to keep your local repository up to date with the official one, you use git-fetch followed by git-rebase:

>     $ git fetch origin
>     $ git rebase origin/master
>     First, rewinding head to replay your work on top of it...
>     Fast-forwarded master to origin/master.

Note that, unlike above, we didn't supply a "-i" option to git-rebase this time.

git-fetch downloads a copy of all the patches at github to your local repository, but it doesn't apply them. git-rebase undoes the changes that are unique to your local repository, applies any new patches from `origin/master`, and then re-applies your patches.

If you have uncommitted changes, the rebase will fail:

>     $ git rebase origin/master
>     src/RideItem.cpp: needs update
>     cannot rebase: you have unstaged changes

Commit your changes with git-commit and then re-run the git-rebase. It will work this time.

For developers who are used to subversion, this need to commit changes before rebasing is the most annoying aspect of git. All I can say is that you won't mind it much after time. Because you can use "git rebase -i" to combine, reorder, and even drop commits, a commit in git is much lighter weight than one in subversion. As I said above, commit early and often.

If the changes from github conflict with yours, you'll have to merge. git-rebase will exit with an error and a list of directions on how to fix things. Read them carefully.