rpremote 0.5.0 → 0.7.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/CHANGELOG.md +13 -0
- data/README.ja.md +30 -46
- data/README.md +30 -47
- data/RELEASING.md +2 -2
- data/THIRD_PARTY_NOTICES.md +1 -1
- data/lib/rpremote/bootsel_command.rb +8 -6
- data/lib/rpremote/build_command.rb +3 -3
- data/lib/rpremote/builder.rb +15 -9
- data/lib/rpremote/cli/filesystem_commands.rb +105 -0
- data/lib/rpremote/cli.rb +20 -105
- data/lib/rpremote/config.rb +6 -5
- data/lib/rpremote/config_show.rb +13 -5
- data/lib/rpremote/deploy_command.rb +85 -55
- data/lib/rpremote/help/dfu_commands.rb +59 -0
- data/lib/rpremote/help/filesystem_commands.rb +36 -0
- data/lib/rpremote/help/project_commands.rb +89 -0
- data/lib/rpremote/help/remote_commands.rb +54 -0
- data/lib/rpremote/help/root_text.rb +63 -0
- data/lib/rpremote/help.rb +27 -300
- data/lib/rpremote/language_source.rb +19 -2
- data/lib/rpremote/mrbgems/definition.rb +116 -0
- data/lib/rpremote/mrbgems/file_writer.rb +26 -0
- data/lib/rpremote/mrbgems/lockfile.rb +131 -0
- data/lib/rpremote/mrbgems.rb +95 -155
- data/lib/rpremote/mrbgems_command.rb +19 -4
- data/lib/rpremote/picoruby_compiler.rb +28 -0
- data/lib/rpremote/picoruby_source_patch.rb +0 -4
- data/lib/rpremote/target.rb +1 -1
- data/lib/rpremote/terminal.rb +0 -2
- data/lib/rpremote/version.rb +1 -1
- data/lib/rpremote.rb +1 -0
- data/patches/picoruby-4.0.4-pwm-sleep-clock.patch +27 -0
- data/sig/rpremote.rbs +20 -4
- data/tasks/firmware_build.rb +10 -2
- metadata +12 -3
- data/patches/picoruby-3.4.5-bootsel.patch +0 -71
- data/patches/picoruby-4.0.3-bootsel.patch +0 -71
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rpremote
|
|
4
|
+
module Help
|
|
5
|
+
def self.ports_text
|
|
6
|
+
"Usage: #{COMMAND_USAGE.fetch(:ports)}\n\nPrints each detected R2P2 CDC 0 serial path, one per line. It does not connect to a board.\n"
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.run_text
|
|
10
|
+
<<~HELP
|
|
11
|
+
Usage: #{COMMAND_USAGE.fetch(:run)}
|
|
12
|
+
|
|
13
|
+
Uploads FILE to R2P2, or main.rb when FILE is a directory, relays output, then removes the temporary remote file. Defaults are the automatic CDC 0 port,
|
|
14
|
+
115200 baud, a 20-second idle timeout, and picoruby. Output from the running program resets the idle timeout.
|
|
15
|
+
It exits nonzero when compatible R2P2 firmware reports a Ruby exception.
|
|
16
|
+
--reset-on-timeout resets R2P2 after the run times out and the serial connection has closed.
|
|
17
|
+
HELP
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.exec_text
|
|
21
|
+
<<~HELP
|
|
22
|
+
Usage: #{COMMAND_USAGE.fetch(:exec)}
|
|
23
|
+
|
|
24
|
+
Runs short Ruby CODE through a temporary remote file, then removes it. Defaults are the automatic CDC 0 port,
|
|
25
|
+
115200 baud, a 20-second idle timeout, and picoruby. Output from the running program resets the idle timeout.
|
|
26
|
+
It exits nonzero when compatible R2P2 firmware reports a Ruby exception.
|
|
27
|
+
HELP
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.monitor_text
|
|
31
|
+
<<~HELP
|
|
32
|
+
Usage: #{COMMAND_USAGE.fetch(:monitor)}
|
|
33
|
+
|
|
34
|
+
Opens a serial monitor for the selected CDC 0 port. Defaults are 115200 baud and 20 seconds; exit with Ctrl-].
|
|
35
|
+
HELP
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.repl_text
|
|
39
|
+
<<~HELP
|
|
40
|
+
Usage: #{COMMAND_USAGE.fetch(:repl)}
|
|
41
|
+
|
|
42
|
+
Opens PicoIRB on the selected CDC 0 port. Defaults are 115200 baud and 20 seconds; exit with Ctrl-].
|
|
43
|
+
HELP
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.reset_text
|
|
47
|
+
<<~HELP
|
|
48
|
+
Usage: #{COMMAND_USAGE.fetch(:reset)}
|
|
49
|
+
|
|
50
|
+
Reboots R2P2 and waits for reconnection. Defaults are the automatic CDC 0 port, 115200 baud, and 20 seconds.
|
|
51
|
+
HELP
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rpremote
|
|
4
|
+
module Help
|
|
5
|
+
TEXT = <<~HELP.freeze
|
|
6
|
+
rpremote - R2P2 remote control for Raspberry Pi Pico 2
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
#{COMMAND_USAGE.fetch(:setup)}
|
|
10
|
+
#{COMMAND_USAGE.fetch(:build)}
|
|
11
|
+
#{COMMAND_USAGE.fetch(:build_clean)}
|
|
12
|
+
#{COMMAND_USAGE.fetch(:bootsel)}
|
|
13
|
+
#{COMMAND_USAGE.fetch(:deploy)}
|
|
14
|
+
#{COMMAND_USAGE.fetch(:dfu_app)}
|
|
15
|
+
#{COMMAND_USAGE.fetch(:dfu_compile)}
|
|
16
|
+
#{COMMAND_USAGE.fetch(:dfu_status)}
|
|
17
|
+
#{COMMAND_USAGE.fetch(:dfu_remove)}
|
|
18
|
+
#{COMMAND_USAGE.fetch(:mrbgems).sub("SUBCOMMAND", "check|list|lock|update")}
|
|
19
|
+
#{COMMAND_USAGE.fetch(:flash)}
|
|
20
|
+
#{COMMAND_USAGE.fetch(:config_show)}
|
|
21
|
+
#{COMMAND_USAGE.fetch(:ports)}
|
|
22
|
+
#{COMMAND_USAGE.fetch(:run)}
|
|
23
|
+
#{COMMAND_USAGE.fetch(:monitor)}
|
|
24
|
+
#{COMMAND_USAGE.fetch(:repl)}
|
|
25
|
+
#{COMMAND_USAGE.fetch(:exec)}
|
|
26
|
+
#{COMMAND_USAGE.fetch(:reset)}
|
|
27
|
+
#{COMMAND_USAGE.fetch(:fs_cp)}
|
|
28
|
+
#{COMMAND_USAGE.fetch(:fs_push)}
|
|
29
|
+
#{COMMAND_USAGE.fetch(:fs_cat)}
|
|
30
|
+
#{COMMAND_USAGE.fetch(:fs_ls)}
|
|
31
|
+
#{COMMAND_USAGE.fetch(:fs_rm)}
|
|
32
|
+
#{COMMAND_USAGE.fetch(:fs_mkdir)}
|
|
33
|
+
|
|
34
|
+
`rpremote build clean` removes only the project's generated `build/` directory.
|
|
35
|
+
|
|
36
|
+
Configuration:
|
|
37
|
+
config/setting.json default project options
|
|
38
|
+
|
|
39
|
+
Options:
|
|
40
|
+
--force download the PicoRuby source again during setup
|
|
41
|
+
--build build the selected firmware before deploy flashes it
|
|
42
|
+
--language-version VERSION
|
|
43
|
+
use a PicoRuby tag or latest (default: latest)
|
|
44
|
+
--cache DIR use another project cache directory
|
|
45
|
+
--lockfile FILE use an explicit Mrbgems.lock for build, deploy, run, or exec
|
|
46
|
+
--no-mrbgems do not build or automatically require locked mrbgems
|
|
47
|
+
--with GROUPS lock common mrbgems and the comma-separated groups
|
|
48
|
+
--without GROUPS exclude the comma-separated groups from the lock
|
|
49
|
+
--board BOARD
|
|
50
|
+
select pico2 or pico2_w (default: pico2)
|
|
51
|
+
--firmware FILE build to, or deploy or flash from, this UF2 path
|
|
52
|
+
--language LANGUAGE
|
|
53
|
+
select the remote language (default: picoruby)
|
|
54
|
+
--config FILE use another configuration file
|
|
55
|
+
--mount DIR use an explicit RP2350 BOOTSEL drive
|
|
56
|
+
--port PORT use an explicit R2P2 CDC 0 device
|
|
57
|
+
--baud RATE serial baud rate (default: 115200)
|
|
58
|
+
--timeout SEC timeout in seconds (default: 20)
|
|
59
|
+
-h, --help show this help
|
|
60
|
+
-V, --version show the version
|
|
61
|
+
HELP
|
|
62
|
+
end
|
|
63
|
+
end
|
data/lib/rpremote/help.rb
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# rubocop:disable Metrics/ModuleLength, Metrics/CyclomaticComplexity
|
|
4
|
-
|
|
5
3
|
module Rpremote
|
|
6
4
|
module Help
|
|
7
5
|
COMMAND_USAGE = {
|
|
8
6
|
setup: "rpremote setup [--language LANGUAGE] [--language-version VERSION] [--force] [--cache DIR]",
|
|
9
7
|
build: "rpremote build [--language LANGUAGE] [--language-version VERSION] [--board BOARD] " \
|
|
10
|
-
"[--firmware FILE] [--cache DIR] [--
|
|
8
|
+
"[--firmware FILE] [--cache DIR] [--lockfile FILE|--no-mrbgems]",
|
|
11
9
|
build_clean: "rpremote build clean",
|
|
12
10
|
bootsel: "rpremote bootsel [--reset-flash-memory] [--mount DIR] [--port PORT] [--baud RATE] [--timeout SEC]",
|
|
13
11
|
deploy: "rpremote deploy PATH [--build] [--language LANGUAGE] [--language-version VERSION] [--board BOARD] " \
|
|
14
|
-
"[--firmware FILE] [--cache DIR] [--
|
|
12
|
+
"[--firmware FILE] [--cache DIR] [--lockfile FILE|--no-mrbgems] [--mount DIR] " \
|
|
15
13
|
"[--port PORT] [--baud RATE] [--timeout SEC]",
|
|
16
14
|
dfu_app: "rpremote dfu app FILE [--type ruby|rite] [--port PORT] [--baud RATE] [--timeout SEC]",
|
|
17
15
|
dfu_compile: "rpremote dfu compile FILE [--output FILE] [--language LANGUAGE] [--language-version VERSION] [--cache DIR]",
|
|
18
16
|
dfu_status: "rpremote dfu status [--port PORT] [--baud RATE] [--timeout SEC]",
|
|
19
17
|
dfu_remove: "rpremote dfu remove [--port PORT] [--baud RATE] [--timeout SEC]",
|
|
20
|
-
mrbgems: "rpremote mrbgems SUBCOMMAND [--file FILE] [--lockfile FILE]",
|
|
18
|
+
mrbgems: "rpremote mrbgems SUBCOMMAND [--file FILE] [--lockfile FILE] [--with GROUPS] [--without GROUPS]",
|
|
21
19
|
flash: "rpremote flash [--firmware FILE] [--language LANGUAGE] [--language-version VERSION] " \
|
|
22
20
|
"[--board BOARD] [--cache DIR] [--mount DIR] [--port PORT] [--timeout SEC]",
|
|
23
21
|
config_show: "rpremote config show [--language LANGUAGE] [--language-version VERSION] [--board BOARD] " \
|
|
24
|
-
"[--cache DIR] [--firmware FILE] [--
|
|
22
|
+
"[--cache DIR] [--firmware FILE] [--lockfile FILE|--no-mrbgems] [--mount DIR] " \
|
|
25
23
|
"[--port PORT] [--baud RATE] [--timeout SEC]",
|
|
26
24
|
ports: "rpremote ports",
|
|
27
|
-
run: "rpremote run FILE [--port PORT] [--baud RATE] [--timeout SEC] [--reset-on-timeout] [--language LANGUAGE]"
|
|
25
|
+
run: "rpremote run FILE [--port PORT] [--baud RATE] [--timeout SEC] [--reset-on-timeout] [--language LANGUAGE] " \
|
|
26
|
+
"[--lockfile FILE|--no-mrbgems]",
|
|
28
27
|
monitor: "rpremote monitor [--port PORT] [--baud RATE] [--timeout SEC]",
|
|
29
28
|
repl: "rpremote repl [--port PORT] [--baud RATE] [--timeout SEC]",
|
|
30
|
-
exec: "rpremote exec CODE [--port PORT] [--baud RATE] [--timeout SEC] [--language LANGUAGE]"
|
|
29
|
+
exec: "rpremote exec CODE [--port PORT] [--baud RATE] [--timeout SEC] [--language LANGUAGE] " \
|
|
30
|
+
"[--lockfile FILE|--no-mrbgems]",
|
|
31
31
|
reset: "rpremote reset [--port PORT] [--baud RATE] [--timeout SEC]",
|
|
32
32
|
fs_cp: "rpremote fs cp SOURCE DESTINATION [--recursive] [--port PORT] [--baud RATE] [--timeout SEC]",
|
|
33
33
|
fs_push: "rpremote fs push LOCAL_DIR :/REMOTE_DIR [--port PORT] [--baud RATE] [--timeout SEC]",
|
|
@@ -36,62 +36,12 @@ module Rpremote
|
|
|
36
36
|
fs_rm: "rpremote fs rm :/REMOTE/PATH [--port PORT] [--baud RATE] [--timeout SEC]",
|
|
37
37
|
fs_mkdir: "rpremote fs mkdir :/REMOTE/PATH [--port PORT] [--baud RATE] [--timeout SEC]"
|
|
38
38
|
}.freeze
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
#{COMMAND_USAGE.fetch(:build)}
|
|
46
|
-
#{COMMAND_USAGE.fetch(:build_clean)}
|
|
47
|
-
#{COMMAND_USAGE.fetch(:bootsel)}
|
|
48
|
-
#{COMMAND_USAGE.fetch(:deploy)}
|
|
49
|
-
#{COMMAND_USAGE.fetch(:dfu_app)}
|
|
50
|
-
#{COMMAND_USAGE.fetch(:dfu_compile)}
|
|
51
|
-
#{COMMAND_USAGE.fetch(:dfu_status)}
|
|
52
|
-
#{COMMAND_USAGE.fetch(:dfu_remove)}
|
|
53
|
-
#{COMMAND_USAGE.fetch(:mrbgems).sub("SUBCOMMAND", "check|list|lock|update")}
|
|
54
|
-
#{COMMAND_USAGE.fetch(:flash)}
|
|
55
|
-
#{COMMAND_USAGE.fetch(:config_show)}
|
|
56
|
-
#{COMMAND_USAGE.fetch(:ports)}
|
|
57
|
-
#{COMMAND_USAGE.fetch(:run)}
|
|
58
|
-
#{COMMAND_USAGE.fetch(:monitor)}
|
|
59
|
-
#{COMMAND_USAGE.fetch(:repl)}
|
|
60
|
-
#{COMMAND_USAGE.fetch(:exec)}
|
|
61
|
-
#{COMMAND_USAGE.fetch(:reset)}
|
|
62
|
-
#{COMMAND_USAGE.fetch(:fs_cp)}
|
|
63
|
-
#{COMMAND_USAGE.fetch(:fs_push)}
|
|
64
|
-
#{COMMAND_USAGE.fetch(:fs_cat)}
|
|
65
|
-
#{COMMAND_USAGE.fetch(:fs_ls)}
|
|
66
|
-
#{COMMAND_USAGE.fetch(:fs_rm)}
|
|
67
|
-
#{COMMAND_USAGE.fetch(:fs_mkdir)}
|
|
68
|
-
|
|
69
|
-
`rpremote build clean` removes only the project's generated `build/` directory.
|
|
70
|
-
|
|
71
|
-
Configuration:
|
|
72
|
-
config/setting.json default project options
|
|
73
|
-
|
|
74
|
-
Options:
|
|
75
|
-
--force download the PicoRuby source again during setup
|
|
76
|
-
--build build the selected firmware before deploy flashes it
|
|
77
|
-
--language-version VERSION
|
|
78
|
-
use R2P2/PicoRuby 4.0.3 or 3.4.2 (default: 4.0.3)
|
|
79
|
-
--cache DIR use another project cache directory
|
|
80
|
-
--mrbgems FILE use an explicit Mrbgems definition during build or deploy
|
|
81
|
-
--no-mrbgems build or deploy without the automatically detected Mrbgems
|
|
82
|
-
--board BOARD
|
|
83
|
-
select pico2 or pico2_w (default: pico2)
|
|
84
|
-
--firmware FILE build to, or deploy or flash from, this UF2 path
|
|
85
|
-
--language LANGUAGE
|
|
86
|
-
select the remote language (default: picoruby)
|
|
87
|
-
--config FILE use another configuration file
|
|
88
|
-
--mount DIR use an explicit RP2350 BOOTSEL drive
|
|
89
|
-
--port PORT use an explicit R2P2 CDC 0 device
|
|
90
|
-
--baud RATE serial baud rate (default: 115200)
|
|
91
|
-
--timeout SEC timeout in seconds (default: 20)
|
|
92
|
-
-h, --help show this help
|
|
93
|
-
-V, --version show the version
|
|
94
|
-
HELP
|
|
39
|
+
COMMAND_TEXT_METHODS = {
|
|
40
|
+
"setup" => :setup_text, "bootsel" => :bootsel_text, "deploy" => :deploy_text,
|
|
41
|
+
"flash" => :flash_text, "ports" => :ports_text, "run" => :run_text,
|
|
42
|
+
"exec" => :exec_text, "monitor" => :monitor_text, "repl" => :repl_text,
|
|
43
|
+
"reset" => :reset_text
|
|
44
|
+
}.freeze
|
|
95
45
|
|
|
96
46
|
def self.requested?(command, args)
|
|
97
47
|
%w[help --help -h].include?(command) || args.include?("--help") || args.include?("-h")
|
|
@@ -100,243 +50,20 @@ module Rpremote
|
|
|
100
50
|
def self.command_text(command, args)
|
|
101
51
|
command = args.shift if command == "help"
|
|
102
52
|
return TEXT if command.nil? || %w[help --help -h].include?(command)
|
|
53
|
+
return args.first == "clean" ? build_clean_text : build_text if command == "build"
|
|
54
|
+
return dfu_text(args.first) if command == "dfu"
|
|
55
|
+
return mrbgems_text(args.first) if command == "mrbgems"
|
|
56
|
+
return config_text(args.first) if command == "config"
|
|
57
|
+
return fs_text(args.first) if command == "fs"
|
|
103
58
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
when "build" then args.first == "clean" ? build_clean_text : build_text
|
|
107
|
-
when "bootsel" then bootsel_text
|
|
108
|
-
when "deploy" then deploy_text
|
|
109
|
-
when "dfu" then dfu_text(args.first)
|
|
110
|
-
when "mrbgems" then mrbgems_text(args.first)
|
|
111
|
-
when "flash" then flash_text
|
|
112
|
-
when "config" then config_text(args.first)
|
|
113
|
-
when "ports" then ports_text
|
|
114
|
-
when "run" then run_text
|
|
115
|
-
when "exec" then exec_text
|
|
116
|
-
when "monitor" then monitor_text
|
|
117
|
-
when "repl" then repl_text
|
|
118
|
-
when "reset" then reset_text
|
|
119
|
-
when "fs" then fs_text(args.first)
|
|
120
|
-
else TEXT
|
|
121
|
-
end
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def self.setup_text
|
|
125
|
-
<<~HELP
|
|
126
|
-
Usage: #{COMMAND_USAGE.fetch(:setup)}
|
|
127
|
-
|
|
128
|
-
Creates config/setting.json when it does not exist, then downloads and prepares PicoRuby source and the official Raspberry Pi nuke_universal.uf2 firmware.
|
|
129
|
-
Options: --language LANGUAGE (picoruby), --language-version VERSION (4.0.3), --cache DIR (firmware), --force.
|
|
130
|
-
This changes the project configuration and source cache but does not connect to a board.
|
|
131
|
-
HELP
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
def self.build_text
|
|
135
|
-
<<~HELP
|
|
136
|
-
Usage: #{COMMAND_USAGE.fetch(:build)}
|
|
137
|
-
|
|
138
|
-
Builds a custom UF2 for the selected target. It writes generated files under build/ and the selected firmware path.
|
|
139
|
-
Options: --language LANGUAGE, --language-version VERSION, --board BOARD (pico2), --cache DIR (firmware),
|
|
140
|
-
--firmware FILE, --mrbgems FILE, --no-mrbgems. A project Mrbgems file is used automatically by default.
|
|
141
|
-
HELP
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
def self.build_clean_text
|
|
145
|
-
<<~HELP
|
|
146
|
-
Usage: #{COMMAND_USAGE.fetch(:build_clean)}
|
|
147
|
-
|
|
148
|
-
Removes only the project's generated build/ directory. It does not remove firmware/, Mrbgems, or Mrbgems.lock.
|
|
149
|
-
HELP
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
def self.deploy_text
|
|
153
|
-
<<~HELP
|
|
154
|
-
Usage: #{COMMAND_USAGE.fetch(:deploy)}
|
|
155
|
-
|
|
156
|
-
Enters BOOTSEL when needed, flashes the existing selected UF2, and waits for the R2P2 Shell to become ready.
|
|
157
|
-
With --build, builds the selected custom UF2 before entering BOOTSEL and flashing it.
|
|
158
|
-
If PATH/lib/NAME exists, it copies it to :/lib/NAME, where NAME is the final component of PATH. It then runs PATH/main.rb and preserves its Shell job, so hardware output remains active until the next command.
|
|
159
|
-
The stages run in order and stop at the first failure. Flashing replaces persistent board firmware.
|
|
160
|
-
deploy requires PicoRuby 4.x firmware (currently 4.0.3).
|
|
161
|
-
Options combine build, BOOTSEL, flash, library-copy, and run settings. The first install of firmware with automatic BOOTSEL still requires holding BOOTSEL.
|
|
162
|
-
HELP
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
def self.bootsel_text
|
|
166
|
-
<<~HELP
|
|
167
|
-
Usage: #{COMMAND_USAGE.fetch(:bootsel)}
|
|
168
|
-
|
|
169
|
-
Asks the running R2P2 firmware to restart as an RP2350 BOOTSEL USB volume, then waits for that volume to appear.
|
|
170
|
-
By default it does not write firmware. This requires firmware built by a current rpremote; use physical BOOTSEL once to install it first.
|
|
171
|
-
--reset-flash-memory copies the official Raspberry Pi nuke_universal.uf2 in BOOTSEL mode and erases all external flash memory.
|
|
172
|
-
After resetting flash memory, wait for BOOTSEL to reappear and run `rpremote flash` to install R2P2 again.
|
|
173
|
-
HELP
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
def self.dfu_text(subcommand)
|
|
177
|
-
return dfu_app_text if subcommand == "app"
|
|
178
|
-
return dfu_compile_text if subcommand == "compile"
|
|
179
|
-
return dfu_status_text if subcommand == "status"
|
|
180
|
-
return dfu_remove_text if subcommand == "remove"
|
|
181
|
-
|
|
182
|
-
<<~HELP
|
|
183
|
-
Usage: #{COMMAND_USAGE.fetch(:dfu_app)}
|
|
184
|
-
#{COMMAND_USAGE.fetch(:dfu_compile)}
|
|
185
|
-
#{COMMAND_USAGE.fetch(:dfu_status)}
|
|
186
|
-
#{COMMAND_USAGE.fetch(:dfu_remove)}
|
|
187
|
-
|
|
188
|
-
Stages and inspects PicoModem DFU applications. Run `rpremote dfu SUBCOMMAND --help` for details.
|
|
189
|
-
HELP
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
def self.dfu_app_text
|
|
193
|
-
<<~HELP
|
|
194
|
-
Usage: #{COMMAND_USAGE.fetch(:dfu_app)}
|
|
195
|
-
|
|
196
|
-
Transfers a Ruby source or matching RITE bytecode application to the inactive DFU slot. The next R2P2 restart tries it.
|
|
197
|
-
Options default to the configured or automatically selected CDC 0 port, 115200 baud, and 20 seconds.
|
|
198
|
-
The transfer changes the staged boot application; use `rpremote reset` to restart and require DFU.confirm after a successful boot.
|
|
199
|
-
HELP
|
|
200
|
-
end
|
|
201
|
-
|
|
202
|
-
def self.dfu_compile_text
|
|
203
|
-
<<~HELP
|
|
204
|
-
Usage: #{COMMAND_USAGE.fetch(:dfu_compile)}
|
|
205
|
-
|
|
206
|
-
Compiles Ruby source to bytecode that matches the selected PicoRuby version. The output defaults beside FILE.
|
|
207
|
-
It requires prepared PicoRuby source and does not connect to a board.
|
|
208
|
-
HELP
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
def self.dfu_status_text
|
|
212
|
-
<<~HELP
|
|
213
|
-
Usage: #{COMMAND_USAGE.fetch(:dfu_status)}
|
|
214
|
-
|
|
215
|
-
Prints the active and candidate DFU A/B slots. Defaults are the configured or automatically selected CDC 0 port,
|
|
216
|
-
115200 baud, and 20 seconds. This command reads board state without changing it.
|
|
217
|
-
HELP
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
def self.dfu_remove_text
|
|
221
|
-
<<~HELP
|
|
222
|
-
Usage: #{COMMAND_USAGE.fetch(:dfu_remove)}
|
|
223
|
-
|
|
224
|
-
Permanently removes the Ruby source and bytecode applications from both DFU A/B slots and resets their metadata.
|
|
225
|
-
Run `rpremote reset` afterward to stop the application already running in RAM. It does not remove /home/app.rb,
|
|
226
|
-
/home/app.mrb, other files, embedded mrbgems, or R2P2 firmware.
|
|
227
|
-
HELP
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
def self.mrbgems_text(subcommand)
|
|
231
|
-
action = subcommand || "check|list|lock|update"
|
|
232
|
-
<<~HELP
|
|
233
|
-
Usage: #{COMMAND_USAGE.fetch(:mrbgems).sub("SUBCOMMAND", action)}
|
|
234
|
-
|
|
235
|
-
Checks or lists build dependencies, writes a reproducible lock file, or updates locked GitHub commits.
|
|
236
|
-
`check` and `list` are read-only. `lock` writes Mrbgems.lock; `update` resolves new commits and rewrites it.
|
|
237
|
-
HELP
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
def self.flash_text
|
|
241
|
-
<<~HELP
|
|
242
|
-
Usage: #{COMMAND_USAGE.fetch(:flash)}
|
|
243
|
-
|
|
244
|
-
Copies the selected UF2 to an RP2350 BOOTSEL volume and waits for R2P2 to reconnect. It replaces persistent board firmware.
|
|
245
|
-
Defaults select pico2, firmware/picoruby-4.0.3-pico2.uf2, an automatic BOOTSEL mount and CDC 0 port, and 20 seconds.
|
|
246
|
-
HELP
|
|
247
|
-
end
|
|
248
|
-
|
|
249
|
-
def self.config_text(subcommand)
|
|
250
|
-
unless subcommand == "show"
|
|
251
|
-
return "Usage: #{COMMAND_USAGE.fetch(:config_show)}\n\nUse `rpremote config show --help` for the supported overrides.\n"
|
|
252
|
-
end
|
|
253
|
-
|
|
254
|
-
<<~HELP
|
|
255
|
-
Usage: #{COMMAND_USAGE.fetch(:config_show)}
|
|
256
|
-
|
|
257
|
-
Prints the configuration file, defaults, and command-line overrides after resolution. It does not connect to a board or change state.
|
|
258
|
-
HELP
|
|
259
|
-
end
|
|
260
|
-
|
|
261
|
-
def self.ports_text
|
|
262
|
-
"Usage: #{COMMAND_USAGE.fetch(:ports)}\n\nPrints each detected R2P2 CDC 0 serial path, one per line. It does not connect to a board.\n"
|
|
263
|
-
end
|
|
264
|
-
|
|
265
|
-
def self.run_text
|
|
266
|
-
<<~HELP
|
|
267
|
-
Usage: #{COMMAND_USAGE.fetch(:run)}
|
|
268
|
-
|
|
269
|
-
Uploads FILE to R2P2, or main.rb when FILE is a directory, relays output, then removes the temporary remote file. Defaults are the automatic CDC 0 port,
|
|
270
|
-
115200 baud, a 20-second idle timeout, and picoruby. Output from the running program resets the idle timeout.
|
|
271
|
-
It exits nonzero when compatible R2P2 firmware reports a Ruby exception.
|
|
272
|
-
--reset-on-timeout resets R2P2 after the run times out and the serial connection has closed.
|
|
273
|
-
HELP
|
|
274
|
-
end
|
|
275
|
-
|
|
276
|
-
def self.exec_text
|
|
277
|
-
<<~HELP
|
|
278
|
-
Usage: #{COMMAND_USAGE.fetch(:exec)}
|
|
279
|
-
|
|
280
|
-
Runs short Ruby CODE through a temporary remote file, then removes it. Defaults are the automatic CDC 0 port,
|
|
281
|
-
115200 baud, a 20-second idle timeout, and picoruby. Output from the running program resets the idle timeout.
|
|
282
|
-
It exits nonzero when compatible R2P2 firmware reports a Ruby exception.
|
|
283
|
-
HELP
|
|
284
|
-
end
|
|
285
|
-
|
|
286
|
-
def self.monitor_text
|
|
287
|
-
<<~HELP
|
|
288
|
-
Usage: #{COMMAND_USAGE.fetch(:monitor)}
|
|
289
|
-
|
|
290
|
-
Opens a serial monitor for the selected CDC 0 port. Defaults are 115200 baud and 20 seconds; exit with Ctrl-].
|
|
291
|
-
HELP
|
|
292
|
-
end
|
|
293
|
-
|
|
294
|
-
def self.repl_text
|
|
295
|
-
<<~HELP
|
|
296
|
-
Usage: #{COMMAND_USAGE.fetch(:repl)}
|
|
297
|
-
|
|
298
|
-
Opens PicoIRB on the selected CDC 0 port. Defaults are 115200 baud and 20 seconds; exit with Ctrl-].
|
|
299
|
-
HELP
|
|
300
|
-
end
|
|
301
|
-
|
|
302
|
-
def self.reset_text
|
|
303
|
-
<<~HELP
|
|
304
|
-
Usage: #{COMMAND_USAGE.fetch(:reset)}
|
|
305
|
-
|
|
306
|
-
Reboots R2P2 and waits for reconnection. Defaults are the automatic CDC 0 port, 115200 baud, and 20 seconds.
|
|
307
|
-
HELP
|
|
308
|
-
end
|
|
309
|
-
|
|
310
|
-
def self.fs_text(subcommand)
|
|
311
|
-
return fs_subcommand_text(subcommand) if %w[cp push cat ls rm mkdir].include?(subcommand)
|
|
312
|
-
|
|
313
|
-
<<~HELP
|
|
314
|
-
Usage: #{COMMAND_USAGE.fetch(:fs_cp)}
|
|
315
|
-
#{COMMAND_USAGE.fetch(:fs_push)}
|
|
316
|
-
#{COMMAND_USAGE.fetch(:fs_cat)}
|
|
317
|
-
#{COMMAND_USAGE.fetch(:fs_ls)}
|
|
318
|
-
#{COMMAND_USAGE.fetch(:fs_rm)}
|
|
319
|
-
#{COMMAND_USAGE.fetch(:fs_mkdir)}
|
|
320
|
-
|
|
321
|
-
Uses :/REMOTE/PATH for R2P2 paths. Run `rpremote fs SUBCOMMAND --help` for details.
|
|
322
|
-
HELP
|
|
323
|
-
end
|
|
324
|
-
|
|
325
|
-
def self.fs_subcommand_text(subcommand)
|
|
326
|
-
usage = COMMAND_USAGE.fetch(:"fs_#{subcommand}")
|
|
327
|
-
effect = case subcommand
|
|
328
|
-
when "rm" then "Deletes the remote path permanently."
|
|
329
|
-
when "mkdir" then "Creates a remote directory."
|
|
330
|
-
when "cp" then "Transfers one file, or recursively uploads a local directory with --recursive."
|
|
331
|
-
when "push" then "Creates missing remote directories and recursively uploads a local directory."
|
|
332
|
-
else "Reads remote files."
|
|
333
|
-
end
|
|
334
|
-
<<~HELP
|
|
335
|
-
Usage: #{usage}
|
|
336
|
-
|
|
337
|
-
#{effect} Defaults are the automatic CDC 0 port, 115200 baud, and 20 seconds.
|
|
338
|
-
HELP
|
|
59
|
+
method_name = COMMAND_TEXT_METHODS[command]
|
|
60
|
+
method_name ? public_send(method_name) : TEXT
|
|
339
61
|
end
|
|
340
62
|
end
|
|
341
63
|
end
|
|
342
|
-
|
|
64
|
+
|
|
65
|
+
require_relative "help/root_text"
|
|
66
|
+
require_relative "help/project_commands"
|
|
67
|
+
require_relative "help/dfu_commands"
|
|
68
|
+
require_relative "help/remote_commands"
|
|
69
|
+
require_relative "help/filesystem_commands"
|
|
@@ -36,6 +36,7 @@ module Rpremote
|
|
|
36
36
|
unless File.directory?(source_dir) && !force
|
|
37
37
|
FileUtils.rm_rf(source_dir) if force
|
|
38
38
|
extractor.call(archive_path, cache_dir)
|
|
39
|
+
normalize_extracted_source
|
|
39
40
|
end
|
|
40
41
|
raise ExtractError, "PicoRuby source was not extracted: #{source_dir}" unless File.directory?(source_dir)
|
|
41
42
|
|
|
@@ -45,7 +46,7 @@ module Rpremote
|
|
|
45
46
|
end
|
|
46
47
|
|
|
47
48
|
def archive_url
|
|
48
|
-
"https://github.com/picoruby/picoruby/archive/refs
|
|
49
|
+
"https://github.com/picoruby/picoruby/archive/refs/#{source_ref}.zip"
|
|
49
50
|
end
|
|
50
51
|
|
|
51
52
|
def archive_path
|
|
@@ -60,6 +61,22 @@ module Rpremote
|
|
|
60
61
|
|
|
61
62
|
attr_reader :fetcher, :extractor, :preparer, :patcher
|
|
62
63
|
|
|
64
|
+
def source_ref
|
|
65
|
+
version == "latest" ? "heads/master" : "tags/#{version}"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def extracted_source_dir
|
|
69
|
+
return source_dir unless version == "latest"
|
|
70
|
+
|
|
71
|
+
File.join(cache_dir, "picoruby-master")
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def normalize_extracted_source
|
|
75
|
+
return if extracted_source_dir == source_dir || !File.directory?(extracted_source_dir)
|
|
76
|
+
|
|
77
|
+
FileUtils.mv(extracted_source_dir, source_dir)
|
|
78
|
+
end
|
|
79
|
+
|
|
63
80
|
def fetch_archive
|
|
64
81
|
temporary = "#{archive_path}.part"
|
|
65
82
|
File.binwrite(temporary, fetcher.call(archive_url))
|
|
@@ -88,7 +105,7 @@ module Rpremote
|
|
|
88
105
|
commands << ["git", "remote", "add", "origin", "https://github.com/picoruby/picoruby.git"]
|
|
89
106
|
end
|
|
90
107
|
commands.push(
|
|
91
|
-
["git", "fetch", "--depth", "1", "origin",
|
|
108
|
+
["git", "fetch", "--depth", "1", "origin", source_ref],
|
|
92
109
|
["git", "checkout", "--force", "FETCH_HEAD"],
|
|
93
110
|
["git", "submodule", "update", "--init", "--recursive", "--depth", "1"]
|
|
94
111
|
)
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rpremote
|
|
4
|
+
class Mrbgems
|
|
5
|
+
class Definition
|
|
6
|
+
GEM_OPTIONS = { commit: nil, require: nil, auto_require: true, group: nil }.freeze
|
|
7
|
+
|
|
8
|
+
attr_reader :dependencies, :groups, :vm_name
|
|
9
|
+
|
|
10
|
+
def initialize(filename)
|
|
11
|
+
@directory = File.dirname(filename)
|
|
12
|
+
@dependencies = []
|
|
13
|
+
@groups = []
|
|
14
|
+
@group_stack = []
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def vm(name)
|
|
18
|
+
value = name.to_sym
|
|
19
|
+
raise DefinitionError, "unsupported mrbgems VM: #{name.inspect}" unless VMS.include?(value)
|
|
20
|
+
raise DefinitionError, "mrbgems VM specified more than once" if vm_name
|
|
21
|
+
|
|
22
|
+
@vm_name = value
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def gem(github: nil, path: nil, branch: "main", **options)
|
|
26
|
+
settings = gem_settings(options)
|
|
27
|
+
sources = [github, path].compact
|
|
28
|
+
raise DefinitionError, "gem requires exactly one of github or path" unless sources.length == 1
|
|
29
|
+
|
|
30
|
+
groups = dependency_groups(settings[:group])
|
|
31
|
+
dependency = if github
|
|
32
|
+
github_dependency(
|
|
33
|
+
github, branch, settings[:commit], settings[:require], settings[:auto_require], groups
|
|
34
|
+
)
|
|
35
|
+
else
|
|
36
|
+
path_dependency(
|
|
37
|
+
path, branch, settings[:commit], settings[:require], settings[:auto_require], groups
|
|
38
|
+
)
|
|
39
|
+
end
|
|
40
|
+
key = [dependency.type, dependency.source]
|
|
41
|
+
raise DefinitionError, "duplicate mrbgem: #{dependency.source}" if dependencies.any? do |item|
|
|
42
|
+
[item.type, item.source] == key
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
dependencies << dependency
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def group(*names, &block)
|
|
49
|
+
raise DefinitionError, "mrbgem group requires at least one name" if names.empty?
|
|
50
|
+
raise DefinitionError, "mrbgem group requires a block" unless block
|
|
51
|
+
|
|
52
|
+
normalized = Mrbgems.normalize_groups(names)
|
|
53
|
+
@groups |= normalized
|
|
54
|
+
@group_stack << normalized
|
|
55
|
+
instance_eval(&block)
|
|
56
|
+
ensure
|
|
57
|
+
@group_stack.pop if normalized
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
attr_reader :directory
|
|
63
|
+
|
|
64
|
+
def gem_settings(options)
|
|
65
|
+
unknown = options.keys - GEM_OPTIONS.keys
|
|
66
|
+
raise DefinitionError, "unknown mrbgem option: #{unknown.join(", ")}" unless unknown.empty?
|
|
67
|
+
|
|
68
|
+
settings = GEM_OPTIONS.merge(options)
|
|
69
|
+
require_name = settings[:require]
|
|
70
|
+
unless require_name.nil? || (require_name.is_a?(String) && !require_name.empty? &&
|
|
71
|
+
!require_name.match?(/[\x00-\x1f\x7f]/))
|
|
72
|
+
raise DefinitionError, "invalid mrbgem require name: #{require_name.inspect}"
|
|
73
|
+
end
|
|
74
|
+
unless [true, false].include?(settings[:auto_require])
|
|
75
|
+
raise DefinitionError, "mrbgem auto_require must be true or false: #{settings[:auto_require].inspect}"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
settings
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def current_groups
|
|
82
|
+
@group_stack.flatten.uniq.freeze
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def dependency_groups(group)
|
|
86
|
+
return current_groups if group.nil?
|
|
87
|
+
|
|
88
|
+
names = Array(group)
|
|
89
|
+
raise DefinitionError, "mrbgem group requires at least one name" if names.empty?
|
|
90
|
+
|
|
91
|
+
normalized = Mrbgems.normalize_groups(names)
|
|
92
|
+
@groups |= normalized
|
|
93
|
+
(current_groups + normalized).uniq.freeze
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def github_dependency(source, branch, commit, require_name, auto_require, groups)
|
|
97
|
+
raise DefinitionError, "invalid GitHub mrbgem: #{source.inspect}" unless GITHUB_PATTERN.match?(source.to_s)
|
|
98
|
+
raise DefinitionError, "GitHub mrbgem branch must not be empty" if branch.to_s.empty?
|
|
99
|
+
raise DefinitionError, "invalid Git commit: #{commit.inspect}" if commit && !COMMIT_PATTERN.match?(commit.to_s)
|
|
100
|
+
|
|
101
|
+
Dependency.new(type: :github, source: source, branch: branch,
|
|
102
|
+
commit: commit&.downcase, path: nil, require_name: require_name,
|
|
103
|
+
auto_require: auto_require, groups: groups)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def path_dependency(source, branch, commit, require_name, auto_require, groups)
|
|
107
|
+
raise DefinitionError, "local mrbgem path must not be empty" if source.to_s.empty?
|
|
108
|
+
raise DefinitionError, "local mrbgem does not accept branch or commit" if branch != "main" || commit
|
|
109
|
+
|
|
110
|
+
Dependency.new(type: :path, source: source, branch: nil, commit: nil,
|
|
111
|
+
path: File.expand_path(source, directory), require_name: require_name,
|
|
112
|
+
auto_require: auto_require, groups: groups)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rpremote
|
|
4
|
+
class Mrbgems
|
|
5
|
+
module FileWriter
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
def write_json(filename, contents)
|
|
9
|
+
write_file(filename, "#{JSON.pretty_generate(contents)}\n")
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def write_file(filename, contents)
|
|
13
|
+
return if File.file?(filename) && File.binread(filename) == contents
|
|
14
|
+
|
|
15
|
+
FileUtils.mkdir_p(File.dirname(filename))
|
|
16
|
+
temporary = "#{filename}.tmp-#{Process.pid}"
|
|
17
|
+
File.binwrite(temporary, contents)
|
|
18
|
+
File.rename(temporary, filename)
|
|
19
|
+
ensure
|
|
20
|
+
FileUtils.rm_f(temporary) if temporary
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
include FileWriter
|
|
25
|
+
end
|
|
26
|
+
end
|