vanagon 0.10.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -22,7 +22,7 @@ class Vanagon
22
22
  # @return [Makefile::Rule] The $1 rule
23
23
  def self.rule(target, &block)
24
24
  define_method("#{target}_rule") do
25
- Makefile::Rule.new("#{component.name}-#{target}", environment: component.environment) do |rule|
25
+ Makefile::Rule.new("#{component.name}-#{target}") do |rule|
26
26
  instance_exec(rule, &block)
27
27
  end
28
28
  end
@@ -70,7 +70,7 @@ class Vanagon
70
70
  #
71
71
  # @return [Makefile::Rule]
72
72
  def component_rule
73
- Makefile::Rule.new(component.name, environment: component.environment) do |rule|
73
+ Makefile::Rule.new(component.name) do |rule|
74
74
  rule.dependencies = ["#{component.name}-install"]
75
75
  end
76
76
  end
@@ -81,7 +81,7 @@ class Vanagon
81
81
  # @see [Vanagon::Component::Source]
82
82
  rule("unpack") do |r|
83
83
  r.dependencies = ['file-list-before-build']
84
- r.recipe << component.extract_with
84
+ r.recipe << andand_multiline(component.environment_variables, component.extract_with)
85
85
  r.recipe << "touch #{r.target}"
86
86
  end
87
87
 
@@ -109,6 +109,7 @@ class Vanagon
109
109
 
110
110
  unless component.configure.empty?
111
111
  r.recipe << andand_multiline(
112
+ component.environment_variables,
112
113
  "cd #{component.get_build_dir}",
113
114
  component.configure
114
115
  )
@@ -122,6 +123,7 @@ class Vanagon
122
123
  r.dependencies = ["#{component.name}-configure"]
123
124
  unless component.build.empty?
124
125
  r.recipe << andand_multiline(
126
+ component.environment_variables,
125
127
  "cd #{component.get_build_dir}",
126
128
  component.build
127
129
  )
@@ -135,6 +137,7 @@ class Vanagon
135
137
  r.dependencies = ["#{component.name}-build"]
136
138
  unless component.check.empty? || project.settings[:skipcheck]
137
139
  r.recipe << andand_multiline(
140
+ component.environment_variables,
138
141
  "cd #{component.get_build_dir}",
139
142
  component.check
140
143
  )
@@ -148,6 +151,7 @@ class Vanagon
148
151
  r.dependencies = ["#{component.name}-check"]
149
152
  unless component.install.empty?
150
153
  r.recipe << andand_multiline(
154
+ component.environment_variables,
151
155
  "cd #{component.get_build_dir}",
152
156
  component.install
153
157
  )
@@ -108,7 +108,7 @@ class Vanagon
108
108
  when "xz"
109
109
  %(unxz "#{file}")
110
110
  when "zip"
111
- "unzip '#{file}' || 7za x -r -tzip -o'#{File.basename(file, '.zip')}' '#{file}'"
111
+ "unzip -d '#{File.basename(file, '.zip')}' '#{file}' || 7za x -r -tzip -o'#{File.basename(file, '.zip')}' '#{file}'"
112
112
  else
113
113
  raise Vanagon::Error, "Don't know how to decompress #{extension} archives"
114
114
  end
@@ -31,7 +31,7 @@ class Vanagon
31
31
  filter_out_components(only_build) if only_build
32
32
  loginit('vanagon_hosts.log')
33
33
 
34
- @remote_workdir = options[:remote_workdir]
34
+ @remote_workdir = options[:"remote-workdir"]
35
35
 
36
36
  load_engine(engine, @platform, target)
37
37
  rescue LoadError => e
@@ -120,14 +120,18 @@ class Vanagon
120
120
  puts "Target is #{@engine.target}"
121
121
  retry_task { install_build_dependencies }
122
122
  retry_task { @project.fetch_sources(@workdir) }
123
+
123
124
  @project.make_makefile(@workdir)
124
125
  @project.make_bill_of_materials(@workdir)
125
126
  @project.generate_packaging_artifacts(@workdir)
126
127
  @engine.ship_workdir(@workdir)
127
128
  @engine.dispatch("(cd #{@engine.remote_workdir}; #{@platform.make})")
128
129
  @engine.retrieve_built_artifact
129
- @engine.teardown unless @preserve
130
- cleanup_workdir unless @preserve
130
+
131
+ unless @preserve
132
+ @engine.teardown
133
+ cleanup_workdir
134
+ end
131
135
  rescue => e
132
136
  puts e
133
137
  puts e.backtrace.join("\n")
@@ -146,6 +150,8 @@ class Vanagon
146
150
 
147
151
  puts "rendering Makefile"
148
152
  retry_task { @project.fetch_sources(@workdir) }
153
+ @project.make_bill_of_materials(@workdir)
154
+ @project.generate_packaging_artifacts(@workdir)
149
155
  @project.make_makefile(@workdir)
150
156
  end
151
157
 
@@ -1,4 +1,5 @@
1
1
  require 'forwardable'
2
+ require 'vanagon/extensions/string'
2
3
 
3
4
  class Vanagon
4
5
  # Environment is a validating wrapper around a delegated Hash,
@@ -46,22 +47,21 @@ class Vanagon
46
47
  @data = {}
47
48
  end
48
49
 
49
- # Associates the value given by value with the key given by key. Keys must
50
- # be strings, and should conform to the Open Group's guidelines for portable
51
- # shell variable names:
50
+ # Associates the value given by value with the key given by key. Keys will
51
+ # be cast to Strings, and should conform to the Open Group's guidelines for
52
+ # portable shell variable names:
52
53
  # Environment variable names used by the utilities in the Shell and
53
54
  # Utilities volume of IEEE Std 1003.1-2001 consist solely of uppercase
54
55
  # letters, digits, and the '_' (underscore) from the characters defined
55
56
  # in Portable Character Set and do not begin with a digit.
56
57
  #
57
- # Values must be Strings or Integers, and will be stored precisely as given,
58
+ # Values will be cast to Strings, and will be stored precisely as given,
58
59
  # so any escaped characters, single or double quotes, or whitespace will be
59
60
  # preserved exactly as passed during assignment.
60
61
  #
61
62
  # @param key [String]
62
63
  # @param value [String, Integer]
63
- # @raise [ArgumentError] if key is not a String, or if value is not a
64
- # String or an Integer
64
+ # @raise [ArgumentError] if key or value cannot be cast to a String
65
65
  def []=(key, value)
66
66
  @data.update({ validate_key(key) => validate_value(value) })
67
67
  end
@@ -133,32 +133,49 @@ class Vanagon
133
133
  end
134
134
  alias to_string to_s
135
135
 
136
- def sanitize_value(str)
137
- escaped_variables = str.scan(/\$\$([\w]+)/).flatten
136
+ def sanitize_subshells(str)
137
+ pattern = %r{\$\$\((.*?)\)}
138
+ escaped_variables = str.scan(pattern).flatten
138
139
  return str if escaped_variables.empty?
139
140
 
140
- warning = [%(Value "#{str}" looks like it's escaping one or more strings for shell interpolation.)]
141
- escaped_variables.each { |v| warning.push "\t$$#{v} (will be coerced to $(#{v})" }
141
+ warning = [%(Value "#{str}" looks like it's escaping one or more values for subshell interpolation.)]
142
+ escaped_variables.each { |v| warning.push %(\t"$$(#{v})" will be coerced to "$(shell #{v})") }
142
143
  warning.push <<-eos.undent
143
- All environment variables will be resolved by Make; these variables will
144
- be unesecaped for now, but you should update your projects parameters.
144
+ All environment variables will now be resolved by Make before they're executed
145
+ by the shell. These variables will be mangled for you for now, but you should
146
+ update your project's parameters.
145
147
  eos
146
148
 
147
149
  warn warning.join("\n")
148
- str.gsub(/\$\$([\w]+)/, '$(\1)')
150
+ str.gsub(pattern, '$(shell \1)')
149
151
  end
150
- private :sanitize_value
152
+ private :sanitize_subshells
151
153
 
152
- # Validate that a key is a String, that it does not contain invalid
154
+ def sanitize_variables(str)
155
+ pattern = %r{\$\$([\w]+)}
156
+ escaped_variables = str.scan(pattern).flatten
157
+ return str if escaped_variables.empty?
158
+
159
+ warning = [%(Value "#{str}" looks like it's escaping one or more shell variable names for shell interpolation.)]
160
+ escaped_variables.each { |v| warning.push %(\t"$$#{v}" will be coerced to "$(#{v})") }
161
+ warning.push <<-eos.undent
162
+ All environment variables will now be resolved by Make before they're executed
163
+ by the shell. These variables will be mangled for you for now, but you should
164
+ update your project's parameters.
165
+ eos
166
+
167
+ warn warning.join("\n")
168
+ str.gsub(pattern, '$(\1)')
169
+ end
170
+ private :sanitize_variables
171
+
172
+ # Cast key to a String, and validate that it does not contain invalid
153
173
  # characters, and that it does not begin with a digit
154
- # @param key [String]
174
+ # @param key [Object]
155
175
  # @raise [ArgumentError] if key is not a String, if key contains invalid
156
176
  # characters, or if key begins with a digit
157
177
  def validate_key(str)
158
- unless str.is_a?(String)
159
- raise ArgumentError,
160
- 'environment variable Name must be a String'
161
- end
178
+ str = str.to_s
162
179
 
163
180
  if str[0] =~ /\d/
164
181
  raise ArgumentError,
@@ -175,19 +192,13 @@ class Vanagon
175
192
  end
176
193
  private :validate_key
177
194
 
178
- # Validate that str is a String or an Integer, and that the value
195
+ # Cast str to a String, and validate that the value
179
196
  # of str cannot be split into more than a single String by #shellsplit.
180
- # @param value [String, Integer]
181
- # @raise [ArgumentError] if key is not a String or an Integer
197
+ # @param value [Object]
182
198
  def validate_value(str)
183
- unless str.is_a?(String) || str.is_a?(Integer)
184
- raise ArgumentError,
185
- 'Value must be a String or an Integer'
186
- end
187
-
188
199
  # sanitize the value, which should look for any Shell escaped
189
200
  # variable names inside of the value.
190
- str.is_a?(String) ? sanitize_value(str) : str
201
+ sanitize_variables(sanitize_subshells(str.to_s))
191
202
  end
192
203
  private :validate_value
193
204
  end
@@ -4,7 +4,7 @@ class Vanagon
4
4
  class OptParse
5
5
  FLAGS = {
6
6
  :workdir => ['-w DIR', '--workdir DIR', "Working directory where build source should be put (defaults to a tmpdir)"],
7
- :remote_workdir => ['--remote_workdir DIR', "Working directory where build source should be put on the remote host (defaults to a tmpdir)"],
7
+ :"remote-workdir" => ['-r DIR', '--remote-workdir DIR', "Working directory where build source should be put on the remote host (defaults to a tmpdir)"],
8
8
  :configdir => ['-c', '--configdir DIR', 'Configs dir (defaults to $pwd/configs)'],
9
9
  :target => ['-t HOST', '--target HOST', 'Configure a target machine for build and packaging (defaults to grabbing one from the pooler)'],
10
10
  :engine => ['-e ENGINE', '--engine ENGINE', "A custom engine to use (defaults to the pooler) [base, local, docker, pooler currently supported]"],
@@ -72,6 +72,11 @@ class Vanagon
72
72
  # cross-compiled or natively compiled.
73
73
  attr_accessor :cross_compiled
74
74
 
75
+ # Stores a string, pointing at the shell that should be used
76
+ # if a user needs to change the path or name of the shell that
77
+ # Make will run build recipes in.
78
+ attr_accessor :shell
79
+
75
80
  # A string, containing the script that will be executed on
76
81
  # the remote build target to determine how many CPU cores
77
82
  # are available on that platform. Vanagon will use that count
@@ -208,9 +213,15 @@ class Vanagon
208
213
  @find ||= "find"
209
214
  @sort ||= "sort"
210
215
  @copy ||= "cp"
216
+
217
+ # Our first attempt at defining metadata about a platform
211
218
  @cross_compiled ||= false
212
219
  end
213
220
 
221
+ def shell
222
+ @shell ||= "/bin/bash"
223
+ end
224
+
214
225
  # This allows instance variables to be accessed using the hash lookup syntax
215
226
  def [](key)
216
227
  if instance_variable_get("@#{key}")
@@ -389,5 +400,34 @@ class Vanagon
389
400
  def is_cross_compiled_linux?
390
401
  return (is_cross_compiled? && is_linux?)
391
402
  end
403
+
404
+ # Pass in a packaging override. This needs to be implemented for each
405
+ # individual platform so that this input ends up in the right place.
406
+ #
407
+ # @param project
408
+ # @param var the string that should be added to the build script.
409
+ def package_override(project, var)
410
+ fail "I don't know how to set package overrides for #{name}, teach me?"
411
+ end
412
+
413
+ # Generic adder for build repositories
414
+ #
415
+ # @param *args [Array<String>] List of arguments to pass on to the platform specific method
416
+ # @raise [Vanagon::Error] an arror is raised if the current platform does not define add_repository
417
+ def add_build_repository(*args)
418
+ if self.respond_to?(:add_repository)
419
+ self.provision_with self.send(:add_repository, *args)
420
+ else
421
+ raise Vanagon::Error, "Adding a build repository not defined for #{name}"
422
+ end
423
+ end
424
+
425
+ # Set the command to turn the target machine into a builder for vanagon
426
+ #
427
+ # @param command [String] Command to enable the target machine to build packages for the platform
428
+ def provision_with(command)
429
+ provisioning << command
430
+ provisioning.flatten!
431
+ end
392
432
  end
393
433
  end
@@ -102,6 +102,16 @@ class Vanagon
102
102
  provisioning << "apt-get -qq update"
103
103
  end
104
104
 
105
+
106
+ # Pass in a packaging override. This will get added to the rules file, and
107
+ # is a good way to pass in arbitrary environment variables
108
+ #
109
+ # @param project
110
+ # @param var the string that should be added to the build script.
111
+ def package_override(project, var)
112
+ project.package_overrides << var
113
+ end
114
+
105
115
  # Constructor. Sets up some defaults for the debian platform and calls the parent constructor
106
116
  #
107
117
  # @param name [String] name of the platform
@@ -26,7 +26,7 @@ class Vanagon
26
26
  #
27
27
  # @param name [String] name of the platform
28
28
  # @param block [Proc] DSL definition of the platform to call
29
- def platform(platform_name, &block) # rubocop:disable Metrics/AbcSize
29
+ def platform(platform_name, &block)
30
30
  @platform = case platform_name
31
31
  when /^aix-/
32
32
  Vanagon::Platform::RPM::AIX.new(@name)
@@ -51,7 +51,6 @@ class Vanagon
51
51
  end
52
52
 
53
53
  yield(self)
54
- environment 'VANAGON_PLATFORM', platform_name.tr('-', '.')
55
54
  @platform
56
55
  end
57
56
 
@@ -98,6 +97,13 @@ class Vanagon
98
97
  @platform.make = make_cmd
99
98
  end
100
99
 
100
+ # Set the path for Make's SHELL for the platform
101
+ #
102
+ # @param shell_path [String] Full path to the shell Make should use
103
+ def shell(shell_path)
104
+ @platform.shell = shell_path
105
+ end
106
+
101
107
  # Set the path to tar for the platform
102
108
  #
103
109
  # @param tar [String] Full path to the tar command for the platform
@@ -138,7 +144,6 @@ class Vanagon
138
144
  # @param xcc [Boolean] True if this is a cross-compiled platform
139
145
  def cross_compiled(xcc_flag)
140
146
  @platform.cross_compiled = !!xcc_flag
141
- environment 'VANAGON_PLATFORM_XCC', @platform.cross_compiled.to_s
142
147
  end
143
148
 
144
149
  # define an explicit Dist for the platform (most likely used for RPM platforms)
@@ -179,8 +184,7 @@ class Vanagon
179
184
  #
180
185
  # @param command [String] Command to enable the target machine to build packages for the platform
181
186
  def provision_with(command)
182
- @platform.provisioning << command
183
- @platform.provisioning.flatten!
187
+ @platform.provision_with(command)
184
188
  end
185
189
 
186
190
  # Set the command to install any needed build dependencies for the target machine
@@ -316,7 +320,6 @@ class Vanagon
316
320
  # @param user[String] a user string to login with.
317
321
  def target_user(user = "root")
318
322
  @platform.target_user = user
319
- environment 'VANAGON_PLATFORM_USER', user
320
323
  end
321
324
 
322
325
  # Set the target ip address or hostname to start build
@@ -339,7 +342,6 @@ class Vanagon
339
342
  # @param codename [String] codename for this platform (squeeze for example)
340
343
  def codename(codename)
341
344
  @platform.codename = codename
342
- environment 'VANAGON_PLATFORM_CODENAME', codename
343
345
  end
344
346
 
345
347
  def output_dir(directory)
@@ -379,11 +381,7 @@ class Vanagon
379
381
  # @param *args [Array<String>] List of arguments to pass on to the platform specific method
380
382
  # @raise [Vanagon::Error] an arror is raised if the current platform does not define add_repository
381
383
  def add_build_repository(*args)
382
- if @platform.respond_to?(:add_repository)
383
- self.provision_with @platform.send(:add_repository, *args)
384
- else
385
- raise Vanagon::Error, "Adding a build repository not defined for #{@platform.name}"
386
- end
384
+ @platform.add_build_repository(*args)
387
385
  end
388
386
  end
389
387
  end
@@ -69,6 +69,15 @@ class Vanagon
69
69
  commands
70
70
  end
71
71
 
72
+ # Pass in a packaging override. This will get added to the spec file, and
73
+ # is a good way to pass in arbitrary `%_define` or `%_global`
74
+ #
75
+ # @param project
76
+ # @param var the string that should be added to the build script.
77
+ def package_override(project, var)
78
+ project.package_overrides << var
79
+ end
80
+
72
81
  # Constructor. Sets up some defaults for the rpm platform and calls the parent constructor
73
82
  #
74
83
  # @param name [String] name of the platform
@@ -73,6 +73,13 @@ class Vanagon
73
73
  # project should pass to each platform
74
74
  attr_accessor :environment
75
75
 
76
+ # Extra vars to be set in the spec file or debian rules.
77
+ # Good for setting extra %define or %global things for RPM, or env
78
+ # variables needed in the debian rules file
79
+ # No extra munging will be performed, so these should be set as you want
80
+ # them to appear in your spec/rules files!
81
+ attr_accessor :package_overrides
82
+
76
83
  # Loads a given project from the configdir
77
84
  #
78
85
  # @param name [String] the name of the project
@@ -114,6 +121,7 @@ class Vanagon
114
121
  @replaces = []
115
122
  @provides = []
116
123
  @conflicts = []
124
+ @package_overrides = []
117
125
  end
118
126
 
119
127
  # Magic getter to retrieve settings in the project
@@ -393,7 +401,7 @@ class Vanagon
393
401
  files.push get_files.map(&:path)
394
402
  files.push get_configfiles.map(&:path)
395
403
  if @platform.is_windows?
396
- files.flatten.map { |f| "$$(cygpath --mixed --long-name '#{f}')" }
404
+ files.flatten.map { |f| "$(shell cygpath --mixed --long-name '#{f}')" }
397
405
  else
398
406
  files.flatten
399
407
  end
@@ -462,7 +470,6 @@ class Vanagon
462
470
  #
463
471
  # @param workdir [String] workdir to put the packaging files into
464
472
  def generate_packaging_artifacts(workdir)
465
- FileUtils.install File.join(VANAGON_ROOT, "resources", "metrics", "profiling_shell.sh"), File.join(workdir, "profiling_shell.sh"), :mode => 0775
466
473
  @platform.generate_packaging_artifacts(workdir, @name, binding)
467
474
  end
468
475
  end
@@ -24,7 +24,6 @@ class Vanagon
24
24
  # @param block [Proc] DSL definition of the project to call
25
25
  def project(name, &block)
26
26
  yield(self)
27
- environment 'VANAGON_PROJECT', @name
28
27
  end
29
28
 
30
29
  # Accessor for the project.
@@ -70,6 +69,10 @@ class Vanagon
70
69
  @project.settings[name] = value
71
70
  end
72
71
 
72
+ def settings
73
+ @project.settings
74
+ end
75
+
73
76
  # Sets the description of the project. Mainly for use in packaging.
74
77
  #
75
78
  # @param descr [String] description of the project
@@ -82,9 +85,6 @@ class Vanagon
82
85
  # @param the_name [String] name of the project
83
86
  def name(the_name)
84
87
  @project.name = the_name
85
- # Overwrite the environment variable name using the reset name
86
- # of the project.
87
- environment 'VANAGON_PROJECT_NAME', @project.name
88
88
  end
89
89
 
90
90
  # Sets the homepage for the project. Mainly for use in packaging.
@@ -99,7 +99,6 @@ class Vanagon
99
99
  # @param page [Integer] timeout in seconds
100
100
  def timeout(to)
101
101
  @project.timeout = to
102
- environment 'VANAGON_PROJECT_TIMEOUT', @project.timeout
103
102
  end
104
103
 
105
104
  # Sets the run time requirements for the project. Mainly for use in packaging.
@@ -140,7 +139,6 @@ class Vanagon
140
139
  # @param ver [String] version of the project
141
140
  def version(ver)
142
141
  @project.version = ver.tr('-', '.')
143
- environment 'VANAGON_PROJECT_VERSION', @project.version
144
142
  end
145
143
 
146
144
  # Sets the release for the project. Mainly for use in packaging.
@@ -148,7 +146,6 @@ class Vanagon
148
146
  # @param rel [String] release of the project
149
147
  def release(rel)
150
148
  @project.release = rel
151
- environment 'VANAGON_PROJECT_RELEASE', @project.release
152
149
  end
153
150
 
154
151
  # Sets the version for the project based on a git describe of the
@@ -162,12 +159,28 @@ class Vanagon
162
159
  warn "Directory '#{dirname}' cannot be versioned by git. Maybe it hasn't been tagged yet?"
163
160
  end
164
161
 
162
+ # Get the version string from a git branch name. This will look for a '.'
163
+ # delimited string of numbers of any length and return that as the version.
164
+ # For example, 'maint/1.7.0/fixing-some-bugs' will return '1.7.0' and '4.8.x'
165
+ # will return '4.8'.
166
+ #
167
+ # @return version string parsed from branch name, fails if unable to find version
168
+ def version_from_branch
169
+ branch = Git.open(File.expand_path("..", Vanagon::Driver.configdir)).current_branch
170
+ if branch =~ /(\d+(\.\d+)+)/
171
+ return $1
172
+ else
173
+ fail "Can't find a version in your branch, make sure it matches <number>.<number>, like maint/1.7.0/fixing-some-bugs"
174
+ end
175
+ rescue Git::GitExecuteError => e
176
+ fail "Something went wrong trying to find your git branch.\n#{e}"
177
+ end
178
+
165
179
  # Sets the vendor for the project. Used in packaging artifacts.
166
180
  #
167
181
  # @param vend [String] vendor or author of the project
168
182
  def vendor(vend)
169
183
  @project.vendor = vend
170
- environment 'VANAGON_PROJECT_VENDOR', @project.vendor
171
184
  end
172
185
 
173
186
  # Adds a directory to the list of directories provided by the project, to be included in any packages of the project
@@ -262,7 +275,15 @@ class Vanagon
262
275
  # Counter for the number of times a project should retry a task
263
276
  def retry_count(retry_count)
264
277
  @project.retry_count = retry_count
265
- environment 'VANAGON_PROJECT_RETRY_COUNT', @project.retry_count
278
+ end
279
+
280
+ # Set a package override. Will call the platform-specific implementation
281
+ # This will get set in the spec file, deb rules, etc.
282
+ #
283
+ # @param var the string to be included in the build script
284
+ def package_override(var)
285
+ platform = @project.platform
286
+ platform.package_override(self._project, var)
266
287
  end
267
288
  end
268
289
  end