autoproj 2.7.1 → 2.8.0

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