using data/mappings for processing

This commit is contained in:
Jack Humbert 2023-04-07 16:25:51 -04:00
parent 5dd8efc7c0
commit 92005dee35
8 changed files with 226 additions and 54 deletions

View File

@ -1,5 +1,7 @@
cmake_minimum_required(VERSION 3.20)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
include(UpdateSubmodule)
@ -34,9 +36,9 @@ else()
endif()
resolve_keyboard(${QMK_KEYBOARD_FOLDER} QMK_KEYBOARD_FOLDER_ABS)
process_keyboard()
cmake_path(IS_PREFIX QMK_KEYBOARDS_FOLDER "${QMK_KEYBOARD_FOLDER_ABS}" IS_KEYBOARDS_FOLDER)
resolve_config_h(${QMK_KEYBOARD_FOLDER_ABS} QMK_KEYBOARD_CONFIG_H)
process_keyboard()
resolve_keyboard_h(${QMK_KEYBOARD_FOLDER_ABS} QMK_KEYBOARD_H)
resolve_keymap_c(${QMK_KEYBOARD_FOLDER_ABS} ${QMK_KEYMAP_FOLDER} QMK_KEYMAP_C)
@ -50,12 +52,6 @@ else()
HOMEPAGE_URL ${URL}
VERSION ${DEVICE_VER})
string(REGEX MATCH "^([0-9]+)\\.([0-9]+)\\.([0-9]+)" VERSION_MATCH ${DEVICE_VER})
set(VERSION_MAJOR ${CMAKE_MATCH_1})
set(VERSION_MINOR ${CMAKE_MATCH_2})
set(VERSION_PATCH ${CMAKE_MATCH_3})
math(EXPR VERSION_C_EVAL "${VERSION_MAJOR} * 10000 + ${VERSION_MINOR} * 100 + ${VERSION_PATCH}" OUTPUT_FORMAT HEXADECIMAL)
# add_compile_options(
# -include ${QMK_KEYBOARD_CONFIG_H}
# )
@ -65,11 +61,6 @@ else()
KEYMAP_C="${QMK_KEYMAP_C}"
MATRIX_ROWS=8
MATRIX_COLS=6
VENDOR_ID=${VENDOR_ID}
PRODUCT_ID=${PRODUCT_ID}
DEVICE_VER=${VERSION_C_EVAL}
MANUFACTURER="${MANUFACTURER}"
PRODUCT="${KEYBOARD_NAME}"
)
if(DEFINED DIODE_DIRECTION)

View File

@ -72,7 +72,8 @@ find_file(STARTUP_MK startup_${MCU_STARTUP}.mk
)
get_filename_component(STARTUP_DIR ${STARTUP_MK} DIRECTORY)
ParseMakefile(${STARTUP_MK})
target_sources(qmk PUBLIC ${STARTUPSRC} ${STARTUPASM})
target_sources(qmk PUBLIC ${STARTUPSRC})
target_sources(qmk PUBLIC ${STARTUPASM})
target_include_directories(qmk PUBLIC ${STARTUPINC})
# board paths - we should just standardize these

64
cmake/ParseHeader.cmake Normal file
View File

@ -0,0 +1,64 @@
# Simple CMake utility to read variables from MK files
# - Gets contents from given file (name or path)
# - Parses the assignment statements
# - Makes the same assignments in the PARENT_SCOPE
if(POLICY CMP0007)
cmake_policy(SET CMP0007 NEW)
endif()
function(ParseHeader HeaderFile Prefix)
_ParseHeader(${HeaderFile} ${Prefix})
endfunction()
macro(_ParseHeader HeaderFile Prefix)
message(CHECK_START "Parsing Header")
list(APPEND CMAKE_MESSAGE_INDENT " ")
message(STATUS "Reading \"${HeaderFile}\"")
file(READ "${HeaderFile}" FileContents)
string(REGEX REPLACE "/\\*.*\\*/" "" FileContents ${FileContents})
# replace the \ newlines with spaces
string(REGEX REPLACE "\\\\\r?\n *" " " FileContents ${FileContents})
# turn each line into an item in a list
string(REGEX REPLACE "\r?\n" ";" FileLines ${FileContents})
list(REMOVE_ITEM FileLines "")
foreach(line ${FileLines})
# remove comments from the ends of each line
string(REGEX REPLACE "//.*" "" line ${line})
# remove now-empty lines
if("${line}" STREQUAL "")
continue()
endif()
# try to process includes, if the file exists
if(line MATCHES "^#include \"(.+)\"")
set(INCLUDED_HEADER ${CMAKE_MATCH_1})
if(EXISTS ${INCLUDED_HEADER})
_ParseHeader("${INCLUDED_HEADER}" ${Prefix})
else()
message(STATUS "Could not read ${INCLUDED_HEADER}")
endif()
continue()
endif()
# array
if(line MATCHES "#define ([A-Za-z0-9_]+) {(.*)}")
set(VARIABLE_NAME ${CMAKE_MATCH_1})
set(VARIABLE_VALUE ${CMAKE_MATCH_2})
set(${Prefix}${VARIABLE_NAME} ${VARIABLE_VALUE})
endif()
# regular variable
if(line MATCHES "#define ([A-Za-z0-9_]+) (.*)")
set(VARIABLE_NAME ${CMAKE_MATCH_1})
set(VARIABLE_VALUE ${CMAKE_MATCH_2})
set(${Prefix}${VARIABLE_NAME} ${VARIABLE_VALUE})
endif()
endforeach()
list(POP_BACK CMAKE_MESSAGE_INDENT)
message(CHECK_PASS "Complete")
endmacro()

View File

@ -8,65 +8,91 @@ if(POLICY CMP0007)
endif()
function(ParseMakefile MKFile)
_ParseMakefile(${MKFile})
_ParseMakefile(${MKFile} ${ARGN})
endfunction()
macro(_ParseMakefile MKFile)
message(VERBOSE "Parsing ${MKFile}")
message(CHECK_START "Parsing Makefile")
list(APPEND CMAKE_MESSAGE_INDENT " ")
message(STATUS "Reading \"${MKFile}\"")
file(READ "${MKFile}" FileContents)
# replace the \ newlines with spaces
string(REGEX REPLACE "\\\\\r?\n *" " " FileContents ${FileContents})
string(REGEX REPLACE "(\r?\n)+" ";" FileLines ${FileContents})
# turn each line into an item in a list
string(REGEX REPLACE "\r?\n" ";" FileLines ${FileContents})
list(REMOVE_ITEM FileLines "")
foreach(line ${FileLines})
# if(line MATCHES "\\$\\((.+)\\)")
# set(MAKE_VARIABLE "${CMAKE_MATCH_1}")
# string(REPLACE "$(${MAKE_VARIABLE})" "${${MAKE_VARIABLE}}" line ${line})
# endif()
# message(STATUS "From: ${line}")
# remove comments from the ends of each line
string(REGEX REPLACE "#.*" "" line ${line})
# remove now-empty lines
if("${line}" STREQUAL "")
continue()
endif()
string(REGEX REPLACE "\\$\\(([^\\(\\)]+)\\)" "${CHIBIOS}" line ${line})
# try to process includes, if the file exists
if(line MATCHES "^-?include (.+)$")
set(MAKE_CHILD ${CMAKE_MATCH_1})
if(EXISTS ${MAKE_CHILD})
_ParseMakefile("${MAKE_CHILD}" ${ARGN})
else()
message(STATUS "Could not read ${MAKE_CHILD}")
endif()
continue()
endif()
# message(STATUS "To : ${line}")
# if(line MATCHES "^include (.+)$")
# set(MAKE_CHILD ${CMAKE_MATCH_1})
# message(STATUS "Reading ${MAKE_CHILD}")
# _ParseMakefile("${MAKE_CHILD}")
# else()
# turn the assignment into a list with the first item being the variable name
string(REPLACE "=" ";" line_split ${line})
list(LENGTH line_split count)
if(count LESS 2)
message(VERBOSE "Skipping ${line}")
message(STATUS "Skipping ${line}")
continue()
endif()
list(GET line_split -1 value)
string(STRIP ${value} value)
separate_arguments(value)
# separate_arguments(value)
# string(REPLACE " " ";" value ${value})
list(REMOVE_AT line_split -1)
foreach(var_name ${line_split})
string(STRIP ${var_name} var_name)
# replace $(?) with the variable ? from cmake
if(value MATCHES "\\$\\(([^\\(\\)]+)\\)")
set(MAKE_VARIABLE "${CMAKE_MATCH_1}")
string(REPLACE "$(${MAKE_VARIABLE})" "${${MAKE_VARIABLE}}" value ${value})
endif()
# look for +, assuming it used to be +=
if(${var_name} MATCHES "([^ \\+]+) *\\+")
message(VERBOSE "Appending '${CMAKE_MATCH_1}' with '${value}'")
set(${CMAKE_MATCH_1} ${${CMAKE_MATCH_1}} ${value} PARENT_SCOPE)
message(STATUS "Appending \"${CMAKE_MATCH_1}\" with \"${value}\"")
# read parent variable in local & append
set(LOCAL_${CMAKE_MATCH_1} ${CMAKE_MATCH_1})
# APPEND accepts spaces between values
list(APPEND LOCAL_${CMAKE_MATCH_1} ${value})
set(${CMAKE_MATCH_1} ${LOCAL_${CMAKE_MATCH_1}})
set(${CMAKE_MATCH_1} ${LOCAL_${CMAKE_MATCH_1}} PARENT_SCOPE)
else()
# set needs ; between elements to be considered a list
string(REGEX REPLACE " +" ";" value ${value})
# try to find variable in cache and FORCE wtih INTERNAL if it exists
if(DEFINED CACHE${${var_name}})
message(VERBOSE "Caching '${var_name}' to '${value}'")
message(STATUS "Caching \"${var_name}\" to \"${value}\"")
# set locally so replacement still work
set(${var_name} ${value})
set(${var_name} ${value} CACHE INTERNAL "")
else()
message(VERBOSE "Setting '${var_name}' to '${value}'")
message(STATUS "Setting \"${var_name}\" to \"${value}\"")
set(${var_name} ${value})
set(${var_name} ${value} PARENT_SCOPE)
endif()
endif()
endforeach()
# endif()
endforeach()
list(POP_BACK CMAKE_MESSAGE_INDENT)
message(CHECK_PASS "Complete")
endmacro()

View File

@ -1,17 +1,78 @@
include(ParseMakefile)
include(Utils)
macro(process_keyboard)
message(CHECK_START "Processing keyboard")
list(APPEND CMAKE_MESSAGE_INDENT " ")
validate_json(${QMK_KEYBOARD_FOLDER_ABS}/info.json keyboard QMK_KEYBOARD_INFO_JSON_STRING)
# process rules from info.json
file(READ ${CMAKE_SOURCE_DIR}/data/mappings/info_rules.hjson JSON_STRING)
string(JSON MAPPING_LENGTH LENGTH ${JSON_STRING})
math(EXPR MAX "${MAPPING_LENGTH} - 1")
foreach(IDX RANGE ${MAX})
string(JSON RULE_KEY MEMBER ${JSON_STRING} ${IDX})
# string(JSON INFO_KEY GET ${JSON_STRING} ${RULE_KEY} info_key)
json_get_with_default(INFO_KEY ${JSON_STRING} _ ${RULE_KEY} info_key)
string(REPLACE "." " " INFO_KEYS ${INFO_KEY})
string(JSON RULE_VALUE ERROR_VARIABLE RULE_KEY_NOT_FOUND GET ${QMK_KEYBOARD_INFO_JSON_STRING} ${INFO_KEYS})
if(${RULE_KEY_NOT_FOUND} STREQUAL "NOTFOUND")
json_get_with_default(VALUE_TYPE ${JSON_STRING} raw ${RULE_KEY} value_type)
if(${VALUE_TYPE} STREQUAL "list")
string(JSON NUM_VALUES LENGTH ${RULE_VALUE})
math(EXPR MAX "${NUM_VALUES} - 1")
foreach(IDX RANGE ${MAX})
string(JSON VALUE GET ${RULE_VALUE} ${IDX})
list(APPEND ${RULE_KEY} ${VALUE})
endforeach()
message(STATUS "Found rule '${INFO_KEY}': '${${RULE_KEY}}' assigned to '${RULE_KEY}'")
else()
set(${RULE_KEY} ${RULE_VALUE})
message(STATUS "Found rule '${INFO_KEY}': '${RULE_VALUE}' assigned to '${RULE_KEY}'")
endif()
endif()
endforeach()
# process definitions from info.json
file(READ ${CMAKE_SOURCE_DIR}/data/mappings/info_config.hjson JSON_STRING)
string(JSON MAPPING_LENGTH LENGTH ${JSON_STRING})
math(EXPR MAX "${MAPPING_LENGTH} - 1")
foreach(IDX RANGE ${MAX})
string(JSON CONFIG_KEY MEMBER ${JSON_STRING} ${IDX})
json_get_with_default(INFO_KEY ${JSON_STRING} _ ${CONFIG_KEY} info_key)
string(REPLACE "." ";" INFO_KEYS ${INFO_KEY})
# string(JSON CONFIG_VALUE ERROR_VARIABLE CONFIG_KEY_NOT_FOUND GET ${QMK_KEYBOARD_INFO_JSON_STRING} ${INFO_KEYS})
json_get_with_default(CONFIG_VALUE ${QMK_KEYBOARD_INFO_JSON_STRING} NOTFOUND ${INFO_KEYS})
if(NOT CONFIG_VALUE STREQUAL "NOTFOUND")
set(${CONFIG_KEY} ${CONFIG_VALUE})
json_get_with_default(VALUE_TYPE ${JSON_STRING} raw ${CONFIG_KEY} value_type)
if(${VALUE_TYPE} STREQUAL "str")
add_compile_definitions(${CONFIG_KEY}="${CONFIG_VALUE}")
message(STATUS "Found definition '${INFO_KEY}': '\"${CONFIG_VALUE}\"' assigned to '${CONFIG_KEY}'")
elseif(${VALUE_TYPE} STREQUAL "bcd_version")
string(REGEX MATCH "^([0-9]+)\\.([0-9]+)\\.([0-9]+)" VERSION_MATCH ${CONFIG_VALUE})
set(VERSION_MAJOR ${CMAKE_MATCH_1})
set(VERSION_MINOR ${CMAKE_MATCH_2})
set(VERSION_PATCH ${CMAKE_MATCH_3})
math(EXPR BCD_VERSION "${VERSION_MAJOR} * 10000 + ${VERSION_MINOR} * 100 + ${VERSION_PATCH}" OUTPUT_FORMAT HEXADECIMAL)
add_compile_definitions(${CONFIG_KEY}=${BCD_VERSION})
message(STATUS "Found definition '${INFO_KEY}': '${BCD_VERSION}' assigned to '${CONFIG_KEY}'")
else()
add_compile_definitions(${CONFIG_KEY}=${CONFIG_VALUE})
message(STATUS "Found definition '${INFO_KEY}': '${CONFIG_VALUE}' assigned to '${CONFIG_KEY}'")
endif()
endif()
endforeach()
string(JSON KEYBOARD_NAME GET ${QMK_KEYBOARD_INFO_JSON_STRING} keyboard_name)
string(JSON MANUFACTURER GET ${QMK_KEYBOARD_INFO_JSON_STRING} manufacturer)
string(JSON URL GET ${QMK_KEYBOARD_INFO_JSON_STRING} url)
string(JSON QMK_MCU GET ${QMK_KEYBOARD_INFO_JSON_STRING} processor)
string(JSON VENDOR_ID GET ${QMK_KEYBOARD_INFO_JSON_STRING} usb vid)
string(JSON PRODUCT_ID GET ${QMK_KEYBOARD_INFO_JSON_STRING} usb pid)
string(JSON DEVICE_VER GET ${QMK_KEYBOARD_INFO_JSON_STRING} usb device_version)
string(JSON DIODE_DIRECTION ERROR_VARIABLE _ GET ${QMK_KEYBOARD_INFO_JSON_STRING} diode_direction)
string(JSON BOOTLOADER ERROR_VARIABLE _ GET ${QMK_KEYBOARD_INFO_JSON_STRING} bootloader)
ParseMakefile(${QMK_KEYBOARD_FOLDER_ABS}/rules.mk)
list(POP_BACK CMAKE_MESSAGE_INDENT)
message(CHECK_PASS "info.json validated and loaded")
ParseMakefile(${QMK_KEYBOARD_FOLDER_ABS}/rules.mk)
endmacro()

View File

@ -1,3 +1,5 @@
include(Utils)
function(resolve_keyboard KEYBOARD KEYBOAD_FOLDER_ABS_STR)
message(VERBOSE "Resolving ${KEYBOARD}")
if(EXISTS "${CMAKE_SOURCE_DIR}/keyboards/${KEYBOARD}")
@ -42,17 +44,19 @@ function(resolve_keyboard KEYBOARD KEYBOAD_FOLDER_ABS_STR)
endfunction()
function(resolve_config_h KEYBOARD_FOLDER_ABS CONFIG_H_STR)
set(${CONFIG_H_STR} PARENT_SCOPE)
if(${IS_KEYBOARDS_FOLDER})
file(RELATIVE_PATH RELATIVE_KEYBOARD_FOLDER ${QMK_KEYBOARDS_FOLDER} ${KEYBOARD_FOLDER_ABS})
# get the deepest config.h
while(NOT ${RELATIVE_KEYBOARD_FOLDER} STREQUAL "")
if(EXISTS "${QMK_KEYBOARDS_FOLDER}/${RELATIVE_KEYBOARD_FOLDER}/config.h")
set(${CONFIG_H_STR} "${QMK_KEYBOARDS_FOLDER}/${RELATIVE_KEYBOARD_FOLDER}/config.h" PARENT_SCOPE)
return()
parent_list(PREPEND ${CONFIG_H_STR} "${QMK_KEYBOARDS_FOLDER}/${RELATIVE_KEYBOARD_FOLDER}/config.h")
# set(${CONFIG_H_STR} "${QMK_KEYBOARDS_FOLDER}/${RELATIVE_KEYBOARD_FOLDER}/config.h" PARENT_SCOPE)
# return()
endif()
get_filename_component(RELATIVE_KEYBOARD_FOLDER ${RELATIVE_KEYBOARD_FOLDER} DIRECTORY)
endwhile()
message(FATAL_ERROR "Could not find config.h in ${KEYBOARD_FOLDER_ABS}")
# message(FATAL_ERROR "Could not find config.h in ${KEYBOARD_FOLDER_ABS}")
else()
if(EXISTS "${KEYBOARD_FOLDER_ABS}/config.h")
set(${CONFIG_H_STR} "${KEYBOARD_FOLDER_ABS}/config.h" PARENT_SCOPE)
@ -112,26 +116,26 @@ function(resolve_keymap_c KEYBOARD_FOLDER_ABS KEYMAP_FOLDER KEYMAP_C_STR)
endif()
endif()
string(JSON COMMUNITY_LAYOUTS ERROR_VARIABLE NO_COMMUNITY_LAYOUTS GET ${QMK_KEYBOARD_INFO_JSON_STRING} community_layouts)
# string(JSON COMMUNITY_LAYOUTS ERROR_VARIABLE NO_COMMUNITY_LAYOUTS GET ${QMK_KEYBOARD_INFO_JSON_STRING} community_layouts)
if(${NO_COMMUNITY_LAYOUTS} STREQUAL "NOTFOUND")
string(JSON NUM_LAYOUTS LENGTH ${COMMUNITY_LAYOUTS})
math(EXPR MAX "${NUM_LAYOUTS} - 1")
foreach(IDX RANGE ${MAX})
string(JSON LAYOUT GET ${COMMUNITY_LAYOUTS} ${IDX})
# if(${NO_COMMUNITY_LAYOUTS} STREQUAL "NOTFOUND")
# string(JSON NUM_LAYOUTS LENGTH ${COMMUNITY_LAYOUTS})
# math(EXPR MAX "${NUM_LAYOUTS} - 1")
foreach(LAYOUT ${LAYOUTS})
# foreach(IDX RANGE ${MAX})
# string(JSON LAYOUT GET ${COMMUNITY_LAYOUTS} ${IDX})
if(EXISTS "${CMAKE_SOURCE_DIR}/layouts/community/${LAYOUT}/${KEYMAP_FOLDER}/keymap.c")
set(${KEYMAP_C_STR} "${CMAKE_SOURCE_DIR}/layouts/community/${LAYOUT}/${KEYMAP_FOLDER}/keymap.c" PARENT_SCOPE)
return()
endif()
endforeach()
endif()
# endif()
message(FATAL_ERROR "Could not resolve keymap '${KEYMAP_FOLDER}'")
endfunction()
function(resolve_keyboard_includes KEYBOARD_FOLDER_ABS)
if(${IS_KEYBOARDS_FOLDER})
file(RELATIVE_PATH RELATIVE_KEYBOARD_FOLDER ${QMK_KEYBOARDS_FOLDER} ${KEYBOARD_FOLDER_ABS})
# get the deepest config.h
while(NOT ${RELATIVE_KEYBOARD_FOLDER} STREQUAL "")
target_include_directories(qmk PUBLIC "${CMAKE_SOURCE_DIR}/keyboards/${RELATIVE_KEYBOARD_FOLDER}")
get_filename_component(RELATIVE_KEYBOARD_FOLDER ${RELATIVE_KEYBOARD_FOLDER} DIRECTORY)

25
cmake/Utils.cmake Normal file
View File

@ -0,0 +1,25 @@
macro(json_get_with_default KEY_STR JSON_STR DEFAULT)
# message(STATUS "Getting ${ARGN} for ${KEY_STR}")
string(JSON ${KEY_STR} ERROR_VARIABLE JSON_ERROR GET ${JSON_STR} ${ARGN})
if(NOT ${JSON_ERROR} STREQUAL "NOTFOUND")
set(${KEY_STR} ${DEFAULT})
endif()
endmacro()
macro(json_get KEY_STR JSON_STR)
# message(STATUS "Getting ${ARGN} for ${KEY_STR}")
string(JSON ${KEY_STR} ERROR_VARIABLE JSON_ERROR GET ${JSON_STR} ${ARGN})
if(NOT ${JSON_ERROR} STREQUAL "NOTFOUND")
unset(${KEY_STR})
endif()
endmacro()
macro(parent_list ACTION LIST_STR)
set(ARGS ${ARGN})
string(REPLACE ";" " " ARGS ${ARGS})
set(LOCAL_LIST ${${LIST_STR}})
list(${ACTION} LOCAL_LIST ${ARGS})
# set in current scope too
set(${LIST_STR} ${LOCAL_LIST})
set(${LIST_STR} ${LOCAL_LIST} PARENT_SCOPE)
endmacro()

View File

@ -129,7 +129,7 @@
"TAPPING_FORCE_HOLD": {"info_key": "tapping.force_hold", "value_type": "bool", "deprecated": true},
"TAPPING_FORCE_HOLD_PER_KEY": {"info_key": "tapping.force_hold_per_key", "value_type": "bool", "deprecated": true},
"IGNORE_MOD_TAP_INTERRUPT": {"info_key": "_deprecated.ignore_mod_tap_interrupt", "value_type": "bool", "deprecated": true},
"IGNORE_MOD_TAP_INTERRUPT_PER_KEY": {"info_key": "_invalid.ignore_mod_tap_interrupt_per_key", "invalid": true}
"IGNORE_MOD_TAP_INTERRUPT_PER_KEY": {"info_key": "_invalid.ignore_mod_tap_interrupt_per_key", "invalid": true},
// USB params, need to mark as failure when specified in config.h, rather than deprecated
"PRODUCT_ID": {"info_key": "usb.pid", "value_type": "hex", "deprecated": true, "replace_with": "`usb.pid` in info.json"},