Buildr C0 Coverage Information - RCov

lib/buildr/ide/eclipse.rb

Name Total Lines Lines of Code Total Coverage Code Coverage
lib/buildr/ide/eclipse.rb 433 268
16.86%
20.52%

Key

Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.

Coverage Details

1 # Licensed to the Apache Software Foundation (ASF) under one or more
2 # contributor license agreements.  See the NOTICE file distributed with this
3 # work for additional information regarding copyright ownership.  The ASF
4 # licenses this file to you under the Apache License, Version 2.0 (the
5 # "License"); you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 #    http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
13 # License for the specific language governing permissions and limitations under
14 # the License.
15 
16 
17 require 'buildr/core/project'
18 require 'buildr/packaging'
19 
20 
21 module Buildr
22   module Eclipse #:nodoc:
23     include Extension
24 
25     class Eclipse
26 
27       attr_reader :options
28       attr_writer :name
29 
30       def initialize(project)
31         @project = project
32         @options = Options.new(project)
33       end
34 
35       def name
36         return @name if @name
37         return @project.id.split('-').last if @options.short_names
38         @project.id
39       end
40 
41       # :call-seq:
42       #   classpath_variables :VAR => '/path/to/location'
43       # Sets classpath variables to be used for library path substitution
44       # on the project.
45       #
46       def classpath_variables(*values)
47         fail "eclipse.classpath_variables expects a single hash argument" if values.size > 1
48         if values.size == 1
49           fail "eclipse.classpath_variables expects a Hash argument" unless values[0].is_a? Hash
50           # convert keys to strings
51           values = values[0].inject({}) { |h, (k,v)| h[k.to_s] = @project.path_to(v); h }
52           @variables = values.merge(@variables || {})
53         end
54         @variables || (@project.parent ? @project.parent.eclipse.classpath_variables : default_classpath_variables)
55       end
56 
57       def default_classpath_variables
58         vars = {}
59         vars[:SCALA_HOME] = ENV['SCALA_HOME'] if ENV['SCALA_HOME']
60         vars[:JAVA_HOME]  = ENV['JAVA_HOME']  if ENV['JAVA_HOME']
61         vars
62       end
63 
64       # :call-seq:
65       #   natures=(natures)
66       # Sets the Eclipse project natures on the project.
67       #
68       def natures=(var)
69         @natures = arrayfy(var)
70       end
71 
72       # :call-seq:
73       #   natures() => [n1, n2]
74       # Returns the Eclipse project natures on the project.
75       # They may be derived from the parent project if no specific natures have been set
76       # on the project.
77       #
78       # An Eclipse project nature is used internally by Eclipse to determine the aspects of a project.
79       def natures(*values)
80         if values.size > 0
81           @natures ||= []
82           @natures += values.flatten
83         else
84           @natures || (@project.parent ? @project.parent.eclipse.natures : [])
85         end
86       end
87 
88       # :call-seq:
89       #   classpath_containers=(cc)
90       # Sets the Eclipse project classpath containers on the project.
91       #
92       def classpath_containers=(var)
93         @classpath_containers = arrayfy(var)
94       end
95 
96       # :call-seq:
97       #   classpath_containers() => [con1, con2]
98       # Returns the Eclipse project classpath containers on the project.
99       # They may be derived from the parent project if no specific classpath containers have been set
100       # on the project.
101       #
102       # A classpath container is an Eclipse pre-determined ensemble of dependencies made available to
103       # the project classpath.
104       def classpath_containers(*values)
105         if values.size > 0
106           @classpath_containers ||= []
107           @classpath_containers += values.flatten
108         else
109           @classpath_containers || (@project.parent ? @project.parent.eclipse.classpath_containers : [])
110         end
111       end
112 
113       # :call-seq:
114       #   exclude_libs() => [lib1, lib2]
115       # Returns the an array of libraries to be excluded from the generated Eclipse classpath
116       def exclude_libs(*values)
117         if values.size > 0
118           @exclude_libs ||= []
119           @exclude_libs += values.flatten
120         else
121           @exclude_libs || (@project.parent ? @project.parent.eclipse.exclude_libs : [])
122         end
123       end
124 
125       # :call-seq:
126       #   exclude_libs=(lib1, lib2)
127       # Sets libraries to be excluded from the generated Eclipse classpath
128       #
129       def exclude_libs=(libs)
130         @exclude_libs = arrayfy(libs)
131       end
132 
133       # :call-seq:
134       #   builders=(builders)
135       # Sets the Eclipse project builders on the project.
136       #
137       def builders=(var)
138         @builders = arrayfy(var)
139       end
140 
141       # :call-seq:
142       #   builders() => [b1, b2]
143       # Returns the Eclipse project builders on the project.
144       # They may be derived from the parent project if no specific builders have been set
145       # on the project.
146       #
147       # A builder is an Eclipse background job that parses the source code to produce built artifacts.
148       def builders(*values)
149         if values.size > 0
150           @builders ||= []
151           @builders += values.flatten
152         else
153           @builders || (@project.parent ? @project.parent.eclipse.builders : [])
154         end
155       end
156 
157       private
158 
159       def arrayfy(obj)
160         obj.is_a?(Array) ? obj : [obj]
161       end
162     end
163 
164     class Options
165 
166       attr_writer :m2_repo_var, :short_names
167 
168       def initialize(project)
169         @project = project
170       end
171 
172       # The classpath variable used to point at the local maven2 repository.
173       # Example:
174       #   eclipse.options.m2_repo_var = 'M2_REPO'
175       def m2_repo_var(*values)
176         fail "m2_repo_var can only accept one value: #{values}" if values.size > 1
177         if values.size > 0
178           @m2_repo_var = values[0]
179         else
180           @m2_repo_var || (@project.parent ? @project.parent.eclipse.options.m2_repo_var : 'M2_REPO')
181         end
182       end
183 
184       def short_names
185         @short_names || (@project.parent ? @project.parent.eclipse.options.short_names : false)
186       end
187     end
188 
189     def eclipse
190       @eclipse ||= Eclipse.new(self)
191       @eclipse
192     end
193 
194     first_time do
195       # Global task "eclipse" generates artifacts for all projects.
196       desc 'Generate Eclipse artifacts for all projects'
197       Project.local_task('eclipse'=>'artifacts') { |name|  "Generating Eclipse project for #{name}" }
198     end
199 
200     before_define do |project|
201       project.recursive_task('eclipse')
202     end
203 
204     after_define(:eclipse => :package) do |project|
205       # Need to enhance because using project.projects during load phase of the
206       # buildfile has harmful side-effects on project definition order
207       project.enhance do
208         eclipse = project.task('eclipse')
209         # We don't create the .project and .classpath files if the project contains projects.
210         if project.projects.empty?
211 
212           eclipse.enhance [ file(project.path_to('.classpath')), file(project.path_to('.project')) ]
213 
214           # The only thing we need to look for is a change in the Buildfile.
215           file(project.path_to('.classpath')=>Buildr.application.buildfile) do |task|
216             if (project.eclipse.natures.reject { |x| x.is_a?(Symbol) }.size > 0)
217               info "Writing #{task.name}"
218 
219               m2repo = Buildr::Repositories.instance.local
220 
221               File.open(task.name, 'w') do |file|
222                 classpathentry = ClasspathEntryWriter.new project, file
223                 classpathentry.write do
224                   # Note: Use the test classpath since Eclipse compiles both "main" and "test" classes using the same classpath
225                   cp = project.test.compile.dependencies.map(&:to_s) - [ project.compile.target.to_s, project.resources.target.to_s ]
226                   cp = cp.uniq
227 
228                   # Convert classpath elements into applicable Project objects
229                   cp.collect! { |path| Buildr.projects.detect { |prj| prj.packages.detect { |pkg| pkg.to_s == path } } || path }
230 
231                   # Remove excluded libs
232                   cp -= project.eclipse.exclude_libs.map(&:to_s)
233 
234                   # project_libs: artifacts created by other projects
235                   project_libs, others = cp.partition { |path| path.is_a?(Project) }
236 
237                   # Separate artifacts under known classpath variable paths
238                   # including artifacts located in local Maven2 repository
239                   vars = []
240                   project.eclipse.classpath_variables.merge(project.eclipse.options.m2_repo_var => m2repo).each do |name, path|
241                     matching, others = others.partition { |f| File.expand_path(f.to_s).index(path) == 0 }
242                     matching.each do |m|
243                       vars << [m, name, path]
244                     end
245                   end
246 
247                   # Generated: Any non-file classpath elements in the project are assumed to be generated
248                   libs, generated = others.partition { |path| File.file?(path.to_s) }
249 
250                   classpathentry.src project.compile.sources + generated
251                   classpathentry.src project.resources
252 
253                   if project.test.compile.target
254                     classpathentry.src project.test.compile
255                     classpathentry.src project.test.resources
256                   end
257 
258                   # Classpath elements from other projects
259                   classpathentry.src_projects project_libs
260 
261                   classpathentry.output project.compile.target if project.compile.target
262                   classpathentry.lib libs
263                   classpathentry.var vars
264 
265                   project.eclipse.classpath_containers.each { |container|
266                     classpathentry.con container
267                   }
268                 end
269               end
270             end
271           end
272 
273           # The only thing we need to look for is a change in the Buildfile.
274           file(project.path_to('.project')=>Buildr.application.buildfile) do |task|
275             info "Writing #{task.name}"
276             File.open(task.name, 'w') do |file|
277               xml = Builder::XmlMarkup.new(:target=>file, :indent=>2)
278               xml.projectDescription do
279                 xml.name project.eclipse.name
280                 xml.projects
281                 unless project.eclipse.builders.empty?
282                   xml.buildSpec do
283                     project.eclipse.builders.each { |builder|
284                       xml.buildCommand do
285                         xml.name builder
286                       end
287                     }
288                   end
289                 end
290                 unless project.eclipse.natures.empty?
291                   xml.natures do
292                     project.eclipse.natures.each { |nature|
293                       xml.nature nature unless nature.is_a? Symbol
294                     }
295                   end
296                 end
297               end
298             end
299           end
300         end
301       end
302     end
303 
304 
305     # Writes 'classpathentry' tags in an xml file.
306     # It converts tasks to paths.
307     # It converts absolute paths to relative paths.
308     # It ignores duplicate directories.
309     class ClasspathEntryWriter #:nodoc:
310       def initialize project, target
311         @project = project
312         @xml = Builder::XmlMarkup.new(:target=>target, :indent=>2)
313         @excludes = [ '**/.svn/', '**/CVS/' ].join('|')
314         @paths_written = []
315       end
316 
317       def write &block
318         @xml.classpath &block
319       end
320 
321       def con path
322         @xml.classpathentry :kind=>'con', :path=>path
323       end
324 
325       def lib libs
326         libs.map(&:to_s).sort.uniq.each do |path|
327           @xml.classpathentry :kind=>'lib', :path=>relative(path)
328         end
329       end
330 
331       # Write a classpathentry of kind 'src'.
332       # Accept an array of absolute paths or a task.
333       def src arg
334         if [:sources, :target].all? { |message| arg.respond_to?(message) }
335           src_from_task arg
336         else
337           src_from_absolute_paths arg
338         end
339       end
340 
341       # Write a classpathentry of kind 'src' for dependent projects.
342       # Accept an array of projects.
343       def src_projects project_libs
344         project_libs.map { |project| project.eclipse.name }.sort.uniq.each do |eclipse_name|
345           @xml.classpathentry :kind=>'src', :combineaccessrules=>'false', :path=>"/#{eclipse_name}"
346         end
347       end
348 
349       def output target
350         @xml.classpathentry :kind=>'output', :path=>relative(target)
351       end
352 
353       # Write a classpathentry of kind 'var' (variable) for a library in a local repo.
354       # * +libs+ is an array of library paths.
355       # * +var_name+ is a variable name as defined in Eclipse (e.g., 'M2_REPO').
356       # * +var_value+ is the value of this variable (e.g., '/home/me/.m2').
357       # E.g., <tt>var([lib1, lib2], 'M2_REPO', '/home/me/.m2/repo')</tt>
358       def var(libs)
359         libs.each do |lib_path, var_name, var_value|
360           lib_artifact = file(lib_path)
361 
362           attribs = { :kind => 'var', :path => lib_path }
363 
364           if lib_artifact.respond_to? :sources_artifact
365             attribs[:sourcepath] = lib_artifact.sources_artifact
366           end
367 
368           if lib_artifact.respond_to? :javadoc_artifact
369             attribs[:javadocpath] = lib_artifact.javadoc_artifact
370           end
371 
372           # make all paths relative
373           attribs.each_key do |k|
374             attribs[k] = attribs[k].to_s.sub(var_value, var_name.to_s) if k.to_s =~ /path/
375           end
376 
377           @xml.classpathentry attribs
378         end
379       end
380 
381     private
382 
383       # Find a path relative to the project's root directory if possible. If the
384       # two paths do not share the same root the absolute path is returned. This
385       # can happen on Windows, for instance, when the two paths are not on the
386       # same drive.
387       def relative path
388         path or raise "Invalid path '#{path.inspect}'"
389         msg = [:to_path, :to_str, :to_s].find { |msg| path.respond_to? msg }
390         path = path.__send__(msg)
391         begin
392           relative = Util.relative_path(File.expand_path(path), @project.path_to)
393           if relative['..']
394             # paths don't share same root
395             Util.normalize_path(path)
396           else
397             relative
398           end
399         rescue ArgumentError
400           Util.normalize_path(path)
401         end
402       end
403 
404       def src_from_task task
405         src_from_absolute_paths task.sources, task.target
406       end
407 
408       def src_from_absolute_paths absolute_paths, output=nil
409         relative_paths = absolute_paths.map { |src| relative(src) }
410         relative_paths.sort.uniq.each do |path|
411           unless @paths_written.include?(path)
412             attributes = { :kind=>'src', :path=>path, :excluding=>@excludes }
413             attributes[:output] = relative(output) if output
414             @xml.classpathentry attributes
415             @paths_written << path
416           end
417         end
418       end
419     end
420 
421   end
422 
423 end # module Buildr
424 
425 class Buildr::Project
426   include Buildr::Eclipse
427 end
428 
429 # Order is significant for auto-detection, from most specific to least
430 require 'buildr/ide/eclipse/plugin'
431 require 'buildr/ide/eclipse/scala'
432 require 'buildr/ide/eclipse/java'
433 

Generated on 2011-07-06 23:35:37 -0700 with rcov 0.9.8