rpremote 0.1.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 +7 -0
- data/CHANGELOG.md +24 -0
- data/LICENSE +21 -0
- data/README.ja.md +122 -0
- data/README.md +124 -0
- data/RELEASING.md +54 -0
- data/THIRD_PARTY_NOTICES.md +29 -0
- data/exe/rpremote +7 -0
- data/lib/rpremote/build_command.rb +38 -0
- data/lib/rpremote/builder.rb +123 -0
- data/lib/rpremote/checksum.rb +27 -0
- data/lib/rpremote/cli.rb +267 -0
- data/lib/rpremote/config.rb +137 -0
- data/lib/rpremote/device.rb +56 -0
- data/lib/rpremote/dfu_command.rb +155 -0
- data/lib/rpremote/dfu_compiler.rb +50 -0
- data/lib/rpremote/flash_command.rb +45 -0
- data/lib/rpremote/flasher.rb +131 -0
- data/lib/rpremote/help.rb +60 -0
- data/lib/rpremote/language.rb +15 -0
- data/lib/rpremote/language_source.rb +126 -0
- data/lib/rpremote/mrbgems.rb +288 -0
- data/lib/rpremote/mrbgems_command.rb +48 -0
- data/lib/rpremote/picomodem.rb +293 -0
- data/lib/rpremote/remote_path.rb +27 -0
- data/lib/rpremote/resetter.rb +73 -0
- data/lib/rpremote/runner.rb +48 -0
- data/lib/rpremote/serial.rb +72 -0
- data/lib/rpremote/setup_command.rb +34 -0
- data/lib/rpremote/shell.rb +161 -0
- data/lib/rpremote/target.rb +46 -0
- data/lib/rpremote/terminal.rb +91 -0
- data/lib/rpremote/version.rb +5 -0
- data/lib/rpremote.rb +30 -0
- data/sig/rpremote.rbs +404 -0
- data/tasks/firmware_build.rb +108 -0
- data/tasks/release.rake +12 -0
- data/tasks/release_check.rb +83 -0
- metadata +82 -0
data/sig/rpremote.rbs
ADDED
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
module Rpremote
|
|
2
|
+
VERSION: String
|
|
3
|
+
|
|
4
|
+
class Error < StandardError
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
module Language
|
|
8
|
+
SUPPORTED: Array[String]
|
|
9
|
+
|
|
10
|
+
def self.validate!: (String language) -> String
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class Target
|
|
14
|
+
DEFAULT_LANGUAGE: String
|
|
15
|
+
DEFAULT_LANGUAGE_VERSION: String
|
|
16
|
+
DEFAULT_BOARD: String
|
|
17
|
+
DEFAULT_CACHE_DIR: String
|
|
18
|
+
IDENTIFIER_PATTERN: Regexp
|
|
19
|
+
|
|
20
|
+
attr_reader language: String
|
|
21
|
+
attr_reader language_version: String
|
|
22
|
+
attr_reader board: String
|
|
23
|
+
attr_reader cache_dir: String
|
|
24
|
+
|
|
25
|
+
def initialize: (
|
|
26
|
+
?language: String,
|
|
27
|
+
?language_version: String,
|
|
28
|
+
?board: String,
|
|
29
|
+
?cache_dir: String,
|
|
30
|
+
?firmware: String?
|
|
31
|
+
) -> void
|
|
32
|
+
def source_dir: (?root: String) -> String
|
|
33
|
+
def firmware_path: (?root: String?) -> String
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
module Checksum
|
|
37
|
+
def self.crc16: (String data, ?Integer initial) -> Integer
|
|
38
|
+
def self.crc32: (String data, ?Integer initial) -> Integer
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
module RemotePath
|
|
42
|
+
PREFIX: String
|
|
43
|
+
|
|
44
|
+
def self.remote?: (String path) -> bool
|
|
45
|
+
def self.unwrap: (String path) -> String
|
|
46
|
+
def self.validate: (String path) -> String
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class Config
|
|
50
|
+
DEFAULT_PATH: String
|
|
51
|
+
OPTION_TYPES: Hash[Symbol, untyped]
|
|
52
|
+
COMMAND_OPTIONS: Hash[String, Array[Symbol]]
|
|
53
|
+
|
|
54
|
+
class Result < Data
|
|
55
|
+
attr_reader path: String
|
|
56
|
+
attr_reader created: bool
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
class Error < Rpremote::Error
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def self.load: (
|
|
63
|
+
String command,
|
|
64
|
+
?filename: String,
|
|
65
|
+
?required: bool,
|
|
66
|
+
?cwd: String
|
|
67
|
+
) -> Hash[Symbol, untyped]
|
|
68
|
+
def self.extract_option!: (Array[String] args) -> [String, bool]
|
|
69
|
+
def self.load_command: (String? command, filename: String, required: bool) -> Hash[Symbol, untyped]
|
|
70
|
+
def self.setup: (?filename: String, ?cwd: String) -> Result
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
class PicoModem
|
|
74
|
+
CHUNK_SIZE: Integer
|
|
75
|
+
DEFAULT_TIMEOUT: Float
|
|
76
|
+
DFU_START: Integer
|
|
77
|
+
DFU_ACK: Integer
|
|
78
|
+
DFU_MAGIC: String
|
|
79
|
+
DFU_VERSION: Integer
|
|
80
|
+
DFU_TYPES: Array[String]
|
|
81
|
+
|
|
82
|
+
class Error < Rpremote::Error
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
class TimeoutError < Error
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
class ProtocolError < Error
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
class ChecksumError < ProtocolError
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
class DeviceError < Error
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
attr_reader io: untyped
|
|
98
|
+
attr_reader timeout: Float
|
|
99
|
+
|
|
100
|
+
def initialize: (untyped io, ?timeout: Float) -> void
|
|
101
|
+
def upload: (String path, String data) -> Integer
|
|
102
|
+
def download: (String path) -> String
|
|
103
|
+
def dfu: (String data, type: String) -> Integer
|
|
104
|
+
def send_frame: (Integer command, ?String payload) -> Integer
|
|
105
|
+
def receive_frame: () -> [Integer, String]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
class Device
|
|
109
|
+
class NotFoundError < Rpremote::Error
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
class MultipleDevicesError < Rpremote::Error
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def self.ports: () -> Array[String]
|
|
116
|
+
def self.main_port: (?String? explicit_port, ?available_ports: Array[String]) -> String
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
class Serial
|
|
120
|
+
BAUD_RATE: Integer
|
|
121
|
+
|
|
122
|
+
class ConfigurationError < Rpremote::Error
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def self.open: [T] (String path, ?baud: Integer) { (Serial) -> T } -> T
|
|
126
|
+
| (String path, ?baud: Integer) -> Serial
|
|
127
|
+
|
|
128
|
+
attr_reader path: String
|
|
129
|
+
attr_reader baud: Integer
|
|
130
|
+
attr_reader io: File
|
|
131
|
+
|
|
132
|
+
def initialize: (String path, ?baud: Integer) -> void
|
|
133
|
+
def read_nonblock: (Integer length) -> String
|
|
134
|
+
def write: (String data) -> Integer
|
|
135
|
+
def flush: () -> untyped
|
|
136
|
+
def close: () -> untyped
|
|
137
|
+
def closed?: () -> bool
|
|
138
|
+
def to_io: () -> File
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
class LanguageSource
|
|
142
|
+
class Error < Rpremote::Error
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
class DownloadError < Error
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
class ExtractError < Error
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
attr_reader language: String
|
|
152
|
+
attr_reader version: String
|
|
153
|
+
attr_reader cache_dir: String
|
|
154
|
+
|
|
155
|
+
def initialize: (
|
|
156
|
+
?language: String,
|
|
157
|
+
?version: String,
|
|
158
|
+
?cache_dir: String,
|
|
159
|
+
?fetcher: untyped,
|
|
160
|
+
?extractor: untyped,
|
|
161
|
+
?preparer: untyped
|
|
162
|
+
) -> void
|
|
163
|
+
def setup: (?force: bool) -> String
|
|
164
|
+
def archive_url: () -> String
|
|
165
|
+
def archive_path: () -> String
|
|
166
|
+
def source_dir: () -> String
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
class Builder
|
|
170
|
+
class Error < Rpremote::Error
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def initialize: (?root: String, ?runner: untyped, ?mrbgems_class: untyped) -> void
|
|
174
|
+
def build: (
|
|
175
|
+
?language: String,
|
|
176
|
+
?language_version: String,
|
|
177
|
+
?board: String,
|
|
178
|
+
?cache_dir: String,
|
|
179
|
+
?firmware: String?,
|
|
180
|
+
?mrbgems: String | false | nil,
|
|
181
|
+
?output: IO,
|
|
182
|
+
?error: IO
|
|
183
|
+
) -> void
|
|
184
|
+
def clean: (?output: IO) -> void
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
class Mrbgems
|
|
188
|
+
DEFAULT_PATH: String
|
|
189
|
+
DEFAULT_LOCK_PATH: String
|
|
190
|
+
LOCK_VERSION: Integer
|
|
191
|
+
|
|
192
|
+
class Error < Rpremote::Error
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
class DefinitionError < Error
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
class LockError < Error
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
class Dependency < Data
|
|
202
|
+
attr_reader type: Symbol
|
|
203
|
+
attr_reader source: String
|
|
204
|
+
attr_reader branch: String?
|
|
205
|
+
attr_reader commit: String?
|
|
206
|
+
attr_reader path: String?
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
class Overlay < Data
|
|
210
|
+
attr_reader path: String
|
|
211
|
+
attr_reader fingerprint: String
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
attr_reader path: String
|
|
215
|
+
attr_reader lock_path: String
|
|
216
|
+
|
|
217
|
+
def initialize: (
|
|
218
|
+
?path: String,
|
|
219
|
+
?lock_path: String?,
|
|
220
|
+
?cwd: String,
|
|
221
|
+
?resolver: untyped
|
|
222
|
+
) -> void
|
|
223
|
+
def exist?: () -> bool
|
|
224
|
+
def dependencies: () -> Array[Dependency]
|
|
225
|
+
def vm: () -> Symbol?
|
|
226
|
+
def check: () -> Array[Dependency]
|
|
227
|
+
def lock: (?update: bool) -> Hash[String, untyped]
|
|
228
|
+
def read_lock: (?required: bool) -> Hash[String, untyped]?
|
|
229
|
+
def generate_overlay: (
|
|
230
|
+
base_config: String,
|
|
231
|
+
target: String,
|
|
232
|
+
directory: String,
|
|
233
|
+
?update: bool
|
|
234
|
+
) -> Overlay
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
class Shell
|
|
238
|
+
DEFAULT_TIMEOUT: Float
|
|
239
|
+
PROMPT_START: String
|
|
240
|
+
PROMPT_END: String
|
|
241
|
+
CURSOR_POSITION_QUERY: String
|
|
242
|
+
CURSOR_POSITION_RESPONSE: String
|
|
243
|
+
|
|
244
|
+
class Error < Rpremote::Error
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
class TimeoutError < Error
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
class ProtocolError < Error
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
class CommandError < Error
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
attr_reader io: untyped
|
|
257
|
+
attr_reader timeout: Float
|
|
258
|
+
|
|
259
|
+
def self.quote_argument: (String argument) -> String
|
|
260
|
+
def initialize: (untyped io, ?timeout: Float) -> void
|
|
261
|
+
def synchronize!: () -> nil
|
|
262
|
+
def send_command: (String command) -> nil
|
|
263
|
+
def execute: (String command) -> String
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
class Terminal
|
|
267
|
+
EXIT_BYTE: Integer
|
|
268
|
+
BUFFER_SIZE: Integer
|
|
269
|
+
|
|
270
|
+
attr_reader serial_io: untyped
|
|
271
|
+
attr_reader input: IO
|
|
272
|
+
attr_reader output: IO
|
|
273
|
+
|
|
274
|
+
def initialize: (
|
|
275
|
+
untyped serial_io,
|
|
276
|
+
?input: IO,
|
|
277
|
+
?output: IO,
|
|
278
|
+
?selector: untyped
|
|
279
|
+
) -> void
|
|
280
|
+
def run: (?exit_sequence: String?) -> nil
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
class Runner
|
|
284
|
+
REMOTE_DIRECTORY: String
|
|
285
|
+
|
|
286
|
+
attr_reader io: untyped
|
|
287
|
+
attr_reader timeout: Float
|
|
288
|
+
|
|
289
|
+
def initialize: (
|
|
290
|
+
untyped io,
|
|
291
|
+
?timeout: Float,
|
|
292
|
+
?modem_class: untyped,
|
|
293
|
+
?shell_class: untyped,
|
|
294
|
+
?path_factory: untyped
|
|
295
|
+
) -> void
|
|
296
|
+
def run: (String data) -> String
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
class Resetter
|
|
300
|
+
DEFAULT_TIMEOUT: Float
|
|
301
|
+
RETRY_INTERVAL: Float
|
|
302
|
+
PROBE_TIMEOUT: Float
|
|
303
|
+
|
|
304
|
+
class TimeoutError < Rpremote::Error
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def initialize: (
|
|
308
|
+
?serial: untyped,
|
|
309
|
+
?timeout: Numeric,
|
|
310
|
+
?shell_class: untyped,
|
|
311
|
+
?sleeper: untyped,
|
|
312
|
+
?clock: untyped,
|
|
313
|
+
?port_probe: untyped
|
|
314
|
+
) -> void
|
|
315
|
+
def reset: (String port_path, ?baud: Integer) -> String
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
class Flasher
|
|
319
|
+
DEFAULT_VOLUMES_ROOT: String
|
|
320
|
+
DEFAULT_TIMEOUT: Float
|
|
321
|
+
INFO_FILE: String
|
|
322
|
+
|
|
323
|
+
class Result < Data
|
|
324
|
+
attr_reader mount: String
|
|
325
|
+
attr_reader destination: String
|
|
326
|
+
attr_reader port: String
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
class Error < Rpremote::Error
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
class MountNotFoundError < Error
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
class InvalidTargetError < Error
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
class TimeoutError < Error
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
attr_reader volumes_root: String
|
|
342
|
+
attr_reader timeout: Float
|
|
343
|
+
|
|
344
|
+
def initialize: (
|
|
345
|
+
?volumes_root: String,
|
|
346
|
+
?timeout: Numeric,
|
|
347
|
+
?mount_probe: untyped,
|
|
348
|
+
?port_probe: untyped,
|
|
349
|
+
?sleeper: untyped
|
|
350
|
+
) -> void
|
|
351
|
+
def flash: (String uf2_path, ?mount: String?, ?port: String?) -> Result
|
|
352
|
+
def find_mount: (?String? explicit_mount) -> String
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
class DfuCommand
|
|
356
|
+
DEFAULT_TIMEOUT: Float
|
|
357
|
+
TYPES: Hash[String, String]
|
|
358
|
+
|
|
359
|
+
def self.run: (
|
|
360
|
+
Array[String] args,
|
|
361
|
+
defaults: Hash[Symbol, untyped],
|
|
362
|
+
?output: IO,
|
|
363
|
+
?serial: untyped,
|
|
364
|
+
?device: untyped,
|
|
365
|
+
?services: Hash[Symbol, untyped]
|
|
366
|
+
) -> void
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
class DfuCompiler
|
|
370
|
+
class Error < Rpremote::Error
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def self.compile: (String source, target: Target, ?destination: String?, ?output: IO, ?runner: untyped) -> String
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
class SetupCommand
|
|
377
|
+
def self.run: (
|
|
378
|
+
Array[String] args,
|
|
379
|
+
defaults: Hash[Symbol, untyped],
|
|
380
|
+
config: untyped,
|
|
381
|
+
config_filename: String,
|
|
382
|
+
?output: IO
|
|
383
|
+
) -> void
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
class BuildCommand
|
|
387
|
+
def self.run: (
|
|
388
|
+
Array[String] args,
|
|
389
|
+
defaults: Hash[Symbol, untyped],
|
|
390
|
+
?output: IO,
|
|
391
|
+
?error: IO,
|
|
392
|
+
?builder: Builder
|
|
393
|
+
) -> void
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
class FlashCommand
|
|
397
|
+
def self.run: (
|
|
398
|
+
Array[String] args,
|
|
399
|
+
defaults: Hash[Symbol, untyped],
|
|
400
|
+
?output: IO,
|
|
401
|
+
?flasher: untyped
|
|
402
|
+
) -> void
|
|
403
|
+
end
|
|
404
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "fileutils"
|
|
5
|
+
|
|
6
|
+
REPOSITORY_ROOT = File.expand_path(ENV.fetch("RPREMOTE_ROOT", File.expand_path("../..", __dir__)))
|
|
7
|
+
DEFAULT_BOARD = "pico2"
|
|
8
|
+
SUPPORTED_BOARDS = %w[pico2 pico2_w].freeze
|
|
9
|
+
|
|
10
|
+
def command!(*command, chdir:, env: {})
|
|
11
|
+
prefix = env.map { |key, value| "#{key}=#{value}" }.join(" ")
|
|
12
|
+
puts "+ #{prefix} #{command.join(" ")}".strip
|
|
13
|
+
abort "Command failed: #{command.join(" ")}" unless system(env, *command, chdir: chdir)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
language = ENV.fetch("RPREMOTE_LANGUAGE", "picoruby")
|
|
17
|
+
target_version = ENV.fetch("RPREMOTE_LANGUAGE_VERSION", "4.0.3")
|
|
18
|
+
board = ENV.fetch("RPREMOTE_BOARD", DEFAULT_BOARD)
|
|
19
|
+
abort "Unsupported language: #{language}" unless language == "picoruby"
|
|
20
|
+
abort "Unsupported board: #{board}" unless SUPPORTED_BOARDS.include?(board)
|
|
21
|
+
|
|
22
|
+
picoruby_root = File.expand_path(ENV.fetch("PICORUBY_DIR"))
|
|
23
|
+
config_name = ENV.fetch("RPREMOTE_CONFIG_NAME", "r2p2-#{language}-#{board}")
|
|
24
|
+
base_config_path = File.join(picoruby_root, "build_config", "#{config_name}.rb")
|
|
25
|
+
config_path = ENV.fetch("RPREMOTE_MRUBY_CONFIG", base_config_path)
|
|
26
|
+
fingerprint = ENV.fetch("RPREMOTE_MRBGEMS_FINGERPRINT", nil)
|
|
27
|
+
build_name = [config_name, target_version, fingerprint].compact.join("-")
|
|
28
|
+
r2p2_gem_dir = File.join(picoruby_root, "mrbgems/picoruby-r2p2")
|
|
29
|
+
pico_sdk_path = File.join(r2p2_gem_dir, "lib/pico-sdk")
|
|
30
|
+
pico_extras_path = File.join(r2p2_gem_dir, "lib/pico-extras")
|
|
31
|
+
output_dir = File.join(REPOSITORY_ROOT, "build", build_name)
|
|
32
|
+
mruby_build_dir = File.join(picoruby_root, "build", config_name)
|
|
33
|
+
cmake_source_dir = File.join(r2p2_gem_dir, "cmake")
|
|
34
|
+
|
|
35
|
+
unless File.file?(File.join(picoruby_root, "Rakefile"))
|
|
36
|
+
abort "PicoRuby source not found: #{picoruby_root}\nSet PICORUBY_DIR to a PicoRuby checkout."
|
|
37
|
+
end
|
|
38
|
+
abort "Build config not found: #{base_config_path}" unless File.file?(base_config_path)
|
|
39
|
+
abort "Generated build config not found: #{config_path}" unless File.file?(config_path)
|
|
40
|
+
build_config = File.read(base_config_path)
|
|
41
|
+
mrubyc_vm = build_config.match?(/mrubyc_hal|conf\.femtoruby/)
|
|
42
|
+
uses_wifi = build_config.match?(/\bUSE_WIFI\b/)
|
|
43
|
+
command!("rake", "r2p2:setup", chdir: picoruby_root) unless File.directory?(pico_sdk_path)
|
|
44
|
+
|
|
45
|
+
version_header = File.join(picoruby_root, "include/version.h")
|
|
46
|
+
version = File.read(version_header)[/#define PICORUBY_VERSION "(.+?)"/, 1]
|
|
47
|
+
abort "Could not determine PicoRuby version from #{version_header}" unless version
|
|
48
|
+
abort "PicoRuby source is #{version}, but #{target_version} was requested." unless version == target_version
|
|
49
|
+
|
|
50
|
+
cache_file = File.join(output_dir, "CMakeCache.txt")
|
|
51
|
+
if File.file?(cache_file) && !File.read(cache_file).include?("CMAKE_HOME_DIRECTORY:INTERNAL=#{cmake_source_dir}")
|
|
52
|
+
puts "Removing stale CMake output: #{output_dir}"
|
|
53
|
+
FileUtils.rm_rf(output_dir)
|
|
54
|
+
end
|
|
55
|
+
FileUtils.mkdir_p(output_dir)
|
|
56
|
+
build_env = {
|
|
57
|
+
"MRUBY_CONFIG" => config_path,
|
|
58
|
+
"PICORB_BOARD" => board,
|
|
59
|
+
"PICO_SDK_PATH" => pico_sdk_path,
|
|
60
|
+
"PICO_EXTRAS_PATH" => pico_extras_path
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# PicoRuby's build directory is keyed by the target name, not by MRUBY_CONFIG.
|
|
64
|
+
# A generated overlay therefore shares the base target's objects. In particular,
|
|
65
|
+
# mrblib.c includes picogem_init.c, but Make does not track that generated include
|
|
66
|
+
# as a dependency. Reusing the directory can leave a removed or added local
|
|
67
|
+
# mrbgem out of libmruby.a. Custom mrbgems must start from a fresh target build.
|
|
68
|
+
if fingerprint && File.directory?(mruby_build_dir)
|
|
69
|
+
puts "Removing stale PicoRuby build output: #{mruby_build_dir}"
|
|
70
|
+
FileUtils.rm_rf(mruby_build_dir)
|
|
71
|
+
end
|
|
72
|
+
command!("rake", chdir: picoruby_root, env: build_env)
|
|
73
|
+
|
|
74
|
+
cmake_definitions = [
|
|
75
|
+
"-D", "PICORUBY_ROOT=#{picoruby_root}",
|
|
76
|
+
"-D", "R2P2_GEM_DIR=#{r2p2_gem_dir}",
|
|
77
|
+
"-D", "EXTRA_LIBRARY_PATH=#{File.join(mruby_build_dir, "lib")}",
|
|
78
|
+
"-D", "EXTRA_INCLUDE_DIR=#{File.join(mruby_build_dir, "include")}",
|
|
79
|
+
"-D", "PICO_CYW43_SUPPORTED=1",
|
|
80
|
+
"-D", "MRUBY_CONFIG=#{config_name}",
|
|
81
|
+
"-D", "PICORB_VM_MRUBY=#{mrubyc_vm ? 0 : 1}",
|
|
82
|
+
"-D", "PICORB_VM_MRUBYC=#{mrubyc_vm ? 1 : 0}",
|
|
83
|
+
"-D", "R2P2_NAME=R2P2-PICORUBY",
|
|
84
|
+
"-D", "R2P2_BOARD_NAME=#{board.upcase}",
|
|
85
|
+
"-D", "R2P2_VERSION=#{version}",
|
|
86
|
+
"-D", "PICO_PLATFORM=rp2350",
|
|
87
|
+
"-D", "PICO_BOARD=#{board}",
|
|
88
|
+
"-D", "CMAKE_BUILD_TYPE=Release",
|
|
89
|
+
"-D", "NDEBUG=1",
|
|
90
|
+
"-D", "R2P2_NO_SHARED_ALLOC=OFF"
|
|
91
|
+
]
|
|
92
|
+
cmake_definitions.push("-D", "USE_WIFI=1") if uses_wifi
|
|
93
|
+
command!("cmake", "-S", cmake_source_dir, "-B", output_dir, *cmake_definitions,
|
|
94
|
+
chdir: picoruby_root, env: build_env)
|
|
95
|
+
command!("cmake", "--build", output_dir, chdir: picoruby_root)
|
|
96
|
+
|
|
97
|
+
uf2_files = Dir[File.join(output_dir, "*.uf2")]
|
|
98
|
+
abort "Build finished but no UF2 file was created in #{output_dir}" if uf2_files.empty?
|
|
99
|
+
|
|
100
|
+
built_firmware = uf2_files.max_by { |path| File.mtime(path) }
|
|
101
|
+
destination = ENV.fetch("RPREMOTE_FIRMWARE", nil)
|
|
102
|
+
if destination
|
|
103
|
+
FileUtils.mkdir_p(File.dirname(destination))
|
|
104
|
+
FileUtils.cp(built_firmware, destination)
|
|
105
|
+
puts "Built custom firmware: #{destination}"
|
|
106
|
+
else
|
|
107
|
+
puts "Built custom firmware: #{built_firmware}"
|
|
108
|
+
end
|
data/tasks/release.rake
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "release_check"
|
|
4
|
+
|
|
5
|
+
namespace :release do
|
|
6
|
+
desc "Validate and smoke-test the release gem without publishing it"
|
|
7
|
+
task check: %i[spec rubocop] do
|
|
8
|
+
sh "bundle exec rbs validate"
|
|
9
|
+
sh "gem build rpremote.gemspec"
|
|
10
|
+
Rpremote::ReleaseCheck.call
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "bundler"
|
|
5
|
+
require "open3"
|
|
6
|
+
require "rbconfig"
|
|
7
|
+
require "rubygems/package"
|
|
8
|
+
require "tmpdir"
|
|
9
|
+
require_relative "../lib/rpremote/version"
|
|
10
|
+
|
|
11
|
+
module Rpremote
|
|
12
|
+
module ReleaseCheck
|
|
13
|
+
EXPECTED_FILES = %w[
|
|
14
|
+
CHANGELOG.md
|
|
15
|
+
LICENSE
|
|
16
|
+
README.md
|
|
17
|
+
README.ja.md
|
|
18
|
+
RELEASING.md
|
|
19
|
+
THIRD_PARTY_NOTICES.md
|
|
20
|
+
exe/rpremote
|
|
21
|
+
tasks/firmware_build.rb
|
|
22
|
+
].freeze
|
|
23
|
+
|
|
24
|
+
module_function
|
|
25
|
+
|
|
26
|
+
def call
|
|
27
|
+
gem_file = File.expand_path("rpremote-#{Rpremote::VERSION}.gem")
|
|
28
|
+
validate_package(gem_file)
|
|
29
|
+
smoke_test(gem_file)
|
|
30
|
+
puts "release check passed: #{File.basename(gem_file)}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def validate_package(gem_file)
|
|
34
|
+
package = Gem::Package.new(gem_file)
|
|
35
|
+
specification = package.spec
|
|
36
|
+
missing_files = EXPECTED_FILES - package.contents
|
|
37
|
+
changelog_entry = "## [#{Rpremote::VERSION}]"
|
|
38
|
+
|
|
39
|
+
unless specification.version.to_s == Rpremote::VERSION
|
|
40
|
+
abort "built gem version does not match #{Rpremote::VERSION}"
|
|
41
|
+
end
|
|
42
|
+
unless File.read("CHANGELOG.md").include?(changelog_entry)
|
|
43
|
+
abort "CHANGELOG.md has no entry for #{Rpremote::VERSION}"
|
|
44
|
+
end
|
|
45
|
+
abort "built gem is missing: #{missing_files.join(", ")}" unless missing_files.empty?
|
|
46
|
+
abort "built gem does not require MFA" unless specification.metadata["rubygems_mfa_required"] == "true"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def smoke_test(gem_file)
|
|
50
|
+
Dir.mktmpdir("rpremote-release-") do |directory|
|
|
51
|
+
gem_home = File.join(directory, "gems")
|
|
52
|
+
bin_dir = File.join(directory, "bin")
|
|
53
|
+
FileUtils.mkdir_p([gem_home, bin_dir])
|
|
54
|
+
environment = { "GEM_HOME" => gem_home, "GEM_PATH" => gem_home }
|
|
55
|
+
|
|
56
|
+
install_gem(environment, gem_home, bin_dir, gem_file)
|
|
57
|
+
run_cli(environment, bin_dir)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def install_gem(environment, gem_home, bin_dir, gem_file)
|
|
62
|
+
command = [
|
|
63
|
+
RbConfig.ruby, "-S", "gem", "install", "--local", "--no-document",
|
|
64
|
+
"--install-dir", gem_home, "--bindir", bin_dir, gem_file
|
|
65
|
+
]
|
|
66
|
+
run!(environment, command, "isolated gem install")
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def run_cli(environment, bin_dir)
|
|
70
|
+
stdout = run!(environment, [File.join(bin_dir, "rpremote"), "--version"], "installed CLI")
|
|
71
|
+
abort "installed CLI reported #{stdout.strip.inspect}" unless stdout.strip == Rpremote::VERSION
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def run!(environment, command, description)
|
|
75
|
+
stdout, stderr, status = Bundler.with_unbundled_env do
|
|
76
|
+
Open3.capture3(environment, *command)
|
|
77
|
+
end
|
|
78
|
+
abort "#{description} failed:\n#{stdout}#{stderr}" unless status.success?
|
|
79
|
+
|
|
80
|
+
stdout
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rpremote
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- ogom
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: Embed public or local mrbgems in reproducible R2P2 firmware, flash it,
|
|
13
|
+
and control the device.
|
|
14
|
+
email:
|
|
15
|
+
- ogom@users.noreply.github.com
|
|
16
|
+
executables:
|
|
17
|
+
- rpremote
|
|
18
|
+
extensions: []
|
|
19
|
+
extra_rdoc_files: []
|
|
20
|
+
files:
|
|
21
|
+
- CHANGELOG.md
|
|
22
|
+
- LICENSE
|
|
23
|
+
- README.ja.md
|
|
24
|
+
- README.md
|
|
25
|
+
- RELEASING.md
|
|
26
|
+
- THIRD_PARTY_NOTICES.md
|
|
27
|
+
- exe/rpremote
|
|
28
|
+
- lib/rpremote.rb
|
|
29
|
+
- lib/rpremote/build_command.rb
|
|
30
|
+
- lib/rpremote/builder.rb
|
|
31
|
+
- lib/rpremote/checksum.rb
|
|
32
|
+
- lib/rpremote/cli.rb
|
|
33
|
+
- lib/rpremote/config.rb
|
|
34
|
+
- lib/rpremote/device.rb
|
|
35
|
+
- lib/rpremote/dfu_command.rb
|
|
36
|
+
- lib/rpremote/dfu_compiler.rb
|
|
37
|
+
- lib/rpremote/flash_command.rb
|
|
38
|
+
- lib/rpremote/flasher.rb
|
|
39
|
+
- lib/rpremote/help.rb
|
|
40
|
+
- lib/rpremote/language.rb
|
|
41
|
+
- lib/rpremote/language_source.rb
|
|
42
|
+
- lib/rpremote/mrbgems.rb
|
|
43
|
+
- lib/rpremote/mrbgems_command.rb
|
|
44
|
+
- lib/rpremote/picomodem.rb
|
|
45
|
+
- lib/rpremote/remote_path.rb
|
|
46
|
+
- lib/rpremote/resetter.rb
|
|
47
|
+
- lib/rpremote/runner.rb
|
|
48
|
+
- lib/rpremote/serial.rb
|
|
49
|
+
- lib/rpremote/setup_command.rb
|
|
50
|
+
- lib/rpremote/shell.rb
|
|
51
|
+
- lib/rpremote/target.rb
|
|
52
|
+
- lib/rpremote/terminal.rb
|
|
53
|
+
- lib/rpremote/version.rb
|
|
54
|
+
- sig/rpremote.rbs
|
|
55
|
+
- tasks/firmware_build.rb
|
|
56
|
+
- tasks/release.rake
|
|
57
|
+
- tasks/release_check.rb
|
|
58
|
+
homepage: https://github.com/ogom/rpremote
|
|
59
|
+
licenses:
|
|
60
|
+
- MIT
|
|
61
|
+
metadata:
|
|
62
|
+
homepage_uri: https://github.com/ogom/rpremote
|
|
63
|
+
source_code_uri: https://github.com/ogom/rpremote/tree/main/packages/rpremote
|
|
64
|
+
rubygems_mfa_required: 'true'
|
|
65
|
+
rdoc_options: []
|
|
66
|
+
require_paths:
|
|
67
|
+
- lib
|
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
69
|
+
requirements:
|
|
70
|
+
- - ">="
|
|
71
|
+
- !ruby/object:Gem::Version
|
|
72
|
+
version: 4.0.0
|
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
74
|
+
requirements:
|
|
75
|
+
- - ">="
|
|
76
|
+
- !ruby/object:Gem::Version
|
|
77
|
+
version: '0'
|
|
78
|
+
requirements: []
|
|
79
|
+
rubygems_version: 4.0.10
|
|
80
|
+
specification_version: 4
|
|
81
|
+
summary: Build and control custom PicoRuby firmware on Raspberry Pi Pico
|
|
82
|
+
test_files: []
|