autobuild 0.1
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.
- data/README +284 -0
- data/bin/autobuild +81 -0
- data/lib/autobuild/config.rb +112 -0
- data/lib/autobuild/environment.rb +16 -0
- data/lib/autobuild/import/cvs.rb +47 -0
- data/lib/autobuild/import/svn.rb +39 -0
- data/lib/autobuild/importer.rb +24 -0
- data/lib/autobuild/logging.rb +105 -0
- data/lib/autobuild/package.rb +83 -0
- data/lib/autobuild/packages/autotools.rb +115 -0
- data/lib/autobuild/packages/genom.rb +70 -0
- data/lib/autobuild/packages/import.rb +19 -0
- data/lib/autobuild/subcommand.rb +32 -0
- data/lib/autobuild/timestamps.rb +48 -0
- metadata +82 -0
data/README
ADDED
@@ -0,0 +1,284 @@
|
|
1
|
+
= Introduction
|
2
|
+
== What's autobuild ?
|
3
|
+
Autobuild is a builder for a set of software packages. It takes as input a yaml config file as input and
|
4
|
+
|
5
|
+
* imports the package from a SCM or (optionnaly) updates it
|
6
|
+
* configures it (for instance for autoconf-based packages)
|
7
|
+
* builds and installs it
|
8
|
+
|
9
|
+
It takes the dependencies between packages into account in its build process.
|
10
|
+
|
11
|
+
== Example config explained
|
12
|
+
|
13
|
+
1. This section is not used at all by autobuild. It is only here to define references
|
14
|
+
used in the reste of the config file
|
15
|
+
|
16
|
+
variables:
|
17
|
+
- &openrobots ':ext:sjoyeux@cvs.laas.fr/cvs/openrobots'
|
18
|
+
- &openprs_cvs ':ext:sjoyeux@cvs.laas.fr:/usr/local/openprs/CVS-Repository'
|
19
|
+
- &compilers [ "CC=gcc-3.3", "CXX=g++-3.3" ]
|
20
|
+
- &global_prefix /home/doudou/laas/openrobots
|
21
|
+
|
22
|
+
2. <tt>autobuild-config</tt> holds the configuration of the build tool itself. See the
|
23
|
+
<b>Autobuild configuration</b> section for the available configuration options.
|
24
|
+
|
25
|
+
autobuild-config:
|
26
|
+
srcdir: &srcdir /home/doudou/laas/openrobots/tools
|
27
|
+
prefix: &prefix /home/doudou/laas/openrobots/build/tools
|
28
|
+
clean-log: true
|
29
|
+
|
30
|
+
mail:
|
31
|
+
to: sjoyeux@laas.fr
|
32
|
+
|
33
|
+
environment:
|
34
|
+
PATH: [ "/bin", "/usr/bin" ]
|
35
|
+
LD_LIBRARY_PATH:
|
36
|
+
PKG_CONFIG_PATH:
|
37
|
+
|
38
|
+
3. +programs+ defines the tools autobuild should use instead of the its default values.
|
39
|
+
See each the rest of the documentation to know what values are used.
|
40
|
+
|
41
|
+
programs:
|
42
|
+
aclocal: "aclocal-1.9"
|
43
|
+
automake: "automake-1.9"
|
44
|
+
|
45
|
+
|
46
|
+
4. the +packages+ sections is where packages are defined
|
47
|
+
|
48
|
+
packages:
|
49
|
+
|
50
|
+
5. options in <tt>common-config</tt> are merged in each package configuration. See
|
51
|
+
<b>Configuring packages</b> for the detailed merging rules.
|
52
|
+
|
53
|
+
common-config:
|
54
|
+
configureflags: *compilers
|
55
|
+
prefix: ""
|
56
|
+
|
57
|
+
6. Tell autobuild to build the pocolibs package. Two common options here:
|
58
|
+
*type*:: the builder to use. Available package types are currently +autotools+,
|
59
|
+
+import+ and +genom+ (a tool used at my lab;)). See the <b>Available
|
60
|
+
package types</b> section for their configuration.
|
61
|
+
*import*:: the importer to use. Source definition for the importer is always in
|
62
|
+
the +source+ option.
|
63
|
+
|
64
|
+
pocolibs:
|
65
|
+
type: autotools
|
66
|
+
import: cvs
|
67
|
+
source: [ *openrobots, pocolibs ]
|
68
|
+
|
69
|
+
|
70
|
+
7. Autobuild takes the dependencies between packages into account. Just list in +depends+
|
71
|
+
the packages which should be installed before this one is built.
|
72
|
+
|
73
|
+
genom:
|
74
|
+
type: autotools
|
75
|
+
import: cvs
|
76
|
+
source: [ *openrobots, genom ]
|
77
|
+
depends: pocolibs
|
78
|
+
|
79
|
+
|
80
|
+
|
81
|
+
|
82
|
+
|
83
|
+
= Configuration file
|
84
|
+
|
85
|
+
The config file is {a Yaml file}[http://yaml4r.sourceforge.net/cookbook]. It is a hash where
|
86
|
+
each element is a section:
|
87
|
+
|
88
|
+
section1:
|
89
|
+
option1: value
|
90
|
+
option2: value
|
91
|
+
section2:
|
92
|
+
|
93
|
+
In the documentation, I'll sometime use paths to reference the config options. For instance,
|
94
|
+
+option1+ is <tt>section1/option1</tt>. The +to+ option in the configuration example
|
95
|
+
is <tt>autobuild-config/mail/to</tt>
|
96
|
+
|
97
|
+
Autobuild uses three sections:
|
98
|
+
|
99
|
+
<b>autobuild-config</b>:: main autobuild configuration. See <b>Autobuild configuration</b> below
|
100
|
+
*programs*:: the programs used by importers and builders. See each tool
|
101
|
+
documentation for the list of used options
|
102
|
+
*packages*:: the list of packages to build. See <b>Package configuration</b> below
|
103
|
+
|
104
|
+
|
105
|
+
== Autobuild configuration (<tt>autobuild-config</tt>)
|
106
|
+
=== Misc options
|
107
|
+
*noupdate*:: if true, do not update the packages that are already imported (default: false). You
|
108
|
+
can also add the <tt>--noupdate</tt> on the command line
|
109
|
+
|
110
|
+
=== Directories (<tt>autobuild-config/srcdir</tt>, <tt>autobuild-config/prefix</tt> and <tt>autobuild-config/logdir</tt>)
|
111
|
+
*srcdir*:: the path where programs are to be imported. See <b>Packages configuration</b> for
|
112
|
+
more information on how this option is used.
|
113
|
+
*prefix*:: the path where programs are to be installed. See <b>Packages configuration</b> for more
|
114
|
+
information on how this option is used.
|
115
|
+
*logdir*:: by default, autobuild does not displays the output of each subcommand it launches.
|
116
|
+
Insteads, it saves this output in log files. These log files are saved in +logdir+.
|
117
|
+
The default value is <tt>prefix/autobuild</tt>
|
118
|
+
<b>clean-log</b>:: if we must remove all logfiles before each autobuild run or if we should append to them. The
|
119
|
+
default is +true+, that is log files are removed. Note that if mailing is active, all log
|
120
|
+
files are attached to notification mail.
|
121
|
+
|
122
|
+
|
123
|
+
=== Environment (<tt>autobuild-config/environment</tt>)
|
124
|
+
The <tt>autobuild-config/environment</tt> section lists the initial values for
|
125
|
+
the environment variables. While packages builders shall update the environment
|
126
|
+
variables as needed, you can have to set up a proper initial environment. Arrays
|
127
|
+
are converted into strings by joining elements by ':'
|
128
|
+
|
129
|
+
For instance
|
130
|
+
autobuild-config:
|
131
|
+
environment:
|
132
|
+
PATH: [ "/bin", "/usr/bin" ]
|
133
|
+
LD_LIBRARY_PATH:
|
134
|
+
PKG_CONFIG_PATH:
|
135
|
+
|
136
|
+
sets
|
137
|
+
|
138
|
+
PATH="/bin:/usr/bin"
|
139
|
+
LD_LIBRARY_PATH=""
|
140
|
+
PKG_CONFIG_PATH=""
|
141
|
+
|
142
|
+
|
143
|
+
=== Mail (<tt>autobuild-config/mail</tt>)
|
144
|
+
If you want to receive a mail when the build has finished (on success and failures), set
|
145
|
+
at least the +to+ option, like this:
|
146
|
+
|
147
|
+
autobuild-config:
|
148
|
+
mail:
|
149
|
+
from: autobuild@mymachine.rubyrules.org
|
150
|
+
to: myself+autobuild@rubyrules.org
|
151
|
+
smtp: localhost
|
152
|
+
|
153
|
+
Other options are:
|
154
|
+
*from*:: what to set in the <tt>From:</tt> field, default is <tt>autobuild@hostname</tt>
|
155
|
+
*smtp*:: the stmp server to use. Default is +localhost+
|
156
|
+
|
157
|
+
All log files relative to the current build are attached to the mail.
|
158
|
+
|
159
|
+
|
160
|
+
|
161
|
+
== Configuring packages (<tt>packages/*</tt>)
|
162
|
+
=== The <tt>packages/common-config</tt> section
|
163
|
+
If you want to add common options in each package, just set it them here. Note that the options
|
164
|
+
are /merged/ in the package config. It neither replaces the values in the package nor be replaced
|
165
|
+
by them. The merging strategy depends on the way the <tt>common-config</tt> is specified:
|
166
|
+
* if it is an array, the package option is converted to an array and the common option is appended
|
167
|
+
* if it is a string, the package option is converted to a string and the common option is appended
|
168
|
+
with a space inserted between the two
|
169
|
+
* if it is a boolean value, it is <em>overriden</em> by the package value
|
170
|
+
* any other values are forbidden
|
171
|
+
|
172
|
+
To ease the config file writing, all options that accept arrays also accept a string when there
|
173
|
+
is only one element. <b>Do not</b> do that in the <tt>common-config</tt> option.
|
174
|
+
|
175
|
+
=== Package definition
|
176
|
+
All subsections of the <tt>packages/</tt> section but <tt>common-config</tt> are package definitions
|
177
|
+
The section name is used as the package name. The package definition sets up the importer to get the
|
178
|
+
source and the builder to build and install it.
|
179
|
+
|
180
|
+
*type*:: the package type. See the <b>Available package types</b> sections
|
181
|
+
|
182
|
+
*srcdir*:: where sources are to be imported. If this is a relative path, it is relative to the global
|
183
|
+
<tt>/autobuild-config/srcdir</tt> option. Otherwise, the absolute path is used. If no +srcdir+
|
184
|
+
is given, the package name is used, so that the default import dir is
|
185
|
+
<em>global srcdir</em>/<em>package_name</em>.
|
186
|
+
|
187
|
+
*Note* because of most SCM operations, it is forbidden that two packages have the same srcdir.
|
188
|
+
Empty srcdir are forbidden for the same reason.
|
189
|
+
|
190
|
+
*importer*::
|
191
|
+
the importer type. For now, only +cvs+ and +svn+ are available. See the <b>Available importers</b>
|
192
|
+
section.
|
193
|
+
|
194
|
+
<b>importer/source</b>::
|
195
|
+
where the importer should get the sources. The format of this options depends on
|
196
|
+
the importer used.
|
197
|
+
|
198
|
+
*prefix*:: where the program is to be installed. If this is a relative path, it is relative to the global
|
199
|
+
<tt>/autobuild-config/prefix</tt> option. Otherwise, the absolute path is used. If no prefix
|
200
|
+
is given, the package name is used, so that the default install dir is
|
201
|
+
<em>global prefix</em>/<em>package name</em>
|
202
|
+
|
203
|
+
*depends*:: the array of packages that should be built and installed before this one is built. <tt>depends: [ foo ]</tt>
|
204
|
+
is equivalent to <tt>depends: foo</tt>. To make the use of +depends+ in the <tt>common-config</tt>
|
205
|
+
section possible, this package name is automatically removed from the package +depends+ array.
|
206
|
+
*provides*:: defines aliases for this package. As for +depends+, an array with only element can be replaced
|
207
|
+
by the simple value.
|
208
|
+
|
209
|
+
== Available importers
|
210
|
+
=== CVS (<tt>type: cvs</tt>)
|
211
|
+
*source*:: the source specification is a [ repository, module ] array
|
212
|
+
*cvsup*:: options to add to cvs up. Defaults to '-dP'
|
213
|
+
*cvsco*:: options to add to cvs ci, Defaults to -P
|
214
|
+
|
215
|
+
=== Subversion (<tt>type: svn</tt>)
|
216
|
+
*source*:: the svn URL. To ease the Yaml nodes reference (the <tt>*ref</tt> form), it can be an array
|
217
|
+
which is then converted into a path by joining the elements with '/'. For instance:
|
218
|
+
|
219
|
+
packages:
|
220
|
+
foo:
|
221
|
+
import: svn
|
222
|
+
source: [ *my_home_repository, /trunk/foo ]
|
223
|
+
|
224
|
+
*svnup*:: options to add to svn up. Defaults to ''
|
225
|
+
*svnco*:: options to add to svn co. Defaults to ''
|
226
|
+
|
227
|
+
== Available package types (<tt>packages/</tt><em>name</em><tt>/type</tt>)
|
228
|
+
=== Source only (<tt>type: import</tt>)
|
229
|
+
Use +import+ if you need the package sources but don't need to build it. You just need
|
230
|
+
to set up the importer and +srcdir+. +prefix+ is ignored.
|
231
|
+
|
232
|
+
packages:
|
233
|
+
bar:
|
234
|
+
type: import
|
235
|
+
import: cvs
|
236
|
+
source: [ *my_repository, "bar" ]
|
237
|
+
srcdir: &bar_srcdir bar
|
238
|
+
|
239
|
+
=== Autotools (<tt>type: autotools</tt>)
|
240
|
+
Use this to build GNU autotools-based packages. This handles autoconf-only packages as
|
241
|
+
well as automake-based packages.
|
242
|
+
|
243
|
+
==== Configuration programs
|
244
|
+
The <tt>autotools</tt> packages use four programs: *autoheader*, *autoconf*, *aclocal*,
|
245
|
+
*automake*. The default values can be overriden in the <tt>/programs</tt> section. For
|
246
|
+
instance, to be sure that automake 1.9 is used, you set
|
247
|
+
|
248
|
+
programs:
|
249
|
+
automake: automake-1.9
|
250
|
+
|
251
|
+
Autobuild tries to detect what tools it should run
|
252
|
+
* +aclocal+ and +autoheader+ are always used
|
253
|
+
* +autoconf+ is used if there is <tt>configure.ac</tt> or <tt>configure.in</tt> in the import dir
|
254
|
+
* +automake+ is used if there is a <tt>Makefile.am</tt> file in the import dir
|
255
|
+
* you can force to enable or disable any of these four tools in each package config by setting the tool
|
256
|
+
flag to true or false. For instance, if you don't want package +foo+ to use automake, you say
|
257
|
+
|
258
|
+
packages:
|
259
|
+
foo:
|
260
|
+
automake: false
|
261
|
+
|
262
|
+
==== Build programs
|
263
|
+
The only program used during the build phase is +make+. The make command can too be overriden in the
|
264
|
+
<tt>programs</tt> section.
|
265
|
+
|
266
|
+
programs:
|
267
|
+
make: gnumake
|
268
|
+
|
269
|
+
==== Other options
|
270
|
+
*builddir*:: the directory where the build takes place. For now, it has to be a relative path,
|
271
|
+
which is added to the import dir. The default value is "build"
|
272
|
+
*configureflags*:: array of options to add to the +configure+ command line. To ease the reuse of
|
273
|
+
Yaml references (the <tt>*label</tt> form), you can use an array of arrays, in which
|
274
|
+
case the second array levels are joined using ''. For example if our +foo+ package
|
275
|
+
needs the source of the +bar+ package we added as an import package earlier,
|
276
|
+
|
277
|
+
configureflags: [ [ --with-bar-source=, *srcdir, '/', *bar_srcdir ] ]
|
278
|
+
depends: bar
|
279
|
+
|
280
|
+
= Copyright and license
|
281
|
+
Author:: Sylvain Joyeux <sylvain.joyeux@m4x.org>
|
282
|
+
Copyright:: Copyright (c) 2005 Sylvain Joyeux
|
283
|
+
License:: GPL
|
284
|
+
|
data/bin/autobuild
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
#! /usr/bin/ruby -w
|
2
|
+
|
3
|
+
# :main: README
|
4
|
+
|
5
|
+
require 'rake'
|
6
|
+
require 'ostruct'
|
7
|
+
require 'optparse'
|
8
|
+
|
9
|
+
require 'autobuild/config'
|
10
|
+
require 'autobuild/logging'
|
11
|
+
|
12
|
+
def parse_options(args)
|
13
|
+
options = OpenStruct.new
|
14
|
+
options.noupdate = false
|
15
|
+
options.srcdir = nil
|
16
|
+
options.prefix = nil
|
17
|
+
options.builddir = "build"
|
18
|
+
options.logdir = nil
|
19
|
+
$VERBOSE = false
|
20
|
+
|
21
|
+
parser = OptionParser.new do |opts|
|
22
|
+
opts.banner = "Usage: autobuild [options] config.yml"
|
23
|
+
opts.separator ""
|
24
|
+
|
25
|
+
opts.on("--srcdir PATH", "Find or imports sources in PATH") do |p|
|
26
|
+
options.srcdir = p
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on("--prefix PATH", "Packages are installed in PATH") do |p|
|
30
|
+
options.prefix = p
|
31
|
+
options.logdir = "#{p}/autobuild"
|
32
|
+
end
|
33
|
+
|
34
|
+
opts.on("--logdir", "Where logs are saved (default: <prefix>/autobuild)") do |p|
|
35
|
+
options.logdir = p
|
36
|
+
end
|
37
|
+
|
38
|
+
opts.on("--noupdate", "Do not update already checked-out sources") do
|
39
|
+
options.noupdate = true
|
40
|
+
end
|
41
|
+
|
42
|
+
opts.on("--verbose", "Display output of commands on stdout") do
|
43
|
+
$VERBOSE = true
|
44
|
+
end
|
45
|
+
|
46
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
47
|
+
puts opts
|
48
|
+
exit
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
parser.parse!(args)
|
53
|
+
if !args[0]
|
54
|
+
puts parser
|
55
|
+
exit
|
56
|
+
end
|
57
|
+
|
58
|
+
[ options, args[0], args[1..-1] ]
|
59
|
+
end
|
60
|
+
|
61
|
+
# Load the command line options
|
62
|
+
options, conffile, targets = parse_options(ARGV)
|
63
|
+
Config.load(conffile, options)
|
64
|
+
|
65
|
+
$DEBUG = true
|
66
|
+
if $DEBUG
|
67
|
+
$trace = true
|
68
|
+
end
|
69
|
+
|
70
|
+
begin
|
71
|
+
if targets.empty?
|
72
|
+
Task[:default].invoke
|
73
|
+
else
|
74
|
+
targets.each { |t| Task[t.to_sym].invoke }
|
75
|
+
end
|
76
|
+
success
|
77
|
+
rescue BuildException => error
|
78
|
+
error(error, "Error during build of #{error.target}")
|
79
|
+
exit(1)
|
80
|
+
end
|
81
|
+
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
require 'autobuild/logging'
|
5
|
+
require 'autobuild/package'
|
6
|
+
require 'autobuild/importer'
|
7
|
+
|
8
|
+
module Config
|
9
|
+
def self.load(conffile, user_options)
|
10
|
+
data = YAML.load( File.open(conffile) )
|
11
|
+
|
12
|
+
get_autobuild_config(data, user_options)
|
13
|
+
get_package_config(data)
|
14
|
+
rescue ConfigException => error
|
15
|
+
error(error, "Error in config file '#{conffile}'")
|
16
|
+
exit(1)
|
17
|
+
rescue ImportException => error
|
18
|
+
error(error, "Error: unable to import #{p}")
|
19
|
+
exit(1)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.get_autobuild_config(data, options)
|
23
|
+
$PROGRAMS = (data["programs"] or "make")
|
24
|
+
|
25
|
+
setup = data["autobuild-config"]
|
26
|
+
raise ConfigException, "no autobuild-config block" if !setup
|
27
|
+
|
28
|
+
$SRCDIR = (options.srcdir or setup["srcdir"])
|
29
|
+
$PREFIX = (options.prefix or setup["prefix"])
|
30
|
+
if !$SRCDIR || !$PREFIX
|
31
|
+
raise ConfigException, "you must at least set srcdir and prefix in the config files"
|
32
|
+
end
|
33
|
+
|
34
|
+
$LOGDIR = (options.logdir or setup["logdir"] or "#{$PREFIX}/autobuild")
|
35
|
+
|
36
|
+
FileUtils.mkdir_p $SRCDIR if !File.directory?($SRCDIR)
|
37
|
+
FileUtils.mkdir_p $LOGDIR if !File.directory?($LOGDIR)
|
38
|
+
if setup["clean-log"]
|
39
|
+
puts "Cleaning log dir #{$LOGDIR}"
|
40
|
+
FileUtils.rm_rf Dir.glob("#{$LOGDIR}/*")
|
41
|
+
end
|
42
|
+
|
43
|
+
$MAIL = setup["mail"]
|
44
|
+
$NOUPDATE = (options.noupdate or setup["noupdate"] or false)
|
45
|
+
|
46
|
+
envvars = setup["environment"]
|
47
|
+
envvars.each { |k, v|
|
48
|
+
ENV[k] = ( v.to_a.collect { |path| path.to_a.join("") }.join(":") )
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.add_package(name, config)
|
53
|
+
# Get the package type
|
54
|
+
package_type = config[:type].to_sym
|
55
|
+
require "autobuild/packages/#{package_type}"
|
56
|
+
|
57
|
+
# Build the importer object, if there is one
|
58
|
+
import_type = config[:import]
|
59
|
+
if import_type
|
60
|
+
require "autobuild/import/#{import_type}"
|
61
|
+
if !config.has_key?(:source)
|
62
|
+
raise ConfigException, "missing 'source' option in the '#{name}' package description"
|
63
|
+
end
|
64
|
+
|
65
|
+
config[:import] = Import.method(import_type).call(config[:source], config)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Set the default dir if needed
|
69
|
+
config[:srcdir] ||= name
|
70
|
+
config[:prefix] ||= name
|
71
|
+
|
72
|
+
# Build the rake rules for this package
|
73
|
+
Package.build(package_type, name, config)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Get the package config
|
77
|
+
def self.get_package_config(data)
|
78
|
+
setup = data["packages"]
|
79
|
+
|
80
|
+
# Get the common config block
|
81
|
+
common_config = Hash.new
|
82
|
+
setup["common-config"].each { |k, v| common_config[k.to_sym] = v } if setup.has_key?("common-config")
|
83
|
+
|
84
|
+
setup.each do |p, yml_config|
|
85
|
+
next if p == "common-config"
|
86
|
+
|
87
|
+
# Change keys into symbols
|
88
|
+
config = {}
|
89
|
+
yml_config.each do |k, v|
|
90
|
+
config[k.to_sym] = v
|
91
|
+
end
|
92
|
+
|
93
|
+
# Merge the common config
|
94
|
+
config = config.merge(common_config) { |k, v1, v2|
|
95
|
+
if v2.respond_to?(:to_ary)
|
96
|
+
v1.to_a | v2.to_ary
|
97
|
+
elsif v2.respond_to?(:to_str)
|
98
|
+
v1.to_s + " " + v2.to_str
|
99
|
+
end
|
100
|
+
}
|
101
|
+
# Remove p -> p dependency which may come from common_config
|
102
|
+
if config.has_key?(:depends)
|
103
|
+
config[:depends] = config[:depends].to_a.find_all { |el| el != p }
|
104
|
+
end
|
105
|
+
|
106
|
+
add_package(p, config)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
private_class_method :get_autobuild_config, :get_package_config, :add_package
|
111
|
+
end
|
112
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
def pathvar(path, varname)
|
2
|
+
if File.directory?(path)
|
3
|
+
oldpath = ENV[varname]
|
4
|
+
if oldpath.empty?
|
5
|
+
ENV[varname] = path
|
6
|
+
else
|
7
|
+
ENV[varname] = "#{oldpath}:#{path}"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def update_environment(newprefix)
|
13
|
+
pathvar("#{newprefix}/bin", 'PATH')
|
14
|
+
pathvar("#{newprefix}/lib/pkgconfig", 'PKG_CONFIG_PATH')
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'autobuild/subcommand'
|
2
|
+
require 'autobuild/importer'
|
3
|
+
|
4
|
+
class CVSImporter < Importer
|
5
|
+
def initialize(root, name, options)
|
6
|
+
@root = root
|
7
|
+
@module = name
|
8
|
+
|
9
|
+
@program = ($PROGRAMS[:cvs] || 'cvs')
|
10
|
+
@up = (options[:cvsup] || '-dP')
|
11
|
+
@co = (options[:cvsco] || '-P')
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def update(package)
|
17
|
+
Dir.chdir(package.srcdir) {
|
18
|
+
begin
|
19
|
+
subcommand(package.target, 'cvs', "#{@program} up #{@up}")
|
20
|
+
rescue SubcommandFailed => e
|
21
|
+
raise ImportException.new(e), "failed to update #{modulename}"
|
22
|
+
end
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def checkout(package)
|
27
|
+
head, tail = File.split(package.srcdir)
|
28
|
+
cvsroot = @root
|
29
|
+
modulename = @module
|
30
|
+
|
31
|
+
FileUtils.mkdir_p(head) if !File.directory?(head)
|
32
|
+
Dir.chdir(head) {
|
33
|
+
begin
|
34
|
+
subcommand(package.target, 'cvs', "#{@program} -d #{cvsroot} co #{@co} -d #{tail} #{modulename}")
|
35
|
+
rescue SubcommandFailed => e
|
36
|
+
raise ImportException.new(e), "failed to check out #{modulename}"
|
37
|
+
end
|
38
|
+
}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
module Import
|
43
|
+
def self.cvs(source, package_options)
|
44
|
+
CVSImporter.new(source[0], source[1], package_options)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'autobuild/subcommand'
|
2
|
+
require 'autobuild/importer'
|
3
|
+
|
4
|
+
class SVNImporter < Importer
|
5
|
+
def initialize(source, options)
|
6
|
+
@source = source.to_a.join("/")
|
7
|
+
|
8
|
+
@program = ($PROGRAMS[:svn] || 'svn')
|
9
|
+
@up = options[:svnup]
|
10
|
+
@co = options[:svnco]
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def update(package)
|
16
|
+
Dir.chdir(package.srcdir) {
|
17
|
+
begin
|
18
|
+
subcommand(package.target, 'svn', "#{program} up #{@up}")
|
19
|
+
rescue SubcommandFailed => e
|
20
|
+
raise ImportException.new(e), "failed to update #{modulename}"
|
21
|
+
end
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
def checkout(package)
|
26
|
+
begin
|
27
|
+
subcommand(package.target, 'svn', "#{program} co #{@co} #{@source} #{package.srcdir}")
|
28
|
+
rescue SubcommandFailed => e
|
29
|
+
raise ImportException.new(e), "failed to check out #{modulename}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module Import
|
35
|
+
def self.svn(source, options)
|
36
|
+
SVNImporter.new(source, options)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Importer
|
2
|
+
def import(package)
|
3
|
+
srcdir = package.srcdir
|
4
|
+
if File.directory?(srcdir)
|
5
|
+
if $NOUPDATE
|
6
|
+
puts "Not updating #{package.target} since noupdate is set"
|
7
|
+
return
|
8
|
+
end
|
9
|
+
|
10
|
+
update(package)
|
11
|
+
|
12
|
+
elsif File.exists?(srcdir)
|
13
|
+
raise ImportException, "#{srcdir} exists but is not a directory"
|
14
|
+
else
|
15
|
+
begin
|
16
|
+
checkout(package)
|
17
|
+
rescue ImportException => error
|
18
|
+
FileUtils.rm_rf package.srcdir
|
19
|
+
raise error
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'rmail'
|
2
|
+
require 'rmail/serialize'
|
3
|
+
require 'net/smtp'
|
4
|
+
require 'socket'
|
5
|
+
|
6
|
+
class SubcommandFailed < Exception
|
7
|
+
attr_reader :target, :command, :logfile, :status
|
8
|
+
def initialize(target, command, logfile, status)
|
9
|
+
@target = target
|
10
|
+
@command = command
|
11
|
+
@logfile = logfile
|
12
|
+
@status = status
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class ConfigException < Exception
|
17
|
+
def mail?; false end
|
18
|
+
end
|
19
|
+
|
20
|
+
class ImportException < SubcommandFailed
|
21
|
+
def mail?; true end
|
22
|
+
|
23
|
+
def initialize(subcommand)
|
24
|
+
super(subcommand.target, subcommand.command, subcommand.logfile, subcommand.status)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
class BuildException < SubcommandFailed
|
28
|
+
def mail?; true end
|
29
|
+
|
30
|
+
def initialize(subcommand)
|
31
|
+
super(subcommand.target, subcommand.command, subcommand.logfile, subcommand.status)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def success
|
36
|
+
message = "Build finished successfully at #{Time.now}"
|
37
|
+
puts message
|
38
|
+
send_mail("Build success", message) if $MAIL
|
39
|
+
end
|
40
|
+
|
41
|
+
def error(object, place)
|
42
|
+
if object.kind_of?(SubcommandFailed)
|
43
|
+
body = <<EOF
|
44
|
+
#{place}: #{object.message}
|
45
|
+
command '#{object.command}' failed with status #{object.status}
|
46
|
+
see #{File.basename(object.logfile)} for details
|
47
|
+
EOF
|
48
|
+
|
49
|
+
message = <<EOF
|
50
|
+
#{place}: #{object.message}
|
51
|
+
command '#{object.command}' failed with status #{object.status}
|
52
|
+
see #{object.logfile} for details
|
53
|
+
EOF
|
54
|
+
else
|
55
|
+
body = message = "#{place}: #{object.message}"
|
56
|
+
end
|
57
|
+
|
58
|
+
puts message
|
59
|
+
send_mail("Build failed", body) if $MAIL
|
60
|
+
end
|
61
|
+
|
62
|
+
module RMail
|
63
|
+
class Message
|
64
|
+
def add_file(path, content_type='text/plain')
|
65
|
+
part = RMail::Message.new
|
66
|
+
part.header.set('Content-Type', content_type)
|
67
|
+
part.header.set('Content-Disposition', 'attachment', 'filename' => File.basename(path))
|
68
|
+
part.body = ''
|
69
|
+
File.open(path) do |file|
|
70
|
+
part.body << file.readlines.join("\n")
|
71
|
+
end
|
72
|
+
self.add_part(part)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def send_mail(subject, body)
|
78
|
+
from = ($MAIL['from'] || "autobuild@#{Socket.gethostname}")
|
79
|
+
to = $MAIL['to']
|
80
|
+
smtp = ($MAIL['smtp'] || "localhost" )
|
81
|
+
|
82
|
+
mail = RMail::Message.new
|
83
|
+
mail.header.date = Time.now
|
84
|
+
mail.header.from = from
|
85
|
+
mail.header.to = to
|
86
|
+
mail.header.subject = subject
|
87
|
+
|
88
|
+
part = RMail::Message.new
|
89
|
+
part.header.set('Content-Type', 'text/plain')
|
90
|
+
part.body = body
|
91
|
+
mail.add_part(part)
|
92
|
+
|
93
|
+
# Attach log files
|
94
|
+
Dir.glob("#{$LOGDIR}/*.log") do |file|
|
95
|
+
mail.add_file(file)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Send the mail
|
99
|
+
smtp = Net::SMTP.new(smtp, Integer($MAIL['port'] || 25))
|
100
|
+
smtp.start {
|
101
|
+
smtp.send_mail RMail::Serialize.write('', mail), from, to
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'autobuild/timestamps'
|
2
|
+
require 'autobuild/environment'
|
3
|
+
require 'autobuild/subcommand'
|
4
|
+
|
5
|
+
class Package
|
6
|
+
@@packages = {}
|
7
|
+
|
8
|
+
attr_reader :dependencies
|
9
|
+
attr_reader :target, :srcdir, :prefix
|
10
|
+
|
11
|
+
def installstamp; "#{prefix}/#{target}-#{STAMPFILE}" end
|
12
|
+
def self.[](target); @@packages[target] end
|
13
|
+
|
14
|
+
def initialize(target, options)
|
15
|
+
@target = Package.name2target(target)
|
16
|
+
raise ConfigException, "Package #{target} is already defined" if Package[target]
|
17
|
+
|
18
|
+
@options = options
|
19
|
+
@dependencies = Array.new
|
20
|
+
@provides = Array.new
|
21
|
+
|
22
|
+
srcdir, prefix =
|
23
|
+
(options[:srcdir] or target.to_s),
|
24
|
+
(options[:prefix] or "")
|
25
|
+
|
26
|
+
srcdir = File.expand_path(srcdir, $SRCDIR)
|
27
|
+
prefix = File.expand_path(prefix, $PREFIX)
|
28
|
+
|
29
|
+
@srcdir, @prefix = srcdir, prefix
|
30
|
+
@import = options[:import]
|
31
|
+
@import.import(self) if @import
|
32
|
+
|
33
|
+
file installstamp
|
34
|
+
task @target => installstamp
|
35
|
+
|
36
|
+
@options[:depends].to_a.each { |p| depends_on(p) }
|
37
|
+
@options[:provides].to_a.each { |p| provides(p) }
|
38
|
+
@@packages[target] = self
|
39
|
+
end
|
40
|
+
|
41
|
+
@@factories = Hash.new
|
42
|
+
|
43
|
+
def depends_on(p)
|
44
|
+
p = Package.name2target(p)
|
45
|
+
raise :bla if !(Symbol === p)
|
46
|
+
task target => p
|
47
|
+
puts "#{target} depends on #{p}"
|
48
|
+
|
49
|
+
@dependencies << p
|
50
|
+
end
|
51
|
+
|
52
|
+
def provides(p)
|
53
|
+
p = Package.name2target(p)
|
54
|
+
@@packages[p] = self
|
55
|
+
puts "Defining #{p} as an alias to #{target}"
|
56
|
+
task p => target
|
57
|
+
|
58
|
+
@provides << p
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.all; @@packages; end
|
62
|
+
def self.name2target(name)
|
63
|
+
if name.respond_to?(:to_str)
|
64
|
+
name.to_str.gsub(/-\//, '_').to_sym
|
65
|
+
elsif name.respond_to?(:to_sym)
|
66
|
+
name.to_sym
|
67
|
+
else
|
68
|
+
raise TypeError, "expected either a symbol or a string, got #{name.class}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
def self.factory(type, klass)
|
72
|
+
@@factories[type] = klass
|
73
|
+
end
|
74
|
+
def self.build(type, name, options)
|
75
|
+
raise ConfigException, "#{type} is not a valide package type" if !@@factories.has_key?(type)
|
76
|
+
|
77
|
+
target = name2target(name)
|
78
|
+
task :default => [ target ]
|
79
|
+
raise ConfigException, "there is already a package named #{target}" if Package[target]
|
80
|
+
return @@factories[type].new(target, options)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'autobuild/timestamps'
|
2
|
+
require 'autobuild/environment'
|
3
|
+
require 'autobuild/package'
|
4
|
+
require 'autobuild/subcommand'
|
5
|
+
|
6
|
+
class Autotools < Package
|
7
|
+
factory :autotools, self
|
8
|
+
|
9
|
+
attr_reader :builddir
|
10
|
+
|
11
|
+
def buildstamp;
|
12
|
+
"#{builddir}/#{target}-#{STAMPFILE}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(target, options)
|
16
|
+
super(target, options)
|
17
|
+
|
18
|
+
@builddir = (options[:builddir] || "build")
|
19
|
+
raise ConfigException, "Autotools packages need a non-empty builddir" if (@builddir.nil? || @builddir.empty?)
|
20
|
+
raise ConfigException, "No support for absolute builddirs" if (Pathname.new(@builddir).absolute?)
|
21
|
+
@builddir = File.expand_path(builddir, srcdir)
|
22
|
+
|
23
|
+
regen_targets
|
24
|
+
|
25
|
+
file "#{builddir}/config.status" => "#{srcdir}/configure" do
|
26
|
+
configure
|
27
|
+
end
|
28
|
+
|
29
|
+
source_tree srcdir, builddir
|
30
|
+
file srcdir => dependencies if !dependencies.empty?
|
31
|
+
file buildstamp => [ srcdir, "#{builddir}/config.status" ] do
|
32
|
+
build
|
33
|
+
end
|
34
|
+
file installstamp => [ buildstamp ] do
|
35
|
+
install
|
36
|
+
update_environment(prefix)
|
37
|
+
end
|
38
|
+
update_environment(prefix)
|
39
|
+
end
|
40
|
+
|
41
|
+
def regen_targets
|
42
|
+
conffile = "#{srcdir}/configure"
|
43
|
+
if File.exists?("#{conffile}.ac")
|
44
|
+
file conffile => [ "#{conffile}.ac" ]
|
45
|
+
else
|
46
|
+
file conffile => [ "#{conffile}.in" ]
|
47
|
+
end
|
48
|
+
file conffile do
|
49
|
+
Dir.chdir(srcdir) {
|
50
|
+
$PROGRAMS["aclocal"] ||= "aclocal"
|
51
|
+
$PROGRAMS["autoconf"] ||= "autoconf"
|
52
|
+
$PROGRAMS["autoheader"] ||= "autoheader"
|
53
|
+
$PROGRAMS["automake"] ||= "automake"
|
54
|
+
|
55
|
+
begin
|
56
|
+
subcommand(target, "configure", $PROGRAMS["aclocal"]) if @options[:aclocal]
|
57
|
+
subcommand(target, "configure", $PROGRAMS["autoconf"]) if @options[:autoconf]
|
58
|
+
subcommand(target, "configure", $PROGRAMS["autoheader"]) if @options[:autoheader]
|
59
|
+
subcommand(target, "configure", $PROGRAMS["automake"]) if @options[:automake]
|
60
|
+
rescue SubcommandFailed => e
|
61
|
+
raise BuildException.new(e), "failed to build the configure environment of #{target}"
|
62
|
+
end
|
63
|
+
}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def configure
|
68
|
+
if File.exists?(builddir) && !File.directory?(builddir)
|
69
|
+
raise BuildException, "#{builddir} already exists but is not a directory"
|
70
|
+
end
|
71
|
+
|
72
|
+
FileUtils.mkdir_p builddir if !File.directory?(builddir)
|
73
|
+
Dir.chdir(builddir) {
|
74
|
+
command = "#{srcdir}/configure --no-create --prefix=#{prefix}"
|
75
|
+
|
76
|
+
configureflags = @options[:configureflags].to_a.collect { |item|
|
77
|
+
item.to_a.join("")
|
78
|
+
}.join(" ")
|
79
|
+
command += " #{configureflags}" if !configureflags.empty?
|
80
|
+
|
81
|
+
begin
|
82
|
+
subcommand(target, "configure", command)
|
83
|
+
rescue SubcommandFailed => e
|
84
|
+
raise BuildException.new(e), "failed to configure #{target}"
|
85
|
+
end
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
def build
|
90
|
+
Dir.chdir(builddir) {
|
91
|
+
begin
|
92
|
+
subcommand(target, "build", "./config.status")
|
93
|
+
$PROGRAMS["make"] ||= "make"
|
94
|
+
subcommand(target, "build", $PROGRAMS["make"])
|
95
|
+
rescue SubcommandFailed => e
|
96
|
+
raise BuildException.new(e), "failed to build #{target}"
|
97
|
+
end
|
98
|
+
}
|
99
|
+
touch_stamp(buildstamp)
|
100
|
+
end
|
101
|
+
|
102
|
+
def install
|
103
|
+
Dir.chdir(builddir) {
|
104
|
+
make = ($PROGRAMS["make"] or "make")
|
105
|
+
begin
|
106
|
+
subcommand(target, "install", "#{make} install")
|
107
|
+
rescue SubcommandFailed => e
|
108
|
+
raise BuildException.new(e), "failed to install #{builddir}"
|
109
|
+
end
|
110
|
+
}
|
111
|
+
touch_stamp(installstamp)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'autobuild/packages/autotools'
|
2
|
+
|
3
|
+
class GenomModule < Autotools
|
4
|
+
def initialize(target, options)
|
5
|
+
super(target, options)
|
6
|
+
get_requires
|
7
|
+
get_provides
|
8
|
+
end
|
9
|
+
|
10
|
+
def genomstamp; "#{srcdir}/.genom/genom-stamp" end
|
11
|
+
|
12
|
+
def get_requires
|
13
|
+
File.open("#{srcdir}/#{target}.gen") do |f|
|
14
|
+
f.each_line { |line|
|
15
|
+
if line =~ /^require\s*:\s*([\w\-]+(?:\s*,\s*[\w\-]+)*);/
|
16
|
+
$1.split(/, /).each { |name|
|
17
|
+
depends_on name
|
18
|
+
file genomstamp => Package.name2target(name)
|
19
|
+
}
|
20
|
+
elsif line =~ /^require/
|
21
|
+
puts "failed to math #{line}"
|
22
|
+
end
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def get_provides
|
28
|
+
File.open("#{srcdir}/configure.ac.user") do |f|
|
29
|
+
f.each_line { |line|
|
30
|
+
if line =~ /^\s*EXTRA_PKGCONFIG\s*=\s*"?([\w\-]+(?:\s+[\w\-]+)*)"?/
|
31
|
+
$1.split(/\s+/).each { |pkg|
|
32
|
+
provides pkg
|
33
|
+
}
|
34
|
+
end
|
35
|
+
}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
def regen_targets
|
41
|
+
file buildstamp => genomstamp
|
42
|
+
file genomstamp => [ :genom, "#{srcdir}/#{target}.gen" ] do
|
43
|
+
Dir.chdir(srcdir) {
|
44
|
+
cmdline = "genom " + @options[:genomflags].to_a.join(" ") + " #{target}"
|
45
|
+
begin
|
46
|
+
subcommand(target, 'genom', cmdline)
|
47
|
+
rescue SubcommandFailed => e
|
48
|
+
raise BuildException.new(e), "failed to generate module #{target}"
|
49
|
+
end
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
acuser = "#{srcdir}/configure.ac.user"
|
54
|
+
if File.exists?(acuser)
|
55
|
+
file "#{srcdir}/configure" => acuser do
|
56
|
+
# configure does not depend on the .gen file
|
57
|
+
# since the generation takes care of rebuilding configure
|
58
|
+
# if .gen has changed
|
59
|
+
begin
|
60
|
+
subcommand(target, 'genom', cmdline)
|
61
|
+
rescue SubcommandFailed => e
|
62
|
+
raise BuildException.new(e), "failed to generate module #{target}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
factory :genom, self
|
69
|
+
end
|
70
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'autobuild/timestamps'
|
2
|
+
require 'autobuild/package'
|
3
|
+
|
4
|
+
class ImporterPackage < Package
|
5
|
+
def installstamp
|
6
|
+
"#{srcdir}/#{STAMPFILE}"
|
7
|
+
end
|
8
|
+
def initialize(target, options)
|
9
|
+
super(target, options)
|
10
|
+
source_tree srcdir, installstamp
|
11
|
+
file installstamp => srcdir do
|
12
|
+
touch_stamp installstamp
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
factory :import, ImporterPackage
|
17
|
+
end
|
18
|
+
|
19
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'autobuild/logging'
|
2
|
+
|
3
|
+
def subcommand(target, type, command)
|
4
|
+
logname = "#{$LOGDIR}/#{target}-#{type}.log"
|
5
|
+
puts "#{target}: running #{command}\n (output goes to #{logname})"
|
6
|
+
|
7
|
+
status = File.open(logname, "a") { |logfile|
|
8
|
+
pid = fork {
|
9
|
+
if $VERBOSE
|
10
|
+
$stderr.dup.reopen(logfile.dup)
|
11
|
+
$stdout.dup.reopen(logfile.dup)
|
12
|
+
else
|
13
|
+
$stderr.reopen(logfile.dup)
|
14
|
+
$stdout.reopen(logfile.dup)
|
15
|
+
end
|
16
|
+
|
17
|
+
if !exec(*command.split(" "))
|
18
|
+
raise "Error running command"
|
19
|
+
end
|
20
|
+
}
|
21
|
+
childpid, childstatus = Process.wait2(pid)
|
22
|
+
childstatus
|
23
|
+
}
|
24
|
+
|
25
|
+
if status.exitstatus > 0
|
26
|
+
raise SubcommandFailed.new(target, command, logname, status.exitstatus)
|
27
|
+
return false
|
28
|
+
else
|
29
|
+
return true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'find'
|
2
|
+
require 'rake/tasklib'
|
3
|
+
|
4
|
+
STAMPFILE = "autobuild-stamp"
|
5
|
+
|
6
|
+
def tree_timestamp(path, *exclude)
|
7
|
+
latest = Time.at(0)
|
8
|
+
latest_file = ""
|
9
|
+
dot = "."[0]
|
10
|
+
|
11
|
+
exclude.collect! { |e| File.expand_path(e, path) }
|
12
|
+
Find.find(path) { |p|
|
13
|
+
Find.prune if File.basename(p)[0] == dot
|
14
|
+
exclude.each { |pattern|
|
15
|
+
Find.prune if File.fnmatch?(pattern, p)
|
16
|
+
}
|
17
|
+
next if File.directory?(p)
|
18
|
+
|
19
|
+
p_time = File.mtime(p)
|
20
|
+
if latest < p_time
|
21
|
+
latest = p_time
|
22
|
+
latest_file = p
|
23
|
+
end
|
24
|
+
}
|
25
|
+
|
26
|
+
return latest
|
27
|
+
end
|
28
|
+
|
29
|
+
class SourceTreeTask < Task
|
30
|
+
attr_accessor :exclude
|
31
|
+
def timestamp
|
32
|
+
tree_timestamp(name, "*CVS", *@exclude)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
def source_tree(path, exclude, &block)
|
36
|
+
task = SourceTreeTask.define_task(path, &block)
|
37
|
+
task.exclude = exclude
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_stamp(stampfile)
|
41
|
+
return Time.at(0) if !File.exists?(stampfile)
|
42
|
+
return File.mtime(stampfile)
|
43
|
+
end
|
44
|
+
|
45
|
+
def touch_stamp(stampfile)
|
46
|
+
File.open(stampfile, "w") { |*| }
|
47
|
+
end
|
48
|
+
|
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.11
|
3
|
+
specification_version: 1
|
4
|
+
name: autobuild
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: "0.1"
|
7
|
+
date: 2005-08-31 00:00:00 +02:00
|
8
|
+
summary: Rake-based utility to build and install multiple packages with dependencies
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
- lib
|
12
|
+
email: sylvain.joyeux@m4x.org
|
13
|
+
homepage:
|
14
|
+
rubyforge_project:
|
15
|
+
description: "autobuild imports, configures, builds and installs software packages (mainly
|
16
|
+
C/C++ autotools packages for now) with dependencies. It can be used in
|
17
|
+
community-based software development to make sure that nothing is broken in the
|
18
|
+
build process of a set of packages."
|
19
|
+
autorequire:
|
20
|
+
default_executable:
|
21
|
+
bindir: bin
|
22
|
+
has_rdoc: true
|
23
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
24
|
+
requirements:
|
25
|
+
-
|
26
|
+
- ">"
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: 0.0.0
|
29
|
+
version:
|
30
|
+
platform: ruby
|
31
|
+
signing_key:
|
32
|
+
cert_chain:
|
33
|
+
authors:
|
34
|
+
- Sylvain Joyeux
|
35
|
+
files:
|
36
|
+
- lib/autobuild/config.rb
|
37
|
+
- lib/autobuild/importer.rb
|
38
|
+
- lib/autobuild/package.rb
|
39
|
+
- lib/autobuild/timestamps.rb
|
40
|
+
- lib/autobuild/logging.rb
|
41
|
+
- lib/autobuild/subcommand.rb
|
42
|
+
- lib/autobuild/environment.rb
|
43
|
+
- lib/autobuild/import/cvs.rb
|
44
|
+
- lib/autobuild/import/svn.rb
|
45
|
+
- lib/autobuild/packages/autotools.rb
|
46
|
+
- lib/autobuild/packages/genom.rb
|
47
|
+
- lib/autobuild/packages/import.rb
|
48
|
+
- bin/autobuild
|
49
|
+
- README
|
50
|
+
test_files: []
|
51
|
+
rdoc_options:
|
52
|
+
- "--title"
|
53
|
+
- Autobuild
|
54
|
+
- "--main"
|
55
|
+
- README
|
56
|
+
extra_rdoc_files:
|
57
|
+
- README
|
58
|
+
executables:
|
59
|
+
- autobuild
|
60
|
+
extensions: []
|
61
|
+
requirements: []
|
62
|
+
dependencies:
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: rake
|
65
|
+
version_requirement:
|
66
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
67
|
+
requirements:
|
68
|
+
-
|
69
|
+
- ">"
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: 0.0.0
|
72
|
+
version:
|
73
|
+
- !ruby/object:Gem::Dependency
|
74
|
+
name: rmail
|
75
|
+
version_requirement:
|
76
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
77
|
+
requirements:
|
78
|
+
-
|
79
|
+
- ">"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: 0.0.0
|
82
|
+
version:
|