aboutsummaryrefslogtreecommitdiff
path: root/lib/python/qmk
diff options
context:
space:
mode:
authorErovia <erovia@users.noreply.github.com>2019-10-28 09:23:06 +0100
committerskullydazed <skullydazed@users.noreply.github.com>2020-02-15 15:19:03 -0800
commit988bfffca2715df3f227c462533d350ecbeac6c0 (patch)
tree975be087ae9944b31516a44f193b595791d93ed2 /lib/python/qmk
parent8ff72d9517132fe82c6e2d1dc8fc56f7cab4b6a0 (diff)
downloadqmk_firmware-988bfffca2715df3f227c462533d350ecbeac6c0.tar.gz
qmk_firmware-988bfffca2715df3f227c462533d350ecbeac6c0.zip
Major rework, no regex/globbing, more walking
Instead of using regexes and globbing to find the rules.mk and keymap.c files, walk the directory tree to find them. Also, do away with the concept of revision.
Diffstat (limited to 'lib/python/qmk')
-rw-r--r--lib/python/qmk/keymap.py52
-rw-r--r--lib/python/qmk/makefile.py94
2 files changed, 67 insertions, 79 deletions
diff --git a/lib/python/qmk/keymap.py b/lib/python/qmk/keymap.py
index 1024f8fc6..113b885de 100644
--- a/lib/python/qmk/keymap.py
+++ b/lib/python/qmk/keymap.py
@@ -2,8 +2,6 @@
2""" 2"""
3import os 3import os
4from traceback import format_exc 4from traceback import format_exc
5import re
6import glob
7 5
8import qmk.path 6import qmk.path
9import qmk.makefile 7import qmk.makefile
@@ -100,27 +98,6 @@ def write(keyboard, keymap, layout, layers):
100 98
101 return keymap_file 99 return keymap_file
102 100
103def find_keymaps(base_path, revision = "", community = False):
104 """ Find the available keymaps for a keyboard and revision pair.
105
106 Args:
107 base_path: The base path of the keyboard.
108
109 revision: The keyboard's revision.
110
111 community: Set to True for the layouts under layouts/community.
112
113 Returns:
114 a set with the keymaps's names
115 """
116 path_wildcard = os.path.join(base_path, "**", "keymap.c")
117 if community:
118 path_regex = re.compile(r"^" + re.escape(base_path) + "(\S+)" + os.path.sep + "keymap\.c")
119 else:
120 path_regex = re.compile(r"^" + re.escape(base_path) + "(?:" + re.escape(revision) + os.path.sep + ")?keymaps" + os.path.sep + "(\S+)" + os.path.sep + "keymap\.c")
121 names = [path_regex.sub(lambda name: name.group(1), path) for path in glob.iglob(path_wildcard, recursive = True) if path_regex.search(path)]
122 return set(names)
123
124def list_keymaps(keyboard_name): 101def list_keymaps(keyboard_name):
125 """ List the available keymaps for a keyboard. 102 """ List the available keymaps for a keyboard.
126 103
@@ -130,25 +107,28 @@ def list_keymaps(keyboard_name):
130 Returns: 107 Returns:
131 a set with the names of the available keymaps 108 a set with the names of the available keymaps
132 """ 109 """
133 if os.path.sep in keyboard_name:
134 keyboard, revision = os.path.split(os.path.normpath(keyboard_name))
135 else:
136 keyboard = keyboard_name
137 revision = ""
138
139 # parse all the rules.mk files for the keyboard 110 # parse all the rules.mk files for the keyboard
140 rules_mk = qmk.makefile.get_rules_mk(keyboard, revision) 111 rules_mk = qmk.makefile.get_rules_mk(keyboard_name)
141 names = set() 112 names = set()
142 113
143 if rules_mk: 114 if rules_mk:
144 # get the keymaps from the keyboard's directory 115 # qmk_firmware/keyboards
145 kb_base_path = os.path.join(os.getcwd(), "keyboards", keyboard) + os.path.sep 116 keyboards_dir = os.path.join(os.getcwd(), "keyboards")
146 names = find_keymaps(kb_base_path, revision) 117 # path to the keyboard's directory
118 kb_path = os.path.join(keyboards_dir, keyboard_name)
119 # walk up the directory tree until keyboards_dir
120 # and collect all directories' name with keymap.c file in it
121 while kb_path != keyboards_dir:
122 keymaps_dir = os.path.join(kb_path, "keymaps")
123 if os.path.exists(keymaps_dir):
124 names = names.union([keymap for keymap in os.listdir(keymaps_dir) if os.path.isfile(os.path.join(keymaps_dir, keymap, "keymap.c"))])
125 kb_path = os.path.dirname(kb_path)
147 126
148 # if community layouts are supported, get them 127 # if community layouts are supported, get them
149 if "LAYOUTS" in rules_mk: 128 if "LAYOUTS" in rules_mk:
150 for layout in rules_mk["LAYOUTS"]["value"].split(): 129 for layout in rules_mk["LAYOUTS"].split():
151 cl_base_path = os.path.join(os.getcwd(), "layouts", "community", layout) + os.path.sep 130 cl_path = os.path.join(os.getcwd(), "layouts", "community", layout)
152 names = names.union(find_keymaps(cl_base_path, revision, community = True)) 131 if os.path.exists(cl_path):
132 names = names.union([keymap for keymap in os.listdir(cl_path) if os.path.isfile(os.path.join(cl_path, keymap, "keymap.c"))])
153 133
154 return sorted(names) 134 return sorted(names)
diff --git a/lib/python/qmk/makefile.py b/lib/python/qmk/makefile.py
index 6ed6b4b70..c53f12ac7 100644
--- a/lib/python/qmk/makefile.py
+++ b/lib/python/qmk/makefile.py
@@ -1,72 +1,80 @@
1""" Functions for working with Makefiles 1""" Functions for working with Makefiles
2""" 2"""
3import os 3import os
4import glob
5import re
6 4
7import qmk.path 5import qmk.path
8from qmk.errors import NoSuchKeyboardError 6from qmk.errors import NoSuchKeyboardError
9 7
10def parse_rules_mk(file_path): 8def parse_rules_mk_file(file, rules_mk=None):
11 """ Parse a rules.mk file 9 """Turn a rules.mk file into a dictionary.
12 10
13 Args: 11 Args:
14 file_path: path to the rules.mk file 12 file: path to the rules.mk file
13 rules_mk: already parsed rules.mk the new file should be merged with
15 14
16 Returns: 15 Returns:
17 a dictionary with the file's content 16 a dictionary with the file's content
18 """ 17 """
19 # regex to match lines uncommented lines and get the data 18 if not rules_mk:
20 # group(1) = option's name 19 rules_mk = {}
21 # group(2) = operator (eg.: '=', '+=')
22 # group(3) = value(s)
23 rules_mk_regex = re.compile(r"^\s*(\w+)\s*([\?\:\+\-]?=)\s*(\S.*?)(?=\s*(\#|$))")
24 parsed_file = dict()
25 mk_content = qmk.path.file_lines(file_path)
26 for line in mk_content:
27 found = rules_mk_regex.search(line)
28 if found:
29 parsed_file[found.group(1)] = dict(operator = found.group(2), value = found.group(3))
30 return parsed_file
31 20
32def merge_rules_mk_files(base, revision): 21 if os.path.exists(file):
33 """ Merge a keyboard revision's rules.mk file with 22 rules_mk_lines = qmk.path.file_lines(file)
34 the 'base' rules.mk file
35 23
36 Args: 24 for line in rules_mk_lines:
37 base: the base rules.mk file's content as dictionary 25 # Filter out comments
38 revision: the revision's rules.mk file's content as dictionary 26 if line.strip().startswith("#"):
27 continue
39 28
40 Returns: 29 # Strip in-line comments
41 a dictionary with the merged content 30 if '#' in line:
42 """ 31 line = line[:line.index('#')].strip()
43 return {**base, **revision} 32
33 if '=' in line:
34 # Append
35 if '+=' in line:
36 key, value = line.split('+=', 1)
37 if key.strip() not in rules_mk:
38 rules_mk[key.strip()] = value.strip()
39 else:
40 rules_mk[key.strip()] += ' ' + value.strip()
41 # Set if absent
42 elif "?=" in line:
43 key, value = line.split('?=', 1)
44 if key.strip() not in rules_mk:
45 rules_mk[key.strip()] = value.strip()
46 else:
47 if ":=" in line:
48 line.replace(":","")
49 key, value = line.split('=', 1)
50 rules_mk[key.strip()] = value.strip()
44 51
45def get_rules_mk(keyboard, revision = ""): 52 return rules_mk
53
54def get_rules_mk(keyboard):
46 """ Get a rules.mk for a keyboard 55 """ Get a rules.mk for a keyboard
47 56
48 Args: 57 Args:
49 keyboard: name of the keyboard 58 keyboard: name of the keyboard
50 revision: revision of the keyboard 59
60 Raises:
61 NoSuchKeyboardError: when the keyboard does not exists
51 62
52 Returns: 63 Returns:
53 a dictionary with the content of the rules.mk file 64 a dictionary with the content of the rules.mk file
54 """ 65 """
55 base_path = os.path.join(os.getcwd(), "keyboards", keyboard) + os.path.sep 66 # Start with qmk_firmware/keyboards
56 rules_mk = dict() 67 kb_path = os.path.join(os.getcwd(), "keyboards")
57 if os.path.exists(base_path + os.path.sep + revision): 68 # walk down the directory tree
58 rules_mk_path_wildcard = os.path.join(base_path, "**", "rules.mk") 69 # and collect all rules.mk files
59 rules_mk_regex = re.compile(r"^" + base_path + "(?:" + revision + os.path.sep + ")?rules.mk") 70 if os.path.exists(os.path.join(kb_path, keyboard)):
60 paths = [path for path in glob.iglob(rules_mk_path_wildcard, recursive = True) if rules_mk_regex.search(path)] 71 rules_mk = dict()
61 for file_path in paths: 72 for directory in keyboard.split(os.path.sep):
62 rules_mk[revision if revision in file_path else "base"] = parse_rules_mk(file_path) 73 kb_path = os.path.join(kb_path, directory)
74 rules_mk_path = os.path.join(kb_path, "rules.mk")
75 if os.path.exists(rules_mk_path):
76 rules_mk = parse_rules_mk_file(rules_mk_path, rules_mk)
63 else: 77 else:
64 raise NoSuchKeyboardError("The requested keyboard and/or revision does not exist.") 78 raise NoSuchKeyboardError("The requested keyboard and/or revision does not exist.")
65 79
66 # if the base or the revision directory does not contain a rules.mk
67 if len(rules_mk) == 1:
68 rules_mk = rules_mk[revision]
69 # if both directories contain rules.mk files
70 elif len(rules_mk) == 2:
71 rules_mk = merge_rules_mk_files(rules_mk["base"], rules_mk[revision])
72 return rules_mk 80 return rules_mk