autobuild 1.2.15 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,6 +5,38 @@ require 'autobuild/exceptions'
5
5
  # various RCS into the package source directory. A list of patches to apply
6
6
  # after the import can be given in the +:patches+ option.
7
7
  class Autobuild::Importer
8
+ # Instances of the Importer::Status class represent the status of a current
9
+ # checkout w.r.t. the remote repository.
10
+ class Status
11
+ # Remote and local are at the same point
12
+ UP_TO_DATE = 0
13
+ # Local contains all data that remote has, but has new commits
14
+ ADVANCED = 1
15
+ # Next update will require a merge
16
+ NEEDS_MERGE = 2
17
+ # Next update will be simple (no merge)
18
+ SIMPLE_UPDATE = 3
19
+
20
+ # The update status
21
+ attr_accessor :status
22
+ # True if there is code in the working copy that is not committed
23
+ attr_accessor :uncommitted_code
24
+
25
+ # An array of strings that represent commits that are in the remote
26
+ # repository and not in this one (would be merged by an update)
27
+ attr_accessor :remote_commits
28
+ # An array of strings that represent commits that are in the local
29
+ # repository and not in the remote one (would be pushed by an update)
30
+ attr_accessor :local_commits
31
+
32
+ def initialize
33
+ @status = -1
34
+ @uncommitted_code = false
35
+ @remote_commits = Array.new
36
+ @local_commits = Array.new
37
+ end
38
+ end
39
+
8
40
  # Creates a new Importer object. The options known to Importer are:
9
41
  # [:patches] a list of patch to apply after import
10
42
  #
@@ -20,10 +52,13 @@ class Autobuild::Importer
20
52
  srcdir = package.srcdir
21
53
  if File.directory?(srcdir)
22
54
  if Autobuild.do_update
55
+ Autobuild.progress "updating #{package.name}"
23
56
  update(package)
24
57
  patch(package)
25
58
  else
26
- puts "Not updating #{package.name}"
59
+ if Autobuild.verbose
60
+ puts " not updating #{package.name}"
61
+ end
27
62
  return
28
63
  end
29
64
 
@@ -31,6 +66,7 @@ class Autobuild::Importer
31
66
  raise ConfigException, "#{srcdir} exists but is not a directory"
32
67
  else
33
68
  begin
69
+ Autobuild.progress "checking out #{package.name}"
34
70
  checkout(package)
35
71
  patch(package)
36
72
  rescue Autobuild::Exception
@@ -68,6 +104,10 @@ class Autobuild::Importer
68
104
  end
69
105
  end
70
106
 
107
+ if !patches.empty?
108
+ Autobuild.progress "patching #{package.name}"
109
+ end
110
+
71
111
  # Do not be smart, remove all already applied patches
72
112
  # and then apply the new ones
73
113
  begin
@@ -11,6 +11,35 @@ module Autobuild
11
11
  # - import
12
12
  # - prepare
13
13
  # - build & install
14
+ #
15
+ # In the first stage checks the source out and/or updates it.
16
+ #
17
+ # In the second stage, packages create their dependency structure to handle
18
+ # specific build systems. For instance, it is there that build systems like
19
+ # CMake are handled so that reconfiguration happens if needed. In the same
20
+ # way, it is there that code generation will happen as well.
21
+ #
22
+ # Finally, the build stage actually calls the package's build targets (of
23
+ # the form "package_name-build", which will trigger the build if needed.
24
+ #
25
+ # <b>Autodetecting dependencies</b>
26
+ # There are two sides in dependency autodetection. The first side is that
27
+ # packages must declare what they provide. One example is the handling of
28
+ # pkgconfig dependencies: packages must declare that they provide a
29
+ # pkgconfig definition. This side of the autodetection must be done just
30
+ # after the package's import, by overloading the #import method:
31
+ #
32
+ # def import
33
+ # super
34
+ #
35
+ # # Do autodetection and call Package#provides
36
+ # end
37
+ #
38
+ # Note that, in most cases, the overloaded import method *must* begin with
39
+ # "super".
40
+ #
41
+ # The other side is the detection itself. That must be done by overloading
42
+ # the #prepare method.
14
43
  class Package
15
44
  @@packages = {}
16
45
  @@provides = {}
@@ -23,6 +52,9 @@ module Autobuild
23
52
  # set the installation directory. If a relative path is given,
24
53
  # it is relative to Autobuild.prefix. Defaults to ''
25
54
  attr_writer :prefix
55
+ # Sets the log directory. If no value is set, the package will use
56
+ # Autobuild.logdir
57
+ attr_writer :logdir
26
58
 
27
59
  # Sets importer object for this package. Defined for backwards compatibility.
28
60
  # Use the #importer attribute instead
@@ -44,7 +76,9 @@ module Autobuild
44
76
  # has finished. The path is absolute
45
77
  #
46
78
  # A package is sucessfully built when it is installed
47
- def installstamp; "#{Autobuild.logdir}/#{name}-#{STAMPFILE}" end
79
+ def installstamp
80
+ File.join(@logdir || Autobuild.logdir, "#{name}-#{STAMPFILE}")
81
+ end
48
82
 
49
83
  def initialize(spec)
50
84
  @dependencies = Array.new
@@ -66,17 +100,8 @@ module Autobuild
66
100
 
67
101
  @doc_dir ||= 'doc'
68
102
  @doc_target_dir ||= name
69
-
70
- # Declare the installation stampfile
71
- file installstamp do
72
- Dir.chdir(srcdir) do
73
- Autobuild.apply_post_install(name, @post_install)
74
- end
75
- end
76
- # Add dependencies declared in spec
77
- depends_on *depends if depends
78
103
 
79
- # Define the import task
104
+ # Define the default tasks
80
105
  task "#{name}-import" do import end
81
106
  task :import => "#{name}-import"
82
107
 
@@ -84,7 +109,7 @@ module Autobuild
84
109
  task "#{name}-prepare" => "#{name}-import" do prepare end
85
110
  task :prepare => "#{name}-prepare"
86
111
 
87
- task "#{name}-build" => ["#{name}-prepare", installstamp]
112
+ task "#{name}-build" => "#{name}-prepare"
88
113
  task :build => "#{name}-build"
89
114
 
90
115
  task(name) do
@@ -96,10 +121,34 @@ module Autobuild
96
121
  end
97
122
  end
98
123
  task :default => name
124
+
125
+ # The dependencies will be declared in the prepare phase, so save
126
+ # them there for now
127
+ @spec_dependencies = depends
99
128
  end
100
129
 
130
+ # Call the importer if there is one. Autodetection of "provides" should
131
+ # be done there as well. See the documentation of Autobuild::Package for
132
+ # more information.
101
133
  def import; @importer.import(self) if @importer end
102
- def prepare; end
134
+ # Create all the dependencies required to reconfigure and/or rebuild the
135
+ # package when required. The package's build target is called
136
+ # "package_name-build".
137
+ def prepare
138
+ super if defined? super
139
+
140
+ task "#{name}-build" => installstamp
141
+ # Declare the installation stampfile
142
+ file installstamp do
143
+ Dir.chdir(srcdir) do
144
+ Autobuild.apply_post_install(name, @post_install)
145
+ end
146
+ end
147
+ # Add dependencies declared in spec
148
+ depends_on *@spec_dependencies if @spec_dependencies
149
+
150
+ Autobuild.update_environment prefix
151
+ end
103
152
 
104
153
  # Directory in which the documentation target will have generated the
105
154
  # documentation (if any). The interpretation of relative directories
@@ -141,6 +190,9 @@ module Autobuild
141
190
  raise
142
191
  else
143
192
  STDERR.puts "W: failed to generate documentation for #{name}"
193
+ if e.kind_of?(SubcommandFailed)
194
+ STDERR.puts "W: see #{e.logfile} for more details"
195
+ end
144
196
  end
145
197
  end
146
198
  end
@@ -183,7 +235,9 @@ module Autobuild
183
235
  end
184
236
  end
185
237
 
186
- # This package depends on +packages+
238
+ # This package depends on +packages+. It means that its build will
239
+ # always be triggered after the packages listed in +packages+ are built
240
+ # and installed.
187
241
  def depends_on(*packages)
188
242
  packages.each do |p|
189
243
  p = p.to_s
@@ -198,7 +252,8 @@ module Autobuild
198
252
  end
199
253
  end
200
254
 
201
- # Declare that this package provides +packages+
255
+ # Declare that this package provides +packages+. In effect, the names
256
+ # listed in +packages+ are aliases for this package.
202
257
  def provides(*packages)
203
258
  packages.each do |p|
204
259
  p = p.to_s
@@ -51,8 +51,6 @@ module Autobuild
51
51
  @configureflags = []
52
52
 
53
53
  super
54
-
55
- Autobuild.update_environment(prefix)
56
54
  end
57
55
 
58
56
  def install_doc(relative_to = builddir)
@@ -63,6 +61,7 @@ module Autobuild
63
61
  def with_doc(target = 'doc')
64
62
  doc_task do
65
63
  Dir.chdir(builddir) do
64
+ Autobuild.progress "generating documentation for #{name}"
66
65
  Subprocess.run(name, 'doc', Autobuild.tool(:make), target)
67
66
  yield if block_given?
68
67
  end
@@ -120,6 +119,8 @@ module Autobuild
120
119
  end
121
120
 
122
121
  def prepare
122
+ super
123
+
123
124
  configureflags.flatten!
124
125
 
125
126
  # Check if config.status has been generated with the
@@ -146,7 +147,7 @@ module Autobuild
146
147
  configure
147
148
  end
148
149
 
149
- source_tree srcdir do |pkg|
150
+ Autobuild.source_tree srcdir do |pkg|
150
151
  pkg.exclude << Regexp.new("^#{Regexp.quote(builddir)}")
151
152
  end
152
153
  file buildstamp => [ srcdir, "#{builddir}/config.status" ] do
@@ -157,10 +158,10 @@ module Autobuild
157
158
 
158
159
  file installstamp => buildstamp do
159
160
  install
160
- Autobuild.update_environment(prefix)
161
161
  end
162
- end
163
162
 
163
+ Autobuild.update_environment(prefix)
164
+ end
164
165
 
165
166
  private
166
167
  # Adds a target to rebuild the autotools environment
@@ -180,6 +181,7 @@ module Autobuild
180
181
  using[:autogen] = %w{autogen autogen.sh}.find { |f| File.exists?(f) }
181
182
  end
182
183
 
184
+ Autobuild.progress "generating build system for #{name}"
183
185
  if using[:autogen]
184
186
  Subprocess.run(name, 'configure', File.expand_path(using[:autogen]))
185
187
  else
@@ -221,6 +223,7 @@ module Autobuild
221
223
  command = [ "#{srcdir}/configure", "--no-create", "--prefix=#{prefix}" ]
222
224
  command += Array[*configureflags]
223
225
 
226
+ Autobuild.progress "configuring build system for #{name}"
224
227
  Subprocess.run(name, 'configure', *command)
225
228
  }
226
229
  end
@@ -228,18 +231,21 @@ module Autobuild
228
231
  # Do the build in builddir
229
232
  def build
230
233
  Dir.chdir(builddir) {
234
+ Autobuild.progress "building #{name}"
231
235
  Subprocess.run(name, 'build', './config.status')
232
236
  Subprocess.run(name, 'build', Autobuild.tool(:make))
233
237
  }
234
- touch_stamp(buildstamp)
238
+ Autobuild.touch_stamp(buildstamp)
235
239
  end
236
240
 
237
241
  # Install the result in prefix
238
242
  def install
239
243
  Dir.chdir(builddir) {
244
+ Autobuild.progress "installing #{name}"
240
245
  Subprocess.run(name, 'install', Autobuild.tool(:make), 'install')
241
246
  }
242
- touch_stamp(installstamp)
247
+ Autobuild.touch_stamp(installstamp)
248
+ Autobuild.update_environment(prefix)
243
249
  end
244
250
  end
245
251
  end
@@ -39,6 +39,7 @@ module Autobuild
39
39
  def with_doc(target = 'doc')
40
40
  doc_task do
41
41
  Dir.chdir(builddir) do
42
+ Autobuild.progress "generating documentation for #{name}"
42
43
  Subprocess.run(name, 'doc', Autobuild.tool(:make), target)
43
44
  yield if block_given?
44
45
  end
@@ -65,6 +66,15 @@ module Autobuild
65
66
  end
66
67
  end
67
68
 
69
+ def import
70
+ super
71
+
72
+ Dir.glob(File.join(srcdir, "*.pc.in")) do |file|
73
+ file = File.basename(file, ".pc.in")
74
+ provides "pkgconfig/#{file}"
75
+ end
76
+ end
77
+
68
78
  def prepare
69
79
  super
70
80
 
@@ -115,6 +125,7 @@ module Autobuild
115
125
  end
116
126
  command << srcdir
117
127
 
128
+ Autobuild.progress "generating and configuring build system for #{name}"
118
129
  Subprocess.run(name, 'configure', *command)
119
130
  super
120
131
  end
@@ -123,18 +134,21 @@ module Autobuild
123
134
  # Do the build in builddir
124
135
  def build
125
136
  Dir.chdir(builddir) do
137
+ Autobuild.progress "building #{name}"
126
138
  if always_reconfigure || !File.file?('Makefile')
127
139
  Subprocess.run(name, 'build', Autobuild.tool(:cmake), '.')
128
140
  end
129
141
  Subprocess.run(name, 'build', Autobuild.tool(:make))
130
142
  end
131
- touch_stamp(buildstamp)
143
+ Autobuild.touch_stamp(buildstamp)
132
144
  end
133
145
 
134
146
  # Install the result in prefix
135
147
  def install
136
148
  Dir.chdir(builddir) do
149
+ Autobuild.progress "installing #{name}"
137
150
  Subprocess.run(name, 'install', Autobuild.tool(:make), 'install')
151
+ Autobuild.update_environment prefix
138
152
  end
139
153
  super
140
154
  end
@@ -89,10 +89,10 @@ module Autobuild
89
89
  genom_pkg = PkgConfig.new('genom')
90
90
 
91
91
  includedir = File.join(genom_pkg.includedir, 'genom')
92
- source_tree includedir
92
+ Autobuild.source_tree includedir
93
93
 
94
94
  canvasdir = File.join(genom_pkg.prefix, "share", "genom", genom_pkg.version);;
95
- source_tree canvasdir
95
+ Autobuild.source_tree canvasdir
96
96
 
97
97
  binary = File.join(genom_pkg.exec_prefix, "bin", "genom")
98
98
  file binary
@@ -121,6 +121,7 @@ module Autobuild
121
121
  file genomstamp => genom_dependencies
122
122
  file genomstamp => srcdir do
123
123
  Dir.chdir(srcdir) do
124
+ Autobuild.progress "generating GenoM files for #{package.name}"
124
125
  Subprocess.run(name, 'genom', *cmdline)
125
126
  end
126
127
  end
@@ -130,6 +131,7 @@ module Autobuild
130
131
  # configure does not depend on the .gen file
131
132
  # since the generation takes care of rebuilding configure
132
133
  # if .gen has changed
134
+ Autobuild.progress "generating build system for #{package.name}"
133
135
  Dir.chdir(srcdir) { Subprocess.run(name, 'genom', File.expand_path('autogen')) }
134
136
  end
135
137
 
@@ -16,15 +16,20 @@ module Autobuild
16
16
  def initialize(*args)
17
17
  @exclude = []
18
18
  super
19
+ end
20
+
21
+ def prepare
22
+ super
23
+
19
24
  exclude << Regexp.new("^#{Regexp.quote(installstamp)}")
20
25
 
21
- source_tree(srcdir) do |pkg|
26
+ Autobuild.source_tree(srcdir) do |pkg|
22
27
  pkg.exclude.concat exclude
23
28
  exclude.freeze
24
29
  end
25
30
 
26
31
  file installstamp => srcdir do
27
- touch_stamp installstamp
32
+ Autobuild.touch_stamp installstamp
28
33
  end
29
34
  end
30
35
  end
@@ -3,11 +3,141 @@ module Autobuild
3
3
  Orogen.new(opts, &proc)
4
4
  end
5
5
 
6
+
7
+ # This discards everything but the calls to import_types_from,
8
+ # using_task_library and using_toolkit. This is used to automatically
9
+ # discover the dependencies without resorting to an actual build
10
+ class FakeOrogenEnvironment
11
+ class BlackHole
12
+ def initialize(*args)
13
+ end
14
+ def method_missing(*args)
15
+ self
16
+ end
17
+ def self.method_missing(*args)
18
+ self
19
+ end
20
+ def self.const_missing(*args)
21
+ self
22
+ end
23
+ end
24
+ StaticDeployment = BlackHole
25
+ StaticDeployment::Logger = BlackHole
26
+ TaskContext = BlackHole
27
+
28
+ class FakeDeployment
29
+ attr_reader :env
30
+ def initialize(env, name)
31
+ @env = env
32
+ env.provides << "pkgconfig/orogen-#{name}"
33
+ end
34
+ def add_default_logger
35
+ env.using_task_library 'logger'
36
+ BlackHole
37
+ end
38
+ def task(*args)
39
+ method_missing(*args)
40
+ end
41
+ def const_missing(*args)
42
+ BlackHole
43
+ end
44
+ def method_missing(*args)
45
+ BlackHole
46
+ end
47
+ def self.const_missing(*args)
48
+ BlackHole
49
+ end
50
+ end
51
+
52
+ def self.orocos_target
53
+ user_target = ENV['OROCOS_TARGET']
54
+ if @orocos_target
55
+ @orocos_target.dup
56
+ elsif user_target && !user_target.empty?
57
+ user_target
58
+ else
59
+ 'gnulinux'
60
+ end
61
+ end
62
+
63
+ attr_reader :project_name, :dependencies, :provides
64
+ def self.load(file)
65
+ FakeOrogenEnvironment.new.load(file)
66
+ end
67
+
68
+ def initialize
69
+ @dependencies = Array.new
70
+ @provides = Array.new
71
+ end
72
+
73
+ def load(file)
74
+ Kernel.eval(File.read(file), binding)
75
+ self
76
+ end
77
+
78
+ def name(name)
79
+ @project_name = name
80
+ nil
81
+ end
82
+ def using_library(*names)
83
+ @dependencies.concat(names)
84
+ nil
85
+ end
86
+ def using_toolkit(*names)
87
+ names = names.map { |n| "#{n}-toolkit-#{FakeOrogenEnvironment.orocos_target}" }
88
+ @dependencies.concat(names)
89
+ nil
90
+ end
91
+ def using_task_library(*names)
92
+ names = names.map { |n| "#{n}-tasks-#{FakeOrogenEnvironment.orocos_target}" }
93
+ @dependencies.concat(names)
94
+ nil
95
+ end
96
+
97
+ def static_deployment(name = nil, &block)
98
+ deployment("test_#{project_name}", &block)
99
+ end
100
+ def deployment(name, &block)
101
+ deployment = FakeDeployment.new(self, name)
102
+ deployment.instance_eval(&block) if block
103
+ deployment
104
+ end
105
+
106
+ def self.const_missing(*args)
107
+ BlackHole
108
+ end
109
+ def const_missing(*args)
110
+ BlackHole
111
+ end
112
+ def method_missing(*args)
113
+ BlackHole
114
+ end
115
+ end
116
+
117
+ # This class represents packages generated by orogen. oroGen is a
118
+ # specification and code generation tool for the Orocos/RTT integration
119
+ # framework. See http://doudou.github.com/orogen for more information.
120
+ #
121
+ # This class extends the CMake package class to handle the code generation
122
+ # step. Moreover, it will load the orogen specification and automatically
123
+ # add the relevant pkg-config dependencies as dependencies.
124
+ #
125
+ # This requires that the relevant packages define the pkg-config definitions
126
+ # they install in the pkgconfig/ namespace. It means that a "driver/camera"
127
+ # package (for instance) that installs a "camera.pc" file will have to
128
+ # provide the "pkgconfig/camera" virtual package. This is done automatically
129
+ # by the CMake package handler if the source contains a camera.pc.in file,
130
+ # but can also be done manually with a call to Package#provides:
131
+ #
132
+ # pkg.provides "pkgconfig/camera"
133
+ #
6
134
  class Orogen < CMake
7
135
  class << self
8
136
  attr_accessor :corba
9
137
  end
10
138
 
139
+ attr_reader :orogen_spec
140
+
11
141
  attr_accessor :corba
12
142
 
13
143
  attr_accessor :orogen_file
@@ -16,18 +146,6 @@ module Autobuild
16
146
  super
17
147
 
18
148
  @orogen_file ||= "#{File.basename(name)}.orogen"
19
-
20
- # Find out where orogen is, and make sure the configurestamp depend
21
- # on it. Ignore if orogen is too old to have a --base-dir option
22
- orogen_root = File.join(`orogen --base-dir`.chomp, 'orogen')
23
- if !orogen_root.empty?
24
- file genstamp => Autobuild.source_tree(orogen_root)
25
- end
26
-
27
- file configurestamp => genstamp
28
- file genstamp => File.join(srcdir, orogen_file) do
29
- regen
30
- end
31
149
  end
32
150
 
33
151
  def depends_on(*packages)
@@ -38,6 +156,43 @@ module Autobuild
38
156
  end
39
157
  end
40
158
 
159
+ def import
160
+ super
161
+
162
+ @orogen_spec = FakeOrogenEnvironment.load(File.join(srcdir, orogen_file))
163
+ provides "pkgconfig/#{orogen_spec.project_name}-toolkit-#{FakeOrogenEnvironment.orocos_target}"
164
+ provides "pkgconfig/#{orogen_spec.project_name}-tasks-#{FakeOrogenEnvironment.orocos_target}"
165
+ orogen_spec.provides.each do |name|
166
+ provides name
167
+ end
168
+ end
169
+
170
+ def prepare
171
+ super
172
+
173
+ # If required, load the component's specification and add
174
+ # dependencies based on the orogen specification.
175
+ orogen_spec.dependencies.each do |pkg_name|
176
+ target = "pkgconfig/#{pkg_name}"
177
+ if Autobuild::Package[target]
178
+ depends_on target
179
+ end
180
+ end
181
+
182
+ # Find out where orogen is, and make sure the configurestamp depend
183
+ # on it. Ignore if orogen is too old to have a --base-dir option
184
+ orogen_root = `orogen --base-dir`.chomp
185
+ if orogen_root.empty?
186
+ raise ConfigException, "cannot find the orogen tool"
187
+ end
188
+ orogen_root = File.join(orogen_root, 'orogen')
189
+ file genstamp => Autobuild.source_tree(orogen_root)
190
+
191
+ file configurestamp => genstamp
192
+ file genstamp => File.join(srcdir, orogen_file) do
193
+ regen
194
+ end
195
+ end
41
196
  def genstamp; File.join(srcdir, '.orogen', 'orogen-stamp') end
42
197
 
43
198
  def regen
@@ -45,9 +200,10 @@ module Autobuild
45
200
  cmdline << '--corba' if corba
46
201
  cmdline << orogen_file
47
202
 
203
+ Autobuild.progress "generating oroGen project #{name}"
48
204
  Dir.chdir(srcdir) do
49
205
  Subprocess.run name, 'orogen', *cmdline
50
- touch_stamp genstamp
206
+ Autobuild.touch_stamp genstamp
51
207
  end
52
208
  end
53
209
  end