autoproj 2.7.1 → 2.8.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.
Files changed (44) hide show
  1. checksums.yaml +5 -5
  2. data/Rakefile +31 -1
  3. data/autoproj.gemspec +2 -0
  4. data/bin/alocate +3 -5
  5. data/bin/alog +3 -3
  6. data/bin/amake +3 -4
  7. data/bin/aup +4 -3
  8. data/bin/autoproj +1 -6
  9. data/bin/autoproj_bootstrap +153 -47
  10. data/bin/autoproj_install +153 -47
  11. data/lib/autoproj/autobuild_extensions/dsl.rb +5 -0
  12. data/lib/autoproj/bash_completion.rb +26 -0
  13. data/lib/autoproj/cli/base.rb +4 -4
  14. data/lib/autoproj/cli/build.rb +2 -3
  15. data/lib/autoproj/cli/main.rb +52 -2
  16. data/lib/autoproj/cli/main_global.rb +39 -0
  17. data/lib/autoproj/cli/osdeps.rb +2 -1
  18. data/lib/autoproj/cli/update.rb +13 -1
  19. data/lib/autoproj/configuration.rb +14 -2
  20. data/lib/autoproj/environment.rb +48 -31
  21. data/lib/autoproj/ops/install.rb +153 -47
  22. data/lib/autoproj/shell_completion.rb +164 -0
  23. data/lib/autoproj/templates/helpers.bash.erb +79 -0
  24. data/lib/autoproj/templates/helpers.zsh.erb +38 -0
  25. data/lib/autoproj/templates/main.bash.erb +35 -0
  26. data/lib/autoproj/templates/main.zsh.erb +9 -0
  27. data/lib/autoproj/templates/subcommand.bash.erb +50 -0
  28. data/lib/autoproj/templates/subcommand.zsh.erb +51 -0
  29. data/lib/autoproj/version.rb +1 -1
  30. data/lib/autoproj/workspace.rb +97 -19
  31. data/lib/autoproj/zsh_completion.rb +43 -0
  32. data/shell/autoproj_bash +67 -0
  33. data/shell/autoproj_zsh +26 -0
  34. data/shell/completion/alocate_bash +68 -0
  35. data/shell/completion/alocate_zsh +22 -0
  36. data/shell/completion/alog_bash +61 -0
  37. data/shell/completion/alog_zsh +20 -0
  38. data/shell/completion/amake_bash +77 -0
  39. data/shell/completion/amake_zsh +27 -0
  40. data/shell/completion/aup_bash +89 -0
  41. data/shell/completion/aup_zsh +34 -0
  42. data/shell/completion/autoproj_bash +1556 -0
  43. data/shell/completion/autoproj_zsh +1005 -0
  44. metadata +51 -3
@@ -0,0 +1,164 @@
1
+ require 'autoproj/cli/main'
2
+ require 'erb'
3
+
4
+ module Autoproj
5
+ # Generates shell completion for code for a given Thor subclass
6
+ class ShellCompletion
7
+ # The CLI
8
+ attr_reader :cli
9
+ # The command name
10
+ attr_reader :name
11
+ # A hash describing the CLI
12
+ attr_reader :cli_metadata
13
+
14
+ TEMPLATES_DIR = File.join(File.dirname(__FILE__), 'templates')
15
+
16
+ def initialize(name = 'autoproj', cli: Autoproj::CLI::Main, command: nil)
17
+ @cli = cli
18
+ @name = name
19
+
20
+ generate_metadata
21
+ return unless command
22
+ @cli_metadata = subcommand_by_name(*command)
23
+ @cli_metadata[:name] = "__#{name}"
24
+ end
25
+
26
+ def generate_metadata
27
+ @cli_metadata = { name: "__#{name}",
28
+ description: nil,
29
+ options: [],
30
+ subcommands: subcommand_metadata(cli) }
31
+
32
+ setup_completion_functions
33
+ populate_help_subcommands
34
+ end
35
+
36
+ def setup_completion_functions
37
+ %w[which exec].each do |command|
38
+ setup_executable_completion(subcommand_by_name(command))
39
+ end
40
+
41
+ %w[cache manifest].each do |command|
42
+ setup_file_completion(subcommand_by_name(command))
43
+ end
44
+
45
+ # TODO: investigate how to handle 'plugin' subcommands completion,
46
+ # leaving disabled for now
47
+ # TODO: reset subcommand needs a custom completer,
48
+ # leaving disabled for now
49
+ # TODO: log subcommand needs a custom completer,
50
+ # leaving disabled for now
51
+ ['bootstrap', 'envsh', 'reconfigure', 'reset', 'log', 'query',
52
+ 'switch-config', %w[global register], %w[global status],
53
+ %w[plugin install], %w[plugin remove], %w[plugin list]].each do |command|
54
+ disable_completion(subcommand_by_name(*command))
55
+ end
56
+ end
57
+
58
+ def generate
59
+ template_file = File.join(TEMPLATES_DIR, self.class::MAIN_FUNCTION_TEMPLATE)
60
+ erb = File.read(template_file)
61
+ ::ERB.new(erb, nil, '-').result(binding)
62
+ end
63
+
64
+ def subcommand_by_name(*name, metadata: cli_metadata)
65
+ subcommand = metadata
66
+
67
+ name.each do |subcommand_name|
68
+ subcommand = subcommand[:subcommands].find do |s|
69
+ s[:name] == subcommand_name
70
+ end
71
+ end
72
+ subcommand
73
+ end
74
+
75
+ def populate_help_subcommands(command_metadata = cli_metadata)
76
+ help_subcommand = subcommand_by_name('help',
77
+ metadata: command_metadata)
78
+
79
+ if help_subcommand
80
+ help_subcommand[:options] = []
81
+ disable_completion(help_subcommand)
82
+ end
83
+
84
+ command_metadata[:subcommands].each do |subcommand|
85
+ next if subcommand[:name] == 'help'
86
+ populate_help_subcommands(subcommand)
87
+ next unless help_subcommand
88
+ help_subcommand[:subcommands] << { name: subcommand[:name],
89
+ aliases: [],
90
+ description: subcommand[:description],
91
+ options: [],
92
+ subcommands: [] }
93
+ end
94
+ end
95
+
96
+ def render_subcommand_function(subcommand, options = {})
97
+ prefix = options[:prefix] || []
98
+ source = []
99
+
100
+ prefix = (prefix + [subcommand[:name]])
101
+ function_name = prefix.join('_')
102
+ depth = prefix.size + 1
103
+
104
+ template_file = File.join(TEMPLATES_DIR, self.class::SUBCOMMAND_FUNCTION_TEMPLATE)
105
+ erb = ::ERB.new(File.read(template_file), nil, '-')
106
+
107
+ source << erb.result(binding)
108
+ subcommand[:subcommands].each do |subcommand|
109
+ source << render_subcommand_function(subcommand, prefix: prefix)
110
+ end
111
+ source.join("\n").strip + "\n"
112
+ end
113
+
114
+ def subcommand_metadata(cli)
115
+ result = []
116
+ cli.all_commands.reject { |_, t| t.hidden? }.each do |(name, command)|
117
+ aliases = cli.map.select do |_, original_name|
118
+ name == original_name
119
+ end.map(&:first)
120
+ result << generate_command_metadata(cli, name, command, aliases)
121
+ end
122
+ result
123
+ end
124
+
125
+ def generate_command_metadata(cli, name, command, aliases)
126
+ subcommands = if (subcommand_class = cli.subcommand_classes[name])
127
+ subcommand_metadata(subcommand_class)
128
+ else
129
+ []
130
+ end
131
+
132
+ info = { name: hyphenate(name),
133
+ aliases: aliases.map { |a| hyphenate(a) },
134
+ usage: command.usage,
135
+ description: command.description,
136
+ options: options_metadata(cli.class_options) +
137
+ options_metadata(command.options),
138
+ subcommands: subcommands }
139
+
140
+ if subcommands.empty?
141
+ setup_package_completion(info)
142
+ else
143
+ info[:options] = []
144
+ disable_completion(info)
145
+ end
146
+ info
147
+ end
148
+
149
+ def options_metadata(options)
150
+ options.reject { |_, option| option.hide }.map do |_, option|
151
+ names = ["--#{hyphenate(option.name)}"]
152
+ names += ["--no-#{hyphenate(option.name)}"] if option.boolean?
153
+ names += option.aliases.map { |a| "-#{hyphenate(a)}" }
154
+
155
+ { names: names,
156
+ description: option.description }
157
+ end
158
+ end
159
+
160
+ def hyphenate(s)
161
+ s.to_s.tr('_', '-')
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env bash
2
+
3
+ function acd {
4
+ local pkg_path
5
+ pkg_path=$(autoproj locate $1)
6
+
7
+ if [ $? != 0 ]; then
8
+ return 1
9
+ else
10
+ pkg_path=$(echo $pkg_path | tail -1)
11
+ cd $pkg_path
12
+ return 0
13
+ fi
14
+ }
15
+
16
+ type -p _get_comp_words_by_ref || return 1
17
+
18
+ _autoproj_installed_packages() {
19
+ local packages="`ruby 2>/dev/null <<EOF
20
+ begin
21
+ require 'autoproj'
22
+
23
+ path = Autoproj.workspace.installation_manifest_path
24
+ manifest = Autoproj::InstallationManifest.new(path)
25
+
26
+ manifest.load
27
+ manifest.each_package { |pkg| puts pkg.name }
28
+ rescue
29
+ end
30
+ EOF`"
31
+
32
+ COMPREPLY=( $( compgen -W "$packages" -- "$cur" ) )
33
+ }
34
+
35
+ _autoproj_subcommands() {
36
+ local subcommands="$1"
37
+
38
+ local counter=$((command_pos + 1))
39
+ while [ "$counter" -lt "$cword" ]; do
40
+ case "${words[$counter]}" in
41
+ $(_autoproj_to_extglob "$subcommands") )
42
+ subcommand_pos=$counter
43
+ local subcommand=${words[$counter]}
44
+ local completions_func=_autoproj_${command}_${subcommand//-/_}
45
+ declare -F "$completions_func" >/dev/null && "$completions_func"
46
+ return 0
47
+ ;;
48
+ esac
49
+ (( counter++ ))
50
+ done
51
+ return 1
52
+ }
53
+
54
+ _autoproj_to_alternatives() {
55
+ local parts=( $1 )
56
+ local IFS='|'
57
+ echo "${parts[*]}"
58
+ }
59
+
60
+ _autoproj_to_extglob() {
61
+ local extglob=$( _autoproj_to_alternatives "$1" )
62
+ echo "@($extglob)"
63
+ }
64
+
65
+ _acd()
66
+ {
67
+ local cur prev words cword
68
+ _get_comp_words_by_ref -n : cur prev words cword
69
+ case "$cword" in
70
+ 1)
71
+ _autoproj_installed_packages
72
+ ;;
73
+ *)
74
+ COMPREPLY=()
75
+ ;;
76
+ esac
77
+ }
78
+
79
+ complete -F _acd acd
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env bash
2
+
3
+ function acd {
4
+ local pkg_path
5
+ pkg_path=$(autoproj locate $1)
6
+
7
+ if [ $? != 0 ]; then
8
+ return 1
9
+ else
10
+ pkg_path=$(echo $pkg_path | tail -1)
11
+ cd $pkg_path
12
+ return 0
13
+ fi
14
+ }
15
+
16
+ _autoproj_installed_packages() {
17
+ ruby 2>/dev/null <<EOF | while IFS= read -r; do packages+=("$REPLY"); done
18
+
19
+ begin
20
+ require 'autoproj'
21
+
22
+ path = Autoproj.workspace.installation_manifest_path
23
+ manifest = Autoproj::InstallationManifest.new(path)
24
+
25
+ manifest.load
26
+ manifest.each_package { |pkg| puts pkg.name }
27
+ rescue
28
+ end
29
+ EOF
30
+
31
+ compadd -a packages
32
+ }
33
+
34
+ _acd () {
35
+ _arguments -s "1:package:_autoproj_installed_packages"
36
+ }
37
+
38
+ compdef _acd acd
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env bash
2
+
3
+ __<%= name %>_<%= name %>()
4
+ {
5
+ __<%= name %>
6
+ }
7
+
8
+ _<%= name %>()
9
+ {
10
+ local cur prev words cword
11
+ local command='<%= name %>'
12
+ local counter=1
13
+
14
+ _get_comp_words_by_ref -n : cur prev words cword
15
+
16
+ while [ "$counter" -lt "$cword" ]; do
17
+ case "${words[$counter]}" in
18
+ -*)
19
+ break
20
+ ;;
21
+ *)
22
+ command="${words[$counter]}"
23
+ break
24
+ ;;
25
+ esac
26
+ (( counter++ ))
27
+ done
28
+
29
+ local completions_func=__<%= name %>_${command//-/_}
30
+ $completions_func
31
+ }
32
+
33
+ <%= render_subcommand_function(cli_metadata) %>
34
+
35
+ complete -F _<%= name %> <%= name %>
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env zsh
2
+
3
+ _<%= name %>() {
4
+ __<%= name %>
5
+ }
6
+
7
+ <%= render_subcommand_function(cli_metadata) %>
8
+
9
+ compdef _<%= name %> <%= name %>
@@ -0,0 +1,50 @@
1
+ <%- if subcommand[:subcommands].any? %>
2
+ <%= function_name %>() {
3
+ local subcommands="
4
+ <%- subcommand[:subcommands].each do |subcommand| -%>
5
+ <%= subcommand[:name] %>
6
+ <%- subcommand[:aliases].each do |_alias| -%>
7
+ <%= _alias %>
8
+ <%- end -%>
9
+ <%- end -%>
10
+ "
11
+
12
+ local options="
13
+ <%- subcommand[:options].each do |option| -%>
14
+ <%- option[:names].each do |name| -%>
15
+ <%= name %>
16
+ <%- end -%>
17
+ <%- end -%>
18
+ "
19
+
20
+ _autoproj_subcommands "$subcommands" && return
21
+
22
+ case "$cur" in
23
+ -*)
24
+ COMPREPLY=( $( compgen -W "$options" -- "$cur" ) )
25
+ ;;
26
+ *)
27
+ COMPREPLY=( $( compgen -W "$subcommands" -- "$cur" ) )
28
+ ;;
29
+ esac
30
+ }
31
+ <%- else %>
32
+ <%= function_name %>() {
33
+ local options="
34
+ <%- subcommand[:options].each do |option| -%>
35
+ <%- option[:names].each do |name| -%>
36
+ <%= name %>
37
+ <%- end -%>
38
+ <%- end -%>
39
+ "
40
+
41
+ case "$cur" in
42
+ -*)
43
+ COMPREPLY=($(compgen -W "$options" -- ${cur}))
44
+ ;;
45
+ *)
46
+ <%= subcommand[:completer] %>
47
+ ;;
48
+ esac
49
+ }
50
+ <%- end %>
@@ -0,0 +1,51 @@
1
+ <%- if subcommand[:subcommands].any? %>
2
+ <%= function_name %>() {
3
+ readonly local DEPTH=<%= depth %>
4
+
5
+ case $CURRENT in
6
+ $DEPTH)
7
+ _arguments \
8
+ <%- subcommand[:options].each do |option| -%>
9
+ <%= escape_option_names(option[:names]) %><%= quote(bracket(option[:description])) if option[:description] %> \
10
+ <%- end -%>
11
+ '*: :->subcommands'
12
+
13
+ case $state in
14
+ subcommands)
15
+ _values \
16
+ 'subcommand' \
17
+ <%- subcommand[:subcommands].each do |subcommand| -%>
18
+ <%= quote(subcommand[:name] + bracket(subcommand[:description])) %> \
19
+ <%- subcommand[:aliases].each do |_alias| -%>
20
+ <%= quote(_alias + bracket(subcommand[:description])) %> \
21
+ <%- end -%>
22
+ <%- end -%>
23
+ ;
24
+ ;;
25
+ esac
26
+ ;;
27
+ *)
28
+ case $words[$DEPTH] in
29
+ <%- subcommand[:subcommands].each do |subcommand| -%>
30
+ <%= subcommand[:name] %>)
31
+ <%= function_name %>_<%= subcommand[:name] %>
32
+ ;;
33
+ <%- subcommand[:aliases].each do |_alias| -%>
34
+ <%= _alias %>)
35
+ <%= function_name %>_<%= subcommand[:name] %>
36
+ ;;
37
+ <%- end -%>
38
+ <%- end -%>
39
+ esac
40
+ ;;
41
+ esac
42
+ }
43
+ <%- else %>
44
+ <%= function_name %>() {
45
+ _arguments \
46
+ <%- subcommand[:options].each do |option| -%>
47
+ <%= escape_option_names(option[:names]) %><%= quote(bracket(option[:description])) if option[:description] %> \
48
+ <%- end -%>
49
+ '*:arg:<%= subcommand[:completer] -%>'
50
+ }
51
+ <%- end %>
@@ -1,3 +1,3 @@
1
1
  module Autoproj
2
- VERSION = "2.7.1"
2
+ VERSION = "2.8.0"
3
3
  end
@@ -1,7 +1,18 @@
1
+ require 'autoproj/ops/loader'
2
+ require 'xdg'
3
+
1
4
  module Autoproj
2
5
  class Workspace < Ops::Loader
6
+ # The workspace root as a string
7
+ #
8
+ # New code should prefer {#root_path}
3
9
  attr_reader :root_dir
4
10
 
11
+ # The workspace root
12
+ #
13
+ # This should be used rather than {#root_dir} in new code
14
+ attr_reader :root_path
15
+
5
16
  attr_accessor :config
6
17
  attr_reader :env
7
18
 
@@ -42,6 +53,7 @@ def initialize(root_dir,
42
53
  os_package_resolver: OSPackageResolver.new,
43
54
  package_managers: OSPackageInstaller::PACKAGE_MANAGERS)
44
55
  @root_dir = root_dir
56
+ @root_path = Pathname.new(root_dir)
45
57
  @ruby_version_keyword = "ruby#{RUBY_VERSION.split('.')[0, 2].join("")}"
46
58
  @osdep_suffixes = Array.new
47
59
 
@@ -286,6 +298,7 @@ def setup
286
298
  setup_ruby_version_handling
287
299
  migrate_bundler_and_autoproj_gem_layout
288
300
  load_config
301
+ register_workspace
289
302
  rewrite_shims
290
303
  autodetect_operating_system
291
304
  config.validate_ruby_executable
@@ -347,7 +360,8 @@ def install_ruby_shims
347
360
  def rewrite_shims
348
361
  gemfile = File.join(dot_autoproj_dir, 'Gemfile')
349
362
  binstubs = File.join(dot_autoproj_dir, 'bin')
350
- Ops::Install.rewrite_shims(binstubs, config.ruby_executable, root_dir, gemfile, config.gems_gem_home)
363
+ Ops::Install.rewrite_shims(binstubs, config.ruby_executable,
364
+ root_dir, gemfile, config.gems_gem_home)
351
365
  end
352
366
 
353
367
  def update_bundler
@@ -435,7 +449,7 @@ def set_as_main_workspace
435
449
  if block_given?
436
450
  begin
437
451
  yield
438
- ensure
452
+ ensure
439
453
  clear_main_workspace
440
454
  end
441
455
  end
@@ -457,24 +471,89 @@ def load_main_initrb
457
471
  load_if_present(local_source, config_dir, "init.rb")
458
472
  end
459
473
 
474
+ def self.find_path(xdg_var, xdg_path, home_path)
475
+ home_dir = begin Dir.home
476
+ rescue ArgumentError
477
+ return
478
+ end
479
+
480
+ xdg_path = File.join(XDG[xdg_var].to_path, 'autoproj', xdg_path)
481
+ home_path = File.join(home_dir, home_path)
482
+
483
+ if File.exist?(xdg_path)
484
+ xdg_path
485
+ elsif File.exist?(home_path)
486
+ home_path
487
+ else
488
+ xdg_path
489
+ end
490
+ end
491
+
492
+ def self.find_user_config_path(xdg_path, home_path = xdg_path)
493
+ find_path('CONFIG_HOME', xdg_path, home_path)
494
+ end
495
+
496
+ def self.rcfile_path
497
+ find_user_config_path('rc', '.autoprojrc')
498
+ end
499
+
500
+ def self.find_user_data_path(xdg_path, home_path = xdg_path)
501
+ find_path('DATA_HOME', xdg_path, File.join('.autoproj', home_path))
502
+ end
503
+
504
+ def self.find_user_cache_path(xdg_path, home_path = xdg_path)
505
+ find_path('CACHE_HOME', xdg_path, File.join('.autoproj', home_path))
506
+ end
507
+
508
+ RegisteredWorkspace = Struct.new :root_dir, :prefix_dir, :build_dir
509
+
510
+ def self.registered_workspaces
511
+ path = find_user_data_path('workspaces.yml')
512
+ if File.file?(path)
513
+ yaml = (YAML.load(File.read(path)) || [])
514
+ fields = RegisteredWorkspace.members.map(&:to_s)
515
+ yaml.map do |h|
516
+ values = h.values_at(*fields)
517
+ RegisteredWorkspace.new(*values)
518
+ end.compact
519
+ else
520
+ []
521
+ end
522
+ end
523
+
524
+ def self.save_registered_workspaces(workspaces)
525
+ workspaces = workspaces.map do |w|
526
+ Hash['root_dir' => w.root_dir,
527
+ 'prefix_dir' => w.prefix_dir,
528
+ 'build_dir' => w.build_dir]
529
+ end
530
+
531
+ path = find_user_data_path('workspaces.yml')
532
+ FileUtils.mkdir_p(File.dirname(path))
533
+ Ops.atomic_write(path) do |io|
534
+ io.write YAML.dump(workspaces)
535
+ end
536
+ end
537
+
538
+ def register_workspace
539
+ current_workspaces = Workspace.registered_workspaces
540
+ existing = current_workspaces.find { |w| w.root_dir == root_dir }
541
+ if existing
542
+ existing.prefix_dir = prefix_dir
543
+ existing.build_dir = build_dir
544
+ else
545
+ current_workspaces << self
546
+ end
547
+ Workspace.save_registered_workspaces(current_workspaces)
548
+ end
549
+
460
550
  # Loads the .autoprojrc file
461
551
  #
462
552
  # This is included in {setup}
463
553
  def load_autoprojrc
464
554
  set_as_main_workspace
465
-
466
- # Load the user-wide autoproj RC file
467
- home_dir =
468
- begin Dir.home
469
- rescue ArgumentError
470
- end
471
-
472
- if home_dir
473
- rcfile = File.join(home_dir, '.autoprojrc')
474
- if File.file?(rcfile)
475
- Kernel.load rcfile
476
- end
477
- end
555
+ rcfile = Workspace.rcfile_path
556
+ Kernel.load(rcfile) if File.file?(rcfile)
478
557
  end
479
558
 
480
559
  def load_package_sets(only_local: false,
@@ -507,7 +586,7 @@ def load_packages(selection = manifest.default_packages(false), options = Hash.n
507
586
  def load_all_available_package_manifests
508
587
  manifest.load_all_available_package_manifests
509
588
  end
510
-
589
+
511
590
  def setup_all_package_directories
512
591
  # Override the package directories from our reused installations
513
592
  imported_packages = Set.new
@@ -561,7 +640,7 @@ def setup_package_directories(pkg)
561
640
  pkg.doc_target_dir = File.join(prefix_dir, 'doc', pkg_name)
562
641
  pkg.logdir = File.join(pkg.prefix, "log")
563
642
  end
564
-
643
+
565
644
  def compute_builddir(pkg)
566
645
  # If we're given an absolute build dir, we have to append the
567
646
  # package name to it to make it unique
@@ -735,7 +814,7 @@ def register_package(package, block = nil, package_set = manifest.main_package_s
735
814
  # `cmd` is not executable. Otherwise, looks for an executable named
736
815
  # `cmd` in PATH and returns it, or raises if it cannot be found. The
737
816
  # exception contains a more detailed reason for failure
738
- #
817
+ #
739
818
  #
740
819
  # @param [String] cmd
741
820
  # @return [String] the resolved program
@@ -761,4 +840,3 @@ def self.env
761
840
  workspace.env
762
841
  end
763
842
  end
764
-