private override

29Jun/103

Syntax highlighting for technical presentations

Ever wanted a quick/easy/automated way to get syntax highlighted code from your editor of choice into PowerPoint?

EVERY time I do a technical presentation I need this.  Usually I resort to taking a screenshot, or finding an “export to html” type plugin for the editor I’m using at the time (Visual Studio, Vim, IntelliJ IDEA, etc.) and then try to get that somehow into my slides.

The problem I usually run into, is that while I like coding on a dark background with a 14pt font, that’s not usually that great for presentations.  So I switch to my “presentation settings” that has a light background and bigger font size, and then switch back once I’m done taking screenshots or outputting to html. [ugh, what a pain… terribly manual].

Then what happens when I spot a bug in code that’s in PowerPoint, or I want to add a comment, or I need to change the syntax highlighting theme of the entire presentation because it doesn’t go well with the lighting in the room.  UGH!  What a disaster.

Basically, it’s always an uphill struggle, and it really bothered me… so I decided to fix it once and for all.

Solution

I’m going to solely discuss PowerPoint (for Windows or Mac) here.  I don’t have a copy of iWork, and I’m too cheap to buy one.  The reason I have Office for the Mac, is because I got it for free… KeyNote/Mac zealots: feel free to be zealous, but don’t hate on me because I’m frugal… unless you want to buy me a copy of iWork.

First, download: http://www.andre-simon.de/doku/highlight/en/highlight.html

Windows Solution

The highlight tool can output many formats, but the important one for Windows is RTF.

So something like:

highlight < infile > outfile –-rtf --syntax=rb  --style=vim --font=”Lucida Console” --font-size=18

This will  take the source code file ‘infile’ and syntax color it as Ruby to an RTF file and output it to ‘outfile’.  The text will be 18pt Lucida Console and syntax highlighted with the “vim” color scheme.

There are lots of themes included, you can ask highlight for help (highlight --help), and it’ll tell you all the options available, as well as all the options available for output, and for languages that it supports.

Next, in PowerPoint, do “Insert Object” on your target slide, and choose “Create from file”.  Make sure you check the “Link” checkbox before pressing OK.

Now, whenever your source changes, re-run your command line, then you can choose “Update Link” on your embedded object in PowerPoint, or if you close and then re-open PowerPoint, it’ll give you the option to update all your links.

Mac Solution

My version of PowerPoint for Mac doesn’t like being able to link to RTF files, but it does allow linking to images.

Highlight can output to SVG, but my PowerPoint doesn’t like that either.

Now download inkscape, if you haven’t already.  It will allow you to rasterize the SVG into a PNG, which PowerPoint does like.  You can export your PNG from inkscape from the command line like this:

<path to inkscape> –export-png=<png file> <svg file>

The path to my inkscape command line runner is at:

/Applications/Inkscape.app/Contents/Resources/bin/inkscape

It turns out that inkscape doesn’t like external CSS files, which is what highlight gives you with your SVG, so you can merge your CSS file into your SVG file with a little script.  The other cool bit about SVG, is you can tweak it, if you want, since it’s just XML.

Now, in PowerPoint, choose “Insert Picture” and browse to your newly generated PNG.  Make sure the “Link to File” checkbox is checked.

I wrote a script to do this for the latest presentation I did on dependency injection.  You can take a look at the script here [source.rb].   It’s Ruby, and you can see where I merge the CSS and SVG files together, and where I mess with the line spacing as well (I wasn’t happy with the default line spacing, so I tweaked it to my preference).  All the source/images/PowerPoint for that presentation are available here [dependencies presentation] if you want to check them out to see how the whole process works.

Now, when I need to change the font and syntax theme throughout my entire presentation because it doesn’t match the lighting in the room, it’s super-simple: I change the configuration, re-run my script, update PowerPoint, and chill.

Tagged as: , , 3 Comments
23Sep/091

Autotest… in .NET

The first time I saw autotest (presented by Anthony), the idea of Continuous Testing captured me.

I live in a .NET world most of the time, and I know of no similar solution for .NET.  It’s been awhile since that first time, and I’ve tinkered here and there trying to get something comparable, but usually come up short.  That is until I found watchr.

Watchr gave me the file change detection capabilities I needed, and the extensibility to do whatever I want when a file has been detected as changed.  This made it incredibly easy to hook up some autotest goodness in my .NET world.

You'll have to have ruby installed, and gems.  Then, the very first thing you'll have to do is

gem install watchr --source=http://gemcutter.org

Here is my watchr script:

require 'autotest.rb'

watch( '^.*UnitTest.*.cs$' ) do |match|
  run_test(match.to_s)
end

This is basically just a regex that says to watch any *.cs files that also contain the string “UnitTest”, and when it finds a change in a file matching that description, call run_test with the matched file name.

So all the magic is in autotest.rb… lets check it out:

require 'rexml/document'

def build(test_project)
  `msbuild /nologo #{test_project}`
end

def mstest(test_container, test_results_file, tests_to_run)
  tests_to_run = ([""] << tests_to_run).flatten

  File.delete(test_results_file) if File.exists?(test_results_file)
  `mstest /nologo /resultsfile:#{test_results_file} /testcontainer:#{test_container} #{tests_to_run.join(" /test:")}`
  test_results = process_mstest_results(test_results_file)
  File.delete(test_results_file) if File.exists?(test_results_file)

  return test_results
end

def process_mstest_results(results_file)
  results = {}
  File.open(results_file) do |file|
    xml = REXML::Document.new(file)

    results[:num_tests] = xml.get_elements("//UnitTestResult").length
    failures = []
    xml.elements.each("//UnitTestResult[@outcome='Failed']") do |e|
      failure = {}
      failure[:message] = e.elements["Output/ErrorInfo/Message"].get_text

      stack = e.elements["Output/ErrorInfo/StackTrace"].get_text.value
      stack_match = /^.*at (.*) in(.*):line (\d+)$/.match(stack)

      failure[:stack] = stack_match[1] if stack_match
      failure[:location] = stack_match[2] if stack_match
      failure[:line] = stack_match[3] if stack_match

      failure[:stack] = stack if !stack_match

      failures << failure
    end
    results[:failures] = failures
  end

  return results
end

def show_results(results)
  puts "#{results[:num_tests]} tests run (#{results[:failures].length} failures)"
  results[:failures].each do |failure|
      puts "---------------------------------------"
      puts "Message: #{failure[:message]}"
      puts "Location: #{failure[:location]}"
      puts "Line: #{failure[:line]}"
      puts "Stack Trace: #{failure[:stack]}"
  end
end

def run_test(file_name)
  test_container = ""
  test_results_file = "result.trx"
  test_project = ""

  system("cls")
  system("echo Detected change in:")
  system("echo   #{file_name}")
  system("echo Building and Testing")

  test_namespace = ''
  test_class = ''
  test_names = []

  File.open(file_name, "r") do |f|
    f.each do |line|
      ns_match = /^namespace (.*)$/.match(line)
      test_namespace = ns_match[1] if ns_match

      class_match = /^\s*public class (.\w*).*$/.match(line)
      test_class = class_match[1] if class_match

      test_name_match = /^\s*public void (\w*).*$/.match(line)
      test_names << test_name_match[1] if test_name_match
    end
  end

  test_names = test_names.map { |n| "#{test_namespace}.#{test_class}.#{n}" }

  build(test_project)
  results = mstest(test_container, test_results_file, test_names)
  show_results(results)
end

The key parts (I think) are the fact that I’m using MSTest to run my tests (this can easily be modified to run your framework of choice… note MSTest is not my choice ;) ).  The result parsing is also specific to the MSTest output format, but should be simple enough for any framework that can output XML. Also, I'm making some assumptions based on my project... we've got one unit test project, so I know I can run tests in a single DLL, and rebuilding only that project, I don't have to worry about choosing the correct project and output dll to build and run tests in.

To get the thing up and running, just run

watchr <path to watchr script>

Please, use/adapt/give feedback/whatever at will.

Go forth and autotest, .NET comrades!

23Feb/090

build automation evolution – CruiseControl.rb and .NET (and TFS!)

Last time we talked about using RAKE instead of NAnt/MSBuild to build .NET projects.  Start there if you're curious, or if you missed that episode.  I'll wait.

I've recently been helping out a colleague to get his build server up and running for his new project.  It has been a major pain.  The source code repository is TFS, the build automation tool is MSBuild, and the CI server is Cruise Control .NET.  While these three are all decoupled from each other (TFS/TeamBuild, does really like MSBuild, however), its a bit of a pain to configure all of the XML, get plugins in the right place, etc. to get everything to work just right.  I've been there, done that myself several times, its doable, but not always the easiest thing to do.

Installing CruiseControl.rb is a breeze, except there is an issue with the latest release when trying to run it on Windows.  So instead of downloading it from the website, I would suggest pulling the repository with git, and using that version instead.  First, install msysgit.

Then you can execute the following command in your console

git clone git://rubyforge.org/cruisecontrolrb.git

The rest of the steps are pretty simple and straightforward:

cruise add projectname -u https://path.to.your.svn.repo/yourproject/trunk
cruise start

That will start up the builder and the dashboard.  The dashboard, by default will live on port 3333, so browse to http://yourmachine:3333 to view your dashboard.  The path you use above should point to the directory where your RAKE file lives, that will make it easiest for CruiseControl.rb to get it right.

It's pretty dead-simple to configure your project's builder too, you get sample configuration by default in your %USERDIR%/.cruise/projects/projectname/cruise_config.rb that you can modify however you want.  Here is what that sample looks like:

# Project-specific configuration for CruiseControl.rb

Project.configure do |project|

  # Send email notifications about broken and fixed builds to email1@your.site, email2@your.site (default: send to nobody)
  # project.email_notifier.emails = ['email1@your.site', 'email2@your.site']

  # Set email 'from' field to john@doe.com:
  # project.email_notifier.from = 'john@doe.com'

  # Build the project by invoking rake task 'custom'
  # project.rake_task = 'custom'

  # Build the project by invoking shell script "build_my_app.sh". Keep in mind that when the script is invoked,
  # current working directory is [cruise data]/projects/your_project/work, so if you do not keep build_my_app.sh
  # in version control, it should be '../build_my_app.sh' instead
  # project.build_command = 'build_my_app.sh'

  # Ping Subversion for new revisions every 5 minutes (default: 30 seconds)
  # project.scheduler.polling_interval = 5.minutes

end

Since it is just ruby code, I find that much more appealing than a big nasty XML configuration file, but I guess that's just my opinion.

Hooking it up to TFS

I live in a TFS world at the office, so I have to play by those rules.  But I learned from Morpheus that "... rules of a computer system... can be bent. Others can be broken."  I'm just bending them.

The SvnBridge provides this rule bending behavior, by letting your subversion clients talk to your TFS repository, thereby allowing CruiseControl.rb to poll against what it thinks is a Subversion repository, but is actually a TFS repository.  It's really simple to checkout your TFS repository against it using your favorite SVN repository, so I won't go into it here.

Build Outputs

The one thing I wanted to make super simple was putting things in the right place for build outputs.  Turns out this is fairly simple after a few minutes with the docs.  CruiseControl.rb sets an environment variable telling you where to put things.  I abstracted a getter over top of it, so I can conditionally pick a different output location if I'm not inside of a CruiseControl.rb build.  It looks like this:

def output_dir
  if ENV.keys.include?('CC_BUILD_ARTIFACTS')
    return ENV['CC_BUILD_ARTIFACTS']
  else
    return 'results'
  end
end

As mentioned last time, you can see the latest version of the full RakeFile mentioned above here: http://jonfuller.googlecode.com/svn/trunk/code/CoreLib/RakeFile

I'm feeling some serious CI/Build Automation bliss, hopefully this will get you on your way there too!

23Feb/095

build automation evolution – Rake and .NET

When I first started with build automation, I started out with NAnt.  I loved NAnt. NAnt loved me. We were happy.  I could program anything with the NAnt XML goodness.  If there wasn't a function or task to do what I wanted, I simply wrote one, compiled it,  and wrote some more XML; it couldn't be any more simple!  I think the part I liked the most was the instant gratification I had with being able to automate something that would/could not otherwise be automated [at least not in a simple manner] with a little bit of XML programming.

Soon after NAnt gained popularity, MSBuild was released from Microsoft, which eventually effectively squashed NAnt (IMHO, no stats to back this up).  We never migrated our scripts over to MSBuild because we had significant investment in NAnt already, but it wasn't hard to shell off to MSBuild to compile our solutions.  Eventually I worked on a new project (at a new company) and needed to learn how to use MSBuild since we were using TFS on that project.

Shortly after I started integrating MSBuild into NAnt, and then started learning MSBuild, I started feeling a twinge.  Now that automation is a given, I need something more than programming in this extremely limited XML environment.  Sure, I can write a new MSBuild task just like I did in NAnt, but is it worth it?  My answer is an emphatic no.  I need a great user experience.  Something that feels nice AND is powerful.

Enter RAKE.

It sounds like MAKE; if it looks and feels like MAKE, I might vomit!  No thanks!

Glad you brought that up, Dear Reader (If Hanselman can reference you like that, I can too).  It's not really like MAKE.  In fact, the things you do inside of a RAKE file, is write Ruby code!  RAKE really gives you a nice [internal] DSL for automating tasks.  If there is something you want to do that isn't built in, write a little ruby code to do it.  No compilation and putting the dll in the write place, etc. etc.  Programming in Ruby vs. XML... now that feels nice (requirement #1 above).

But wait!  RAKE is for building Ruby and Rails apps, we can't possibly use it for .NET!

RAKE, just like Ant, NAnt or MSBuild, is a general purpose, task based automation tool.  It may be written in Ruby, but it can build .NET solutions (with the help of MSBuild), Java projects (with the help of Ant or Maven), or Flex, or whatever.  I call that powerful (requirement #2 above).

Please note I'm not claiming to be the first person to do this in .NET, I've found lots of other guys doing it too.

Here is an example of my first rake script for .NET (some pieces borrowed heavily from the Fluent NH guys... thanks!).

Enjoy/Discuss.

An always updated version of this file can be found here: http://jonfuller.googlecode.com/svn/trunk/code/CoreLib/RakeFile

require "BuildUtils.rb"
include FileTest
require 'rubygems'
gem 'rubyzip'
require 'zip/zip'
require 'zip/zipfilesystem'

#building stuff
COMPILE_TARGET = "debug"
CLR_VERSION = "v3.5"
SOLUTION = "src/CoreLib.sln"
MAIN_PROJECT = "CoreLib"

# versioning stuff
BUILD_NUMBER = "0.1.0."
PRODUCT = "CoreLib"
COPYRIGHT = "Copyright © 2009 Jon Fuller"
COMPANY = "Jon Fuller"
COMMON_ASSEMBLY_INFO = "src/CommonAssemblyInfo.cs"

desc "Compiles, tests"
task :all => [:default]

desc "Compiles, tests"
task :default => [:compile, :unit_test, :package]

desc "Update the version information for the build"
task :version do
  builder = AsmInfoBuilder.new BUILD_NUMBER,
    :product   => PRODUCT,
    :copyright => COPYRIGHT,
    :company   => COMPANY
  builder.write COMMON_ASSEMBLY_INFO
end

desc "Prepares the working directory for a new build"
task :clean do
  Dir.mkdir output_dir unless exists?(output_dir)
end

desc "Compiles the app"
task :compile => [:clean, :version] do
  MSBuildRunner.compile :compilemode  => COMPILE_TARGET,
    :solutionfile => SOLUTION,
    :clrversion   => CLR_VERSION
end

desc "Runs unit tests"
task :unit_test => :compile do
  runner = NUnitRunner.new :compilemode => COMPILE_TARGET,
    :source       => 'src',
    :tools        => 'tools',
    :results_file => File.join(output_dir, "nunit.xml")
  runner.executeTests Dir.glob("src/*Test*").map { |proj| proj.split('/').last }
end

desc "Displays a list of tasks"
task :help do
  taskHash = Hash[*(`rake.cmd -T`.split(/\n/).collect { |l| l.match(/rake (\S+)\s+\#\s(.+)/).to_a }.collect { |l| [l[1], l[2]] }).flatten] 

  indent = "                          "

  puts "rake #{indent}#Runs the 'default' task"

  taskHash.each_pair do |key, value|
    if key.nil?
      next
    end
    puts "rake #{key}#{indent.slice(0, indent.length - key.length)}##{value}"
  end
end

desc "Packages the binaries into a zip"
task :package => :compile do
  source_files = Dir.glob("src/#{MAIN_PROJECT}/bin/#{COMPILE_TARGET}/**/*")
  dest_files = source_files.map{ |f| f.sub("src/#{MAIN_PROJECT}/bin/#{COMPILE_TARGET}/", "#{MAIN_PROJECT}/")}
  Zip::ZipFile.open(File.join(output_dir, "#{MAIN_PROJECT}.zip"), 'w') do |zipfile|
    0.upto(source_files.size-1) do |i|
        puts "Zipping #{source_files[i]} to #{dest_files[i]}"
        zipfile.add(dest_files[i], source_files[i])
    end
  end
end

def output_dir
  if ENV.keys.include?('CC_BUILD_ARTIFACTS')
    return ENV['CC_BUILD_ARTIFACTS']
  else
    return 'results'
  end
end