autobuild 1.7.10 → 1.7.11.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -27,12 +27,15 @@ lib/autobuild/packages/gnumake.rb
27
27
  lib/autobuild/packages/import.rb
28
28
  lib/autobuild/packages/orogen.rb
29
29
  lib/autobuild/packages/pkgconfig.rb
30
+ lib/autobuild/packages/ruby.rb
31
+ lib/autobuild/utility.rb
30
32
  lib/autobuild/parallel.rb
31
33
  lib/autobuild/pkgconfig.rb
32
34
  lib/autobuild/reporting.rb
33
35
  lib/autobuild/subcommand.rb
34
36
  lib/autobuild/timestamps.rb
35
37
  lib/autobuild/version.rb
38
+ lib/autobuild/rake_task_extension.rb
36
39
  samples/openrobots.autobuild
37
40
  test/data/cvsroot.tar
38
41
  test/data/svnroot.tar
@@ -4,11 +4,40 @@ if defined? Rake::DSL
4
4
  include Rake::DSL
5
5
  end
6
6
 
7
+ module Autobuild
8
+ end
9
+
10
+ begin
11
+ require 'rmail'
12
+ require 'rmail/serialize'
13
+ Autobuild::HAS_RMAIL = true
14
+ rescue LoadError
15
+ Autobuild::HAS_RMAIL = false
16
+ end
17
+
18
+ require 'net/smtp'
19
+ require 'socket'
20
+ require 'etc'
21
+ require 'find'
22
+ require 'thread'
23
+ require 'pathname'
24
+ require 'shellwords'
25
+ require 'find'
26
+ require 'rake/tasklib'
27
+ require 'fileutils'
28
+
7
29
  require 'autobuild/version'
8
- require 'autobuild/config'
9
- require 'autobuild/configurable'
10
30
  require 'autobuild/environment'
11
31
  require 'autobuild/exceptions'
32
+ require 'autobuild/pkgconfig'
33
+ require 'autobuild/reporting'
34
+ require 'autobuild/subcommand'
35
+ require 'autobuild/timestamps'
36
+ require 'autobuild/parallel'
37
+ require 'autobuild/utility'
38
+ require 'autobuild/config'
39
+
40
+ require 'autobuild/importer'
12
41
  require 'autobuild/import/cvs'
13
42
  require 'autobuild/import/darcs'
14
43
  require 'autobuild/importer'
@@ -17,6 +46,9 @@ require 'autobuild/import/hg'
17
46
  require 'autobuild/import/svn'
18
47
  require 'autobuild/import/archive'
19
48
  require 'autobuild/import/tar'
49
+
50
+ require 'autobuild/package'
51
+ require 'autobuild/configurable'
20
52
  require 'autobuild/packages/autotools'
21
53
  require 'autobuild/packages/cmake'
22
54
  require 'autobuild/packages/genom'
@@ -24,9 +56,7 @@ require 'autobuild/packages/import'
24
56
  require 'autobuild/packages/orogen'
25
57
  require 'autobuild/packages/pkgconfig'
26
58
  require 'autobuild/packages/dummy'
27
- require 'autobuild/pkgconfig'
28
- require 'autobuild/reporting'
29
- require 'autobuild/subcommand'
30
- require 'autobuild/timestamps'
31
- require 'autobuild/parallel'
59
+ require 'autobuild/packages/ruby'
60
+
61
+ require 'autobuild/rake_task_extension'
32
62
 
@@ -33,14 +33,47 @@ end
33
33
  module Autobuild
34
34
  class << self
35
35
  %w{ nice srcdir prefix
36
- verbose debug do_update do_build do_rebuild do_forced_build only_doc do_doc doc_errors
36
+ verbose debug do_update do_build do_rebuild do_forced_build
37
37
  daemonize clean_log packages default_packages
38
- doc_prefix keep_oldlogs}.each do |name|
38
+ keep_oldlogs}.each do |name|
39
39
  attr_accessor name
40
40
  end
41
41
 
42
+ # @return [{String=>Class<Utility>}] the known utilities
43
+ # @see {register_utility_class}
44
+ attr_reader :utilities
45
+
46
+ def register_utility_class(name, klass)
47
+ utilities[name] = klass
48
+ singleton_class.class_eval do
49
+ attr_accessor "only_#{name}"
50
+ attr_accessor "do_#{name}"
51
+ attr_accessor "#{name}_prefix"
52
+ attr_accessor "pass_#{name}_errors"
53
+ end
54
+ instance_variable_set "@only_#{name}", false
55
+ instance_variable_set "@do_#{name}", false
56
+ instance_variable_set "@pass_#{name}_errors", false
57
+ instance_variable_set "@#{name}_prefix", name
58
+ end
59
+
60
+ def create_utility(utility_name, package)
61
+ if klass = utilities[utility_name]
62
+ package.utilities[utility_name] = klass.new(utility_name, package)
63
+ else raise ArgumentError, "there is no utility called #{utility_name}, available utilities are #{utilities.keys.sort.join(", ")}"
64
+ end
65
+ end
66
+
42
67
  # Configure the programs used by different packages
43
68
  attr_reader :programs
69
+ # A cache of entries in programs to their resolved full path
70
+ #
71
+ # @return [{String=>[String,String,String]}] the triplet (full path,
72
+ # tool name, value of ENV['PATH']). The last two values are used to
73
+ # invalidate the cache when needed
74
+ #
75
+ # @see tool_in_path
76
+ attr_reader :programs_in_path
44
77
  # The directory in which logs are saved. Defaults to PREFIX/log.
45
78
  attr_writer :logdir
46
79
 
@@ -52,6 +85,9 @@ module Autobuild
52
85
  do_build && !only_doc && packages.empty?
53
86
  end
54
87
  end
88
+ @utilities = Hash.new
89
+ register_utility_class 'doc', Utility
90
+ register_utility_class 'test', Utility
55
91
 
56
92
  @console = HighLine.new
57
93
 
@@ -74,11 +110,10 @@ module Autobuild
74
110
  DEFAULT_OPTIONS = { :nice => nil,
75
111
  :srcdir => Dir.pwd, :prefix => Dir.pwd, :logdir => nil,
76
112
  :verbose => false, :debug => false, :do_build => true, :do_forced_build => false, :do_rebuild => false, :do_update => true,
77
- :daemonize => false, :packages => [], :default_packages => [],
78
- :only_doc => false, :do_doc => true, :doc_errors => false,
79
- :doc_prefix => 'doc', :keep_oldlogs => false }
113
+ :daemonize => false, :packages => [], :default_packages => [], :keep_oldlogs => false }
80
114
 
81
115
  @programs = Hash.new
116
+ @programs_in_path = Hash.new
82
117
  DEFAULT_OPTIONS.each do |name, value|
83
118
  send("#{name}=", value)
84
119
  end
@@ -167,6 +202,40 @@ module Autobuild
167
202
  programs[name.to_sym] || programs[name.to_s] || name.to_s
168
203
  end
169
204
 
205
+ # Resolves the absolute path to a given tool
206
+ def tool_in_path(name)
207
+ path, path_name, path_env = programs_in_path[name]
208
+ current = tool(name)
209
+ if path_env != ENV['PATH'] || path_name != current
210
+ # Delete the current entry given that it is invalid
211
+ programs_in_path.delete(name)
212
+ if current[0, 1] == "/"
213
+ # This is already a full path
214
+ path = current
215
+ else
216
+ path = ENV['PATH'].split(':').
217
+ find { |dir| File.exists?(File.join(dir, current)) }
218
+ if path
219
+ path = File.join(path, current)
220
+ end
221
+ end
222
+
223
+ if !path
224
+ raise ArgumentError, "tool #{name}, set to #{current}, can not be found in PATH=#{path_env}"
225
+ end
226
+
227
+ # Verify that the new value is a file and is executable
228
+ if !File.file?(path)
229
+ raise ArgumentError, "tool #{name} is set to #{current}, but this resolves to #{path} which is not a file"
230
+ elsif !File.executable?(path)
231
+ raise ArgumentError, "tool #{name} is set to #{current}, but this resolves to #{path} which is not executable"
232
+ end
233
+ programs_in_path[name] = [path, current, ENV['PATH']]
234
+ end
235
+
236
+ return path
237
+ end
238
+
170
239
  # Gets autobuild options from the command line and returns the
171
240
  # remaining elements
172
241
  def commandline(args)
@@ -192,7 +261,7 @@ module Autobuild
192
261
  opts.on("--rebuild", "clean and rebuild") do |v| Autobuild.do_forced_build = v end
193
262
  opts.on("--only-doc", "only generate documentation") do |v| Autobuild.only_doc = v end
194
263
  opts.on("--no-doc", "don't generate documentation") do |v| Autobuild.do_doc = v end
195
- opts.on("--doc-errors", "treat documentation failure as error") do |v| Autobuild.doc_errors = v end
264
+ opts.on("--doc-errors", "treat documentation failure as error") do |v| Autobuild.pass_doc_errors = v end
196
265
 
197
266
  opts.separator ""
198
267
  opts.separator "Program output"
@@ -239,7 +308,7 @@ module Autobuild
239
308
  end
240
309
  end
241
310
 
242
- def self.apply(packages, buildname = "autobuild")
311
+ def self.apply(packages, buildname = "autobuild", phases = [])
243
312
  if Autobuild.mail[:to]
244
313
  if !Autobuild::HAS_RMAIL
245
314
  Autobuild.warn "RMail is not available. Mail notification is disabled"
@@ -261,13 +330,16 @@ module Autobuild
261
330
  end
262
331
  end
263
332
 
264
- if Autobuild.only_doc
265
- phases = ['doc']
266
- else
267
- phases = ['import']
268
- phases += ['prepare', 'build'] if Autobuild.do_build
269
- phases << 'doc' if Autobuild.do_doc
333
+ if phases.empty?
334
+ if Autobuild.only_doc
335
+ phases = ['doc']
336
+ else
337
+ phases = ['import']
338
+ phases += ['prepare', 'build'] if Autobuild.do_build
339
+ phases << 'doc' if Autobuild.do_doc
340
+ end
270
341
  end
342
+
271
343
  phases.each do |phase|
272
344
  # We create a dummy task listing what needs to be done, and then we
273
345
  # call it
@@ -1,10 +1,3 @@
1
- require 'pathname'
2
- require 'autobuild/timestamps'
3
- require 'autobuild/environment'
4
- require 'autobuild/package'
5
- require 'autobuild/subcommand'
6
- require 'shellwords'
7
-
8
1
  module Autobuild
9
2
  # Base class for packages that require a configuration + build step.
10
3
  #
@@ -414,10 +414,9 @@ module Autobuild
414
414
  end
415
415
 
416
416
  require 'rbconfig'
417
- ruby_arch = File.basename(RbConfig::CONFIG['archdir'])
418
- candidates = %w{rubylibdir archdir sitelibdir sitearchdir vendorlibdir vendorarchdir}.
417
+ %w{rubylibdir archdir sitelibdir sitearchdir vendorlibdir vendorarchdir}.
419
418
  map { |key| RbConfig::CONFIG[key] }.
420
- map { |path| path.gsub(/.*lib(?:32|64)?\/(\w*ruby\/)/, '\\1') }.
419
+ map { |path| path.gsub(/.*lib(?:32|64)?\//, '\\1') }.
421
420
  each do |subdir|
422
421
  if File.directory?("#{newprefix}/lib/#{subdir}")
423
422
  env_add_path("RUBYLIB", "#{newprefix}/lib/#{subdir}")
@@ -32,6 +32,30 @@ module Autobuild
32
32
  # Known URI schemes for +url+
33
33
  VALID_URI_SCHEMES = [ 'file', 'http', 'https', 'ftp' ]
34
34
 
35
+ class << self
36
+ # The directory in which downloaded files are saved
37
+ #
38
+ # It defaults, by order of priority, to the archives/ subdirectory
39
+ # of the environment variable AUTOBUILD_CACHE_DIR (if set), to the
40
+ # AUTOBUILD_ARCHIVES_CACHE_DIR (if set) environment variable and to
41
+ # #{prefix}/cache
42
+ def cachedir
43
+ if @cachedir then @cachedir
44
+ elsif dir = ENV['AUTOBUILD_ARCHIVES_CACHE_DIR']
45
+ @cachedir = File.expand_path(dir)
46
+ elsif dir = ENV['AUTOBUILD_CACHE_DIR']
47
+ @cachedir = File.join(File.expand_path(dir), 'archives')
48
+ else
49
+ @cachedir = "#{Autobuild.prefix}/cache"
50
+ end
51
+ end
52
+
53
+ # Sets the directory in which files get cached
54
+ attr_writer :cachedir
55
+ end
56
+
57
+ @cachedir = nil
58
+
35
59
  # Returns the unpack mode from the file name
36
60
  def self.filename_to_mode(filename)
37
61
  case filename
@@ -112,7 +136,7 @@ module Autobuild
112
136
  size = File.stat(@url.path).size
113
137
  mtime = File.stat(@url.path).mtime
114
138
  else
115
- open @url, :content_length_proc => lambda { |size| } do |file|
139
+ open @url, :content_length_proc => lambda { |v| size = v } do |file|
116
140
  mtime = file.last_modified
117
141
  end
118
142
  end
@@ -163,7 +187,9 @@ module Autobuild
163
187
  # The unpack mode. One of Zip, Bzip, Gzip or Plain
164
188
  attr_reader :mode
165
189
  # The directory in which remote files are cached
166
- def cachedir; @options[:cachedir] end
190
+ #
191
+ # Defaults to ArchiveImporter.cachedir
192
+ attr_accessor :cachedir
167
193
  # The directory contained in the tar file
168
194
  #
169
195
  # DEPRECATED use #archive_dir instead
@@ -192,7 +218,7 @@ module Autobuild
192
218
  if !@options.has_key?(:update_cached_file)
193
219
  @options[:update_cached_file] = false
194
220
  end
195
- @options[:cachedir] ||= "#{Autobuild.prefix}/cache"
221
+ @cachedir = @options[:cachedir] || ArchiveImporter.cachedir
196
222
 
197
223
  relocate(url)
198
224
  end
@@ -214,7 +240,11 @@ module Autobuild
214
240
  end
215
241
  end
216
242
 
217
- def update(package) # :nodoc:
243
+ def update(package,only_local = false) # :nodoc:
244
+ if only_local
245
+ Autobuild.warn "The importer #{self.class} does not support local updates, skipping #{self}"
246
+ return
247
+ end
218
248
  needs_update = update_cache(package)
219
249
 
220
250
  if !File.file?(checkout_digest_stamp(package))
@@ -1,7 +1,3 @@
1
- require 'autobuild/config'
2
- require 'autobuild/subcommand'
3
- require 'autobuild/importer'
4
-
5
1
  module Autobuild
6
2
  class CVSImporter < Importer
7
3
  # Creates a new importer which gets the module +name+ from the
@@ -38,7 +34,11 @@ module Autobuild
38
34
 
39
35
  private
40
36
 
41
- def update(package) # :nodoc:
37
+ def update(package,only_local=false) # :nodoc:
38
+ if only_local
39
+ Autobuild.warn "The importer #{self.class} does not support local updates, skipping #{self}"
40
+ return
41
+ end
42
42
  Dir.chdir(package.srcdir) do
43
43
  if !File.exists?("#{package.srcdir}/CVS/Root")
44
44
  raise ConfigException.new(package, 'import'), "#{package.srcdir} is not a CVS working copy"
@@ -22,7 +22,11 @@ module Autobuild
22
22
 
23
23
  private
24
24
 
25
- def update(package) # :nodoc:
25
+ def update(package,only_local=false) # :nodoc:
26
+ if only_local
27
+ Autobuild.warn "The importer #{self.class} does not support local updates, skipping #{self}"
28
+ return
29
+ end
26
30
  if !File.directory?( File.join(package.srcdir, '_darcs') )
27
31
  raise ConfigException.new(package, 'import'), "#{package.srcdir} is not a Darcs repository"
28
32
  end
@@ -5,6 +5,36 @@ require 'utilrb/kernel/options'
5
5
 
6
6
  module Autobuild
7
7
  class Git < Importer
8
+ class << self
9
+ # Sets the default alternates path used by all Git importers
10
+ #
11
+ # Setting it explicitly overrides any value we get from the
12
+ # AUTOBUILD_CACHE_DIR and AUTOBUILD_GIT_CACHE_DIR environment
13
+ # variables.
14
+ #
15
+ # @see default_alternates
16
+ attr_writer :default_alternates
17
+
18
+ # A default list of repositories that should be used as reference
19
+ # repositories for all Git importers
20
+ #
21
+ # It is initialized (by order of priority) using the
22
+ # AUTOBUILD_GIT_CACHE_DIR and AUTOBUILD_CACHE_DIR environment
23
+ # variables
24
+ #
25
+ # @return [Array]
26
+ # @see default_alternates=, Git#alternates
27
+ def default_alternates
28
+ if @default_alternates then @default_alternates
29
+ elsif cache_dir = ENV['AUTOBUILD_GIT_CACHE_DIR']
30
+ @default_alternates = cache_dir.split(':').map { |path| File.expand_path(path) }
31
+ elsif cache_dir = ENV['AUTOBUILD_CACHE_DIR']
32
+ @default_alternates = cache_dir.split(':').map { |path| File.join(File.expand_path(path), 'git') }
33
+ else Array.new
34
+ end
35
+ end
36
+ end
37
+
8
38
  # Creates an importer which tracks the given repository
9
39
  # and branch. +source+ is [repository, branch]
10
40
  #
@@ -14,6 +44,7 @@ module Autobuild
14
44
  # Autobuild.programs['git'] = 'my_git_tool'
15
45
  def initialize(repository, branch = nil, options = {})
16
46
  @repository = repository.to_str
47
+ @alternates = Git.default_alternates.dup
17
48
 
18
49
  if branch.respond_to?(:to_hash)
19
50
  options = branch.to_hash
@@ -28,11 +59,12 @@ module Autobuild
28
59
  Autobuild.warn " Autobuild.git 'git://gitorious.org/rock/buildconf.git', :branch => 'master'"
29
60
  end
30
61
 
31
- gitopts, common = Kernel.filter_options options, :push_to => nil, :branch => nil, :tag => nil, :commit => nil
62
+ gitopts, common = Kernel.filter_options options, :push_to => nil, :branch => nil, :tag => nil, :commit => nil, :with_submodules => false
32
63
  if gitopts[:branch] && branch
33
64
  raise ConfigException, "git branch specified with both the option hash and the explicit parameter"
34
65
  end
35
66
  @push_to = gitopts[:push_to]
67
+ @with_submodules = gitopts[:with_submodules]
36
68
  branch = gitopts[:branch] || branch
37
69
  tag = gitopts[:tag]
38
70
  commit = gitopts[:commit]
@@ -40,6 +72,7 @@ module Autobuild
40
72
  @branch = branch || 'master'
41
73
  @tag = tag
42
74
  @commit = commit
75
+ @remote_name = 'autobuild'
43
76
  super(common)
44
77
  end
45
78
 
@@ -50,6 +83,11 @@ module Autobuild
50
83
  "git:#{repository}"
51
84
  end
52
85
 
86
+ # The name of the remote that should be set up by the importer
87
+ #
88
+ # Defaults to 'autobuild'
89
+ attr_accessor :remote_name
90
+
53
91
  # The remote repository URL.
54
92
  #
55
93
  # See also #push_to
@@ -67,6 +105,11 @@ module Autobuild
67
105
  # Defaults to #branch
68
106
  attr_writer :remote_branch
69
107
 
108
+ # Set to true if checkout should be done with submodules
109
+ #
110
+ # Defaults to #false
111
+ attr_writer :with_submodules
112
+
70
113
  # The branch this importer is tracking
71
114
  #
72
115
  # If set, both commit and tag have to be nil.
@@ -77,6 +120,24 @@ module Autobuild
77
120
  # If not set, it defaults to #branch
78
121
  attr_writer :local_branch
79
122
 
123
+ # A list of local (same-host) repositories that will be used instead of
124
+ # the remote one when possible. It has one major issue (see below), so
125
+ # use at your own risk.
126
+ #
127
+ # The paths must point to the git directory, so either the .git
128
+ # directory in a checked out git repository, or the repository itself in
129
+ # a bare repository.
130
+ #
131
+ # A default reference repository can be given through the
132
+ # AUTOBUILD_GIT_CACHE environment variable.
133
+ #
134
+ # Note that it has the major caveat that if objects disappear from the
135
+ # reference repository, the current one will be broken. See the git
136
+ # documentation for more information.
137
+ #
138
+ # @return [Array<String>]
139
+ attr_accessor :alternates
140
+
80
141
  # The branch that should be used on the local clone
81
142
  #
82
143
  # Defaults to #branch
@@ -106,6 +167,10 @@ module Autobuild
106
167
  # a fast-forward
107
168
  def merge?; !!@merge end
108
169
 
170
+ #Return true if the git checkout should be done with submodules
171
+ #detaul it false
172
+ def with_submodules?; !!@with_submodules end
173
+
109
174
  # Set the merge flag. See #merge?
110
175
  def merge=(flag); @merge = flag end
111
176
 
@@ -151,70 +216,73 @@ module Autobuild
151
216
  end
152
217
  end
153
218
 
219
+ def update_cache(package, cache_dir, phase)
220
+ remote_name = package.name.gsub(/[^\w]/, '_')
221
+ Subprocess.run(*git, "remote.#{remote_name}.url", repository)
222
+ Subprocess.run(*git, "remote.#{remote_name}.fetch", "+refs/heads/*:refs/remotes/#{remote_name}/*")
223
+ Subprocess.run(*git, 'fetch', '--tags', remote_name)
224
+ end
225
+
226
+ # Updates the git repository's configuration for the target remote
227
+ def update_remotes_configuration(package, phase)
228
+ git = [package, phase, Autobuild.tool(:git), '--git-dir', File.join(package.importdir, '.git'), 'config', '--replace-all']
229
+ Subprocess.run(*git, "remote.#{remote_name}.url", repository)
230
+ if push_to
231
+ Subprocess.run(*git, "remote.#{remote_name}.pushurl", push_to)
232
+ end
233
+ Subprocess.run(*git, "remote.#{remote_name}.fetch", "+refs/heads/*:refs/remotes/#{remote_name}/*")
234
+
235
+ if remote_branch && local_branch
236
+ Subprocess.run(*git, "remote.#{remote_name}.push", "refs/heads/#{local_branch}:refs/heads/#{remote_branch}")
237
+ else
238
+ Subprocess.run(*git, "remote.#{remote_name}.push", "refs/heads/*:refs/heads/*")
239
+ end
240
+
241
+ if local_branch
242
+ Subprocess.run(*git, "branch.#{local_branch}.remote", remote_name)
243
+ Subprocess.run(*git, "branch.#{local_branch}.merge", "refs/heads/#{local_branch}")
244
+ end
245
+ end
246
+
154
247
  # Fetches updates from the remote repository. Returns the remote commit
155
248
  # ID on success, nil on failure. Expects the current directory to be the
156
249
  # package's source directory.
157
250
  def fetch_remote(package)
158
251
  validate_importdir(package)
159
- Dir.chdir(package.importdir) do
160
- # If we are checking out a specific commit, we don't know which
161
- # branch to refer to in git fetch. So, we have to set up the
162
- # remotes and call git fetch directly (so that all branches get
163
- # fetch)
164
- #
165
- # Otherwise, do git fetch now
166
- #
167
- # Doing it now is better as it makes sure that we replace the
168
- # configuration parameters only if the repository and branch are
169
- # OK (i.e. we keep old working configuration instead)
170
- if branch || tag
171
- Subprocess.run(package, :import, Autobuild.tool('git'), 'fetch', repository, branch || tag)
172
- elsif commit
173
- Subprocess.run(package, :import, Autobuild.tool('git'), 'fetch', repository)
174
- end
175
-
176
- Subprocess.run(package, :import, Autobuild.tool('git'), 'config',
177
- "--replace-all", "remote.autobuild.url", repository)
178
- if push_to
179
- Subprocess.run(package, :import, Autobuild.tool('git'), 'config',
180
- "--replace-all", "remote.autobuild.pushurl", push_to)
181
- end
182
- Subprocess.run(package, :import, Autobuild.tool('git'), 'config',
183
- "--replace-all", "remote.autobuild.fetch", "+refs/heads/*:refs/remotes/autobuild/*")
184
-
185
- if remote_branch && local_branch
186
- Subprocess.run(package, :import, Autobuild.tool('git'), 'config',
187
- "--replace-all", "remote.autobuild.push", "refs/heads/#{local_branch}:refs/heads/#{remote_branch}")
188
- else
189
- Subprocess.run(package, :import, Autobuild.tool('git'), 'config',
190
- "--replace-all", "remote.autobuild.push", "refs/heads/*:refs/heads/*")
191
- end
192
-
193
- if local_branch
194
- Subprocess.run(package, :import, Autobuild.tool('git'), 'config',
195
- "--replace-all", "branch.#{local_branch}.remote", "autobuild")
196
- Subprocess.run(package, :import, Autobuild.tool('git'), 'config',
197
- "--replace-all", "branch.#{local_branch}.merge", "refs/heads/#{local_branch}")
198
- end
199
-
200
- # Now get the actual commit ID from the FETCH_HEAD file, and
201
- # return it
202
- commit_id = if File.readable?( File.join('.git', 'FETCH_HEAD') )
203
- fetch_commit = File.readlines( File.join('.git', 'FETCH_HEAD') ).
204
- delete_if { |l| l =~ /not-for-merge/ }
205
- if !fetch_commit.empty?
206
- fetch_commit.first.split(/\s+/).first
207
- end
208
- end
209
-
210
- # Update the remote tag if needs be
211
- if branch && commit_id
212
- Subprocess.run(package, :import, Autobuild.tool('git'), 'update-ref',
213
- "-m", "updated by autobuild", "refs/remotes/autobuild/#{remote_branch}", commit_id)
252
+ git = [package, :import, Autobuild.tool('git'), '--git-dir', File.join(package.importdir, '.git')]
253
+
254
+ # If we are checking out a specific commit, we don't know which
255
+ # branch to refer to in git fetch. So, we have to set up the
256
+ # remotes and call git fetch directly (so that all branches get
257
+ # fetch)
258
+ #
259
+ # Otherwise, do git fetch now
260
+ #
261
+ # Doing it now is better as it makes sure that we replace the
262
+ # configuration parameters only if the repository and branch are
263
+ # OK (i.e. we keep old working configuration instead)
264
+ refspec = [branch || tag].compact
265
+ Subprocess.run(*git, 'fetch', '--tags', repository, *refspec)
266
+
267
+ update_remotes_configuration(package, :import)
268
+
269
+ # Now get the actual commit ID from the FETCH_HEAD file, and
270
+ # return it
271
+ commit_id = if File.readable?( File.join(package.importdir, '.git', 'FETCH_HEAD') )
272
+ fetch_commit = File.readlines( File.join(package.importdir, '.git', 'FETCH_HEAD') ).
273
+ delete_if { |l| l =~ /not-for-merge/ }
274
+ if !fetch_commit.empty?
275
+ fetch_commit.first.split(/\s+/).first
214
276
  end
277
+ end
215
278
 
216
- commit_id
279
+ # Update the remote tag if needs be
280
+ if branch && commit_id
281
+ Subprocess.run(*git, 'update-ref',
282
+ "-m", "updated by autobuild", "refs/remotes/#{remote_name}/#{remote_branch}", commit_id)
217
283
  end
284
+
285
+ commit_id
218
286
  end
219
287
 
220
288
  # Returns a Importer::Status object that represents the status of this
@@ -224,7 +292,7 @@ module Autobuild
224
292
  validate_importdir(package)
225
293
  remote_commit = nil
226
294
  if only_local
227
- remote_commit = `git show-ref -s refs/remotes/autobuild/#{remote_branch}`.chomp
295
+ remote_commit = `git show-ref -s refs/remotes/#{remote_name}/#{remote_branch}`.chomp
228
296
  else
229
297
  remote_commit =
230
298
  begin fetch_remote(package)
@@ -355,10 +423,48 @@ module Autobuild
355
423
  Status.new(status, fetch_commit, head_commit, common_commit)
356
424
  end
357
425
 
358
- def update(package)
426
+ # Updates the git alternates file in the already checked out package to
427
+ # match {#alternates}
428
+ #
429
+ # @param [Package] package the already checked-out package
430
+ # @return [void]
431
+ def update_alternates(package)
432
+ alternates_path = File.join(package.importdir, '.git', 'objects', 'info', 'alternates')
433
+ current_alternates =
434
+ if File.file?(alternates_path)
435
+ File.readlines(alternates_path).map(&:strip).find_all { |l| !l.empty? }
436
+ else Array.new
437
+ end
438
+
439
+ alternates = self.alternates.map do |path|
440
+ File.join(path, 'objects')
441
+ end
442
+
443
+ if current_alternates.sort != alternates.sort
444
+ # Warn that something is fishy, but assume that the user knows
445
+ # what he is doing
446
+ package.warn "%s: the list of git alternates listed in the repository differs from the one set up in autobuild."
447
+ package.warn "%s: I will update, but that is dangerous"
448
+ package.warn "%s: using git alternates is for advanced users only, who know git very well."
449
+ package.warn "%s: Don't complain if something breaks"
450
+ end
451
+ if alternates.empty?
452
+ FileUtils.rm_f alternates_path
453
+ else
454
+ File.open(alternates_path, 'w') do |io|
455
+ io.write alternates.join("\n")
456
+ end
457
+ end
458
+ end
459
+
460
+ def update(package,only_local = false)
359
461
  validate_importdir(package)
462
+ update_alternates(package)
360
463
  Dir.chdir(package.importdir) do
361
- fetch_commit = fetch_remote(package)
464
+ #Checking if we should only merge our repro to remotes/HEAD without updateing from the remote side...
465
+ if !only_local
466
+ fetch_commit = fetch_remote(package)
467
+ end
362
468
 
363
469
  # If we are tracking a commit/tag, just check it out and return
364
470
  if commit || tag
@@ -429,17 +535,25 @@ module Autobuild
429
535
  FileUtils.mkdir_p base_dir
430
536
  end
431
537
 
538
+ clone_options = ['-o', remote_name]
539
+
540
+ if with_submodules?
541
+ clone_options << '--recurse-submodules'
542
+ end
543
+ alternates.each do |path|
544
+ clone_options << '--reference' << path
545
+ end
432
546
  Subprocess.run(package, :import,
433
- Autobuild.tool('git'), 'clone', '-o', 'autobuild', repository, package.importdir)
547
+ Autobuild.tool('git'), 'clone', *clone_options, repository, package.importdir)
434
548
 
435
549
  Dir.chdir(package.importdir) do
436
550
  if push_to
437
551
  Subprocess.run(package, :import, Autobuild.tool('git'), 'config',
438
- "--replace-all", "remote.autobuild.pushurl", push_to)
552
+ "--replace-all", "remote.#{remote_name}.pushurl", push_to)
439
553
  end
440
554
  if local_branch && remote_branch
441
555
  Subprocess.run(package, :import, Autobuild.tool('git'), 'config',
442
- "--replace-all", "remote.autobuild.push", "refs/heads/#{local_branch}:refs/heads/#{remote_branch}")
556
+ "--replace-all", "remote.#{remote_name}.push", "refs/heads/#{local_branch}:refs/heads/#{remote_branch}")
443
557
  end
444
558
 
445
559
  # If we are tracking a commit/tag, just check it out
@@ -453,10 +567,10 @@ module Autobuild
453
567
  current_branch = `git symbolic-ref HEAD`.chomp
454
568
  if current_branch == "refs/heads/#{local_branch}"
455
569
  Subprocess.run(package, :import, Autobuild.tool('git'),
456
- 'reset', '--hard', "autobuild/#{branch}")
570
+ 'reset', '--hard', "#{remote_name}/#{branch}")
457
571
  else
458
572
  Subprocess.run(package, :import, Autobuild.tool('git'),
459
- 'checkout', '-b', local_branch, "autobuild/#{branch}")
573
+ 'checkout', '-b', local_branch, "#{remote_name}/#{branch}")
460
574
  end
461
575
  end
462
576
  end
@@ -466,6 +580,38 @@ module Autobuild
466
580
  def relocate(repository)
467
581
  @repository = repository
468
582
  end
583
+
584
+ # Tests whether the given directory is a git repository
585
+ def self.can_handle?(path)
586
+ File.directory?(File.join(path, '.git'))
587
+ end
588
+
589
+ # Returns a hash that represents the configuration of a git importer
590
+ # based on the information contained in the git configuration
591
+ #
592
+ # @raise [ArgumentError] if the path does not point to a git repository
593
+ def self.vcs_definition_for(path)
594
+ if !can_handle?(path)
595
+ raise ArgumentError, "#{path} is not a git repository"
596
+ end
597
+
598
+ Dir.chdir(path) do
599
+ vars = `git config -l`.
600
+ split("\n").
601
+ inject(Hash.new) do |h, line|
602
+ k, v = line.strip.split('=', 2)
603
+ h[k] = v
604
+ h
605
+ end
606
+ url = vars["remote.#{remote_name}.url"] ||
607
+ vars['remote.origin.url']
608
+ if url
609
+ return Hash[:type => :git, :url => url]
610
+ else
611
+ return Hash[:type => :git]
612
+ end
613
+ end
614
+ end
469
615
  end
470
616
 
471
617
  # Creates a git importer which gets the source for the given repository and branch