autoproj 2.8.8 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/alog +5 -3
- data/lib/autoproj.rb +2 -0
- data/lib/autoproj/cli/base.rb +8 -1
- data/lib/autoproj/cli/build.rb +5 -3
- data/lib/autoproj/cli/locate.rb +5 -1
- data/lib/autoproj/cli/osdeps.rb +1 -0
- data/lib/autoproj/cli/status.rb +13 -4
- data/lib/autoproj/cli/update.rb +9 -5
- data/lib/autoproj/default.osdeps +14 -0
- data/lib/autoproj/manifest.rb +7 -9
- data/lib/autoproj/ops/build.rb +48 -2
- data/lib/autoproj/ops/configuration.rb +18 -0
- data/lib/autoproj/os_package_installer.rb +27 -0
- data/lib/autoproj/os_repository_installer.rb +53 -0
- data/lib/autoproj/os_repository_resolver.rb +137 -0
- data/lib/autoproj/package_managers/apt_dpkg_manager.rb +70 -10
- data/lib/autoproj/package_managers/bundler_manager.rb +9 -2
- data/lib/autoproj/package_managers/debian_version.rb +123 -0
- data/lib/autoproj/package_managers/manager.rb +9 -0
- data/lib/autoproj/package_managers/pip_manager.rb +4 -0
- data/lib/autoproj/package_selection.rb +36 -10
- data/lib/autoproj/package_set.rb +39 -4
- data/lib/autoproj/repository_managers/apt.rb +289 -0
- data/lib/autoproj/repository_managers/manager.rb +26 -0
- data/lib/autoproj/repository_managers/unknown_os_manager.rb +30 -0
- data/lib/autoproj/test.rb +1 -0
- data/lib/autoproj/version.rb +1 -1
- data/lib/autoproj/workspace.rb +21 -7
- metadata +8 -2
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Autoproj
|
5
|
+
# Manager for OS repository provided by package sets
|
6
|
+
class OSRepositoryResolver
|
7
|
+
# All the information contained in all the OSrepos files
|
8
|
+
attr_reader :all_definitions
|
9
|
+
|
10
|
+
# The operating system
|
11
|
+
attr_accessor :operating_system
|
12
|
+
|
13
|
+
def self.load(file)
|
14
|
+
raise ArgumentError, "no such file or directory: #{file}" unless File.file?(file)
|
15
|
+
|
16
|
+
error_t = if defined? Psych::SyntaxError
|
17
|
+
[ArgumentError, Psych::SyntaxError]
|
18
|
+
else
|
19
|
+
ArgumentError
|
20
|
+
end
|
21
|
+
|
22
|
+
result = new
|
23
|
+
file = File.expand_path(file)
|
24
|
+
begin
|
25
|
+
data = YAML.safe_load(File.read(file)) || {}
|
26
|
+
verify_definitions(data)
|
27
|
+
rescue *error_t => e
|
28
|
+
raise ConfigError.new, "error in #{file}: #{e.message}", e.backtrace
|
29
|
+
end
|
30
|
+
result.merge(new(data, file))
|
31
|
+
result
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize(defs = [], file = nil, operating_system: nil)
|
35
|
+
@operating_system = operating_system
|
36
|
+
@all_definitions = Set.new
|
37
|
+
if file
|
38
|
+
defs.each do |def_|
|
39
|
+
all_definitions << [[file], def_]
|
40
|
+
end
|
41
|
+
else
|
42
|
+
defs.each do |def_|
|
43
|
+
all_definitions << [[], def_]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def merge(info)
|
49
|
+
@all_definitions += info.all_definitions
|
50
|
+
end
|
51
|
+
|
52
|
+
def definitions
|
53
|
+
all_definitions.map(&:last).uniq
|
54
|
+
end
|
55
|
+
|
56
|
+
def all_entries
|
57
|
+
definitions.map do |distribution|
|
58
|
+
distribution.values.map do |release|
|
59
|
+
release.map(&:values)
|
60
|
+
end
|
61
|
+
end.flatten.uniq
|
62
|
+
end
|
63
|
+
|
64
|
+
def entry_matches?(entry, identifiers)
|
65
|
+
!(entry.keys.first.split(',').map(&:strip) & identifiers).empty?
|
66
|
+
end
|
67
|
+
|
68
|
+
def resolved_entries
|
69
|
+
os_name, os_version = operating_system
|
70
|
+
os_version << 'default' unless os_version.include?('default')
|
71
|
+
|
72
|
+
distribution_filtered = definitions.select do |entry|
|
73
|
+
entry_matches?(entry, os_name)
|
74
|
+
end.map(&:values).flatten
|
75
|
+
|
76
|
+
release_filtered = distribution_filtered.select { |entry| entry_matches?(entry, os_version) }
|
77
|
+
release_filtered.map(&:values).flatten.uniq
|
78
|
+
end
|
79
|
+
|
80
|
+
# OS repos definitions must follow the format:
|
81
|
+
#
|
82
|
+
# - distribution:
|
83
|
+
# - release:
|
84
|
+
# - key1: value1
|
85
|
+
# key2: value2
|
86
|
+
#
|
87
|
+
# The key, value pairs are OS dependent, and will be verified/parsed by the
|
88
|
+
# corresponding 'repository manager'. Thus, for a debian-like distribution
|
89
|
+
# one would have something like:
|
90
|
+
#
|
91
|
+
# - ubuntu:
|
92
|
+
# - xenial:
|
93
|
+
# - type: repo
|
94
|
+
# repo: 'deb http://br.archive.ubuntu.com/ubuntu/ xenial main restricted'
|
95
|
+
def self.verify_definitions(array, path = [])
|
96
|
+
verify_type(array, Array, path)
|
97
|
+
array.each do |entry|
|
98
|
+
verify_type(entry, Hash, path)
|
99
|
+
verify_os(entry, (path + [entry]))
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.verify_os(hash, path = [])
|
104
|
+
verify_type(hash, Hash, path)
|
105
|
+
hash.each do |key, value|
|
106
|
+
verify_type(key, String, path)
|
107
|
+
verify_release(value, (path + [value]))
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def self.verify_release(array, path = [])
|
112
|
+
verify_type(array, Array, path)
|
113
|
+
array.each do |releases|
|
114
|
+
verify_type(releases, Hash, path)
|
115
|
+
|
116
|
+
releases.values.each do |entries|
|
117
|
+
verify_type(entries, Array, path)
|
118
|
+
verify_entries(entries, path + entries)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def self.verify_entries(array, path = [])
|
124
|
+
verify_type(array, Array, path)
|
125
|
+
array.each do |entry|
|
126
|
+
verify_type(entry, Hash, path)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.verify_type(obj, type, path = [])
|
131
|
+
return if obj.is_a?(type)
|
132
|
+
|
133
|
+
raise ArgumentError, "invalid osrepos definition in #{path.join('/')}: "\
|
134
|
+
"expected a #{type}, found a #{obj.class}"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'autoproj/package_managers/debian_version'
|
2
|
+
|
1
3
|
module Autoproj
|
2
4
|
module PackageManagers
|
3
5
|
# Package manager interface for systems that use APT and dpkg for
|
@@ -8,15 +10,36 @@ class AptDpkgManager < ShellScriptManager
|
|
8
10
|
def initialize(ws, status_file = "/var/lib/dpkg/status")
|
9
11
|
@status_file = status_file
|
10
12
|
@installed_packages = nil
|
13
|
+
@installed_versions = nil
|
11
14
|
super(ws, true,
|
12
15
|
%w{apt-get install},
|
13
16
|
%w{DEBIAN_FRONTEND=noninteractive apt-get install -y})
|
14
17
|
end
|
15
18
|
|
16
|
-
def
|
19
|
+
def configure_manager
|
20
|
+
super
|
21
|
+
ws.config.declare 'apt_dpkg_update', 'boolean',
|
22
|
+
default: 'yes',
|
23
|
+
doc: ['Would you like autoproj to keep apt packages up-to-date?']
|
24
|
+
keep_uptodate?
|
25
|
+
end
|
26
|
+
|
27
|
+
def keep_uptodate?
|
28
|
+
ws.config.get('apt_dpkg_update')
|
29
|
+
end
|
30
|
+
|
31
|
+
def keep_uptodate=(flag)
|
32
|
+
ws.config.set('apt_dpkg_update', flag, true)
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.parse_package_status(installed_packages, installed_versions, paragraph)
|
17
36
|
if paragraph =~ /^Status: install ok installed$/
|
18
37
|
if paragraph =~ /^Package: (.*)$/
|
19
|
-
|
38
|
+
package_name = $1
|
39
|
+
installed_packages << package_name
|
40
|
+
if paragraph =~ /^Version: (.*)$/
|
41
|
+
installed_versions[package_name] = DebianVersion.new($1)
|
42
|
+
end
|
20
43
|
end
|
21
44
|
if paragraph =~ /^Provides: (.*)$/
|
22
45
|
installed_packages.merge($1.split(',').map(&:strip))
|
@@ -26,6 +49,7 @@ def self.parse_package_status(installed_packages, paragraph)
|
|
26
49
|
|
27
50
|
def self.parse_dpkg_status(status_file)
|
28
51
|
installed_packages = Set.new
|
52
|
+
installed_versions = {}
|
29
53
|
dpkg_status = File.read(status_file)
|
30
54
|
dpkg_status << "\n"
|
31
55
|
|
@@ -36,17 +60,53 @@ def self.parse_dpkg_status(status_file)
|
|
36
60
|
|
37
61
|
while paragraph_end = dpkg_status.scan_until(/Package: /)
|
38
62
|
paragraph = "Package: #{paragraph_end[0..-10]}"
|
39
|
-
parse_package_status(installed_packages, paragraph)
|
63
|
+
parse_package_status(installed_packages, installed_versions, paragraph)
|
64
|
+
end
|
65
|
+
parse_package_status(installed_packages, installed_versions, "Package: #{dpkg_status.rest}")
|
66
|
+
[installed_packages, installed_versions]
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.parse_apt_cache_paragraph(paragraph)
|
70
|
+
version = '0'
|
71
|
+
if paragraph =~ /^Package: (.*)$/
|
72
|
+
package_name = $1
|
73
|
+
if paragraph =~ /^Version: (.*)$/
|
74
|
+
version = $1
|
75
|
+
end
|
76
|
+
end
|
77
|
+
[package_name, version]
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.parse_packages_versions(packages)
|
81
|
+
packages_versions = {}
|
82
|
+
apt_cache_show = `apt-cache show --no-all-versions #{packages.join(' ')}`
|
83
|
+
apt_cache_show = StringScanner.new(apt_cache_show)
|
84
|
+
if !apt_cache_show.scan(/Package: /)
|
85
|
+
return packages_versions
|
86
|
+
end
|
87
|
+
|
88
|
+
while paragraph_end = apt_cache_show.scan_until(/Package: /)
|
89
|
+
paragraph = "Package: #{paragraph_end[0..-10]}"
|
90
|
+
package_name, version = parse_apt_cache_paragraph(paragraph)
|
91
|
+
packages_versions[package_name] = DebianVersion.new(version)
|
40
92
|
end
|
41
|
-
|
42
|
-
|
93
|
+
package_name, version = parse_apt_cache_paragraph("Package: #{apt_cache_show.rest}")
|
94
|
+
packages_versions[package_name] = DebianVersion.new(version)
|
95
|
+
packages_versions
|
96
|
+
end
|
97
|
+
|
98
|
+
def updated?(package, available_version)
|
99
|
+
# Consider up-to-date if the package is provided by another package (purely virtual)
|
100
|
+
# Ideally, we should check the version of the package that provides it
|
101
|
+
return true unless available_version && @installed_versions[package]
|
102
|
+
|
103
|
+
(available_version <= @installed_versions[package])
|
43
104
|
end
|
44
105
|
|
45
106
|
# On a dpkg-enabled system, checks if the provided package is installed
|
46
107
|
# and returns true if it is the case
|
47
108
|
def installed?(package_name, filter_uptodate_packages: false, install_only: false)
|
48
|
-
@installed_packages
|
49
|
-
|
109
|
+
@installed_packages, @installed_versions = self.class.parse_dpkg_status(status_file) unless @installed_packages && @installed_versions
|
50
110
|
if package_name =~ /^(\w[a-z0-9+-.]+)/
|
51
111
|
@installed_packages.include?($1)
|
52
112
|
else
|
@@ -54,11 +114,12 @@ def installed?(package_name, filter_uptodate_packages: false, install_only: fals
|
|
54
114
|
false
|
55
115
|
end
|
56
116
|
end
|
57
|
-
|
117
|
+
|
58
118
|
def install(packages, filter_uptodate_packages: false, install_only: false)
|
119
|
+
packages_versions = self.class.parse_packages_versions(packages)
|
59
120
|
if filter_uptodate_packages || install_only
|
60
121
|
packages = packages.find_all do |package_name|
|
61
|
-
!installed?(package_name)
|
122
|
+
!installed?(package_name) || (keep_uptodate? && !updated?(package_name, packages_versions[package_name]))
|
62
123
|
end
|
63
124
|
end
|
64
125
|
|
@@ -71,4 +132,3 @@ def install(packages, filter_uptodate_packages: false, install_only: false)
|
|
71
132
|
end
|
72
133
|
end
|
73
134
|
end
|
74
|
-
|
@@ -213,7 +213,7 @@ def self.run_bundler(ws, *commandline, gem_home: nil, gemfile: nil)
|
|
213
213
|
'GEM_PATH' => nil,
|
214
214
|
'BUNDLE_GEMFILE' => gemfile,
|
215
215
|
'RUBYOPT' => nil,
|
216
|
-
'RUBYLIB' =>
|
216
|
+
'RUBYLIB' => rubylib_for_bundler
|
217
217
|
]
|
218
218
|
ws.run 'autoproj', 'osdeps',
|
219
219
|
Autobuild.tool('bundle'), *commandline,
|
@@ -388,6 +388,11 @@ def discover_rubylib
|
|
388
388
|
end
|
389
389
|
end
|
390
390
|
|
391
|
+
def self.rubylib_for_bundler
|
392
|
+
rx = Regexp.new("/gems/#{Regexp.quote("bundler-#{Bundler::VERSION}")}/")
|
393
|
+
$LOAD_PATH.grep(rx).join(File::PATH_SEPARATOR)
|
394
|
+
end
|
395
|
+
|
391
396
|
def discover_bundle_rubylib(silent_errors: false)
|
392
397
|
require 'bundler'
|
393
398
|
gemfile = File.join(ws.prefix_dir, 'gems', 'Gemfile')
|
@@ -398,7 +403,9 @@ def discover_bundle_rubylib(silent_errors: false)
|
|
398
403
|
env = ws.env.resolved_env
|
399
404
|
Tempfile.open 'autoproj-rubylib' do |io|
|
400
405
|
result = Bundler.clean_system(
|
401
|
-
Hash['GEM_HOME' => env['GEM_HOME'], 'GEM_PATH' => env['GEM_PATH'],
|
406
|
+
Hash['GEM_HOME' => env['GEM_HOME'], 'GEM_PATH' => env['GEM_PATH'],
|
407
|
+
'BUNDLE_GEMFILE' => gemfile, 'RUBYOPT' => nil,
|
408
|
+
'RUBYLIB' => self.class.rubylib_for_bundler],
|
402
409
|
Autobuild.tool('ruby'), '-rbundler/setup', '-e', 'puts $LOAD_PATH',
|
403
410
|
out: io, **silent_redirect)
|
404
411
|
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module Autoproj
|
2
|
+
module PackageManagers
|
3
|
+
class DebianVersion
|
4
|
+
attr_reader :version
|
5
|
+
attr_reader :epoch
|
6
|
+
attr_reader :upstream_version
|
7
|
+
attr_reader :debian_revision
|
8
|
+
|
9
|
+
include Comparable
|
10
|
+
|
11
|
+
def initialize(version)
|
12
|
+
@version = version
|
13
|
+
parse_version
|
14
|
+
end
|
15
|
+
|
16
|
+
def split
|
17
|
+
[epoch, upstream_version, debian_revision]
|
18
|
+
end
|
19
|
+
|
20
|
+
def <=>(b)
|
21
|
+
(0..2).inject(0) do |result, i|
|
22
|
+
return result unless result == 0
|
23
|
+
normalize(compare_fragments(self.split[i], b.split[i]))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.compare(a, b)
|
28
|
+
new(a)<=>new(b)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def normalize(value)
|
34
|
+
return -1 if value < 0
|
35
|
+
return 1 if value > 0
|
36
|
+
return 0 if value == 0
|
37
|
+
end
|
38
|
+
|
39
|
+
# Reference: https://www.debian.org/doc/debian-policy/ch-controlfields.html#version
|
40
|
+
def parse_version
|
41
|
+
@epoch = '0'
|
42
|
+
@debian_revision = '0'
|
43
|
+
|
44
|
+
@upstream_version = @version.split(':')
|
45
|
+
if @upstream_version.size > 1
|
46
|
+
@epoch = @upstream_version.first
|
47
|
+
@upstream_version = @upstream_version[1..-1].join(':')
|
48
|
+
else
|
49
|
+
@upstream_version = @upstream_version.first
|
50
|
+
end
|
51
|
+
|
52
|
+
@upstream_version = @upstream_version.split('-')
|
53
|
+
if @upstream_version.size > 1
|
54
|
+
@debian_revision = @upstream_version.last
|
55
|
+
@upstream_version = @upstream_version[0..-2].join('-')
|
56
|
+
else
|
57
|
+
@upstream_version = @upstream_version.first
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def alpha?(look_ahead)
|
62
|
+
look_ahead =~ /[[:alpha:]]/
|
63
|
+
end
|
64
|
+
|
65
|
+
def digit?(look_ahead)
|
66
|
+
look_ahead =~ /[[:digit:]]/
|
67
|
+
end
|
68
|
+
|
69
|
+
def order(c)
|
70
|
+
if digit?(c)
|
71
|
+
return 0
|
72
|
+
elsif alpha?(c)
|
73
|
+
return c.ord
|
74
|
+
elsif c == '~'
|
75
|
+
return -1
|
76
|
+
elsif c
|
77
|
+
return c.ord + 256
|
78
|
+
else
|
79
|
+
return 0
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Ported from https://github.com/Debian/apt/blob/master/apt-pkg/deb/debversion.cc
|
84
|
+
def compare_fragments(a, b)
|
85
|
+
i = 0
|
86
|
+
j = 0
|
87
|
+
while i != a.size && j != b.size
|
88
|
+
first_diff = 0
|
89
|
+
while i != a.size && j != b.size && (!digit?(a[i]) || !digit?(b[j]))
|
90
|
+
vc = order(a[i])
|
91
|
+
rc = order(b[j])
|
92
|
+
return vc-rc if vc != rc
|
93
|
+
i += 1
|
94
|
+
j += 1
|
95
|
+
end
|
96
|
+
|
97
|
+
i += 1 while a[i] == '0'
|
98
|
+
j += 1 while b[j] == '0'
|
99
|
+
while digit?(a[i]) && digit?(b[j])
|
100
|
+
first_diff = a[i].ord - b[j].ord if first_diff == 0
|
101
|
+
i += 1
|
102
|
+
j += 1
|
103
|
+
end
|
104
|
+
|
105
|
+
return 1 if digit?(a[i])
|
106
|
+
return -1 if digit?(b[j])
|
107
|
+
return first_diff if first_diff != 0
|
108
|
+
end
|
109
|
+
|
110
|
+
return 0 if i == a.size && j == b.size
|
111
|
+
|
112
|
+
if i == a.size
|
113
|
+
return 1 if b[j] == '~'
|
114
|
+
return -1
|
115
|
+
end
|
116
|
+
if j == b.size
|
117
|
+
return -1 if a[i] == '~'
|
118
|
+
return 1
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -41,6 +41,11 @@ def strict?
|
|
41
41
|
false
|
42
42
|
end
|
43
43
|
|
44
|
+
# If this package manager depends on OS packages, they should be added here
|
45
|
+
def os_dependencies
|
46
|
+
[]
|
47
|
+
end
|
48
|
+
|
44
49
|
# Create a package manager
|
45
50
|
#
|
46
51
|
# @param [Workspace] ws the underlying workspace
|
@@ -56,6 +61,10 @@ def initialize(ws)
|
|
56
61
|
# This is e.g. needed for python pip or rubygems
|
57
62
|
def initialize_environment
|
58
63
|
end
|
64
|
+
|
65
|
+
# Perform additional configuration required for the package manager
|
66
|
+
def configure_manager
|
67
|
+
end
|
59
68
|
end
|
60
69
|
end
|
61
70
|
end
|