2017-06-28 02:23:30 +02:00
|
|
|
#!/usr/bin/env python2
|
2016-05-25 17:37:40 +02:00
|
|
|
# Script to handle build time requests embedded in C code.
|
|
|
|
#
|
2021-02-18 20:01:40 +01:00
|
|
|
# Copyright (C) 2016-2021 Kevin O'Connor <kevin@koconnor.net>
|
2016-05-25 17:37:40 +02:00
|
|
|
#
|
|
|
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
2017-12-22 00:18:18 +01:00
|
|
|
import sys, os, subprocess, optparse, logging, shlex, socket, time, traceback
|
2016-05-25 17:37:40 +02:00
|
|
|
import json, zlib
|
|
|
|
sys.path.append('./klippy')
|
|
|
|
import msgproto
|
|
|
|
|
|
|
|
FILEHEADER = """
|
2019-02-27 19:15:48 +01:00
|
|
|
/* DO NOT EDIT! This is an autogenerated file. See scripts/buildcommands.py. */
|
2016-05-25 17:37:40 +02:00
|
|
|
|
2017-05-26 15:14:26 +02:00
|
|
|
#include "board/irq.h"
|
2016-05-25 17:37:40 +02:00
|
|
|
#include "board/pgm.h"
|
|
|
|
#include "command.h"
|
2017-05-07 23:02:40 +02:00
|
|
|
#include "compiler.h"
|
2019-03-18 01:41:52 +01:00
|
|
|
#include "initial_pins.h"
|
2016-05-25 17:37:40 +02:00
|
|
|
"""
|
|
|
|
|
|
|
|
def error(msg):
|
|
|
|
sys.stderr.write(msg + "\n")
|
|
|
|
sys.exit(-1)
|
|
|
|
|
2018-11-18 06:56:43 +01:00
|
|
|
Handlers = []
|
|
|
|
|
|
|
|
|
|
|
|
######################################################################
|
|
|
|
# C call list generation
|
|
|
|
######################################################################
|
|
|
|
|
|
|
|
# Create dynamic C functions that call a list of other C functions
|
|
|
|
class HandleCallList:
|
|
|
|
def __init__(self):
|
|
|
|
self.call_lists = {'ctr_run_initfuncs': []}
|
|
|
|
self.ctr_dispatch = { '_DECL_CALLLIST': self.decl_calllist }
|
|
|
|
def decl_calllist(self, req):
|
|
|
|
funcname, callname = req.split()[1:]
|
|
|
|
self.call_lists.setdefault(funcname, []).append(callname)
|
|
|
|
def update_data_dictionary(self, data):
|
|
|
|
pass
|
2018-11-18 08:49:04 +01:00
|
|
|
def generate_code(self, options):
|
2018-11-18 06:56:43 +01:00
|
|
|
code = []
|
|
|
|
for funcname, funcs in self.call_lists.items():
|
|
|
|
func_code = [' extern void %s(void);\n %s();' % (f, f)
|
|
|
|
for f in funcs]
|
|
|
|
if funcname == 'ctr_run_taskfuncs':
|
2021-04-22 01:36:49 +02:00
|
|
|
add_poll = ' irq_poll();\n'
|
|
|
|
func_code = [add_poll + fc for fc in func_code]
|
|
|
|
func_code.append(add_poll)
|
2018-11-18 06:56:43 +01:00
|
|
|
fmt = """
|
|
|
|
void
|
|
|
|
%s(void)
|
|
|
|
{
|
|
|
|
%s
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
code.append(fmt % (funcname, "\n".join(func_code).strip()))
|
|
|
|
return "".join(code)
|
|
|
|
|
|
|
|
Handlers.append(HandleCallList())
|
|
|
|
|
2016-05-25 17:37:40 +02:00
|
|
|
|
2018-11-18 07:14:31 +01:00
|
|
|
######################################################################
|
2019-03-05 18:42:35 +01:00
|
|
|
# Enumeration and static string generation
|
2018-11-18 07:14:31 +01:00
|
|
|
######################################################################
|
|
|
|
|
|
|
|
STATIC_STRING_MIN = 2
|
|
|
|
|
|
|
|
# Generate a dynamic string to integer mapping
|
2019-03-05 18:42:35 +01:00
|
|
|
class HandleEnumerations:
|
2018-11-18 07:14:31 +01:00
|
|
|
def __init__(self):
|
|
|
|
self.static_strings = []
|
2019-03-05 18:42:35 +01:00
|
|
|
self.enumerations = {}
|
|
|
|
self.ctr_dispatch = {
|
|
|
|
'_DECL_STATIC_STR': self.decl_static_str,
|
2019-08-22 19:26:30 +02:00
|
|
|
'DECL_ENUMERATION': self.decl_enumeration,
|
|
|
|
'DECL_ENUMERATION_RANGE': self.decl_enumeration_range
|
2019-03-05 18:42:35 +01:00
|
|
|
}
|
|
|
|
def add_enumeration(self, enum, name, value):
|
|
|
|
enums = self.enumerations.setdefault(enum, {})
|
|
|
|
if name in enums and enums[name] != value:
|
|
|
|
error("Conflicting definition for enumeration '%s %s'" % (
|
|
|
|
enum, name))
|
|
|
|
enums[name] = value
|
|
|
|
def decl_enumeration(self, req):
|
|
|
|
enum, name, value = req.split()[1:]
|
2019-08-22 19:06:15 +02:00
|
|
|
self.add_enumeration(enum, name, int(value, 0))
|
2019-03-05 18:42:35 +01:00
|
|
|
def decl_enumeration_range(self, req):
|
2019-08-22 18:15:44 +02:00
|
|
|
enum, name, value, count = req.split()[1:]
|
2019-08-22 19:06:15 +02:00
|
|
|
self.add_enumeration(enum, name, (int(value, 0), int(count, 0)))
|
2018-11-18 07:14:31 +01:00
|
|
|
def decl_static_str(self, req):
|
|
|
|
msg = req.split(None, 1)[1]
|
2019-03-05 18:42:35 +01:00
|
|
|
if msg not in self.static_strings:
|
2018-11-18 09:13:31 +01:00
|
|
|
self.static_strings.append(msg)
|
2018-11-18 07:14:31 +01:00
|
|
|
def update_data_dictionary(self, data):
|
2019-03-05 18:42:35 +01:00
|
|
|
for i, s in enumerate(self.static_strings):
|
|
|
|
self.add_enumeration("static_string_id", s, i + STATIC_STRING_MIN)
|
|
|
|
data['enumerations'] = self.enumerations
|
2018-11-18 08:49:04 +01:00
|
|
|
def generate_code(self, options):
|
2018-11-18 07:14:31 +01:00
|
|
|
code = []
|
|
|
|
for i, s in enumerate(self.static_strings):
|
|
|
|
code.append(' if (__builtin_strcmp(str, "%s") == 0)\n'
|
|
|
|
' return %d;\n' % (s, i + STATIC_STRING_MIN))
|
|
|
|
fmt = """
|
|
|
|
uint8_t __always_inline
|
|
|
|
ctr_lookup_static_string(const char *str)
|
|
|
|
{
|
|
|
|
%s
|
|
|
|
return 0xff;
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
return fmt % ("".join(code).strip(),)
|
|
|
|
|
2019-03-18 01:41:52 +01:00
|
|
|
HandlerEnumerations = HandleEnumerations()
|
|
|
|
Handlers.append(HandlerEnumerations)
|
2018-11-18 07:14:31 +01:00
|
|
|
|
|
|
|
|
2018-11-18 07:20:52 +01:00
|
|
|
######################################################################
|
|
|
|
# Constants
|
|
|
|
######################################################################
|
|
|
|
|
|
|
|
# Allow adding build time constants to the data dictionary
|
|
|
|
class HandleConstants:
|
|
|
|
def __init__(self):
|
|
|
|
self.constants = {}
|
2019-03-05 05:24:43 +01:00
|
|
|
self.ctr_dispatch = {
|
2019-08-22 19:26:30 +02:00
|
|
|
'DECL_CONSTANT': self.decl_constant,
|
|
|
|
'DECL_CONSTANT_STR': self.decl_constant_str,
|
2019-03-05 05:24:43 +01:00
|
|
|
}
|
|
|
|
def set_value(self, name, value):
|
|
|
|
if name in self.constants and self.constants[name] != value:
|
|
|
|
error("Conflicting definition for constant '%s'" % name)
|
|
|
|
self.constants[name] = value
|
2018-11-18 07:20:52 +01:00
|
|
|
def decl_constant(self, req):
|
2019-03-05 05:24:43 +01:00
|
|
|
name, value = req.split()[1:]
|
2019-08-22 19:06:15 +02:00
|
|
|
self.set_value(name, int(value, 0))
|
2019-03-05 05:24:43 +01:00
|
|
|
def decl_constant_str(self, req):
|
2019-03-18 01:41:52 +01:00
|
|
|
name, value = req.split(None, 2)[1:]
|
2018-11-18 07:20:52 +01:00
|
|
|
value = value.strip()
|
|
|
|
if value.startswith('"') and value.endswith('"'):
|
|
|
|
value = value[1:-1]
|
2019-03-05 05:24:43 +01:00
|
|
|
self.set_value(name, value)
|
2018-11-18 07:20:52 +01:00
|
|
|
def update_data_dictionary(self, data):
|
|
|
|
data['config'] = self.constants
|
2018-11-18 08:49:04 +01:00
|
|
|
def generate_code(self, options):
|
2018-11-18 07:20:52 +01:00
|
|
|
return ""
|
|
|
|
|
2019-03-18 01:41:52 +01:00
|
|
|
HandlerConstants = HandleConstants()
|
|
|
|
Handlers.append(HandlerConstants)
|
|
|
|
|
|
|
|
|
|
|
|
######################################################################
|
|
|
|
# Initial pins
|
|
|
|
######################################################################
|
|
|
|
|
|
|
|
class HandleInitialPins:
|
|
|
|
def __init__(self):
|
|
|
|
self.initial_pins = []
|
|
|
|
self.ctr_dispatch = { 'DECL_INITIAL_PINS': self.decl_initial_pins }
|
|
|
|
def decl_initial_pins(self, req):
|
|
|
|
pins = req.split(None, 1)[1].strip()
|
|
|
|
if pins.startswith('"') and pins.endswith('"'):
|
|
|
|
pins = pins[1:-1]
|
|
|
|
if pins:
|
|
|
|
self.initial_pins = [p.strip() for p in pins.split(',')]
|
|
|
|
HandlerConstants.decl_constant_str(
|
|
|
|
"_DECL_CONSTANT_STR INITIAL_PINS "
|
|
|
|
+ ','.join(self.initial_pins))
|
|
|
|
def update_data_dictionary(self, data):
|
|
|
|
pass
|
|
|
|
def map_pins(self):
|
|
|
|
if not self.initial_pins:
|
|
|
|
return []
|
|
|
|
mp = msgproto.MessageParser()
|
2021-02-18 20:01:40 +01:00
|
|
|
mp.fill_enumerations(HandlerEnumerations.enumerations)
|
|
|
|
pinmap = mp.get_enumerations().get('pin', {})
|
2019-03-18 01:41:52 +01:00
|
|
|
out = []
|
|
|
|
for p in self.initial_pins:
|
|
|
|
flag = "IP_OUT_HIGH"
|
|
|
|
if p.startswith('!'):
|
|
|
|
flag = "0"
|
|
|
|
p = p[1:].strip()
|
|
|
|
if p not in pinmap:
|
|
|
|
error("Unknown initial pin '%s'" % (p,))
|
|
|
|
out.append("\n {%d, %s}, // %s" % (pinmap[p], flag, p))
|
|
|
|
return out
|
|
|
|
def generate_code(self, options):
|
|
|
|
out = self.map_pins()
|
|
|
|
fmt = """
|
|
|
|
const struct initial_pin_s initial_pins[] PROGMEM = {%s
|
|
|
|
};
|
|
|
|
const int initial_pins_size PROGMEM = ARRAY_SIZE(initial_pins);
|
|
|
|
"""
|
|
|
|
return fmt % (''.join(out),)
|
|
|
|
|
|
|
|
Handlers.append(HandleInitialPins())
|
2018-11-18 07:20:52 +01:00
|
|
|
|
|
|
|
|
2019-08-21 18:05:56 +02:00
|
|
|
######################################################################
|
|
|
|
# ARM IRQ vector table generation
|
|
|
|
######################################################################
|
|
|
|
|
|
|
|
# Create ARM IRQ vector table from interrupt handler declarations
|
|
|
|
class Handle_arm_irq:
|
|
|
|
def __init__(self):
|
|
|
|
self.irqs = {}
|
|
|
|
self.ctr_dispatch = { 'DECL_ARMCM_IRQ': self.decl_armcm_irq }
|
|
|
|
def decl_armcm_irq(self, req):
|
|
|
|
func, num = req.split()[1:]
|
2019-08-22 19:06:15 +02:00
|
|
|
num = int(num, 0)
|
2019-08-21 18:05:56 +02:00
|
|
|
if num in self.irqs and self.irqs[num] != func:
|
|
|
|
error("Conflicting IRQ definition %d (old %s new %s)"
|
|
|
|
% (num, self.irqs[num], func))
|
|
|
|
self.irqs[num] = func
|
|
|
|
def update_data_dictionary(self, data):
|
|
|
|
pass
|
|
|
|
def generate_code(self, options):
|
|
|
|
armcm_offset = 16
|
|
|
|
if 1 - armcm_offset not in self.irqs:
|
|
|
|
# The ResetHandler was not defined - don't build VectorTable
|
|
|
|
return ""
|
|
|
|
max_irq = max(self.irqs.keys())
|
|
|
|
table = [" DefaultHandler,\n"] * (max_irq + armcm_offset + 1)
|
|
|
|
defs = []
|
|
|
|
for num, func in self.irqs.items():
|
|
|
|
if num < 1 - armcm_offset:
|
|
|
|
error("Invalid IRQ %d (%s)" % (num, func))
|
|
|
|
defs.append("extern void %s(void);\n" % (func,))
|
|
|
|
table[num + armcm_offset] = " %s,\n" % (func,)
|
|
|
|
table[0] = " &_stack_end,\n"
|
|
|
|
fmt = """
|
|
|
|
extern void DefaultHandler(void);
|
|
|
|
extern uint32_t _stack_end;
|
|
|
|
%s
|
|
|
|
const void *VectorTable[] __visible __section(".vector_table") = {
|
|
|
|
%s};
|
|
|
|
"""
|
|
|
|
return fmt % (''.join(defs), ''.join(table))
|
|
|
|
|
|
|
|
Handlers.append(Handle_arm_irq())
|
|
|
|
|
|
|
|
|
2016-05-25 17:37:40 +02:00
|
|
|
######################################################################
|
2018-11-18 08:24:10 +01:00
|
|
|
# Wire protocol commands and responses
|
2016-05-25 17:37:40 +02:00
|
|
|
######################################################################
|
|
|
|
|
2018-11-18 08:24:10 +01:00
|
|
|
# Dynamic command and response registration
|
|
|
|
class HandleCommandGeneration:
|
|
|
|
def __init__(self):
|
|
|
|
self.commands = {}
|
|
|
|
self.encoders = []
|
2019-03-05 01:50:35 +01:00
|
|
|
self.msg_to_id = dict(msgproto.DefaultMessages)
|
2018-11-18 08:24:10 +01:00
|
|
|
self.messages_by_name = { m.split()[0]: m for m in self.msg_to_id }
|
|
|
|
self.all_param_types = {}
|
|
|
|
self.ctr_dispatch = {
|
2019-08-22 19:26:30 +02:00
|
|
|
'DECL_COMMAND_FLAGS': self.decl_command,
|
2018-11-18 08:24:10 +01:00
|
|
|
'_DECL_ENCODER': self.decl_encoder,
|
|
|
|
'_DECL_OUTPUT': self.decl_output
|
|
|
|
}
|
|
|
|
def decl_command(self, req):
|
|
|
|
funcname, flags, msgname = req.split()[1:4]
|
|
|
|
if msgname in self.commands:
|
|
|
|
error("Multiple definitions for command '%s'" % msgname)
|
|
|
|
self.commands[msgname] = (funcname, flags, msgname)
|
|
|
|
msg = req.split(None, 3)[3]
|
|
|
|
m = self.messages_by_name.get(msgname)
|
|
|
|
if m is not None and m != msg:
|
|
|
|
error("Conflicting definition for command '%s'" % msgname)
|
|
|
|
self.messages_by_name[msgname] = msg
|
|
|
|
def decl_encoder(self, req):
|
|
|
|
msg = req.split(None, 1)[1]
|
|
|
|
msgname = msg.split()[0]
|
|
|
|
m = self.messages_by_name.get(msgname)
|
|
|
|
if m is not None and m != msg:
|
|
|
|
error("Conflicting definition for message '%s'" % msgname)
|
|
|
|
self.messages_by_name[msgname] = msg
|
|
|
|
self.encoders.append((msgname, msg))
|
|
|
|
def decl_output(self, req):
|
|
|
|
msg = req.split(None, 1)[1]
|
|
|
|
self.encoders.append((None, msg))
|
|
|
|
def create_message_ids(self):
|
|
|
|
# Create unique ids for each message type
|
|
|
|
msgid = max(self.msg_to_id.values())
|
|
|
|
for msgname in self.commands.keys() + [m for n, m in self.encoders]:
|
|
|
|
msg = self.messages_by_name.get(msgname, msgname)
|
|
|
|
if msg not in self.msg_to_id:
|
|
|
|
msgid += 1
|
|
|
|
self.msg_to_id[msg] = msgid
|
2021-02-04 22:33:03 +01:00
|
|
|
if msgid >= 128:
|
2018-11-18 16:54:47 +01:00
|
|
|
# The mcu currently assumes all message ids encode to one byte
|
|
|
|
error("Too many message ids")
|
2018-11-18 08:24:10 +01:00
|
|
|
def update_data_dictionary(self, data):
|
2021-02-04 22:33:03 +01:00
|
|
|
# Handle message ids over 96 (they are decoded as negative numbers)
|
|
|
|
msg_to_tag = {msg: msgid if msgid < 96 else msgid - 128
|
|
|
|
for msg, msgid in self.msg_to_id.items()}
|
|
|
|
command_tags = [msg_to_tag[msg]
|
2019-03-05 01:50:35 +01:00
|
|
|
for msgname, msg in self.messages_by_name.items()
|
2021-02-04 22:33:03 +01:00
|
|
|
if msgname in self.commands]
|
|
|
|
response_tags = [msg_to_tag[msg]
|
|
|
|
for msgname, msg in self.messages_by_name.items()
|
|
|
|
if msgname not in self.commands]
|
|
|
|
data['commands'] = { msg: msgtag for msg, msgtag in msg_to_tag.items()
|
|
|
|
if msgtag in command_tags }
|
|
|
|
data['responses'] = { msg: msgtag for msg, msgtag in msg_to_tag.items()
|
|
|
|
if msgtag in response_tags }
|
|
|
|
output = {msg: msgtag for msg, msgtag in msg_to_tag.items()
|
|
|
|
if msgtag not in command_tags and msgtag not in response_tags}
|
2019-03-05 01:50:35 +01:00
|
|
|
if output:
|
|
|
|
data['output'] = output
|
2021-02-18 20:01:40 +01:00
|
|
|
def build_parser(self, msgid, msgformat, msgtype):
|
|
|
|
if msgtype == "output":
|
|
|
|
param_types = msgproto.lookup_output_params(msgformat)
|
|
|
|
comment = "Output: " + msgformat
|
2018-11-18 08:24:10 +01:00
|
|
|
else:
|
2021-02-18 20:01:40 +01:00
|
|
|
param_types = [t for name, t in msgproto.lookup_params(msgformat)]
|
|
|
|
comment = msgformat
|
2018-11-18 08:24:10 +01:00
|
|
|
params = '0'
|
2021-02-18 20:01:40 +01:00
|
|
|
types = tuple([t.__class__.__name__ for t in param_types])
|
2018-11-18 08:24:10 +01:00
|
|
|
if types:
|
|
|
|
paramid = self.all_param_types.get(types)
|
|
|
|
if paramid is None:
|
|
|
|
paramid = len(self.all_param_types)
|
|
|
|
self.all_param_types[types] = paramid
|
|
|
|
params = 'command_parameters%d' % (paramid,)
|
|
|
|
out = """
|
2016-05-25 17:37:40 +02:00
|
|
|
// %s
|
|
|
|
.msg_id=%d,
|
|
|
|
.num_params=%d,
|
|
|
|
.param_types = %s,
|
2021-02-18 20:01:40 +01:00
|
|
|
""" % (comment, msgid, len(types), params)
|
|
|
|
if msgtype == 'response':
|
2018-11-18 08:24:10 +01:00
|
|
|
num_args = (len(types) + types.count('PT_progmem_buffer')
|
|
|
|
+ types.count('PT_buffer'))
|
|
|
|
out += " .num_args=%d," % (num_args,)
|
2016-05-25 17:37:40 +02:00
|
|
|
else:
|
2018-11-18 08:24:10 +01:00
|
|
|
max_size = min(msgproto.MESSAGE_MAX,
|
|
|
|
(msgproto.MESSAGE_MIN + 1
|
2021-02-18 20:01:40 +01:00
|
|
|
+ sum([t.max_length for t in param_types])))
|
2018-11-18 08:24:10 +01:00
|
|
|
out += " .max_size=%d," % (max_size,)
|
|
|
|
return out
|
|
|
|
def generate_responses_code(self):
|
|
|
|
encoder_defs = []
|
|
|
|
output_code = []
|
|
|
|
encoder_code = []
|
|
|
|
did_output = {}
|
|
|
|
for msgname, msg in self.encoders:
|
|
|
|
msgid = self.msg_to_id[msg]
|
|
|
|
if msgid in did_output:
|
|
|
|
continue
|
|
|
|
did_output[msgid] = True
|
|
|
|
code = (' if (__builtin_strcmp(str, "%s") == 0)\n'
|
2021-02-18 20:01:40 +01:00
|
|
|
' return &command_encoder_%s;\n' % (msg, msgid))
|
2018-11-18 08:24:10 +01:00
|
|
|
if msgname is None:
|
2021-02-18 20:01:40 +01:00
|
|
|
parsercode = self.build_parser(msgid, msg, 'output')
|
2018-11-18 08:24:10 +01:00
|
|
|
output_code.append(code)
|
|
|
|
else:
|
2021-02-18 20:01:40 +01:00
|
|
|
parsercode = self.build_parser(msgid, msg, 'command')
|
2018-11-18 08:24:10 +01:00
|
|
|
encoder_code.append(code)
|
|
|
|
encoder_defs.append(
|
|
|
|
"const struct command_encoder command_encoder_%s PROGMEM = {"
|
|
|
|
" %s\n};\n" % (
|
|
|
|
msgid, parsercode))
|
|
|
|
fmt = """
|
2016-05-25 17:37:40 +02:00
|
|
|
%s
|
2017-05-25 22:54:31 +02:00
|
|
|
|
|
|
|
const __always_inline struct command_encoder *
|
|
|
|
ctr_lookup_encoder(const char *str)
|
|
|
|
{
|
|
|
|
%s
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
const __always_inline struct command_encoder *
|
|
|
|
ctr_lookup_output(const char *str)
|
|
|
|
{
|
|
|
|
%s
|
|
|
|
return NULL;
|
|
|
|
}
|
2016-05-25 17:37:40 +02:00
|
|
|
"""
|
2018-11-18 08:24:10 +01:00
|
|
|
return fmt % ("".join(encoder_defs).strip(),
|
|
|
|
"".join(encoder_code).strip(),
|
|
|
|
"".join(output_code).strip())
|
|
|
|
def generate_commands_code(self):
|
|
|
|
cmd_by_id = {
|
|
|
|
self.msg_to_id[self.messages_by_name.get(msgname, msgname)]: cmd
|
|
|
|
for msgname, cmd in self.commands.items()
|
|
|
|
}
|
|
|
|
max_cmd_msgid = max(cmd_by_id.keys())
|
|
|
|
index = []
|
|
|
|
externs = {}
|
|
|
|
for msgid in range(max_cmd_msgid+1):
|
|
|
|
if msgid not in cmd_by_id:
|
|
|
|
index.append(" {\n},")
|
|
|
|
continue
|
|
|
|
funcname, flags, msgname = cmd_by_id[msgid]
|
|
|
|
msg = self.messages_by_name[msgname]
|
|
|
|
externs[funcname] = 1
|
2021-02-18 20:01:40 +01:00
|
|
|
parsercode = self.build_parser(msgid, msg, 'response')
|
2018-11-18 08:24:10 +01:00
|
|
|
index.append(" {%s\n .flags=%s,\n .func=%s\n}," % (
|
|
|
|
parsercode, flags, funcname))
|
|
|
|
index = "".join(index).strip()
|
|
|
|
externs = "\n".join(["extern void "+funcname+"(uint32_t*);"
|
|
|
|
for funcname in sorted(externs)])
|
|
|
|
fmt = """
|
2016-05-25 17:37:40 +02:00
|
|
|
%s
|
|
|
|
|
2017-05-26 19:20:20 +02:00
|
|
|
const struct command_parser command_index[] PROGMEM = {
|
2016-05-25 17:37:40 +02:00
|
|
|
%s
|
|
|
|
};
|
|
|
|
|
2017-05-25 22:54:31 +02:00
|
|
|
const uint8_t command_index_size PROGMEM = ARRAY_SIZE(command_index);
|
2016-05-25 17:37:40 +02:00
|
|
|
"""
|
2018-11-18 08:24:10 +01:00
|
|
|
return fmt % (externs, index)
|
|
|
|
def generate_param_code(self):
|
|
|
|
sorted_param_types = sorted(
|
|
|
|
[(i, a) for a, i in self.all_param_types.items()])
|
|
|
|
params = ['']
|
|
|
|
for paramid, argtypes in sorted_param_types:
|
|
|
|
params.append(
|
|
|
|
'static const uint8_t command_parameters%d[] PROGMEM = {\n'
|
|
|
|
' %s };' % (
|
|
|
|
paramid, ', '.join(argtypes),))
|
|
|
|
params.append('')
|
|
|
|
return "\n".join(params)
|
2018-11-18 08:49:04 +01:00
|
|
|
def generate_code(self, options):
|
|
|
|
self.create_message_ids()
|
2018-11-18 08:24:10 +01:00
|
|
|
parsercode = self.generate_responses_code()
|
|
|
|
cmdcode = self.generate_commands_code()
|
|
|
|
paramcode = self.generate_param_code()
|
|
|
|
return paramcode + parsercode + cmdcode
|
|
|
|
|
|
|
|
Handlers.append(HandleCommandGeneration())
|
2016-05-25 17:37:40 +02:00
|
|
|
|
|
|
|
|
|
|
|
######################################################################
|
|
|
|
# Version generation
|
|
|
|
######################################################################
|
|
|
|
|
|
|
|
# Run program and return the specified output
|
|
|
|
def check_output(prog):
|
|
|
|
logging.debug("Running %s" % (repr(prog),))
|
|
|
|
try:
|
|
|
|
process = subprocess.Popen(shlex.split(prog), stdout=subprocess.PIPE)
|
|
|
|
output = process.communicate()[0]
|
|
|
|
retcode = process.poll()
|
|
|
|
except OSError:
|
|
|
|
logging.debug("Exception on run: %s" % (traceback.format_exc(),))
|
|
|
|
return ""
|
|
|
|
logging.debug("Got (code=%s): %s" % (retcode, repr(output)))
|
|
|
|
if retcode:
|
|
|
|
return ""
|
|
|
|
try:
|
|
|
|
return output.decode()
|
|
|
|
except UnicodeError:
|
|
|
|
logging.debug("Exception on decode: %s" % (traceback.format_exc(),))
|
|
|
|
return ""
|
|
|
|
|
|
|
|
# Obtain version info from "git" program
|
|
|
|
def git_version():
|
|
|
|
if not os.path.exists('.git'):
|
|
|
|
logging.debug("No '.git' file/directory found")
|
|
|
|
return ""
|
2018-02-27 03:50:05 +01:00
|
|
|
ver = check_output("git describe --always --tags --long --dirty").strip()
|
2016-05-25 17:37:40 +02:00
|
|
|
logging.debug("Got git version: %s" % (repr(ver),))
|
|
|
|
return ver
|
|
|
|
|
|
|
|
def build_version(extra):
|
|
|
|
version = git_version()
|
|
|
|
if not version:
|
|
|
|
version = "?"
|
|
|
|
btime = time.strftime("%Y%m%d_%H%M%S")
|
|
|
|
hostname = socket.gethostname()
|
|
|
|
version = "%s-%s-%s%s" % (version, btime, hostname, extra)
|
|
|
|
return version
|
|
|
|
|
2017-12-22 00:18:18 +01:00
|
|
|
# Run "tool --version" for each specified tool and extract versions
|
|
|
|
def tool_versions(tools):
|
|
|
|
tools = [t.strip() for t in tools.split(';')]
|
|
|
|
versions = ['', '']
|
|
|
|
success = 0
|
|
|
|
for tool in tools:
|
|
|
|
# Extract first line from "tool --version" output
|
|
|
|
verstr = check_output("%s --version" % (tool,)).split('\n')[0]
|
|
|
|
# Check if this tool looks like a binutils program
|
|
|
|
isbinutils = 0
|
|
|
|
if verstr.startswith('GNU '):
|
|
|
|
isbinutils = 1
|
|
|
|
verstr = verstr[4:]
|
|
|
|
# Extract version information and exclude program name
|
|
|
|
if ' ' not in verstr:
|
|
|
|
continue
|
|
|
|
prog, ver = verstr.split(' ', 1)
|
|
|
|
if not prog or not ver:
|
|
|
|
continue
|
|
|
|
# Check for any version conflicts
|
|
|
|
if versions[isbinutils] and versions[isbinutils] != ver:
|
|
|
|
logging.debug("Mixed version %s vs %s" % (
|
|
|
|
repr(versions[isbinutils]), repr(ver)))
|
|
|
|
versions[isbinutils] = "mixed"
|
|
|
|
continue
|
|
|
|
versions[isbinutils] = ver
|
|
|
|
success += 1
|
|
|
|
cleanbuild = versions[0] and versions[1] and success == len(tools)
|
|
|
|
return cleanbuild, "gcc: %s binutils: %s" % (versions[0], versions[1])
|
|
|
|
|
2018-11-18 08:49:04 +01:00
|
|
|
# Add version information to the data dictionary
|
|
|
|
class HandleVersions:
|
|
|
|
def __init__(self):
|
|
|
|
self.ctr_dispatch = {}
|
|
|
|
self.toolstr = self.version = ""
|
|
|
|
def update_data_dictionary(self, data):
|
|
|
|
data['version'] = self.version
|
|
|
|
data['build_versions'] = self.toolstr
|
|
|
|
def generate_code(self, options):
|
|
|
|
cleanbuild, self.toolstr = tool_versions(options.tools)
|
|
|
|
self.version = build_version(options.extra)
|
|
|
|
sys.stdout.write("Version: %s\n" % (self.version,))
|
|
|
|
return "\n// version: %s\n// build_versions: %s\n" % (
|
|
|
|
self.version, self.toolstr)
|
|
|
|
|
|
|
|
Handlers.append(HandleVersions())
|
|
|
|
|
2016-05-25 17:37:40 +02:00
|
|
|
|
2018-11-18 08:57:18 +01:00
|
|
|
######################################################################
|
|
|
|
# Identify data dictionary generation
|
|
|
|
######################################################################
|
|
|
|
|
|
|
|
# Automatically generate the wire protocol data dictionary
|
|
|
|
class HandleIdentify:
|
|
|
|
def __init__(self):
|
|
|
|
self.ctr_dispatch = {}
|
|
|
|
def update_data_dictionary(self, data):
|
|
|
|
pass
|
|
|
|
def generate_code(self, options):
|
|
|
|
# Generate data dictionary
|
|
|
|
data = {}
|
|
|
|
for h in Handlers:
|
|
|
|
h.update_data_dictionary(data)
|
2019-03-05 01:50:35 +01:00
|
|
|
datadict = json.dumps(data, separators=(',', ':'), sort_keys=True)
|
2018-11-18 08:57:18 +01:00
|
|
|
|
|
|
|
# Write data dictionary
|
|
|
|
if options.write_dictionary:
|
|
|
|
f = open(options.write_dictionary, 'wb')
|
|
|
|
f.write(datadict)
|
|
|
|
f.close()
|
|
|
|
|
|
|
|
# Format compressed info into C code
|
|
|
|
zdatadict = zlib.compress(datadict, 9)
|
|
|
|
out = []
|
|
|
|
for i in range(len(zdatadict)):
|
|
|
|
if i % 8 == 0:
|
|
|
|
out.append('\n ')
|
|
|
|
out.append(" 0x%02x," % (ord(zdatadict[i]),))
|
|
|
|
fmt = """
|
|
|
|
const uint8_t command_identify_data[] PROGMEM = {%s
|
|
|
|
};
|
|
|
|
|
|
|
|
// Identify size = %d (%d uncompressed)
|
|
|
|
const uint32_t command_identify_size PROGMEM
|
|
|
|
= ARRAY_SIZE(command_identify_data);
|
|
|
|
"""
|
|
|
|
return fmt % (''.join(out), len(zdatadict), len(datadict))
|
|
|
|
|
|
|
|
Handlers.append(HandleIdentify())
|
|
|
|
|
|
|
|
|
2016-05-25 17:37:40 +02:00
|
|
|
######################################################################
|
|
|
|
# Main code
|
|
|
|
######################################################################
|
|
|
|
|
|
|
|
def main():
|
2016-12-23 05:47:46 +01:00
|
|
|
usage = "%prog [options] <cmd section file> <output.c>"
|
2016-05-25 17:37:40 +02:00
|
|
|
opts = optparse.OptionParser(usage)
|
|
|
|
opts.add_option("-e", "--extra", dest="extra", default="",
|
|
|
|
help="extra version string to append to version")
|
2016-09-11 02:01:52 +02:00
|
|
|
opts.add_option("-d", dest="write_dictionary",
|
|
|
|
help="file to write mcu protocol dictionary")
|
2017-12-22 00:18:18 +01:00
|
|
|
opts.add_option("-t", "--tools", dest="tools", default="",
|
|
|
|
help="list of build programs to extract version from")
|
2016-05-25 17:37:40 +02:00
|
|
|
opts.add_option("-v", action="store_true", dest="verbose",
|
|
|
|
help="enable debug messages")
|
|
|
|
|
|
|
|
options, args = opts.parse_args()
|
2016-12-23 05:47:46 +01:00
|
|
|
if len(args) != 2:
|
2016-05-25 17:37:40 +02:00
|
|
|
opts.error("Incorrect arguments")
|
2016-12-23 05:47:46 +01:00
|
|
|
incmdfile, outcfile = args
|
2016-05-25 17:37:40 +02:00
|
|
|
if options.verbose:
|
|
|
|
logging.basicConfig(level=logging.DEBUG)
|
|
|
|
|
|
|
|
# Parse request file
|
2018-11-18 06:56:43 +01:00
|
|
|
ctr_dispatch = { k: v for h in Handlers for k, v in h.ctr_dispatch.items() }
|
2016-05-25 17:37:40 +02:00
|
|
|
f = open(incmdfile, 'rb')
|
|
|
|
data = f.read()
|
|
|
|
f.close()
|
2019-08-22 16:35:26 +02:00
|
|
|
for req in data.split('\n'):
|
2016-05-25 17:37:40 +02:00
|
|
|
req = req.lstrip()
|
2018-11-18 08:24:10 +01:00
|
|
|
if not req:
|
2016-05-25 17:37:40 +02:00
|
|
|
continue
|
2018-11-18 08:24:10 +01:00
|
|
|
cmd = req.split()[0]
|
|
|
|
if cmd not in ctr_dispatch:
|
2016-05-25 17:37:40 +02:00
|
|
|
error("Unknown build time command '%s'" % cmd)
|
2018-11-18 08:24:10 +01:00
|
|
|
ctr_dispatch[cmd](req)
|
2018-11-18 08:57:18 +01:00
|
|
|
|
2016-05-25 17:37:40 +02:00
|
|
|
# Write output
|
2018-11-18 08:57:18 +01:00
|
|
|
code = "".join([FILEHEADER] + [h.generate_code(options) for h in Handlers])
|
2016-05-25 17:37:40 +02:00
|
|
|
f = open(outcfile, 'wb')
|
2018-11-18 08:57:18 +01:00
|
|
|
f.write(code)
|
2016-05-25 17:37:40 +02:00
|
|
|
f.close()
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|