autobuild 0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|