Class: Autoproj::CLI::Locate

Inherits:
InspectionTool show all
Defined in:
lib/autoproj/cli/locate.rb

Overview

Deal with locating a package source or build directory in an existing workspace

It is based on a installation manifest file, a YAML file generated to list that information and thus avoid loading the Autoproj configuration (which takes fairly long).

Defined Under Namespace

Classes: NoSuchDir, NotFound

Constant Summary collapse

RESOLUTION_MODES =
%i[source_dir build_dir prefix_dir log]

Instance Attribute Summary collapse

Attributes inherited from Base

#ws

Instance Method Summary collapse

Methods inherited from InspectionTool

#finalize_setup, #initialize_and_load

Methods inherited from Base

#export_env_sh, #normalize_command_line_package_selection, #notify_env_sh_updated, #resolve_selection, #resolve_user_selection, validate_options, #validate_user_selection

Methods included from Ops::Tools

#common_options, #create_autobuild_package, #load_autoprojrc, #load_main_initrb

Constructor Details

#initialize(ws = Workspace.default, installation_manifest: try_loading_installation_manifest(ws)) ⇒ Locate

Create the locate CLI interface

Parameters:

  • ws (Workspace) (defaults to: Workspace.default)

    the workspace we’re working on

  • installation_manifest (InstallationManifest, nil) (defaults to: try_loading_installation_manifest(ws))

    the manifest. If nil, loads the whole autoproj configuration and rebuilds the manifest



25
26
27
28
29
30
31
32
# File 'lib/autoproj/cli/locate.rb', line 25

def initialize(ws = Workspace.default, installation_manifest: try_loading_installation_manifest(ws))
    super(ws)
    ws.load_config

    if installation_manifest
        update_from_installation_manifest(installation_manifest)
    end
end

Instance Attribute Details

#package_setsObject (readonly)

Returns the value of attribute package_sets.



13
14
15
# File 'lib/autoproj/cli/locate.rb', line 13

def package_sets
  @package_sets
end

#packagesObject (readonly)

Returns the value of attribute packages.



12
13
14
# File 'lib/autoproj/cli/locate.rb', line 12

def packages
  @packages
end

Instance Method Details

#build_dir_of(selection) ⇒ Object

Returns the build directory for a given selection

Raises:

  • (NoSuchDir)

    if the selection points to a package set, or to a package that has no build directory



219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/autoproj/cli/locate.rb', line 219

def build_dir_of(selection)
    if workspace_dir?(selection)
        raise NoSuchDir, "#{selection} points to the workspace itself, which has no build dir"
    elsif find_package_set(selection)
        raise NoSuchDir, "#{selection} is a package set, and package sets do not have build directories"
    else
        pkg = resolve_package(selection)
        if pkg.respond_to?(:builddir) && pkg.builddir
            pkg.builddir
        else
            raise NoSuchDir, "#{selection} resolves to the package #{pkg.name}, which does not have a build directory"
        end
    end
end

#find_package_set(selection) ⇒ PackageSet?

Find a package set that matches a given selection

Parameters:

  • selection (String)

    a string that is matched against the package set name and its various directories. Directories are matched against the full path and must end with /

Returns:



51
52
53
54
55
56
57
58
# File 'lib/autoproj/cli/locate.rb', line 51

def find_package_set(selection)
    package_sets.find do |pkg_set|
        name = pkg_set.name
        name == selection ||
            selection.start_with?("#{pkg_set.raw_local_dir}/") ||
            selection.start_with?("#{pkg_set.user_local_dir}/")
    end
end

#find_packages(selection) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/autoproj/cli/locate.rb', line 60

def find_packages(selection)
    selection_rx = Regexp.new(Regexp.quote(selection))
    candidates = []
    packages.each do |pkg|
        name = pkg.name
        if name == selection || selection.start_with?("#{pkg.srcdir}/")
            return [pkg]
        elsif pkg.respond_to?(:builddir) && pkg.builddir && selection.start_with?("#{pkg.builddir}/")
            return [pkg]
        elsif name =~ selection_rx
            candidates << pkg
        end
    end
    candidates
end

#find_packages_with_directory_shortnames(selection) ⇒ Object



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/autoproj/cli/locate.rb', line 76

def find_packages_with_directory_shortnames(selection)
    *directories, basename = *selection.split("/")
    dirname_rx = directories
                 .map { |d| "#{Regexp.quote(d)}\\w*" }
                 .join("/")

    rx        = Regexp.new("#{dirname_rx}/#{Regexp.quote(basename)}")
    rx_strict = Regexp.new("#{dirname_rx}/#{Regexp.quote(basename)}$")

    candidates = []
    candidates_strict = []
    packages.each do |pkg|
        name = pkg.name
        candidates << pkg if name =~ rx
        candidates_strict << pkg if name =~ rx_strict
    end

    if candidates.size > 1 && candidates_strict.size == 1
        candidates_strict
    else
        candidates
    end
end

#initialize_from_workspaceObject



100
101
102
103
104
105
106
# File 'lib/autoproj/cli/locate.rb', line 100

def initialize_from_workspace
    initialize_and_load
    finalize_setup # this exports the manifest

    @packages = ws.manifest.each_autobuild_package.to_a
    @package_sets = ws.manifest.each_package_set.to_a
end

#logs_of(selection, log: nil) ⇒ Object

Resolve logs available for what points to the given selection

The workspace is resolved as the main configuration

If ‘log’ is nil and multiple logs are available,



239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/autoproj/cli/locate.rb', line 239

def logs_of(selection, log: nil)
    if workspace_dir?(selection) || (pkg_set = find_package_set(selection))
        return [] if log && log != "import"

        name = if pkg_set then pkg_set.name
               else
                   "autoproj main configuration"
               end

        import_log = File.join(ws.log_dir, "#{name}-import.log")
        if File.file?(import_log)
            [import_log]
        else
            []
        end
    else
        pkg = resolve_package(selection)
        Dir.enum_for(:glob, File.join(pkg.logdir, "#{pkg.name}-#{log || '*'}.log")).to_a
    end
end

#prefix_dir_of(selection) ⇒ Object

Returns the prefix directory for a given selection

Raises:

  • (NoSuchDir)

    if the selection points to a package set



205
206
207
208
209
210
211
212
213
# File 'lib/autoproj/cli/locate.rb', line 205

def prefix_dir_of(selection)
    if workspace_dir?(selection)
        ws.prefix_dir
    elsif find_package_set(selection)
        raise NoSuchDir, "#{selection} is a package set, and package sets do not have prefixes"
    else
        resolve_package(selection).prefix
    end
end

#resolve_package(selection) ⇒ PackageDefinition

Resolve the package that matches a given selection

Returns:

Raises:

  • (CLIInvalidArguments)

    if nothing matches

  • (AmbiguousSelection)

    if the selection is ambiguous



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/autoproj/cli/locate.rb', line 163

def resolve_package(selection)
    matching_packages = find_packages(selection)
    if matching_packages.empty?
        matching_packages = find_packages_with_directory_shortnames(selection)
    end

    if matching_packages.size > 1
        # If there is more than one candidate, check if there are some that are not
        # present on disk
        present = matching_packages.find_all { |pkg| File.directory?(pkg.srcdir) }
        matching_packages = present if present.size == 1
    end

    if matching_packages.empty?
        raise CLIInvalidArguments, "cannot find '#{selection}' in the current autoproj installation"
    elsif matching_packages.size > 1
        raise CLIAmbiguousArguments, "multiple packages match '#{selection}' in the current autoproj installation: #{matching_packages.map(&:name).sort.join(', ')}"
    else
        matching_packages.first
    end
end

#run(selections, cache: !!packages,, mode: :source_dir, log: nil) ⇒ Object



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/autoproj/cli/locate.rb', line 127

def run(selections, cache: !!packages, mode: :source_dir, log: nil)
    if !RESOLUTION_MODES.include?(mode)
        raise ArgumentError, "'#{mode}' was expected to be one of #{RESOLUTION_MODES}"
    elsif !cache
        initialize_from_workspace
    end

    selections.each do |string|
        string = "#{File.expand_path(string)}/" if File.directory?(string)
        if mode == :source_dir
            puts source_dir_of(string)
        elsif mode == :build_dir
            puts build_dir_of(string)
        elsif mode == :prefix_dir
            puts prefix_dir_of(string)
        elsif mode == :log
            if (all_logs = (log == "all"))
                log = nil
            end
            result = logs_of(string, log: log)
            if (result.size == 1) || all_logs
                result.each { |p| puts p }
            elsif result.size > 1
                puts select_log_file(result)
            elsif result.empty?
                raise NotFound, "no logs found for #{string}"
            end
        end
    end
end

#select_log_file(log_files) ⇒ Object

Interactively select a log file among a list



261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
# File 'lib/autoproj/cli/locate.rb', line 261

def select_log_file(log_files)
    require "tty/prompt"

    log_files = log_files.map do |path|
        [path, File.stat(path).mtime]
    end.sort_by(&:last).reverse

    choices = Hash.new
    log_files.each do |path, mtime|
        if path =~ /-(\w+)\.log/
            choices["(#{mtime}) #{$1}"] = path
        else
            choices["(#{mtime}) #{path}"] = path
        end
    end

    prompt = TTY::Prompt.new
    begin
        prompt.select("Select the log file", choices)
    rescue TTY::Reader::InputInterrupt
        raise Interrupt
    end
end

#source_dir_of(selection) ⇒ Object

Returns the source directory for a given selection



192
193
194
195
196
197
198
199
200
# File 'lib/autoproj/cli/locate.rb', line 192

def source_dir_of(selection)
    if workspace_dir?(selection)
        ws.root_dir
    elsif (pkg_set = find_package_set(selection))
        pkg_set.user_local_dir
    else
        resolve_package(selection).srcdir
    end
end

#try_loading_installation_manifest(ws = self.ws) ⇒ Object

Load the installation manifest



40
41
42
43
# File 'lib/autoproj/cli/locate.rb', line 40

def try_loading_installation_manifest(ws = self.ws)
    Autoproj::InstallationManifest.from_workspace_root(ws.root_dir)
rescue ConfigError
end

#update_from_installation_manifest(installation_manifest) ⇒ Object



34
35
36
37
# File 'lib/autoproj/cli/locate.rb', line 34

def update_from_installation_manifest(installation_manifest)
    @packages = installation_manifest.each_package.to_a
    @package_sets = installation_manifest.each_package_set.to_a
end

#validate_options(selections, options = Hash.new) ⇒ Object



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/autoproj/cli/locate.rb', line 108

def validate_options(selections, options = Hash.new)
    selections, options = super
    mode = if options.delete(:build)
               :build_dir
           elsif options.delete(:prefix)
               :prefix_dir
           elsif (log_type = options[:log])
               options.delete(:log) if log_type == "log"
               :log
           else
               :source_dir
           end
    options[:mode] ||= mode
    selections << ws.root_dir if selections.empty?
    [selections, options]
end

#workspace_dir?(selection) ⇒ Boolean

Tests whether ‘selection’ points to one of the workspace’s root directories

Returns:

  • (Boolean)


187
188
189
# File 'lib/autoproj/cli/locate.rb', line 187

def workspace_dir?(selection)
    selection == "#{ws.root_dir}/" || selection == "#{ws.prefix_dir}/"
end