Author: boisvert
Date: Fri Aug 17 23:00:57 2012
New Revision: 1374466

URL: http://svn.apache.org/viewvc?rev=1374466&view=rev
Log:
Integrate with Zinc (incremental compilation wrapper for scalac 2.9+)

Modified:
    buildr/trunk/CHANGELOG
    buildr/trunk/doc/languages.textile
    buildr/trunk/lib/buildr/scala/compiler.rb
    buildr/trunk/spec/scala/compiler_spec.rb

Modified: buildr/trunk/CHANGELOG
URL: 
http://svn.apache.org/viewvc/buildr/trunk/CHANGELOG?rev=1374466&r1=1374465&r2=1374466&view=diff
==============================================================================
--- buildr/trunk/CHANGELOG (original)
+++ buildr/trunk/CHANGELOG Fri Aug 17 23:00:57 2012
@@ -1,4 +1,5 @@
 1.4.8 (Pending)
+* Added:   Integrate with Zinc (incremental compilation wrapper for scalac 
2.9+)
 * Changed: Default to Scala 2.9.2, ScalaTest 1.8, Scala Specs2 1.11,
            ScalaCheck 1.10.0.
 * Changed: Scala artifact repository changed to

Modified: buildr/trunk/doc/languages.textile
URL: 
http://svn.apache.org/viewvc/buildr/trunk/doc/languages.textile?rev=1374466&r1=1374465&r2=1374466&view=diff
==============================================================================
--- buildr/trunk/doc/languages.textile (original)
+++ buildr/trunk/doc/languages.textile Fri Aug 17 23:00:57 2012
@@ -202,7 +202,7 @@ The Scala compiler looks for source file
 Any Java source files found in the @src/main/java@ directory will be compiled 
using the Scala/Java joint compiler into the @target/classes@ directory.  Both 
the Java and the Scala sources are compiled with an inclusive classpath, 
meaning that you may have a Java class which depends upon a Scala class which 
depends upon a Java class, all within the same project.  The Java sources will 
be compiled with the same dependencies as the Scala sources with the addition 
of the @scala-library.jar@ file as required for Scala interop.
 
 Note that you cannot use the Groovy *and* the Scala joint compilers in the 
same project.  If both are required, the Groovy joint compiler will take 
precedence.
-+
+
 If you point the @compile@ task at any other source directory, it will use the 
 Scala compiler if any of these directories contains files with the extension 
@.scala@.  The joint compilation of Java sources may only be pointed at an 
alternative directory using the feature to redefine the @_(:src, :main, :java)@ 
path.
 
 When using the Scala compiler, if you don't specify the packaging type, it 
defaults to JAR.
@@ -218,18 +218,25 @@ The Scala compiler supports the followin
 | @:target@       | Bytecode compatibility (e.g. '1.4'). |
 | @:warnings@     | Issue warnings when compiling.  True when running in 
verbose mode. |
 | @:javac@        | A hash of options passed to the @javac@ compiler verbatim. 
|
+| @:incremental@  | If true, enables incremental compilation using Zinc. |
 
 h4. Fast Scala Compiler
 
 You may use @fsc@, the Fast Scala Compiler, which submits compilation jobs to 
a  compilation daemon, by setting the environment variable @USE_FSC@ to @yes@. 
Note that @fsc@ _may_ cache class libraries -- don't forget to run @fsc -reset@ 
if  you upgrade a library.
 
+(Note @fsc@ is not compatible with @zinc@ incremental compilation.)
+
 h4. Rebuild detection
 
+*Scala 2.7*
+
 The Scala 2.7 compiler task assumes that each @.scala@ source file generates a 
 corresponding @.class@ file under @target/classes@ (or @target/test/classses@  
for tests). The source may generate more @.class@ files if it contains more 
than one class, object, trait or for anonymous functions and closures.
 
 For example, @src/main/scala/com/example/MyClass.scala@ should generate at 
least @target/classes/com/example/MyClass.class@. If that it not the case, 
Buildr will always recompile your sources because it will assume this is a new 
source file that has never been compiled before.
 
-Fortunately, Scala 2.8 provides a substantially better interface for 
implementing change detection.  Whenever you use Scala 2.8 (see below), Buildr 
will auto-detect the version and enable this feature dynamically.  After the 
@compile@ task runs, the relevant target directory will contain a @.scala-deps@ 
file, generated by the Scala compiler.  The manner in which this file is used 
can be configured using the @:make@ compiler option.  The following values are 
available:
+*Scala 2.8*
+
+Scala 2.8 provides a substantially better interface for implementing change 
detection.  Whenever you use Scala 2.8 (see below), Buildr will auto-detect the 
version and enable this feature dynamically.  After the @compile@ task runs, 
the relevant target directory will contain a @.scala-deps@ file, generated by 
the Scala compiler.  The manner in which this file is used can be configured 
using the @:make@ compiler option.  The following values are available:
 
 * @:all@ - Disables compiler-level change detection
 * @:changed@ - Only build changed files without considering file dependencies
@@ -247,11 +254,49 @@ Effectively, this is telling the Scala c
 
 To avoid unusual behavior, compiler-level change detection is disabled 
whenever the joint Scala-Java compiler is used.  Thus, any @.java@ files in a 
project handled by the Scala compiler will cause the @:make@ option to be 
ignored and revert to the exclusive use of Buildr's change detection mechanism 
(as described above).
 
-h4. Scala 2.8
+*Scala 2.9 and later*
+
+Starting with Buildr 1.4.8, Buildr integrates with the 
"Zinc":https://github.com/typesafehub/zinc incremental compilation wrapper for 
@scalac@.  Incremental compilation can be enabled 3 ways,
+
+1) By setting the compiler's option directly,
+
+{% highlight ruby %}
+compile.using :incremental => true
+
+compile.options.incremental = true  # same as above
+{% endhighlight %}
 
-As of version 1.4, Buildr has *non-default* support for Scala 2.8.  If your 
@SCALA_HOME@ environment variable is pointing to an installation of Scala 2.8, 
then Buildr will use that compiler and enable 2.8-specific features.
+Note that this won't enable incremental compilation for both @compile@ and 
@test.compile@, you would have to set options on both.  For this reason, it's 
recommended that you set the option on the project instead (see below).
 
-As Scala 2.8 is currently pre-release, it is often desirable to maintain an 
installation of Scala 2.7 concurrently with Scala 2.8, defaulting to the former 
with the option to select the latter on a project-by-project basis.  This is 
most easily accomplished by setting @SCALA_HOME@ to point to the Scala 2.7 
installation and @SCALA28_HOME@ to point to the Scala 2.8 installation.  With 
this configuration in place, Scala 2.8 can be selected for a specific project 
by dynamically reassigning @SCALA_HOME@ at the top of the buildfile (*before* 
@require 'buildr/scala'@):
+2) By setting the project's @scalac_options.incremental@,
+
+{% highlight ruby %}
+project.scalac_options.incremental = true
+{% endhighlight %}
+
+3) By setting the global @scalac.incremental@ option,
+
+in your @buildfile@:
+
+{% highlight ruby %}
+Buildr.settings.build['scalac.incremental'] = true
+{% endhighlight %}
+
+or in your @build.yaml@:
+
+{% highlight yaml %}
+scalac.incremental: true
+{% endhighlight %}
+
+h4. Support for different Scala versions
+
+Buildr defaults to the latest stable Scala version available at the time of 
the release if neither @SCALA_HOME@ nor the @scala.version@ build property are 
set.
+
+If your @SCALA_HOME@ environment variable points to an installation of Scala 
(2.7, 2.8, 2.9, ...), then Buildr will use that compiler and enable 
version-specific features.
+
+You may select the Scala version by dynamically in different ways,
+
+1) By reassigning @SCALA_HOME@ at the top of the buildfile (*before* @require 
'buildr/scala'@):
 
 {% highlight ruby %}
 ENV['SCALA_HOME'] = ENV['SCALA28_HOME']
@@ -260,6 +305,20 @@ require 'buildr/scala'
 ...
 {% endhighlight %}
 
+2) By setting the @scala.version@ build property in your build.yaml file:
+
+{% highlight yaml %}
+scala.version: 2.9.1.RC1
+{% endhighlight %}
+
+3) By setting the @scala.version@ build property in your buildfile:
+
+{% highlight ruby %}
+require 'buildr/scala'
+...
+Buildr.settings.build['scala.version'] = '2.10-M6'
+{% endhighlight %}
+
 h3. Testing with Scala
 
 Buildr supports two main Scala testing frameworks:   
"ScalaTest":http://www.artima.com/scalatest and  
"Specs":http://code.google.com/p/specs/.  
"ScalaCheck":http://code.google.com/p/scalacheck/ is also supported within the 
confines of either of these two frameworks.  Thus, your Specs may use 
ScalaCheck properties, as may your ScalaTest suites.

Modified: buildr/trunk/lib/buildr/scala/compiler.rb
URL: 
http://svn.apache.org/viewvc/buildr/trunk/lib/buildr/scala/compiler.rb?rev=1374466&r1=1374465&r2=1374466&view=diff
==============================================================================
--- buildr/trunk/lib/buildr/scala/compiler.rb (original)
+++ buildr/trunk/lib/buildr/scala/compiler.rb Fri Aug 17 23:00:57 2012
@@ -80,6 +80,10 @@ module Buildr::Scala
   # * :other       -- Array of options to pass to the Scalac compiler as is, 
e.g. -Xprint-types
   class Scalac < Buildr::Compiler::Base
 
+    DEFAULT_ZINC_VERSION  = '0.1.0'
+    DEFAULT_SBT_VERSION   = '0.12.0'
+    DEFAULT_JLINE_VERSION = '1.0'
+
     # The scalac compiler jars are added to classpath at load time,
     # if you want to customize artifact versions, you must set them on the
     #
@@ -93,6 +97,17 @@ module Buildr::Scala
       ns.compiler!     'org.scala-lang:scala-compiler:jar:>=' + version
     end
 
+    ZINC_REQUIRES = ArtifactNamespace.for(self) do |ns|
+      zinc_version  = Buildr.settings.build['zinc.version']  || 
DEFAULT_ZINC_VERSION
+      sbt_version   = Buildr.settings.build['sbt.version']   || 
DEFAULT_SBT_VERSION
+      jline_version = Buildr.settings.build['jline.version'] || 
DEFAULT_JLINE_VERSION
+      ns.zinc!          "com.typesafe.zinc:zinc:jar:>=#{zinc_version}"
+      ns.sbt_interface! "com.typesafe.sbt:sbt-interface:jar:>=#{sbt_version}"
+      ns.incremental!   
"com.typesafe.sbt:incremental-compiler:jar:>=#{sbt_version}"
+      ns.compiler_interface_sources! 
"com.typesafe.sbt:compiler-interface:jar:sources:>=#{sbt_version}"
+      ns.jline!        "jline:jline:jar:>=#{jline_version}"
+    end
+
     class << self
       def scala_home
         env_home = ENV['SCALA_HOME']
@@ -117,11 +132,15 @@ module Buildr::Scala
       end
 
       def dependencies
-        if use_installed?
+        scala_dependencies = if use_installed?
           ['scala-library', 'scala-compiler'].map { |s| 
File.expand_path("lib/#{s}.jar", scala_home) }
         else
           REQUIRES.artifacts.map(&:to_s)
         end
+
+        zinc_dependencies = ZINC_REQUIRES.artifacts.map(&:to_s)
+
+        scala_dependencies + zinc_dependencies
       end
 
       def use_fsc
@@ -155,11 +174,18 @@ module Buildr::Scala
       options[:optimise] ||= false
       options[:make] ||= :transitivenocp if Scala.version? 2.8
       options[:javac] ||= {}
-
       @java = Javac.new(project, options[:javac])
     end
 
     def compile(sources, target, dependencies) #:nodoc:
+      if zinc?
+        compile_with_zinc(sources, target, dependencies)
+      else
+        compile_with_scalac(sources, target, dependencies)
+      end
+    end
+
+    def compile_with_scalac(sources, target, dependencies) #:nodoc:
       check_options(options, OPTIONS + (Scala.version?(2.8) ? [:make] : []))
 
       java_sources = java_sources(sources)
@@ -211,6 +237,40 @@ module Buildr::Scala
       end
     end
 
+    def compile_with_zinc(sources, target, dependencies) #:nodoc:
+
+      java_sources = java_sources(sources)
+
+      dependencies.unshift target
+
+      cmd_args = []
+      cmd_args << '-sbt-interface' << REQUIRES.sbt_interface.artifact
+      cmd_args << '-compiler-interface' << 
REQUIRES.compiler_interface_sources.artifact
+      cmd_args << '-scala-library' << dependencies.find { |d| d =~ 
/scala-library/ }
+      cmd_args << '-scala-compiler' << dependencies.find { |d| d =~ 
/scala-compiler/ }
+      cmd_args << '-classpath' << dependencies.join(File::PATH_SEPARATOR)
+      source_paths = sources.select { |source| File.directory?(source) }
+      cmd_args << '-Ssourcepath' << ("-S" + 
source_paths.join(File::PATH_SEPARATOR)) unless source_paths.empty?
+      cmd_args << '-d' << File.expand_path(target)
+      cmd_args += scalac_args
+      cmd_args << "-debug" if trace?(:scalac)
+
+      cmd_args.map!(&:to_s)
+
+      cmd_args += files_from_sources(sources)
+
+      unless Buildr.application.options.dryrun
+        trace((['com.typesafe.zinc.Main.main'] + cmd_args).join(' '))
+
+        Java.load
+        begin
+          
Java.com.typesafe.zinc.Main.main(cmd_args.to_java(Java.java.lang.String))
+        rescue => e
+          fail "Zinc compiler 
crashed:\n#{e.inspect}\n#{e.backtrace.join("\n")}"
+        end
+      end
+    end
+
   protected
 
     # :nodoc: see Compiler:Base
@@ -255,6 +315,10 @@ module Buildr::Scala
 
   private
 
+    def zinc?
+      (options[:incremental] || @project.scalac_options.incremental || 
(Buildr.settings.build['scalac.incremental'].to_s == "true"))
+    end
+
     def count(file, pattern)
       count = 0
       File.open(file, "r") do |infile|
@@ -283,13 +347,39 @@ module Buildr::Scala
       args << "-deprecation" if options[:deprecation]
       args << "-optimise" if options[:optimise]
       args << "-target:jvm-" + options[:target].to_s if options[:target]
-      args + Array(options[:other])
+      args += Array(options[:other])
+      if zinc?
+        args.map { |arg| "-S" + arg } + Array(options[:zinc_options])
+      else
+        args
+      end
     end
+  end
 
+  module ProjectExtension
+    def scalac_options
+      @scalac ||= ScalacOptions.new(self)
+    end
   end
 
+  class ScalacOptions
+    attr_writer :incremental
+
+    def initialize(project)
+      @project = project
+    end
+
+    def incremental
+      @incremental || (@project.parent ? 
@project.parent.scalac_options.incremental : nil)
+    end
+  end
 end
 
 # Scala compiler comes first, ahead of Javac, this allows it to pick
 # projects that mix Scala and Java code by spotting Scala code first.
 Buildr::Compiler.compilers.unshift Buildr::Scala::Scalac
+
+class Buildr::Project
+  include Buildr::Scala::ProjectExtension
+end
+

Modified: buildr/trunk/spec/scala/compiler_spec.rb
URL: 
http://svn.apache.org/viewvc/buildr/trunk/spec/scala/compiler_spec.rb?rev=1374466&r1=1374465&r2=1374466&view=diff
==============================================================================
--- buildr/trunk/spec/scala/compiler_spec.rb (original)
+++ buildr/trunk/spec/scala/compiler_spec.rb Fri Aug 17 23:00:57 2012
@@ -319,3 +319,39 @@ describe 'scala compiler 2.9 options' do
 
 end if Buildr::Scala.version?(2.9)
 
+describe 'zinc compiler (enabled through Buildr.settings)' do
+  before :each do
+    Buildr.settings.build['scalac.incremental'] = true
+  end
+
+  it 'should compile with zinc' do
+    write 'src/main/scala/com/example/Test.scala', 'package com.example; class 
Test { val i = 1 }'
+    project = define('foo')
+    compile_task = project.compile.using(:scalac)
+    compiler = compile_task.instance_eval { @compiler }
+    compiler.send(:zinc?).should eql(true)
+    compiler.should_receive(:compile_with_zinc).once
+    compile_task.invoke
+  end
+
+  after :each do
+    Buildr.settings.build['scalac.incremental'] = nil
+  end
+
+  it_should_behave_like ScalacCompiler
+end
+
+describe 'zinc compiler (enabled through project.scala_options)' do
+
+  it 'should compile with zinc' do
+    write 'src/main/scala/com/example/Test.scala', 'package com.example; class 
Test { val i = 1 }'
+    project = define('foo')
+    project.scalac_options.incremental = true
+    compile_task = project.compile.using(:scalac)
+    compiler = compile_task.instance_eval { @compiler }
+    compiler.send(:zinc?).should eql(true)
+    compiler.should_receive(:compile_with_zinc).once
+    compile_task.invoke
+  end
+end
+


Reply via email to