qmk-cli 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/LICENSE.txt +5 -0
- data/Makefile +15 -0
- data/README.md +48 -0
- data/bin/qmk +5 -0
- data/lib/cli.rb +89 -0
- data/lib/firmware.rb +136 -0
- data/lib/git.rb +41 -0
- data/lib/makefile.rb +40 -0
- data/lib/programmer.rb +50 -0
- data/qmk_cli.gemspec +13 -0
- metadata +55 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 915b0f8e03509b8c776660b1a9f335441707e1b4
|
4
|
+
data.tar.gz: f4541bde947171682ffa95db7b2d92084d458a5a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9aa6510c0fbad4586d251fab54ae968ee74542c83c596b87cf32e9480d54e3b68a44a017531a878afa19e0efa866b4ce6a57f8308ae679b2e86bf0dbb0229567
|
7
|
+
data.tar.gz: 1d0050383637fc83085fd80fd91a7e9f32049695df559abc0e14d5bed3e7a0d42dcc2f3269e3dc78891dc712e07436b8ae33b63e56917b4e380ae007fef31e35
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*.gem
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,5 @@
|
|
1
|
+
Copyright 2018 Nic Haynes
|
2
|
+
|
3
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
4
|
+
|
5
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
data/Makefile
ADDED
data/README.md
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# qmk-cli
|
2
|
+
|
3
|
+
A thin wrapper around QMK's `make` to make common tasks easier.
|
4
|
+
|
5
|
+
## Why use this?
|
6
|
+
|
7
|
+
- Flashing is more intuitive (Example: `qmk flash planck`)
|
8
|
+
- Automatic programmer detection (No more "Does this board use dfu-programmer/avrdude/teensy/other?")
|
9
|
+
- Supports standalone keymaps
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
Usage:
|
14
|
+
qmk COMMAND [options]
|
15
|
+
|
16
|
+
Commands:
|
17
|
+
flash KEYBOARD Flash a keyboard
|
18
|
+
build KEYBOARD Compile a keyboard
|
19
|
+
clean [KEYBOARD] Cleans up output folders so things compile from scratch
|
20
|
+
setup Clone QMK firmware and checkout latest tag
|
21
|
+
update Update QMK firmware to latest tag
|
22
|
+
|
23
|
+
Options:
|
24
|
+
-k, --keymap KEYMAP Your keymap name (default: `whoami`)
|
25
|
+
-h, --help Show this help message
|
26
|
+
|
27
|
+
## Standalone keymaps
|
28
|
+
|
29
|
+
- Add a `.qmk` file in your keymaps directory to designate this a standalone keymaps directory.
|
30
|
+
- Keyboard directories should be at the root with the keymap files inside. Since these are *your* keymaps there's no need to add additional namespacing.
|
31
|
+
|
32
|
+
View [nicinabox/keymaps](https://github.com/nicinabox/keymaps) for a complete example on how standalone keymaps should be organized.
|
33
|
+
|
34
|
+
## Platforms
|
35
|
+
|
36
|
+
- [x] macOS
|
37
|
+
- [x] linux, probably
|
38
|
+
- [ ] windows
|
39
|
+
|
40
|
+
## Requirements
|
41
|
+
|
42
|
+
- Ruby >= 2
|
43
|
+
- Git
|
44
|
+
- [build tools](https://docs.qmk.fm/getting_started_build_tools.html)
|
45
|
+
|
46
|
+
## License
|
47
|
+
|
48
|
+
ISC
|
data/bin/qmk
ADDED
data/lib/cli.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'firmware'
|
3
|
+
|
4
|
+
USAGE = <<-USAGE
|
5
|
+
Usage:
|
6
|
+
qmk COMMAND [options]
|
7
|
+
|
8
|
+
Commands:
|
9
|
+
flash KEYBOARD Flash a keyboard
|
10
|
+
build KEYBOARD Compile a keyboard
|
11
|
+
clean [KEYBOARD] Cleans up output folders so things compile from scratch
|
12
|
+
keyboards List available keyboards (scope with --keymap)
|
13
|
+
setup Clone QMK firmware and checkout latest tag
|
14
|
+
update Update QMK firmware to latest tag
|
15
|
+
|
16
|
+
Options:
|
17
|
+
USAGE
|
18
|
+
|
19
|
+
module QMK
|
20
|
+
class CLI
|
21
|
+
def initialize(args)
|
22
|
+
@options = parser(args)
|
23
|
+
command, keyboard = args
|
24
|
+
@firmware = Firmware.new(keyboard, @options[:keymap], keymaps_only?)
|
25
|
+
|
26
|
+
self.send(parse_command(command || 'help'))
|
27
|
+
end
|
28
|
+
|
29
|
+
def setup
|
30
|
+
@firmware.setup
|
31
|
+
end
|
32
|
+
|
33
|
+
def update
|
34
|
+
@firmware.update
|
35
|
+
@firmware.update_submodules
|
36
|
+
end
|
37
|
+
|
38
|
+
def build
|
39
|
+
@firmware.make
|
40
|
+
end
|
41
|
+
|
42
|
+
def flash
|
43
|
+
@firmware.make @firmware.programmer
|
44
|
+
end
|
45
|
+
|
46
|
+
def clean
|
47
|
+
@firmware.make 'clean'
|
48
|
+
end
|
49
|
+
|
50
|
+
def keyboards
|
51
|
+
puts @firmware.keyboards
|
52
|
+
end
|
53
|
+
|
54
|
+
def help
|
55
|
+
puts @options[:help]
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
def parser(args)
|
60
|
+
options = {}
|
61
|
+
|
62
|
+
OptionParser.new do |parser|
|
63
|
+
parser.banner = USAGE
|
64
|
+
|
65
|
+
options[:keymap] = keymaps_only? ? `whoami`.strip : nil
|
66
|
+
parser.on("-k", "--keymap KEYMAP", "Your keymap name (default: #{options[:keymap]})") do |v|
|
67
|
+
options[:keymap] = v
|
68
|
+
end
|
69
|
+
|
70
|
+
options[:help] = parser
|
71
|
+
parser.on("-h", "--help", "Show this help message")
|
72
|
+
end.parse!
|
73
|
+
|
74
|
+
options
|
75
|
+
end
|
76
|
+
|
77
|
+
def parse_command(cmd)
|
78
|
+
cmd.gsub(/\-/, '_').downcase
|
79
|
+
end
|
80
|
+
|
81
|
+
def keymaps_only?
|
82
|
+
File.exists? '.qmk'
|
83
|
+
end
|
84
|
+
|
85
|
+
def method_missing(*args)
|
86
|
+
help
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/lib/firmware.rb
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'open3'
|
3
|
+
require 'programmer'
|
4
|
+
require 'git'
|
5
|
+
|
6
|
+
LIB_PATH = "/usr/local/lib/qmk_firmware"
|
7
|
+
|
8
|
+
module QMK
|
9
|
+
class Firmware
|
10
|
+
def initialize(keyboard, keymap, keymaps_only)
|
11
|
+
@keyboard = keyboard
|
12
|
+
@keymap = keymap
|
13
|
+
@repo = Git.new(LIB_PATH)
|
14
|
+
@keymaps_only = keymaps_only
|
15
|
+
end
|
16
|
+
|
17
|
+
def make(target = nil)
|
18
|
+
if @keymaps_only
|
19
|
+
prepare_firmware
|
20
|
+
end
|
21
|
+
|
22
|
+
in_repo do
|
23
|
+
run "make #{make_target(target)}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def setup
|
28
|
+
@repo.clone 'https://github.com/qmk/qmk_firmware.git'
|
29
|
+
|
30
|
+
in_repo do
|
31
|
+
@repo.checkout_latest_tag
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def update_submodules
|
36
|
+
in_repo do
|
37
|
+
run "make git-submodule"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def checkout_stable
|
42
|
+
in_repo do
|
43
|
+
@repo.fetch_origin
|
44
|
+
@repo.checkout_latest_tag
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def update
|
49
|
+
in_repo do
|
50
|
+
@repo.clean
|
51
|
+
checkout_stable
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def keyboards
|
56
|
+
if @keymaps_only
|
57
|
+
standalone_keyboards
|
58
|
+
else
|
59
|
+
qmk_keyboards @keymap
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def standalone_keyboards
|
64
|
+
Dir["**/keymap.c"]
|
65
|
+
.map {|path| File.dirname(path) }
|
66
|
+
.sort
|
67
|
+
end
|
68
|
+
|
69
|
+
def qmk_keyboards(keymap=nil)
|
70
|
+
Dir["#{keyboards_path}/**/#{keymap}/keymap.c"]
|
71
|
+
.map {|path|
|
72
|
+
File.dirname(path)
|
73
|
+
.gsub(keyboards_path, '')
|
74
|
+
.split('/')
|
75
|
+
.reject(&:empty?)
|
76
|
+
.first
|
77
|
+
}
|
78
|
+
.uniq
|
79
|
+
.sort
|
80
|
+
end
|
81
|
+
|
82
|
+
def keyboard_name
|
83
|
+
@keyboard.gsub(/\/rev.*/, '')
|
84
|
+
end
|
85
|
+
|
86
|
+
def programmer
|
87
|
+
Programmer.new(keyboard_path).flasher
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
def in_repo(&block)
|
92
|
+
@repo.in_repo &block
|
93
|
+
end
|
94
|
+
|
95
|
+
def run(cmd)
|
96
|
+
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
|
97
|
+
while line = stdout.gets
|
98
|
+
puts line
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def prepare_firmware
|
104
|
+
keyboard_path = File.expand_path "./#{keyboard_name}"
|
105
|
+
files = Dir.glob "#{keyboard_path}/*"
|
106
|
+
|
107
|
+
FileUtils.mkdir_p keymap_path
|
108
|
+
FileUtils.cp_r files, keymap_path
|
109
|
+
end
|
110
|
+
|
111
|
+
def make_target(target = nil)
|
112
|
+
return target unless @keyboard
|
113
|
+
[@keyboard, @keymap, target].compact.join(':')
|
114
|
+
end
|
115
|
+
|
116
|
+
def keyboards_path
|
117
|
+
"#{@repo.path}/keyboards"
|
118
|
+
end
|
119
|
+
|
120
|
+
def keyboard_path
|
121
|
+
"#{keyboards_path}/#{keyboard_name}"
|
122
|
+
end
|
123
|
+
|
124
|
+
def keymap_path
|
125
|
+
if handwired?
|
126
|
+
keyboard_path
|
127
|
+
else
|
128
|
+
"#{keyboard_path}/keymaps/#{@keymap}"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def handwired?
|
133
|
+
@keyboard =~ /handwired/
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
data/lib/git.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
module QMK
|
2
|
+
class Git
|
3
|
+
def initialize(repo_path)
|
4
|
+
@repo_path = repo_path
|
5
|
+
end
|
6
|
+
|
7
|
+
def path
|
8
|
+
@repo_path
|
9
|
+
end
|
10
|
+
|
11
|
+
def ensure_path_exists
|
12
|
+
`mkdir -p #{@repo_path}`
|
13
|
+
end
|
14
|
+
|
15
|
+
def clone(repo)
|
16
|
+
ensure_path_exists
|
17
|
+
`git clone #{repo} #{@repo_path}`
|
18
|
+
end
|
19
|
+
|
20
|
+
def fetch_origin
|
21
|
+
`git fetch origin master`
|
22
|
+
end
|
23
|
+
|
24
|
+
def checkout_latest_tag
|
25
|
+
`git checkout #{latest_tag}`
|
26
|
+
end
|
27
|
+
|
28
|
+
def latest_tag
|
29
|
+
`git describe --tags $(git rev-list --tags --max-count=1)`
|
30
|
+
end
|
31
|
+
|
32
|
+
def clean
|
33
|
+
`git checkout .`
|
34
|
+
`git clean -df`
|
35
|
+
end
|
36
|
+
|
37
|
+
def in_repo(&block)
|
38
|
+
Dir.chdir @repo_path, &block
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/makefile.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
3
|
+
class Makefile
|
4
|
+
def initialize(include_makefile)
|
5
|
+
@include_makefile = include_makefile
|
6
|
+
@file = Tempfile.new('makefile')
|
7
|
+
|
8
|
+
write
|
9
|
+
end
|
10
|
+
|
11
|
+
def write
|
12
|
+
@file.write contents
|
13
|
+
@file.close
|
14
|
+
end
|
15
|
+
|
16
|
+
def run(variable)
|
17
|
+
`make -f #{@file.path} -f #{@include_makefile} print-#{variable.upcase}`
|
18
|
+
end
|
19
|
+
|
20
|
+
def value(variable)
|
21
|
+
output = run variable
|
22
|
+
_, value = parse output
|
23
|
+
value
|
24
|
+
end
|
25
|
+
|
26
|
+
def parse(output)
|
27
|
+
match = output.match /^(\w+)=(.+)/
|
28
|
+
if match
|
29
|
+
[match[1], match[2]]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def contents
|
34
|
+
<<-contents
|
35
|
+
.PHONY: print-%
|
36
|
+
print-%:
|
37
|
+
\t@echo '$*=$($*)'
|
38
|
+
contents
|
39
|
+
end
|
40
|
+
end
|
data/lib/programmer.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'makefile'
|
2
|
+
|
3
|
+
FLASHERS = {
|
4
|
+
'dfu': ['lufa-dfu', 'qmk-dfu', 'atmel-dfu'],
|
5
|
+
'avrdude': ['caterina', 'usbasploader'],
|
6
|
+
'teensy': 'halfkay',
|
7
|
+
}
|
8
|
+
|
9
|
+
BOOTLOADERS = {
|
10
|
+
'halfkay': ['512', '1024'],
|
11
|
+
'atmel-dfu': '4096',
|
12
|
+
'usbasploader': '2048',
|
13
|
+
}
|
14
|
+
|
15
|
+
class Programmer
|
16
|
+
def initialize(keyboard_path)
|
17
|
+
@keyboard_path = keyboard_path
|
18
|
+
end
|
19
|
+
|
20
|
+
def bootloader
|
21
|
+
filename = "#{@keyboard_path}/rules.mk"
|
22
|
+
parse_bootloader_name(filename) if File.exists? filename
|
23
|
+
end
|
24
|
+
|
25
|
+
def flasher
|
26
|
+
bootloader and FLASHERS.each do |k, v|
|
27
|
+
break k if v.include? bootloader.downcase
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
def bootloader_from_size(size)
|
33
|
+
size and BOOTLOADERS.each do |k, v|
|
34
|
+
break k.to_s if v.include? size
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def parse_bootloader_name(filename)
|
39
|
+
make = Makefile.new(filename)
|
40
|
+
name = make.value 'BOOTLOADER'
|
41
|
+
return name if name
|
42
|
+
|
43
|
+
size = make.value 'BOOTLOADER_SIZE'
|
44
|
+
return bootloader_from_size(size) if size
|
45
|
+
|
46
|
+
opt_defs = make.value 'OPT_DEFS'
|
47
|
+
match = opt_defs.match /BOOTLOADER_SIZE=(\w+)/
|
48
|
+
return bootloader_from_size(match[1]) if match
|
49
|
+
end
|
50
|
+
end
|
data/qmk_cli.gemspec
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "qmk-cli"
|
3
|
+
s.version = "0.2.0"
|
4
|
+
s.date = "2018-02-27"
|
5
|
+
s.summary = "A cli wrapper for QMK Firmware"
|
6
|
+
s.authors = ["Nic Haynes"]
|
7
|
+
s.email = "nic@nicinabox.com"
|
8
|
+
s.files = `git ls-files -z`.split("\x0")
|
9
|
+
s.homepage = "https://github.com/nicinabox/qmk-cli"
|
10
|
+
s.license = "MIT"
|
11
|
+
|
12
|
+
s.executables << "qmk"
|
13
|
+
end
|
metadata
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: qmk-cli
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nic Haynes
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-02-27 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description:
|
14
|
+
email: nic@nicinabox.com
|
15
|
+
executables:
|
16
|
+
- qmk
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- ".gitignore"
|
21
|
+
- LICENSE.txt
|
22
|
+
- Makefile
|
23
|
+
- README.md
|
24
|
+
- bin/qmk
|
25
|
+
- lib/cli.rb
|
26
|
+
- lib/firmware.rb
|
27
|
+
- lib/git.rb
|
28
|
+
- lib/makefile.rb
|
29
|
+
- lib/programmer.rb
|
30
|
+
- qmk_cli.gemspec
|
31
|
+
homepage: https://github.com/nicinabox/qmk-cli
|
32
|
+
licenses:
|
33
|
+
- MIT
|
34
|
+
metadata: {}
|
35
|
+
post_install_message:
|
36
|
+
rdoc_options: []
|
37
|
+
require_paths:
|
38
|
+
- lib
|
39
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
requirements: []
|
50
|
+
rubyforge_project:
|
51
|
+
rubygems_version: 2.5.2
|
52
|
+
signing_key:
|
53
|
+
specification_version: 4
|
54
|
+
summary: A cli wrapper for QMK Firmware
|
55
|
+
test_files: []
|