Saturday, June 30, 2012

Unity TDD with MPLAB C18

I'm lucky enough to be involved in a project at work that requires some microcontroller development. Being a convert to Test Driven Development for enterprise-style code, I was keen to take advantage of a testing framework for my embedded code. I bought James Grenning's book Test-Driven Development for Embedded C to get myself started. The Unity framework seems to be just what I need.

I chose the Microchip PIC family of microcontrollers because they appear to be very popular, flexible, easy to program, there are evaluation boards, and there are so many different variants that it should be possible to find just the right one for any occasion. The MPLAB IDE doesn't come close to the IntelliJ or Eclipse benchmarks, but I'm comfortable doing TDD in a simple text editor (Notepad++), so that's ok.

I downloaded Unity and installed the three source files into my MPLAB project. I wrote a simple test:

    #include "unity.h"

    void setUp(void) { }

    void tearDown(void) { }

    void test_demo()
    {
        TEST_ASSERT_EQUAL_INT(2,3);   
    }

and a simple test runner:

    #include "unity.h"
    void test_demo(void);

    void main(void)
    {
        UnityBegin();
        RUN_TEST(test_demo,1);
        UnityEnd();   
    }

Compiling showed that unity_internals.h was looking for a stdint.h header file, but not finding it. I had to define the constant UNITY_EXCLUDE_STDINT_H in my build. To do that, I used Project > Build Options > Project, and on the MPLAB C18 tab, clicked "Add..." to add it as a preprocessor macro.

The next problem was the lack of a putchar() function. I added this function to my test runner:
int putchar(int c)
{
    putc((char)c,stdout);
}

I selected the MPLAB debugger (Debugger > Select Tool > MPLAB Sim), and enabled the UART output tab (Debugger > Settings > Uart 1 IO > Enable, and choose Window for output). When I clicked Run, my SIM Uart1 window filled up with trash. I decided to check putc:

     void main(void)
    {
         putc('a',stdout);  // try this one
         while(1);
         UnityBegin();
         RUN_TEST(test_demo,1);
         UnityEnd();   
     }

Yes, when that runs, the SIM Uart1 window shows an 'a'. Each time I press reset, I get an extra 'a'. Try puts:

    void main(void)
    {
        putc('a',stdout);
        puts("abcde"); // try this one
        while(1);
        UnityBegin();
        RUN_TEST(test_demo,1);
        UnityEnd();   
    }

Yes, that works too. I looked into UnityBegin() (not much there) and then UnityEnd(). That starts with a UnityPrint. Let's try that one.

    void main(void)
    {
        putc('a',stdout);
        puts("abcde");
        UnityPrint("Does this print?");
        while(1);
        UnityBegin();
        RUN_TEST(test_demo,1);
        UnityEnd();   
    }

Looks like there's a problem with UnityPrint(). After some searching, I discovered (in the C18 C Compiler Getting Started manual) that the C18 compiler puts string constants in the code section, so they are const rom char*, rather than just const char *.

After a lot of experimentation, I decided that I needed to have two versions of the UnityPrint() function: one with a const rom char* parameter, and the original one with const char *. The two versions only differ in the signature and the first line:

    void UnityPrint(const char* string)
    {
        const char* pch = string;

    void UnityPrintRom(const rom char* string)
    {
        const rom char* pch = string;
   
When I changed my code to use UnityPrintRom, it worked. By the way, I don't really understand why it should help to have that apparently unused char c. It seems to be necessary to convince the compiler to use the right addressing.

Now I had to make Unity use UnityPrintRom at the appropriate times. I did this by two global search and replaces in unity.c. The first was to change all occurrences of UnityPrint(" to UnityPrintRom(" (there were 8 of these) and the second was to change UnityPrint(UnityStr to UnityPrintRom(UnityStr (there were 50 of these). The last two to change were UnityPrintRom(file) and UnityPrintRom(Unity.CurrentTestName). And I added a couple of prototypes into unity.h:

    void UnityPrintRom(const rom char* string);
    int putchar(int c);

Now, it works.

    testDemo.c:8:test_demo:FAIL: Expected 2 Was 3
    -----------------------
    1 Tests 1 Failures 0 Ignored
    FAIL

And when I fix up the assertion, I get:

    testDemo.c:1:test_demo:PASS
    -----------------------
    1 Tests 0 Failures 0 Ignored
    OK

The last step was to surround these changes with some #ifdef UNITY_MPLAB directives. The resulting code is now in a fork of the original repository at https://github.com/johnyesberg/Unity.



Sunday, May 13, 2012

Windows Development VMs - a long adventure through Virtualbox, Vagrant, VeeWee, Ruby, RVM, Cygwin

It's a deep rabbit hole, I admit. But the prospects of gold at the end of the mixed-metaphor rainbow are enough to make it worth burrowing all the way. Here's the story so far:

In the last five years or so, I've been enjoying a journey towards continuous delivery, with a number of tour-guides and companions (Erik Dörnenburg, Glennn Moy, Steve Dalton, Craig Aspinall, Rob Nielsen, various Yow! conference attendees and presenters, and many bloggers, especially Uncle Bob and lots of Thoughtworkers). It started with pair programming, and then progressed to test-driven development, and most recently, continuous integration, test, and deployment. At each stage, I found that I had acquired additional armour that  provided increasing levels of courage, confidence, freedom, and protection. It's now hard to go back.

My latest adventure is towards DevOps. The gradual accretion of software packages into my development environment can make it unclear what our actual dependencies are. Rather than creating (or building, or deploying) new projects on existing machines, I want to be able to spin up a shiny new vanilla virtual machine, and install a clean set of known software on it. In the Windows world, I don't want to forget to install the right version of the .Net Framework, or the ASP.NET component, or msdeploy, or all the other little bits that seem to be needed.

I've been running a couple of VMs on Virtualbox for some time. Vagrant is a very neat way to start, configure, and stop various VMs - especially when you need multiple VMs to work together. (I also like cumberbatch for testing such configurations.) All I need is a way to create the right base VM. All that interactive clicking to install Windows, SQLServer, SharePoint, etc is ok, but it's hard to document. A script would be better. And VeeWee is the tool for that. It's been working well on linux-related machines for some time, and support has recently been added for Windows. The VeeWee installation guide recommends using RVM (Ruby Version Manager). So to build RVM on Windows, I first had to install (well, add a few packages to) Cygwin.

So how did it all go? Well, there were a few errors. I'm running on a Windows 7 (64 bit) machine, in case that helps anyone.

First, the RVM on Cygwin script did cause a couple of dialog boxes "expr.exe has stopped working" to pop up for each version of Ruby that it installed (1.9.3-p194 and 1.9.2-p320). The log file suggested problems with the compiler, but it seems to have finished...

John@margaux ~/.rvm/log/ruby-1.9.3-p194/yaml
$ cat configure.log
]  ./configure --prefix="/cygdrive/c/Users/John/.rvm/usr"
checking for a BSD-compatible install... config/install-sh -c
checking whether build environment is sane... yes
./configure: line 2562: /cygdrive/c/Program: No such file or directory
configure: WARNING: `missing' script is too old or missing
checking for a thread-safe mkdir -p... /usr/bin/mkdir -p
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking for gcc... gcc
checking whether the C compiler works... no
configure: error: in `/c/Users/John/.rvm/src/yaml-0.1.4':
configure: error: C compiler cannot create executables
See `config.log' for more details
c:\Users\John\Downloads\UnxUtils\usr\local\wbin\sed.exe: -e expression #1, char 1: Unknown command: ``C''

John@margaux ~/.rvm/log/ruby-1.9.3-p194/yaml
I configured RVM to use 1.9.2 as the default, which is what the VeeWee installation suggested.
rvm --default use 1.9.2
 The second error was in the VeeWee installation "bundle install" step.

John@margaux ~/repos/veewee
$ bundle install
Fetching gem metadata from http://rubygems.org/......
Using rake (0.9.2.2)
Installing CFPropertyList (2.1.1)
Installing Platform (0.4.0)
Installing ansi (1.3.0)
Installing archive-tar-minitar (0.5.2)
Installing builder (3.0.0)
Using bundler (1.1.3)
Installing ffi (1.0.11) with native extensions
Installing childprocess (0.3.2)
Installing diff-lcs (1.1.3)
Installing json (1.5.4) with native extensions
Installing gherkin (2.10.0) with native extensions
Installing cucumber (1.2.0)
Installing erubis (2.7.0)
Installing excon (0.9.6)
Installing formatador (0.2.1)
Installing mime-types (1.18)
Installing multi_json (1.0.4)
Installing net-ssh (2.2.2)
Installing net-scp (1.0.4)
Installing nokogiri (1.5.2) with native extensions
Gem::Installer::ExtensionBuildError: ERROR: Failed to build gem native extension.

        /cygdrive/c/Users/John/.rvm/rubies/ruby-1.9.2-p320/bin/ruby.exe extconf.rb
checking for libxml/parser.h... no
-----
libxml2 is missing.  please visit http://nokogiri.org/tutorials/installing_nokogiri.html for help with installing dependencies.
-----
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of
necessary libraries and/or headers.  Check the mkmf.log file for more
details.  You may need configuration options.

Provided configuration options:
        --with-opt-dir
        --with-opt-include
        --without-opt-include=${opt-dir}/include
        --with-opt-lib
        --without-opt-lib=${opt-dir}/lib
        --with-make-prog
        --without-make-prog
        --srcdir=.
        --curdir
        --ruby=/cygdrive/c/Users/John/.rvm/rubies/ruby-1.9.2-p320/bin/ruby
        --with-zlib-dir
        --without-zlib-dir
        --with-zlib-include
        --without-zlib-include=${zlib-dir}/include
        --with-zlib-lib
        --without-zlib-lib=${zlib-dir}/lib
        --with-iconv-dir
        --without-iconv-dir
        --with-iconv-include
        --without-iconv-include=${iconv-dir}/include
        --with-iconv-lib
        --without-iconv-lib=${iconv-dir}/lib
        --with-xml2-dir
        --without-xml2-dir
        --with-xml2-include
        --without-xml2-include=${xml2-dir}/include
        --with-xml2-lib
        --without-xml2-lib=${xml2-dir}/lib
        --with-xslt-dir
        --without-xslt-dir
        --with-xslt-include
        --without-xslt-include=${xslt-dir}/include
        --with-xslt-lib
        --without-xslt-lib=${xslt-dir}/lib


Gem files will remain installed in /cygdrive/c/Users/John/.rvm/gems/ruby-1.9.2-p320@veewee/gems/nokogiri-1.5.2 for inspection.
Results logged to /cygdrive/c/Users/John/.rvm/gems/ruby-1.9.2-p320@veewee/gems/nokogiri-1.5.2/ext/nokogiri/gem_make.out
An error occured while installing nokogiri (1.5.2), and Bundler cannot continue.
Make sure that `gem install nokogiri -v '1.5.2'` succeeds before bundling.
So I had to install libxml2 to get nokogiri working. I went back to my cygwin setup, added libxml2 and libxml2-devel, and tried again. A similar error, reporting that libxslt is missing. In installed libxslt and libxslt-devel with cygwin setup. Then, the nokogiri & the rest of VeeWee installation went smoothly.

At this point, I was ready to start VeeWee-ing for Vagrant, using these directions. I started by creating a new directory ~/boxes to put my VMs in. But veewee wouldn't work from there. I looked into RVM a little, but didn't find anything obvious, so I decided to leave that as an exercise for later. I could make boxes inside ~/repos/veewee for now.
John@margaux ~/repos/veewee
$ veewee vbox define '2008r2' 'windows-2008R2-serverstandard-amd64'
The basebox '2008r2' has been succesfully created from the template 'windows-2008R2-serverstandard-amd64'
You can now edit the definition files stored in definitions/2008r2 or build the box with:
veewee vbox build '2008r2'
$ veewee vbox build  '2008r2'
Error: We executed a shell command and the exit status was not 0
- Command :VBoxManage -v.
- Exitcode :127.
- Output   :
sh: VBoxManage: command not found

Wrong exit code for command VBoxManage -v

John@margaux ~/repos/veewee
$ VBoxManage
bash: VBoxManage: command not found

John@margaux ~/repos/veewee
$
Looks like my virtualbox directory isn't in the path. Added it to the windows environment variable, and restarted my Cygwin bash. Hmm - not there. C:\Program Files\Oracle\Virtualbox shows up in a Cmd window echo %PATH%, but not in a Cygwin bash echo $PATH. How do I get Cygwin to reload the windows environment? Hooray for Stackoverflow, which already had the answer.
John@margaux ~
$ export PATH="$PATH:$(cygpath -pu "`reg query 'HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment' /v PATH| grep PATH | cut -c23-`")"

John@margaux ~
$ VBoxManage -v
4.1.12r77245

John@margaux ~
$
So would my build work now? No.
$ veewee vbox build  '2008r2'
/cygdrive/c/Users/John/.rvm/gems/ruby-1.9.2-p320@veewee/gems/net-ssh-2.2.2/lib/net/ssh/transport/openssl.rb:1:in `require': no such file to load -- openssl (LoadError)
        from /cygdrive/c/Users/John/.rvm/gems/ruby-1.9.2-p320@veewee/gems/net-ssh-2.2.2/lib/net/ssh/transport/openssl.rb:1:in `'
        from /cygdrive/c/Users/John/.rvm/gems/ruby-1.9.2-p320@veewee/gems/net-ssh-2.2.2/lib/net/ssh/buffer.rb:2:in `require'
        from /cygdrive/c/Users/John/.rvm/gems/ruby-1.9.2-p320@veewee/gems/net-ssh-2.2.2/lib/net/ssh/buffer.rb:2:in `'
        from /cygdrive/c/Users/John/.rvm/gems/ruby-1.9.2-p320@veewee/gems/net-ssh-2.2.2/lib/net/ssh/transport/algorithms.rb:1:in `require'
        from /cygdrive/c/Users/John/.rvm/gems/ruby-1.9.2-p320@veewee/gems/net-ssh-2.2.2/lib/net/ssh/transport/algorithms.rb:1:in `'
        from /cygdrive/c/Users/John/.rvm/gems/ruby-1.9.2-p320@veewee/gems/net-ssh-2.2.2/lib/net/ssh/transport/session.rb:7:in `require'
        from /cygdrive/c/Users/John/.rvm/gems/ruby-1.9.2-p320@veewee/gems/net-ssh-2.2.2/lib/net/ssh/transport/session.rb:7:in `'
        from /cygdrive/c/Users/John/.rvm/gems/ruby-1.9.2-p320@veewee/gems/net-ssh-2.2.2/lib/net/ssh.rb:10:in `require'
        from /cygdrive/c/Users/John/.rvm/gems/ruby-1.9.2-p320@veewee/gems/net-ssh-2.2.2/lib/net/ssh.rb:10:in `'
        from /cygdrive/c/Users/John/repos/veewee/lib/veewee/provider/core/helper/ssh.rb:20:in `require'
        from /cygdrive/c/Users/John/repos/veewee/lib/veewee/provider/core/helper/ssh.rb:20:in `'
        from /cygdrive/c/Users/John/repos/veewee/lib/veewee/provider/core/helper/ssh.rb:18:in `'
        from /cygdrive/c/Users/John/repos/veewee/lib/veewee/provider/core/helper/ssh.rb:4:in `'
        from /cygdrive/c/Users/John/repos/veewee/lib/veewee/provider/core/helper/ssh.rb:3:in `'
        from /cygdrive/c/Users/John/repos/veewee/lib/veewee/provider/core/helper/ssh.rb:2:in `'
        from /cygdrive/c/Users/John/repos/veewee/lib/veewee/provider/core/helper/ssh.rb:1:in `'
        from /cygdrive/c/Users/John/repos/veewee/lib/veewee/provider/core/box.rb:2:in `require'
        from /cygdrive/c/Users/John/repos/veewee/lib/veewee/provider/core/box.rb:2:in `'
        from /cygdrive/c/Users/John/repos/veewee/lib/veewee/provider/virtualbox/box.rb:1:in `require'
        from /cygdrive/c/Users/John/repos/veewee/lib/veewee/provider/virtualbox/box.rb:1:in `'
        from /cygdrive/c/Users/John/repos/veewee/lib/veewee/provider/core/provider.rb:34:in `require'
        from /cygdrive/c/Users/John/repos/veewee/lib/veewee/provider/core/provider.rb:34:in `get_box'
        from /cygdrive/c/Users/John/repos/veewee/lib/veewee/command/virtualbox.rb:17:in `build'
        from /cygdrive/c/Users/John/.rvm/gems/ruby-1.9.2-p320@veewee/gems/thor-0.14.6/lib/thor/task.rb:22:in `run'
        from /cygdrive/c/Users/John/.rvm/gems/ruby-1.9.2-p320@veewee/gems/thor-0.14.6/lib/thor/invocation.rb:118:in `invoke_task'
        from /cygdrive/c/Users/John/.rvm/gems/ruby-1.9.2-p320@veewee/gems/thor-0.14.6/lib/thor.rb:263:in `dispatch'
        from /cygdrive/c/Users/John/.rvm/gems/ruby-1.9.2-p320@veewee/gems/thor-0.14.6/lib/thor/invocation.rb:109:in `invoke'
        from /cygdrive/c/Users/John/.rvm/gems/ruby-1.9.2-p320@veewee/gems/thor-0.14.6/lib/thor.rb:205:in `block in subcommand'
        from /cygdrive/c/Users/John/.rvm/gems/ruby-1.9.2-p320@veewee/gems/thor-0.14.6/lib/thor/task.rb:22:in `run'
        from /cygdrive/c/Users/John/.rvm/gems/ruby-1.9.2-p320@veewee/gems/thor-0.14.6/lib/thor/invocation.rb:118:in `invoke_task'
        from /cygdrive/c/Users/John/.rvm/gems/ruby-1.9.2-p320@veewee/gems/thor-0.14.6/lib/thor.rb:263:in `dispatch'
        from /cygdrive/c/Users/John/.rvm/gems/ruby-1.9.2-p320@veewee/gems/thor-0.14.6/lib/thor/base.rb:389:in `start'
        from /cygdrive/c/Users/John/repos/veewee/bin/veewee:18:in `'
        from /cygdrive/c/Users/John/.rvm/gems/ruby-1.9.2-p320@veewee/bin/veewee:23:in `load'
        from /cygdrive/c/Users/John/.rvm/gems/ruby-1.9.2-p320@veewee/bin/veewee:23:in `
'
How to install openssl for ruby? I tried a few things, but I think what I needed to do was install openssl-devel (as well as the openssl I already had) using Cygwin setup. Then I had to
rvm remove 1.9.2
rvm install 1.9.2
rvm --default use 1.9.2
gem install bundler
bundle install
Finally, I'm at the point where running veewee vbox build '2008r2' will actually download the iso from the Internet!

John@margaux ~/repos/veewee
$ veewee vbox build  '2008r2'
Downloading vbox guest additions iso v 4.1.12 - http://download.virtualbox.org/virtualbox/4.1.12/VBoxGuestAdditions_4.1.12.iso
Creating an iso directory
Checking if isofile VBoxGuestAdditions_4.1.12.iso already exists.
Full path: /cygdrive/c/Users/John/repos/veewee/iso/VBoxGuestAdditions_4.1.12.iso
Moving /tmp/open-uri20120513-4468-1cp4y7g to /cygdrive/c/Users/John/repos/veewee/iso/VBoxGuestAdditions_4.1.12.isooooooooooooooooooooooooooooooooooooooooooooooo|  48.4MB 357.7KB/s ETA:   0:00:00
Building Box 2008r2 with Definition 2008r2:
- postinstall_include : []
- postinstall_exclude : []

We did not find an isofile in /iso.

The definition provided the following download information:
- Download url: http://care.dlservice.microsoft.com//dl/download/7/5/E/75EC4E54-5B02-42D6-8879-D8D3A25FBEF7/7601.17514.101119-1850_x64fre_server_eval_en-us-GRMSXEVAL_EN_DVD.iso
- Md5 Checksum: 4263be2cf3c59177c45085c0a7bc6ca5


Download? (Yes/No) Yes
Checking if isofile 7601.17514.101119-1850_x64fre_server_eval_en-us-GRMSXEVAL_EN_DVD.iso already exists.
Full path: /cygdrive/c/Users/John/repos/veewee/iso/7601.17514.101119-1850_x64fre_server_eval_en-us-GRMSXEVAL_EN_DVD.iso
|  34.9MB 306.6KB/s ETA:   2:46:10

I decided interrupt this download, and to modify my definitions/2008r2/definition.rb by commenting out the iso filename line and adding one I had prepared earlier.
    #:iso_file => "7601.17514.101119-1850_x64fre_server_eval_en-us-GRMSXEVAL_EN_DVD.iso",
    :iso_file => "en_windows_server_2008_r2_standard_enterprise_datacenter_web_x64_dvd_x15-50365.iso",
The next problem seems to be that when running VBoxManage, veewee is passing a Cygwin path, rather than a Windows path.
John@margaux ~/repos/veewee
$ veewee vbox build  '2008r2'
Downloading vbox guest additions iso v 4.1.12 - http://download.virtualbox.org/virtualbox/4.1.12/VBoxGuestAdditions_4.1.12.iso
Checking if isofile VBoxGuestAdditions_4.1.12.iso already exists.
Full path: /cygdrive/c/Users/John/repos/veewee/iso/VBoxGuestAdditions_4.1.12.iso

The isofile VBoxGuestAdditions_4.1.12.iso already exists.
Building Box 2008r2 with Definition 2008r2:
- postinstall_include : []
- postinstall_exclude : []

The isofile en_windows_server_2008_r2_standard_enterprise_datacenter_web_x64_dvd_x15-50365.iso already exists.
Received port hint - 59856
Found port 59856 available
Creating vm 2008r2 : 384M - 1 CPU - Windows2008_64
Creating new harddrive of size 10140
Mounting cdrom: /cygdrive/c/Users/John/repos/veewee/iso/en_windows_server_2008_r2_standard_enterprise_datacenter_web_x64_dvd_x15-50365.iso
Error: We executed a shell command and the exit status was not 0
- Command :VBoxManage storageattach "2008r2" --storagectl "IDE Controller" --type dvddrive --port 0 --device 0 --medium "/cygdrive/c/Users/John/repos/veewee/iso/en_windows_server_2008_r2_standard
_enterprise_datacenter_web_x64_dvd_x15-50365.iso".
- Exitcode :1.
- Output   :
VBoxManage.exe: error: Could not find file for the medium 'C:\cygdrive\c\Users\John\repos\veewee\iso\en_windows_server_2008_r2_standard_enterprise_datacenter_web_x64_dvd_x15-50365.iso' (VERR_PATH
_NOT_FOUND)
VBoxManage.exe: error: Details: code VBOX_E_FILE_ERROR (0x80bb0004), component Medium, interface IMedium, callee IUnknown
Context: "OpenMedium(Bstr(pszFilenameOrUuid).raw(), enmDevType, AccessMode_ReadWrite, fForceNewUuidOnOpen, pMedium.asOutParam())" at line 210 of file VBoxManageDisk.cpp
VBoxManage.exe: error: Invalid UUID or filename "/cygdrive/c/Users/John/repos/veewee/iso/en_windows_server_2008_r2_standard_enterprise_datacenter_web_x64_dvd_x15-50365.iso"

Wrong exit code for command VBoxManage storageattach "2008r2" --storagectl "IDE Controller" --type dvddrive --port 0 --device 0 --medium "/cygdrive/c/Users/John/repos/veewee/iso/en_windows_server
_2008_r2_standard_enterprise_datacenter_web_x64_dvd_x15-50365.iso"

John@margaux ~/repos/veewee
$
At this point, I googled to see what could be done, but found little in the way of advice. Maybe next time, this blog entry will come up.I didn't see anything that talked about VeeWee converting from the Cygwin path to a windows path. So I decided to take matters into my own hands, and modify the VeeWee code. I've read a little about ruby, but I'm not an experienced ruby programmer. I wasn't sure how to write code to detect whether it was running under Cygwin, so someone else may have to help with that soon, and perhaps contribute the results back to the project. But my changes were limited to two files.

In the file ~/repos/veewee/lib/veewee/provider/virtualbox/box/helper/create.rb I added a new method at the beginning of the BoxCommand module, i.e. line 6:
        def cygpath(s)
          `/bin/cygpath -w "#{s}"`.chomp
        end
This was inspired by Kevin Kleinfelter's code in this thread, although I had to add the -w flag to make sure the result was a windows path, not a cygwin path, and add quotes to ensure that it would work when there were spaces in the pathname (virtualbox puts spaces in the "Virtualbox VMs" folder name). I then had to call this method a four times: in the attach_disk, attach_isofile, attach_guest_additions, and attach_floppy methods, so that the resulting construction of the command variable looked like this:
command ="#{@vboxcmd} storageattach \"#{name}\" --storagectl \"SATA Controller\" --port 0 --device 0 --type hdd --medium \"#{cygpath(location)}\""
I modified some of the ui.info lines (including moving them below the command assignment) to print the whole command, rather than just the filename.

The second file that needed changing was ~/repos/veewee/lib/veewee/provider/core/box/floppy.rb. It seemed that the construction of the image of the floppy was not working properly for the same reason as above. So I added the same cypath(s) method. But that wasn't enough either. I saw errors like this:
Unable to access jarfile C:UsersJohnreposveeweelibjavadir2floppy.jar
It seemed that Java was devouring an extra backslash. So I added another method escape to floppy.rb.
       def escape(s)
         s.gsub('\\','\\\\\\\\')
       end
And I added a new line assigning jar_file, and updated the assignment to command, and creating a log message:
           jar_file=File.join(javacode_dir,"dir2floppy.jar")
           command="java -jar #{escape(cygpath(jar_file))} \"#{escape(cygpath(temp_dir))}\" \"#{escape(cygpath(floppy_file))}\""
           ui.info("Creating floppy: #{command}")
           shell_exec("#{command}")
Getting all this right took several iterations of running
$ veewee vbox build  '2008r2' --force
I had to add the --force to make veewee really construct the box again. I also had to manually delete the folder C:\Users\John\VirtualBox VMs\2008r2 each time before running that command. But after all these modifications, I eventually saw a Virtualbox window come up, and install windows (with no user intervention), rebooting a few times along the way. My VeeWee seemed to be waiting for sshd to work, but there seemed to be nothing happening on the VM:
$ veewee vbox build  '2008r2' --force
Downloading vbox guest additions iso v 4.1.12 - http://download.virtualbox.org/virtualbox/4.1.12/VBoxGuestAdditions_4.1.12.iso
Checking if isofile VBoxGuestAdditions_4.1.12.iso already exists.
Full path: /cygdrive/c/Users/John/repos/veewee/iso/VBoxGuestAdditions_4.1.12.iso

The isofile VBoxGuestAdditions_4.1.12.iso already exists.
Building Box 2008r2 with Definition 2008r2:
- postinstall_include : []
- postinstall_exclude : []
- force : true

The isofile en_windows_server_2008_r2_standard_enterprise_datacenter_web_x64_dvd_x15-50365.iso already exists.
Received port hint - 59856
Found port 59856 available
Creating vm 2008r2 : 384M - 1 CPU - Windows2008_64
Creating new harddrive of size 10140
Mounting cdrom: C:\Users\John\repos\veewee\iso\en_windows_server_2008_r2_standard_enterprise_datacenter_web_x64_dvd_x15-50365.iso
Mounting guest additions: C:\Users\John\repos\veewee\iso\VBoxGuestAdditions_4.1.12.iso
Attaching disk: C:\Users\John\VirtualBox VMs/2008r2/2008r2.vdi
Creating floppy: java -jar C:\\Users\\John\\repos\\veewee\\lib\\java\\dir2floppy.jar "C:\\cygwin\\tmp\\d20120513-6476-np2ws7" "C:\\Users\\John\\repos\\veewee\\definitions\\2008r2\\virtualfloppy.v
fd"
Received port hint - 59856
Found port 59856 available
Changing ssh port from 5985 to 59856
Waiting 1 seconds for the machine to boot

Typing:[1]:
Done typing.

Skipping webserver as no kickstartfile was specified
Starting a webserver :
Waiting for ssh login on 127.0.0.1 with user vagrant to sshd on port => 59856 to work, timeout=10000 sec
........................
I noticed that there was a C:\cygwin folder on my new VM, but it was empty. No wonder sshd wasn't connecting. The floppy (A:) contained install-cygwin-sshd.bat which was supposed to install cygwin. But it didn't contain cygwin-setup.exe like it should. (I ran the bat file inside a command prompt so that I could see where the first error was occurring.) So I copied that file to C:\Users\John\repos\veewee\definitions\2008r2, added a reference to that file in the ~/repos/veewee/definitions/2008r2/definition.rb file as below.
    :floppy_files => [
      "Autounattend.xml", # automate install and setup winrm
      "install-cygwin-sshd.bat",
      "install-winrm.bat",
      "cygwin-setup.exe",
      "oracle-cert.cer"],
Then shutdown & deleted my VM, and re-ran veewee build. Now I see Cygwin downloading various packages. It will be nice when it can use the repository from my host machine, rather than downloading from the Internet again. I know it should only have to do this once, but if we're developing...

So now I have a server sitting there with Cygwin installed, but it's not answering the sshd request that my host is trying to make. It seems the install-cygwin-sshd.bat may still not be completing. I don't want to run the whole thing, since it would install cygwin again. So I checked parts individually.
  • The cygrunsrv command worked. 
  • The /etc/group and /etc/passed files are there. 
  • The ssh-host-config command seems to run ok.
  • Although the ssh rule is in the firewall configuration, the SSHD one isn't. When I try to run the command manually, the SSHD one complains. It seems the word "SSHD" near the end might be causing problems, since it works correctly if I leave that bit out. Must look into that a bit more.
  • Running "net start sshd" returns " "System error 1069 has occurred. The service did not start due to a logon failure". Checked in the services control panel, and saw that there was an entry for Cygwin sshd, that indeed was not running. Trying to start it caused the same error.
And I'm out of time for today. I hope I'll be able to finish this journey soon, and describe further experience. I'd be pleased to hear from anyone who can suggest better ways to do all this.