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
+