Sunday, April 21, 2013

Scala with IntelliJ IDEA setup

So, I'm not a Scala IDE fan. Well, actually, I just can't get anything done in Eclipse, but love Scala. Unfortunately, IntelliJ IDE + ScalaPlugin + SBT has proven to be a bit tempermental, so I figured I'd document at least one successful setup here. The codebase I used for this example configuration is freely available at https://github.com/jasonnerothin/clockwork.git, though the code there probably isn't that interesting in and of itself.

The base state I'm working from:
1. A valid install of Scala version 2.9.2 (I have most of $SCALA_HOME/bin linked through .bash_profile, though I don't think that's strictly necessary.)
2. A valid install of sbt. I use version 0.12.0 and have it on my CLI as well.
3. An install of IntelliJ Idea + Scala plugin (I am currently at 12.1.1 Ultimate)
4. A JDK 7 or greater. (Since I'm on a MAC and Oracle's version of 1.7 is a little broken, I had to build an OpenJDK version to get to a decent state.)
5. Some scala codebase with a working build.sbt in place.

Here's mine:

name := "clockwork"
version := "0.0.1"
organization := "com.jasonnerothin"
scalaVersion := "2.9.2"
resolvers ++= Seq(
                    "snapshots"   at "http://oss.sonatype.org/content/repositories/snapshots",
                    "releases"    at "http://oss.sonatype.org/content/repositories/releases",
                    "central"     at "http://repo1.maven.org/maven2"
                )
scalacOptions ++= Seq("-deprecation", "-unchecked")
retrieveManaged := true
libraryDependencies ++= {
  val jodaConvertVersion = "1.2"
  val jodaTimeVersion   = "2.0"
  val junitVersion      = "4.8.2"
  val logbackVersion    = "1.0.6"
  val mockitoVersion    = "1.9.0"
  val scalaTestVersion  = "1.8"
  Seq(
    "ch.qos.logback"    % "logback-classic"         % logbackVersion            % "compile",
    "joda-time"         % "joda-time"               % jodaTimeVersion           % "test",
    "org.joda"          % "joda-convert"            % jodaConvertVersion        % "test",
    "junit"             % "junit"                   % junitVersion              % "test",
    "org.mockito"       % "mockito-all"             % mockitoVersion            % "test",
    "org.scalatest"     % "scalatest_2.9.2"         % scalaTestVersion          % "test"
  )
}

Once all of that is in place, typing 'sbt' from the command line in your project's base directory should get you something like the following:

So, all that would be well and good, but I'm not a SublimeText guy like all the cool kids and really enjoy my code completion and even admit to using a debugger crutch when particularly weak (only for debugging tests of course).

After starting IDEA and installing the scala plugin, you can create a new project using the new project dialog. (In my case after cloning the github clockwork project as clockwork2…) Just point the working directory at the newly cloned workspace.

Note that I'm choosing the same scala and scala compiler version (2.9.2) as is specified in my build.sbt file.

Opening a test file reveals that Idea's a little confused because it doesn't have it's classpath correctly configured:

But repeating the sbt test process (detailed above) creates a few new entries in the project view. In particular, one called "lib_managed":

Selecting each jar (Cmd-Select in Mac) and right click -> Add As Library…:

…adds the libraries we need to the classpath.

From this point, you can right click on a test and hit Run…

FTM!

Tuesday, February 19, 2013

Trying to use gtest to test something (XCode)

Well, despite some problems with getting the Check target to run, I feel confident enough with my gtest build to go ahead and follow these instructions:

Once you are able to compile the Google Test library, you should create a project or build target for your test program. Make sure you have GTEST_ROOT/include in the header search path so that the compiler can find "gtest/gtest.h" when compiling your test. Set up your test project to link with the Google Test library (for example, in Visual Studio, this is done by adding a dependency on gtest.vcproj).
Being as I'm still a XCode newb, I have to suffer the pain of learning the meanings of terms like “header search path”. I'll just tell you the answer FWIW.

So. I start by creating something skeletal and incomplete to test. This will do:

Please pardon my C++ as well. It's been a while.

The secret header sauce is to set the USER_HEADER_SEARCH_PATHS to GTEST_HOME/include (recursive search), like this:

Which gets us to the point where we can write a test like this:

And configure it to compile and link correctly like this:

Now I just have to figure out how to run the tests... But that's a task for tomorrow - other work intercedes.

Saturday, February 16, 2013

Building gtest with xcode 4.6

So there are two easily surmountable issues to get a clean gtest build in XCode 4.6:

  1. Google no longer gives first rate support for the xcode and visual studio metadata files (preferring a tool called CMake instead)
  2. XCode doesn't like a particular compiler flag on one of its targets.
The good news is that once you know which ones to do, you get essentially the same build output as we accomplished on the “ do it by command line” blog post.

So, as before, we modify gtest/xcode/Config/General.xconfig to make it current-compatible:

I then recommend you open the xcode "project" from gtest/xcode/gtest.xcodeproj. Repeated attempts at Command-B, Command-Shift-K (build, clean, build, ...) will turn up errors and warnings that make you turn your SDKROOT to OS X 10.8 and your GCC_VERSION TO Default Compiler (Apple LLVM compiler 4.2) under Build Settings for the collection of targets. (You may of course choose just Apple LLVM compiler 4.2 for identical effect.)

There's a tricky hidden setting for the gtest_unittest-framework, gtest_unittest-static, sample1_unittest-framework, and sample1_unittest-static targets that should be set with LD_NO_PIE = Yes:

If you get an error including the words “lexical or preprocessor issue”, restart xcode. After I restarted, the issue went away, and then I was offered a dialog that would automagically perform a number of build settings changes, which I gladly chose to accept.

When all is said and done, you should get a similar output dialog if you run any of the targets:

FTM!

Sunday, February 10, 2013

gtest setup

So, basically, to get gtest up and running, I follow the README instructions:

  • svn checkout http://googletest.googlecode.com/svn/trunk/ gtest-svn
  • cd !$
  • more README
  • I notice that some features require python, so I just ensure that I've got it:
    Jasons-MacBook-Air-2:make jason$ python --version
    Python 2.7.2
    
  • So now I go to this section:
    Setting up the Build
    --------------------
    
    To build Google Test and your tests that use it, you need to tell your build system where to find its headers and source files.  The exact way to do it depends on which build system you use, and is usually straightforward.
    
  • export GTEST_DIR=$(pwd)
  • Jasons-MacBook-Air-2:gtest-svn jason$ g++ -I${GTEST_DIR}/include -I${GTEST_DIR} -c ${GTEST_DIR}/src/gtest-all.cc
    
    Jasons-MacBook-Air-2:gtest-svn jason$ ar -rv libgtest.a gtest-all.o
    ar: creating archive libgtest.a
    a - gtest-all.o
    
    Jasons-MacBook-Air-2:gtest-svn jason$ ls -lash libgtest*
    1848 -rw-r--r--  1 jason  staff   924K Feb  9 17:08 libgtest.a
    
  • Notice that the hello world recipe works:
    Jasons-MacBook-Air-2:make jason$ cd $GTEST_DIR/make
    Jasons-MacBook-Air-2:make jason$ make
    c++ -I../include -g -Wall -Wextra -c ../samples/sample1.cc
    c++ -I../include -g -Wall -Wextra -c ../samples/sample1_unittest.cc
    c++ -I../include -I.. -g -Wall -Wextra -c \
                ../src/gtest-all.cc
    In file included from ../src/gtest-all.cc:42:
    ../src/gtest.cc:364:12: warning: missing field 'owner_' initializer [-Wmissing-field-initializers]
    GTEST_API_ GTEST_DEFINE_STATIC_MUTEX_(g_linked_ptr_mutex);
               ^
    ../include/gtest/internal/gtest-port.h:1381:79: note: expanded from macro 'GTEST_DEFINE_STATIC_MUTEX_'
        ::testing::internal::MutexBase mutex = { PTHREAD_MUTEX_INITIALIZER, false }
                                                                                  ^
    1 warning generated.
    c++ -I../include -I.. -g -Wall -Wextra -c \
                ../src/gtest_main.cc
    ar rv gtest_main.a gtest-all.o gtest_main.o
    ar: creating archive gtest_main.a
    a - gtest-all.o
    a - gtest_main.o
    c++ -I../include -g -Wall -Wextra -lpthread sample1.o sample1_unittest.o gtest_main.a -o sample1_unittest
    
    Jasons-MacBook-Air-2:make jason$ ./sample1_unittest 
    Running main() from gtest_main.cc
    [==========] Running 6 tests from 2 test cases.
    [----------] Global test environment set-up.
    [----------] 3 tests from FactorialTest
    [ RUN      ] FactorialTest.Negative
    [       OK ] FactorialTest.Negative (0 ms)
    [ RUN      ] FactorialTest.Zero
    [       OK ] FactorialTest.Zero (0 ms)
    [ RUN      ] FactorialTest.Positive
    [       OK ] FactorialTest.Positive (0 ms)
    [----------] 3 tests from FactorialTest (1 ms total)
    
    [----------] 3 tests from IsPrimeTest
    [ RUN      ] IsPrimeTest.Negative
    [       OK ] IsPrimeTest.Negative (0 ms)
    [ RUN      ] IsPrimeTest.Trivial
    [       OK ] IsPrimeTest.Trivial (0 ms)
    [ RUN      ] IsPrimeTest.Positive
    [       OK ] IsPrimeTest.Positive (0 ms)
    [----------] 3 tests from IsPrimeTest (0 ms total)
    
    [----------] Global test environment tear-down
    [==========] 6 tests from 2 test cases ran. (1 ms total)
    [  PASSED  ] 6 tests.
    
  • So, we reverse engineer what just happened for our purposes:
    /*
      A header file about squirrels and such...
    */
    #ifndef SQUIRREL_
    #define SQUIRREL_
    
    #include 
    
    using namespace std;
    
    // Returns a friendly greeting
    string hello(string whom);
    
    // A nut-collecting animal
    string varmint();
    
    #endif // SQUIRREL_
    
  • And, of course, a unit test...
    /*
            A unit test for squirrel functionality.
    */
    
    // need header files for SUT AND the test harness
    #include "squirrel.h"
    #include "gtest/gtest.h"
    #include 
    
    //using namespace std;
    
    // Tests varmint()
    TEST(SquirrelTest, Varmint){
      EXPECT_EQ( "squirrel", varmint() );
    }
    
    // Tests hello()
    TEST(SquirrelTest, Hello){
      EXPECT_EQ( "hello, squirrel!", hello(varmint()) );
      EXPECT_EQ( "hello, world!", hello("world") );
    }
    
  • And the implementation...
    /*
            squirrel.cc : an implementation
    */
    #ifndef SQUIRREL_
    #define SQUIRREL_
    
    #include "squirrel.h"
    #include 
    
    std::string hello(std::string whom){
      return "hello, " + whom + "!";
    }
    
    std::string varmint(){
      return "squirrel";
    }
    
    #endif
    
  • All held together by a build file:
    #!/bin/bash
    # compile my file
    g++ -I. -c squirrel.cc
    
    # and the main gtest file and my unit test file
    g++ -I. -I${GTEST_DIR}/include -c ${GTEST_DIR}/src/gtest_main.cc
    g++ -I${GTEST_DIR}/include -I${GTEST_DIR} -I. -c squirrel_unittest.cc
    
    # link into a libtest.a propertyar -rv libtest.a ${GTEST_DIR}gtest-all.o gtest_main.o squirrel.o               
    g++ -I${GTEST_DIR} -I${GTEST_DIR}include -g -Wall -Wextra -lpthread libtest.a squirrel.o squirrel_unittest.o -o squirrel_unittest               
    ./squirrel_unittest
    
  • Voila!
    Jasons-MacBook-Air-2:squirrel jason$ ./trybuild 
    r - /Users/jason/src/gtest-svn/gtest-all.o
    r - gtest_main.o
    r - squirrel.o
    Running main() from gtest_main.cc
    [==========] Running 2 tests from 1 test case.
    [----------] Global test environment set-up.
    [----------] 2 tests from SquirrelTest
    [ RUN      ] SquirrelTest.Varmint
    [       OK ] SquirrelTest.Varmint (0 ms)
    [ RUN      ] SquirrelTest.Hello
    [       OK ] SquirrelTest.Hello (0 ms)
    [----------] 2 tests from SquirrelTest (0 ms total)
    
    [----------] Global test environment tear-down
    [==========] 2 tests from 1 test case ran. (0 ms total)
    [  PASSED  ] 2 tests.
    Jasons-MacBook-Air-2:squirrel jason$ 
    

So, at this point, we feel pretty pleased with ourselves, knowing that we've successfully TDD'd a simple bit of c++ functionality from the ground up. Next up is to get the whole mess into a quickly reproducible Xcode and github enabled setup...

Saturday, February 9, 2013

C++ setup

Being as I'm using a Mac, I figure I should be able to get plenty of mileage out of Xcode. So I do the following:

  • Install Xcode (version 4.6)
  • New Project...
    • STL C++ Library
    • Product Name: cookbook
    • Type: Standard Dynamic
    • X : Use Automatic Reference Counting
    • Do NOT create local git repo (want it on github instead)
    • Click the play button (which apparently means build): Build succeeded.
  • Enable developer mode? Yes

I've learned languages in the past using other methods, but by far the best experience I've had is with C#:

  1. (Figure out how to write a test)
  2. Write a test
  3. Figure out how to make the test pass
  4. Go back to step 2, rinse repeat
So, that means I'll need a test framework. For my purposes, I've chosen google test, aka gtest, aka Google Test for C++. So, even though I don't even know how to make my program run (command-R button still disabled), I'm going to start by trying to bootstrap gtest into a working test framework for some as-yet-unwritten toy program…

So, I'm trying to work the algorithms in Knuth's Art of Computer Programming. Ideally, all of them. Since I'm sort of a polyglot, I figure I'll focus on implementing in C++ and scala. C++ because it's been a while and scala because I'd like to try to re-spin each algorithm from a functional perspective. Test first, github, the whole 9. I could do it in Java or C#, but I get paid to do that kind of stuff - i.e. BOOOORRRING.

Last time I compiled my own C++ was like 7 years ago on a linux 2.4 kernel. Straight make on the command line sort of thing. Nowadays my rig looks more like this:
Jasons-MacBook-Air-2:src jason$ uname -a
Darwin Jasons-MacBook-Air-2.local 12.2.0 Darwin Kernel Version 12.2.0: Sat Aug 25 00:48:52 PDT 2012; root:xnu-2050.18.24~1/RELEASE_X86_64 x86_64

Step one will be getting a running DEV environment set up. Should be a trick.