qmk-cli 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 915b0f8e03509b8c776660b1a9f335441707e1b4
4
- data.tar.gz: f4541bde947171682ffa95db7b2d92084d458a5a
3
+ metadata.gz: a5e5a93c0d7e2f5455b441ab6856220e0d433695
4
+ data.tar.gz: 1580ecbbbb8f93abce2c79b1332fe8ed130e1b7e
5
5
  SHA512:
6
- metadata.gz: 9aa6510c0fbad4586d251fab54ae968ee74542c83c596b87cf32e9480d54e3b68a44a017531a878afa19e0efa866b4ce6a57f8308ae679b2e86bf0dbb0229567
7
- data.tar.gz: 1d0050383637fc83085fd80fd91a7e9f32049695df559abc0e14d5bed3e7a0d42dcc2f3269e3dc78891dc712e07436b8ae33b63e56917b4e380ae007fef31e35
6
+ metadata.gz: 1fee8427995c73f2abec6c245cd6749de414a23c8823360e7fb07f0d26976169a24bfd6a002265cbb1538307bd60e9359125c0dfb77b5a7b41d14e95af38371b
7
+ data.tar.gz: 2830b17e5ad2e3fc2567b1bec4c5fffe85b2486cf8dd0e2dcd3ef6d2a0007622d00497cf8838051e7fc660912a73121c503e1ab70f1fd56b20134408704628e8
data/Makefile CHANGED
@@ -6,6 +6,10 @@ build: clean
6
6
  install:
7
7
  @gem install *.gem
8
8
 
9
+ .PHONY: uninstall
10
+ uninstall:
11
+ @gem install qmk-cli
12
+
9
13
  .PHONY: publish
10
14
  publish: build
11
15
  @gem push *.gem
@@ -13,3 +17,7 @@ publish: build
13
17
  .PHONY: clean
14
18
  clean:
15
19
  @rm -f *.gem
20
+
21
+ .PHONY: test
22
+ test:
23
+ @ruby -Ilib -e 'ARGV.each { |f| require f }' ./test/test*.rb
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # qmk-cli
2
2
 
3
- A thin wrapper around QMK's `make` to make common tasks easier.
3
+ A wrapper around QMK's `make` to make common tasks easier. **This is a proof of concept and should be considered unstable.**
4
4
 
5
5
  ## Why use this?
6
6
 
@@ -31,6 +31,27 @@ A thin wrapper around QMK's `make` to make common tasks easier.
31
31
 
32
32
  View [nicinabox/keymaps](https://github.com/nicinabox/keymaps) for a complete example on how standalone keymaps should be organized.
33
33
 
34
+ ### .qmk
35
+
36
+ This file tells qmk-cli that the directory contains keymaps only, with the directory named after the keyboard it supports. This works well for common cases, but there are a number of cases in which you may want to organize differently and supply a configuration.
37
+
38
+ Here is an example configuration. All fields are optional.
39
+
40
+ ```yaml
41
+ # Specify your keymap name. Uses `whoami` by default.
42
+ keymap: nic
43
+
44
+ # Map local keyboard directories to qmk keyboard directories if needed.
45
+ keyboards:
46
+ bananasplit_blocked: bananasplit
47
+ clueboard: clueboard/66
48
+ nh60: handwired/nh60
49
+
50
+ keymaps:
51
+ # Override keymap names that differ from your keymap setting above.
52
+ nh60: default
53
+ ```
54
+
34
55
  ## Platforms
35
56
 
36
57
  - [x] macOS
data/lib/cli.rb CHANGED
@@ -21,9 +21,9 @@ module QMK
21
21
  def initialize(args)
22
22
  @options = parser(args)
23
23
  command, keyboard = args
24
- @firmware = Firmware.new(keyboard, @options[:keymap], keymaps_only?)
24
+ @firmware = Firmware.new(keyboard, @options[:keymap], config)
25
25
 
26
- self.send(parse_command(command || 'help'))
26
+ command and self.send(parse_command(command))
27
27
  end
28
28
 
29
29
  def setup
@@ -51,10 +51,6 @@ module QMK
51
51
  puts @firmware.keyboards
52
52
  end
53
53
 
54
- def help
55
- puts @options[:help]
56
- end
57
-
58
54
  private
59
55
  def parser(args)
60
56
  options = {}
@@ -62,13 +58,20 @@ module QMK
62
58
  OptionParser.new do |parser|
63
59
  parser.banner = USAGE
64
60
 
65
- options[:keymap] = keymaps_only? ? `whoami`.strip : nil
61
+ options[:keymap] = config[:keymap]
66
62
  parser.on("-k", "--keymap KEYMAP", "Your keymap name (default: #{options[:keymap]})") do |v|
67
63
  options[:keymap] = v
68
64
  end
69
65
 
66
+ parser.on("-v", "--version", "Show qmk-cli version") do
67
+ spec = Gem::Specification::load("qmk_cli.gemspec")
68
+ puts spec.version
69
+ end
70
+
70
71
  options[:help] = parser
71
- parser.on("-h", "--help", "Show this help message")
72
+ parser.on("-h", "--help", "Show this help message") do
73
+ puts parser
74
+ end
72
75
  end.parse!
73
76
 
74
77
  options
@@ -78,12 +81,32 @@ module QMK
78
81
  cmd.gsub(/\-/, '_').downcase
79
82
  end
80
83
 
81
- def keymaps_only?
84
+ def config
85
+ defaults = {
86
+ standalone_keymaps: standalone_keymaps?,
87
+ keyboards: [],
88
+ keymaps: [],
89
+ keymap: `whoami`.strip
90
+ }
91
+
92
+ if standalone_keymaps?
93
+ c = YAML.load_file('.qmk')
94
+ .each_with_object({}) do |(k,v), memo|
95
+ memo[k.to_sym] = v
96
+ end
97
+
98
+ return defaults.merge c
99
+ end
100
+
101
+ return defaults
102
+ end
103
+
104
+ def standalone_keymaps?
82
105
  File.exists? '.qmk'
83
106
  end
84
107
 
85
108
  def method_missing(*args)
86
- help
109
+ puts @options[:help]
87
110
  end
88
111
  end
89
112
  end
@@ -1,5 +1,6 @@
1
1
  require 'fileutils'
2
2
  require 'open3'
3
+ require 'yaml'
3
4
  require 'programmer'
4
5
  require 'git'
5
6
 
@@ -7,15 +8,18 @@ LIB_PATH = "/usr/local/lib/qmk_firmware"
7
8
 
8
9
  module QMK
9
10
  class Firmware
10
- def initialize(keyboard, keymap, keymaps_only)
11
+ def initialize(keyboard, keymap, config)
12
+ @config = config
13
+
11
14
  @keyboard = keyboard
12
- @keymap = keymap
15
+ @qmk_keyboard = get_keyboard keyboard
16
+ @keymap = get_keymap(keyboard) || keymap
17
+
13
18
  @repo = Git.new(LIB_PATH)
14
- @keymaps_only = keymaps_only
15
19
  end
16
20
 
17
21
  def make(target = nil)
18
- if @keymaps_only
22
+ if @config[:standalone_keymaps]
19
23
  prepare_firmware
20
24
  end
21
25
 
@@ -53,7 +57,7 @@ module QMK
53
57
  end
54
58
 
55
59
  def keyboards
56
- if @keymaps_only
60
+ if @config[:standalone_keymaps]
57
61
  standalone_keyboards
58
62
  else
59
63
  qmk_keyboards @keymap
@@ -66,7 +70,7 @@ module QMK
66
70
  .sort
67
71
  end
68
72
 
69
- def qmk_keyboards(keymap=nil)
73
+ def qmk_keyboards(keymap = nil)
70
74
  Dir["#{keyboards_path}/**/#{keymap}/keymap.c"]
71
75
  .map {|path|
72
76
  File.dirname(path)
@@ -79,15 +83,19 @@ module QMK
79
83
  .sort
80
84
  end
81
85
 
82
- def keyboard_name
83
- @keyboard.gsub(/\/rev.*/, '')
84
- end
85
-
86
86
  def programmer
87
87
  Programmer.new(keyboard_path).flasher
88
88
  end
89
89
 
90
90
  private
91
+ def get_keyboard(local_keyboard)
92
+ @config[:keyboards][local_keyboard] || local_keyboard
93
+ end
94
+
95
+ def get_keymap(keyboard)
96
+ @config[:keymaps][keyboard]
97
+ end
98
+
91
99
  def in_repo(&block)
92
100
  @repo.in_repo &block
93
101
  end
@@ -97,12 +105,16 @@ module QMK
97
105
  while line = stdout.gets
98
106
  puts line
99
107
  end
108
+
109
+ while line = stderr.gets
110
+ puts line
111
+ end
100
112
  end
101
113
  end
102
114
 
103
115
  def prepare_firmware
104
- keyboard_path = File.expand_path "./#{keyboard_name}"
105
- files = Dir.glob "#{keyboard_path}/*"
116
+ local_keyboard_path = File.expand_path "./#{@keyboard}"
117
+ files = Dir.glob "#{local_keyboard_path}/*"
106
118
 
107
119
  FileUtils.mkdir_p keymap_path
108
120
  FileUtils.cp_r files, keymap_path
@@ -110,7 +122,8 @@ module QMK
110
122
 
111
123
  def make_target(target = nil)
112
124
  return target unless @keyboard
113
- [@keyboard, @keymap, target].compact.join(':')
125
+
126
+ [@qmk_keyboard, @keymap, target].compact.join(':')
114
127
  end
115
128
 
116
129
  def keyboards_path
@@ -118,19 +131,23 @@ module QMK
118
131
  end
119
132
 
120
133
  def keyboard_path
121
- "#{keyboards_path}/#{keyboard_name}"
134
+ "#{keyboards_path}/#{@qmk_keyboard}"
122
135
  end
123
136
 
124
137
  def keymap_path
125
- if handwired?
126
- keyboard_path
127
- else
128
- "#{keyboard_path}/keymaps/#{@keymap}"
138
+ if /handwired/ =~ @qmk_keyboard
139
+ return handwired_keymap_path
129
140
  end
141
+
142
+ standard_keymap_path
143
+ end
144
+
145
+ def standard_keymap_path
146
+ "#{keyboard_path}/keymaps/#{@keymap}"
130
147
  end
131
148
 
132
- def handwired?
133
- @keyboard =~ /handwired/
149
+ def handwired_keymap_path
150
+ keyboard_path
134
151
  end
135
152
  end
136
153
  end
@@ -3,24 +3,25 @@ require 'tempfile'
3
3
  class Makefile
4
4
  def initialize(include_makefile)
5
5
  @include_makefile = include_makefile
6
- @file = Tempfile.new('makefile')
6
+ @file = write
7
+ end
7
8
 
8
- write
9
+ def get(variable)
10
+ output = run variable
11
+ _, value = parse output
12
+ value
9
13
  end
10
14
 
15
+ private
11
16
  def write
12
- @file.write contents
13
- @file.close
17
+ file = Tempfile.new('makefile')
18
+ file.write contents
19
+ file.close
20
+ file
14
21
  end
15
22
 
16
23
  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
+ `make -f #{@file.path} -f #{@include_makefile} print-#{variable.to_s.upcase}`
24
25
  end
25
26
 
26
27
  def parse(output)
@@ -24,26 +24,26 @@ class Programmer
24
24
 
25
25
  def flasher
26
26
  bootloader and FLASHERS.each do |k, v|
27
- break k if v.include? bootloader.downcase
27
+ break k.to_s if v.include? bootloader.downcase
28
28
  end
29
29
  end
30
30
 
31
31
  private
32
32
  def bootloader_from_size(size)
33
33
  size and BOOTLOADERS.each do |k, v|
34
- break k.to_s if v.include? size
34
+ break k.to_s if v.include? size.to_s
35
35
  end
36
36
  end
37
37
 
38
38
  def parse_bootloader_name(filename)
39
39
  make = Makefile.new(filename)
40
- name = make.value 'BOOTLOADER'
40
+ name = make.get :bootloader
41
41
  return name if name
42
42
 
43
- size = make.value 'BOOTLOADER_SIZE'
43
+ size = make.get :bootloader_size
44
44
  return bootloader_from_size(size) if size
45
45
 
46
- opt_defs = make.value 'OPT_DEFS'
46
+ opt_defs = make.get :opt_defs
47
47
  match = opt_defs.match /BOOTLOADER_SIZE=(\w+)/
48
48
  return bootloader_from_size(match[1]) if match
49
49
  end
@@ -1,13 +1,13 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "qmk-cli"
3
- s.version = "0.2.0"
3
+ s.version = "0.3.0"
4
4
  s.date = "2018-02-27"
5
5
  s.summary = "A cli wrapper for QMK Firmware"
6
6
  s.authors = ["Nic Haynes"]
7
7
  s.email = "nic@nicinabox.com"
8
8
  s.files = `git ls-files -z`.split("\x0")
9
9
  s.homepage = "https://github.com/nicinabox/qmk-cli"
10
- s.license = "MIT"
10
+ s.license = "ISC"
11
11
 
12
12
  s.executables << "qmk"
13
13
  end
@@ -0,0 +1,78 @@
1
+
2
+
3
+ ifdef TEENSY2
4
+ OPT_DEFS += -DATREUS_TEENSY2
5
+ ATREUS_UPLOAD_COMMAND = teensy_loader_cli -w -mmcu=$(MCU) $(TARGET).hex
6
+ else
7
+ OPT_DEFS += -DATREUS_ASTAR
8
+ ATREUS_UPLOAD_COMMAND = while [ ! -r $(USB) ]; do sleep 1; done; \
9
+ avrdude -p $(MCU) -c avr109 -U flash:w:$(TARGET).hex -P $(USB)
10
+ endif
11
+
12
+ # MCU name
13
+ #MCU = at90usb1287
14
+ MCU = atmega32u4
15
+
16
+ # Processor frequency.
17
+ # This will define a symbol, F_CPU, in all source code files equal to the
18
+ # processor frequency in Hz. You can then use this symbol in your source code to
19
+ # calculate timings. Do NOT tack on a 'UL' at the end, this will be done
20
+ # automatically to create a 32-bit value in your source code.
21
+ #
22
+ # This will be an integer division of F_USB below, as it is sourced by
23
+ # F_USB after it has run through any CPU prescalers. Note that this value
24
+ # does not *change* the processor frequency - it should merely be updated to
25
+ # reflect the processor speed set externally so that the code can use accurate
26
+ # software delays.
27
+ F_CPU = 16000000
28
+
29
+ #
30
+ # LUFA specific
31
+ #
32
+ # Target architecture (see library "Board Types" documentation).
33
+ ARCH = AVR8
34
+
35
+ # Input clock frequency.
36
+ # This will define a symbol, F_USB, in all source code files equal to the
37
+ # input clock frequency (before any prescaling is performed) in Hz. This value may
38
+ # differ from F_CPU if prescaling is used on the latter, and is required as the
39
+ # raw input clock is fed directly to the PLL sections of the AVR for high speed
40
+ # clock generation for the USB and other AVR subsections. Do NOT tack on a 'UL'
41
+ # at the end, this will be done automatically to create a 32-bit value in your
42
+ # source code.
43
+ #
44
+ # If no clock division is performed on the input clock inside the AVR (via the
45
+ # CPU clock adjust registers or the clock division fuses), this will be equal to F_CPU.
46
+ F_USB = $(F_CPU)
47
+
48
+ # Bootloader
49
+ # This definition is optional, and if your keyboard supports multiple bootloaders of
50
+ # different sizes, comment this out, and the correct address will be loaded
51
+ # automatically (+60). See bootloader.mk for all options.
52
+ ifdef TEENSY2
53
+ BOOTLOADER = halfkay
54
+ else
55
+ BOOTLOADER = caterina
56
+ endif
57
+
58
+ # Interrupt driven control endpoint task(+60)
59
+ OPT_DEFS += -DINTERRUPT_CONTROL_ENDPOINT
60
+
61
+
62
+ # Build Options
63
+ # comment out to disable the options.
64
+ #
65
+ #BOOTMAGIC_ENABLE = yes # Virtual DIP switch configuration(+1000)
66
+ MOUSEKEY_ENABLE = yes # Mouse keys(+4700)
67
+ EXTRAKEY_ENABLE = yes # Audio control and System control(+450)
68
+ CONSOLE_ENABLE = yes # Console for debug(+400)
69
+ COMMAND_ENABLE = yes # Commands for debug and configuration
70
+ # Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
71
+ # SLEEP_LED_ENABLE = yes # Breathing sleep LED during USB suspend
72
+ NKRO_ENABLE = yes # USB Nkey Rollover - not yet supported in LUFA
73
+ # BACKLIGHT_ENABLE = yes # Enable keyboard backlight functionality
74
+ # MIDI_ENABLE = YES # MIDI controls
75
+ UNICODE_ENABLE = YES # Unicode
76
+ # BLUETOOTH_ENABLE = yes # Enable Bluetooth with the Adafruit EZ-Key HID
77
+
78
+ USB = /dev/cu.usbmodem1411
@@ -0,0 +1,23 @@
1
+ require 'minitest/autorun'
2
+ require 'makefile'
3
+
4
+ class MakefileTest < Minitest::Test
5
+ def setup
6
+ @make = Makefile.new 'test/rules.mk'
7
+ end
8
+
9
+ def test_basic_value
10
+ value = @make.get :mcu
11
+ assert_equal value, 'atmega32u4'
12
+ end
13
+
14
+ def test_complex_value
15
+ value = @make.get :opt_defs
16
+ assert_equal value, '-DATREUS_ASTAR -DINTERRUPT_CONTROL_ENDPOINT'
17
+ end
18
+
19
+ def test_logical_value
20
+ value = @make.get :bootloader
21
+ assert_equal value, 'caterina'
22
+ end
23
+ end
@@ -0,0 +1,36 @@
1
+ require 'minitest/autorun'
2
+ require 'programmer'
3
+
4
+ class ProgrammerTest < Minitest::Test
5
+ def setup
6
+ @protected_methods = Programmer.private_instance_methods
7
+ Programmer.send(:public, *@protected_methods)
8
+
9
+ @prog = Programmer.new 'test'
10
+ end
11
+
12
+ def test_bootloader
13
+ assert_equal @prog.bootloader, 'caterina'
14
+ end
15
+
16
+ def test_bootloader_not_found
17
+ prog = Programmer.new ''
18
+ assert_nil prog.bootloader
19
+ end
20
+
21
+ def test_flasher
22
+ assert_equal @prog.flasher, 'avrdude'
23
+ end
24
+
25
+ def test_flasher_not_found
26
+ prog = Programmer.new ''
27
+ assert_nil prog.flasher
28
+ end
29
+
30
+ def test_bootloader_mapping
31
+ assert_equal @prog.bootloader_from_size(512), 'halfkay'
32
+ assert_equal @prog.bootloader_from_size(1024), 'halfkay'
33
+ assert_equal @prog.bootloader_from_size(2048), 'usbasploader'
34
+ assert_equal @prog.bootloader_from_size(4096), 'atmel-dfu'
35
+ end
36
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qmk-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nic Haynes
@@ -28,9 +28,12 @@ files:
28
28
  - lib/makefile.rb
29
29
  - lib/programmer.rb
30
30
  - qmk_cli.gemspec
31
+ - test/rules.mk
32
+ - test/test_makefile.rb
33
+ - test/test_programmer.rb
31
34
  homepage: https://github.com/nicinabox/qmk-cli
32
35
  licenses:
33
- - MIT
36
+ - ISC
34
37
  metadata: {}
35
38
  post_install_message:
36
39
  rdoc_options: []