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
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.
|