Skip to content

Java Development Steps

Randall Hauch edited this page Dec 29, 2013 · 6 revisions

After setting up your Java development environment, follow these steps for each task. For example, if the task is to add some small feature to a robot project as described by an issue, then follow this procedure for this task. When completed, you can then follow the same procedure for your next task.

Before starting the task, be sure that there is an issue for your task.

About branches

One of the best things about Git is that it enables the use of branches. You can think of a branch as an independent line of changes to your files. You can easily switch between branches, and Git makes your working directory reflect the current state of that branch. Branches are also very lightweight, and Git can switch between them very quickly - almost instantaneously. The word "branch" is used because you can think of your history of changes like a tree, and a branch in Git is like a branch off of the main trunk of the tree. In fact, in Git you can create a branch off of another branch, just like on a real tree.

So what do you use branches for? Basically, there is always a branch that represent the 'master' codebase, but you can create branches for temporary work or for different streams of work. This probably won't be too common in our FRC work, but it is very common in software projects.

For example, let's say some software project releases 1.0, then proceeds to work on the 2.0. All of this work can be done on the 'master' branch, and the exact version that represents the "1.0" release is simply tagged, and work continues on the 2.0 effort on the 'master' branch. But meanwhile someone using 1.0 reports some problems that need to get fixed. The 'master' branch doesn't reflect 1.0 anymore, since there have been changes that will go into 2.0. A developer can go back to the "1.0" tag on the 'master' branch, and create off of the 1.0 tag/commit a new branch called "1.x". They can then make all of the changes on that 1.x branch to fix the problem (by committing the changes to the 1.x branch), and then release the 1.1 version of the project.

But what Git makes so great is that the developer can switch to the 'master' branch (e.g., the "2.0" effort) and simply pull that commit onto the 'master' branch. That's called "cherry-picking", and it basically merges the same changes that were made in the specific commit on the 1.x branch and re-applies them to the current branch.

We'll use a new branch, which we'll call a "topic branch", for each set of changes we want to make. In other words, if you want to make some changes to the robot code, create a new topic branch off of the 'master' branch, do all your work on that topic branch (including committing changes to that branch), and then post that branch (as a Git pull request) for people to review it. A pull-request is basically a "request to pull these changes into the 'master' branch." All the developers can review the pull request and decide if they want to accept it, and if so one of the mentors will merge the changes in the pull request into the 'master' branch. At that point, the topic branch is not needed anymore.

This may be confusing at first, but once you get the hang of using topic branches it will seem quite natural. And to help you get used to the process, the steps and the commands below will walk you through the whole process.

Step 0. Update your master branch

Before you make any changes locally, the first step is to make sure that you indeed have no local changes. To check, run the git status command from the top-level directory that contains all of our projects, and verify that the output matches the following:

$ git status
# On branch master
nothing to commit, working directory clean

(You might see something other than "master" there, and that's okay. The important this is that you see "nothing to commit, working directory clean".)

Then you can bring your local 'master' branch completely up-to-date with what has been put into the official FRC 4931 "2014" repository:

$ git checkout master
$ git pull upstream master

The checkout command just ensures that you are on the "master" branch in your local git repository. The second command checks for recent changes in ‘upstream’ (the official repo on GitHub.com) that your local repo has not yet seen, and pulls them into your local repository’s ‘master’ branch (the branch that the first command ensures you are currently on).

Note: If you are just keeping a local copy of the Java code so you can build and deploy it to the robot, then you can stop here.

Step 1. Create a topic branch

Now you need to decide a name for your topic branch. Keep it short, and the best approach is to use the issue number. So if you are going to fix issue #28, then use "issue-28" for the name of your topic branch. (To keep things easy, don't use spaces or punctuation marks or unusual characters in branch names. Letters, numbers, '_' and '-' are okay.)

The command to create a new branch is:

$ git checkout -b {new_branch_name}

where {new_branch_name} is the name of your branch. So using our example of using "issue-28" as our topic branch name, the command would be:

$ git checkout -b issue-28

At this point, your working directory (where all the Java source files are) will be on the branch named issue-28, and all changes will be made there.

Step 2. Make your changes

In the Eclipse IDE, make the changes required to satisfy the task/issue. Save and test as required. Periodically stage your changes, which basically makes Git remember the state of your changes so you can get them back. First, see what you've changed:

$ git status

This will show you which files have been modified, and which files have been staged.

As you edit and save your changes inside Eclipse, the IDE will compile the code and tell you if you have any errors. This is by far the easiest way to make changes and verify they at least compile. Once they compile, you may be ready to try deploying the code -- with your changes -- to the robot. To do this, go back to your command line and run the ant <target> <target> ... command.

To use ant you must be inside the directory of a single robot project. For example:

$ cd PrototypeCommandDriveRobot

Then you can use the clean and compile targets with the ant command:

$ ant clean compile

The clean target removes all files that were generated/compiled/built from the source code, while the compile target runs the Java compiler to produce the generated/compiled/built files that can be deployed to the robot.

BTW, ant is a build system that knows that some targets require other targets to be run first (aka, prerequisites). The compile target has the clean target as a prerequisite, so just running the compile target will automatically run clean then compile:

$ ant compile

Step 3. Deploy your code to the robot

When you code compiles and you've staged your changes, then next thing you may want to do is try the code on the robot to make sure that your changes were effective and that they didn't break anything.

Note that sometimes you may not have made all of the changes you know you want to make to address the issue, and the robot code is not yet complete or runnable. That's fine, and you can always skip Step 3 and just commit your changes (Step 4). But don't create a pull-request with changes that have not been run on the robot.

To deploy one of the robot projects in your current code to the robot, you'll need to make sure that your computer is connected to the robot's bridge (e.g., "4931" for our first robot), and that your computer's IP address is "10.49.31.x", where "x" is 6 or higher. (Make sure that the value of "x" does not clash with anyone else's computer that might be connected to the robot's bridge.

Then make sure that you're still in the directory for the specific robot project you want to deploy:

$ pwd
/Users/jsmith/dev/fork-2014

If you're not in the right directory, simply change directories. For example:

$ cd PrototypeCommandDriveRobot

Then you can run ant with the deploy and run targets:

$ ant deploy run

The deploy target has a few prerequisites, including compile, jar, and suite (some of which have their own prerequisites). That means that running the deploy target will always run clean, compile, jar, suite, and deploy. The run target actually has no prerequisites, so you can actually run it by itself.

Also, note that this is the first time we're using the "-Dremoteaddress=10.49.31.2" argument. This actually tells the build system the IP address of the cRIO on the robot.

Here's some sample output from this ant command:

$ ant deploy run
Buildfile: /Users/rhauch/Documents/FRC/fork-2014/PrototypeCommandDriveRobot/build.xml
Trying to override old definition of task classloader

clean:
   [delete] Deleting directory /Users/rhauch/Documents/FRC/fork-2014/PrototypeCommandDriveRobot/build

-actual-compile-compile:
    [mkdir] Created dir: /Users/rhauch/Documents/FRC/fork-2014/PrototypeCommandDriveRobot/build
    [mkdir] Created dir: /Users/rhauch/Documents/FRC/fork-2014/PrototypeCommandDriveRobot/build/classes
     [echo] [crio-compile] ./src, /Users/rhauch/Documents/FRC/fork-2014/sunspotfrcsdk/lib/wpilibj.jar:/Users/rhauch/Documents/FRC/fork-2014/sunspotfrcsdk/lib/networktables-crio.jar, /Users/rhauch/Documents/FRC/fork-2014/sunspotfrcsdk/lib/squawk.jar -> ./build/classes
    [javac] Compiling 16 source files to /Users/rhauch/Documents/FRC/fork-2014/PrototypeCommandDriveRobot/build/classes

-post-compile:

compile:

preverify:
    [mkdir] Created dir: /Users/rhauch/Documents/FRC/fork-2014/PrototypeCommandDriveRobot/build/preverify
    [mkdir] Created dir: /Users/rhauch/Documents/FRC/fork-2014/PrototypeCommandDriveRobot/build/preverify.raw.util
     [echo] [crio-preverify] ./build/classes, , /Users/rhauch/Documents/FRC/fork-2014/sunspotfrcsdk/lib/wpilibj.jar:/Users/rhauch/Documents/FRC/fork-2014/sunspotfrcsdk/lib/networktables-crio.jar, /Users/rhauch/Documents/FRC/fork-2014/sunspotfrcsdk/lib/squawk.jar -> ./build/preverify
    [unjar] Expanding: /Users/rhauch/Documents/FRC/fork-2014/sunspotfrcsdk/lib/wpilibj.jar into /Users/rhauch/Documents/FRC/fork-2014/PrototypeCommandDriveRobot/build/preverify
    [unjar] Expanding: /Users/rhauch/Documents/FRC/fork-2014/sunspotfrcsdk/lib/networktables-crio.jar into /Users/rhauch/Documents/FRC/fork-2014/PrototypeCommandDriveRobot/build/preverify

jar:
     [echo] [crio-jar] ./build/preverify, ./resources -> ./build/app.jar
      [jar] Building jar: /Users/rhauch/Documents/FRC/fork-2014/PrototypeCommandDriveRobot/build/app.jar

suite:
    [mkdir] Created dir: /Users/rhauch/Documents/FRC/fork-2014/PrototypeCommandDriveRobot/build/suite
     [echo] [crio-suite] ./build/app.jar -> ./build/suite/image
     [java] CompilerOracle: exclude com/sun/squawk/Method.getParameterTypes
     [java] CompilerOracle: exclude com/sun/squawk/SymbolParser.getSignatureTypeAt
     [java] CompilerOracle: exclude com/sun/squawk/SymbolParser.stripMethods
     [java] [translating suite image [closed: false, parent: squawk] ...]
     [java] ### Excluding compile: com.sun.squawk.Method::getParameterTypes
     [java] ### Excluding compile: com.sun.squawk.SymbolParser::getSignatureTypeAt
     [java] [Including resource: META-INF/MANIFEST.MF]
     [java] Romizer processed 396 classes and generated 4 files.
    [unzip] Expanding: /Users/rhauch/Documents/FRC/fork-2014/PrototypeCommandDriveRobot/build/app.jar into /Users/rhauch/Documents/FRC/fork-2014/PrototypeCommandDriveRobot/build/suite
     [move] Moving 1 file to /Users/rhauch/Documents/FRC/fork-2014/PrototypeCommandDriveRobot/build/suite
     [move] Moving 1 file to /Users/rhauch/Documents/FRC/fork-2014/PrototypeCommandDriveRobot/build/suite
     [move] Moving 1 file to /Users/rhauch/Documents/FRC/fork-2014/PrototypeCommandDriveRobot/build/suite
   [delete] Deleting: /Users/rhauch/Documents/FRC/fork-2014/PrototypeCommandDriveRobot/image.suite.api

deploy:
     [echo] [crio-configure] Configuration files not included in this version of the sdk
     [echo] [crio-configure] Checking that crio is configured for Java
Host OS: Mac OS X 10.9.1, 10.9.1
Host JVM: Java HotSpot(TM) 64-Bit Server VM 24.45-b08
Target IP: 10.49.31.2
Network interfaces on host:
    en0:  address: 10.49.31.6 netmask: 255.0.0.0 <--- on robot's subnet
[frcupgrade] Connecting FTP @10.49.31.2
     [echo] [crio-deploy] ./build/suite/image.suite -> 10.49.31.2
[frcdeploy] Sending local file image.suite

run:
   [frcrun] [cRIO] [OTA Server] ********* REBOOTING cRIO *********
   [frcrun] [cRIO] 
   [frcrun]    Waiting for cRIO to reboot (1s)
   [frcrun]    Waiting for cRIO to reboot (2s)
   [frcrun]    Waiting for cRIO to reboot (3s)
   [frcrun]    Waiting for cRIO to reboot (4s)
   [frcrun]    Waiting for cRIO to reboot (5s)
   [frcrun]    Waiting for cRIO to reboot (6s)
   [frcrun]    Waiting for cRIO to reboot (7s)
   [frcrun] [cRIO] 
   [frcrun] [cRIO] -> * Loading NiRioRpc.out: NiRioRpc
   [frcrun] [cRIO] * Loading nivissvc.out: nivissvc
   [frcrun] [cRIO] * Loading nivision.out: nivision
   [frcrun] [cRIO] NI-RIO Server 12.0.0b10 started successfully.
   [frcrun] [cRIO] task 0xe1d2a0 (NiRioRpc) deleted: errno=0 (0) status=0 (0)
   [frcrun] [cRIO] * Loading visa32.out: visa32
   [frcrun] [cRIO] * Loading niserial.out: niserial
   [frcrun] [cRIO] * Loading NiFpgaLv.out: NiFpgaLv
   [frcrun] [cRIO] * Loading FRC_FPGA.out: FRC_FPGA
   [frcrun] [cRIO] FPGA Hardware GUID: 0x1394F6DC1FEB42EC6910E5767ED1D22C
   [frcrun] [cRIO] FPGA Software GUID: 0x1394F6DC1FEB42EC6910E5767ED1D22C
   [frcrun] [cRIO] FPGA Hardware Version: 2012
   [frcrun] [cRIO] FPGA Software Version: 2012
   [frcrun] [cRIO] FPGA Hardware Revision: 1.6.4
   [frcrun] [cRIO] FPGA Software Revision: 1.6.4
   [frcrun] [cRIO] * Loading FRC_JavaVM.out: FRC_JavaVM
   [frcrun] [cRIO] 
   [frcrun] [cRIO] 
   [frcrun] [cRIO] [OTA Server] Version: 2012 FRC, Jan  5 2012, 17:20:48
   [frcrun] [cRIO] 
   [frcrun] [cRIO] 
   [frcrun] [cRIO] Welcome to LabVIEW Real-Time 12.0rc7
   [frcrun] [cRIO] task 0xd222a8 (sysapi-rpc) deleted: errno=0 (0) status=0 (0)
   [frcrun] [cRIO] 
   [frcrun] [cRIO] [Squawk VM] Version: 2011 FRC, Nov  5 2011, 14:34:13
   [frcrun] [cRIO] FPGA Hardware GUID: 0x1394f6dc1feb42ec6910e5767ed1d22c
   [frcrun] [cRIO] FPGA Software GUID: 0xa14c11bde4bb64aef6a86fc52a294cd9
   [frcrun] [cRIO] Entering disabled mode!

You do need to watch this output, because it tells you want is going on. For example, if the output of the "build" step may look something like this:

...
deploy:
     [echo] [crio-configure] Configuration files not included in this version of the sdk
     [echo] [crio-configure] Checking that crio is configured for Java
Host OS: Mac OS X 10.9.1, 10.9.1
Host JVM: Java HotSpot(TM) 64-Bit Server VM 24.45-b08
Target IP: 10.49.31.2
Network interfaces on host:
    en0:  address: 10.49.31.6 netmask: 255.0.0.0 <--- on robot's subnet
[frcupgrade] Connecting FTP @10.49.31.2
[frcupgrade] Remote OTA server does not match, upgrading
[frcupgrade] Upgraded OTA server. You may need to reboot cRIO manually
[frcupgrade] Remote VM does not match, upgrading
...

Note the "You may need to reboot cRIO manually" line. If you see this, then before you do anything you will need to use the Driver Station to reboot the cRIO before you try to drive the robot.

Also, the last line of the run step is pretty important:

...
   [frcrun] [cRIO] Welcome to LabVIEW Real-Time 12.0rc7
   [frcrun] [cRIO] task 0xd222a8 (sysapi-rpc) deleted: errno=0 (0) status=0 (0)
   [frcrun] [cRIO] 
   [frcrun] [cRIO] [Squawk VM] Version: 2011 FRC, Nov  5 2011, 14:34:13
   [frcrun] [cRIO] FPGA Hardware GUID: 0x1394f6dc1feb42ec6910e5767ed1d22c
   [frcrun] [cRIO] FPGA Software GUID: 0xa14c11bde4bb64aef6a86fc52a294cd9
   [frcrun] [cRIO] Entering disabled mode!

The "Entering disabled mode!" says that the robot is ready to drive, and that you should enable the robot using the Driver Station. When you do that, you'll actually see additional output from the Java code, including lines like the following:

...
   [frcrun] [cRIO] Entering teleop mode
   [frcrun] [cRIO] Entering disabled mode!
   [frcrun] [cRIO] Entering autonomous mode
   [frcrun] [cRIO] Entering disabled mode!
   [frcrun] [cRIO] Entering teleop mode
   [frcrun] [cRIO] Drive with arcade-style joystick

This shows that the Drive Station was used to switch the robot into disabled, then autonomous, then disabled, then teleop mode, and finally that in teleop mode the joysticks were used to drive the robot. Other kinds of messages will appear, including the "debug" and "toString" methods of some objects.

So, to wrap up this section, if your changes didn't quite work, simply jump back up to Step 2 to correct the code, deploy it to the robot, and run the robot again. Keep repeating this process until you are happy with the changes.

When you are happy with your changes, the next step is to use Git to stage and commit your changes.

Step 4. Stage your changes

In Git, staging files is basically putting your changes into a "holding" area that you may want to commit. You can think of staged files something like:

"If I were to commit the staged files, I'd be okay with it because they work. But I want to keep making changes to try a few more things, and if that goes badly I can get back to the point that I staged everything."

At any point, you can see what you've changed:

$ git status

The output of this command will also tell you what to do next for the files. Typically, when you're happy with the changes, you want to stage your changes:

$ git add .

After you stage your changes, you can see which files you've staged and which files you've changed since staging. Here's the same git status command but with some sample output:

$ git status
rhauch fork-2014 $ git status
# On branch talon
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#	modified:   .gitignore
#	modified:   PrototypeCommandDriveRobot/src/org/frc4931/prototype/Robot.java
#	modified:   PrototypeCommandDriveRobot/src/org/frc4931/prototype/subsystem/DriveTrain.java
#	new file:   PrototypeCommandDriveRobot/src/org/frc4931/prototype/subsystem/JaguarDriveTrain.java
#	new file:   PrototypeCommandDriveRobot/src/org/frc4931/prototype/subsystem/TalonDriveTrain.java
#

All of the files I've changed are now staged. Note how the output also tells me what I can do next. I can either upstage some changes to specific files, or I can proceed with committing. But I also can simply make more changes to my files. If I do that, then those additional changes will not be staged, and this makes it very easy to get back to my staged changes.

First, lets assume that after I've staged all my changes I make some additional changes to two files, the "Robot.java" and "DriveTrain.java" files. I can then run git status to see what I've done:

$ git status
# On branch talon
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#	modified:   .gitignore
#	modified:   PrototypeCommandDriveRobot/src/org/frc4931/prototype/Robot.java
#	modified:   PrototypeCommandDriveRobot/src/org/frc4931/prototype/subsystem/DriveTrain.java
#	new file:   PrototypeCommandDriveRobot/src/org/frc4931/prototype/subsystem/JaguarDriveTrain.java
#	new file:   PrototypeCommandDriveRobot/src/org/frc4931/prototype/subsystem/TalonDriveTrain.java
#
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#	modified:   PrototypeCommandDriveRobot/src/org/frc4931/prototype/Robot.java
#	modified:   PrototypeCommandDriveRobot/src/org/frc4931/prototype/subsystem/DriveTrain.java
#

Notice that the "DriveTrain.java" file has some changes that are saved and not-staged while some additional changes that are not yet staged. The "Robot.java" files has changes that are not yet staged.

If you want to see what changes are staged:

$ git diff --staged

Or, if you want to see what changes have been made but not yet staged, then simply omit the --staged argument:

$ git diff

But look again at the git status output above. It actually tells you want to do next. For example, if you want to discard all of the not-yet-staged changes, then run this command:

$ git checkout -- .

This discard all of the unstaged changes, and reverts your working directory back to the point you last staged. Or, you can also discard all of the unstaged changes in a particular file. For example:

$ git checkout -- PrototypeCommandDriveRobot/src/org/frc4931/prototype/subsystem/DriveTrain.java

Or, you can stage all of your not-yet-staged changes:

$ git add .

Running git status tells you what the status is:

$ git status
rhauch fork-2014 $ git status
# On branch talon
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#	modified:   .gitignore
#	modified:   PrototypeCommandDriveRobot/src/org/frc4931/prototype/Robot.java
#	modified:   PrototypeCommandDriveRobot/src/org/frc4931/prototype/subsystem/DriveTrain.java
#	new file:   PrototypeCommandDriveRobot/src/org/frc4931/prototype/subsystem/JaguarDriveTrain.java
#	new file:   PrototypeCommandDriveRobot/src/org/frc4931/prototype/subsystem/TalonDriveTrain.java
#

This shows that all of my changes are now staged and ready to be committed. (Of course I can make still more changes, and then review, deploy/test, and stage them.

Step 5. Commit your changes

When you think you've reached a point where all the code compiles and has been tested, and where the changes represent a logical set of changes (e.g., they fix a bug, they add a component, etc.), then you can commit these changes using Git:

$ git commit -m "Issue-28 This is a description of my changes" .

This commit is added to the history on your branch.

It is important that all of the code in each commit at least compile. If your code does not compile at this point, then do not commit.

Step 6. Rinse and repeat

If you need to make more changes, then repeat steps 2 and 3 until you have completed the work involved in the issue.

Step 7. Push to GitHub and create a Pull-Request

So far, your changes are only on your local machine in your branch. If your last commit compiles and runs successfully on the robot, then the next step is to make them available to others by pushing your branch to your fork on GitHub, and then create a pull-request to have your work merged into the official "upstream" repository on GitHub. The steps to do this are:

$ git push origin {topic_branch_name}

or, with our example of "issue-28" as the topic branch name:

$ git push origin issue-28

This will simply move all of your changes to the {new_branch_name} branch on your GitHub.com fork of the FRC 4931 "2014" repository. If you followed the procedure to set up your environment, then "origin" is the name that your local Git repository uses for the remote Git repository on your fork.

Then, use your browser to go to your fork on GitHub.com. There, you should see a "Create pull-request" button; simply press it, add a meaningful description of your changes, and then create the pull-request.

(If you don't see a "Create pull-request" button", then look for the "Branch" pull-down menu on the page just above the list of files, and choose your topic branch from that list. Then you'll see a "Create pull-request" button on that page.)

One of the mentors will then be notified and will review your proposed changes, and will merge them into the master branch of the FRC 4931 "2014" repository

Step 8. Wait for merging

At this point, your changes are still in the pull-request, and only one of the mentors can merge the changes into the official "upstream" repository. Once they do, you won't receive emails or notifications that commits have been merged, but you may get an email notification that the pull-request was closed. Or, if a pull-request was for a specific issue, then you might get an email notification when that issue is closed.

Often you'll know somehow that the 'master' branch was changed, and you need to then pull the changes into your local Git.

However, at this point your work on this issue may be done, so you can work on something else. Typically, this will be a different issue, and you'll start again with step 0 but you'll use a different topic branch.

Step 9. Pull the changes (everyone)

All developers need to periodically pull all changes that have been merged into the official FRC 4931 "2014" repository, and this is the same as step 1. Just be sure that you have no staged or unstaged changes in your working directory by first running git status on your current branch:

$ git status
# On branch foo
nothing to commit, working directory clean

Then you can checkout the 'master' branch and pull changes.

$ git checkout master
$ git pull upstream master

This command works even when there were no additional commits, so it's okay to run this as often as you like.

There is an alternative if you do have staged or unstaged changes that you don't necessarily want to commit: you can just have Git check whether there are any recent commits, and if so just download them but don't pull them into the 'master' branch. In other words, you can run this at any time on any branch:

$ git fetch upstream

If there are no changes, this command simply returns with no output. But if the command outputs something like the following:

$ git fetch upstream
git pull From github.com:frc-4931/2014
   49e651c..c3a4a3f  master     -> upstream/master

then that means that the 'master' branch has indeed been changed, and at some convenient point you should checkout the 'master' branch and pull the changes onto it using the commands as described above.

Step 10. Remove your old topic branches

After your pull-request has been merged and closed, your topic branches are no longer needed and you can remove them. First, use git status to make sure you're not on the topic branch. (If you are, just checkout another branch, like 'master'.) Then you can remove the local branch:

$ git branch -d {old_branch_name}

Then, remove the branch from your fork:

$ git push origin :{old_branch_name}

(This command seems strange, but it actually of the form git push origin {local-branch}:{remote-branch}, where you are pushing nothing onto the remote branch. That results in Git removing the remote branch.)