SConscript.py - Build source files from template

Note the .py extension on this file, necessary so that Doxygen parses as a Python script (but not necessary for SCons).

This file builds various files from templates in the templates/ directory in which this file resides. It should be called from the main SConscript.py script.

Setup for template building

 
import os
from string import Template
import csv
import re

from compact_csv import enumeratePic24Ports
 

Import environment from calling SConstruct context

Import('env')
 
 

Functions supporting template file builds

 
A function to do search and replaces on a file, optionally
appending the results to the output file.
def searchAndReplace(

Name of the source file

  sourceFile,

Name of the destination file

  destFileName,

A dictionary of key=value pairs used by template to perform the search and replace operation.

  mapping,
Mode with which to open the destination file. Use the default of ‘w’ to overwrite the destination file with the replaced source file. Use ‘a’ to append the replaced source file to the destination file.
 openMode='w'):
 

Use newline=’’ to preserve current line endings; in particular, this keeps line endings Unix-style, even when run under Windows.

  with open(destFileName, openMode, encoding='utf-8', newline='') as outFile:
    template = Template(open(sourceFile).read())
    outFile.write(template.substitute(mapping))
 
A function to create a .c/.h file from multiple replaces through
a template.
def genFromTemplate(

Name of the template file to use

  templateFileName,

Name of the destination file to create by accumulating multiple passes through the template file

  destFileName,

Number of iterations (passes) through the file to make. During each iteration, $x is replaced by the iteration number.

  iters):

  openMode = 'w'
  for i in range(1, iters + 1):
    searchAndReplace(templateFileName, destFileName,
      {'x' : str(i)}, openMode)
    openMode = 'a'
 

Builds a .c from a template – SCons Builder function formation. Function will build the .c as requested. Has the proper inputs, outputs and returns value to be registered SCons environment as a builder function

def c_template_builder(

Target file for this function to build

  target,

File on which target is built from

  source,

Environment (if needed)

  env):

  s=str(source[0])
  t=str(target[0])
  f=os.path.split(str(target[0]))[-1]
  g=os.path.splitext(f)[0]
  if (g == "pic24_uart"):
    genFromTemplate(s, t, 4)
  if (g == "pic24_i2c"):
    genFromTemplate(s, t, 2)
  if (g == "pic24_ecan"):
    genFromTemplate(s, t, 2)
  if (g == "pic24_spi"):
    genFromTemplate(s, t, 2)
 

This routine builds a pic24_ports_config.h file from its template. To do so:

  1. Open the output and template files.
  2. Write the header.
  3. For each port/pin, write a replaced template.
def genConfigFromTemplate(templateFileName, destFileName):

Read in the template.

  with open(templateFileName, "r", encoding='utf-8', newline='') as templateFile:
    template = Template(templateFile.read())

Open the output file.

  with open(destFileName, "w", encoding='utf-8', newline='') as outFile:

Write the header

    outFile.write(c_license_header)
    outFile.write('/// \\brief Define GPIO configuration macros for all pins of a device.\n' +
                  '/// See pic24_ports.h for more details.\n\n\n');
 

Iterate over every port

    portlist = enumeratePic24Ports()

Add in some extra ports for unit testing

    portlist.extend(['T1', 'T2', 'T3'])
    for Rxy in portlist:

Evaluate the template for this port/pin.

      outFile.write(template.substitute({'x' : Rxy}))
 

This helper routine splits a string containing a space-separated list of processors into a list. The last element of the list gives the port, which is returned separately.

An asterisk (*) after a processor name is removed; this makes it easier to mark which csv entries have been hand-checked.

def splitProcessorNames(port_dict):
  processors = re.split('\*? ', port_dict['Device port / pin'])
  port = processors.pop()
  return processors, port
 
 

This routine builds pic24_ports_tables.h from a template. To do so:

  1. Open the output file and write out the header.
  2. Load in the CSV file containing pin information:
    1. Load three rows; they should be labeled xxx RPy, xxx ANn, and xxx CNm, where xxx is the processor.
    2. For each Rxy value:
      1. For F/H devices: look at the corresponding RPy, ANn, and CNm. If any are non-empty, write a table entry.
      2. For E devices: Write a #define for RPy or ANn if either are non-empty.
  3. Write out the footer.
def genTablesFromTemplate(csvFileName, destFileName):
  portlist = enumeratePic24Ports()

Read in the CSV containing device information.

  with open(csvFileName, "r", encoding='utf-8') as csvFile:
    csv_dict_reader = csv.DictReader(csvFile).__iter__()

Open the output file.

    with open(destFileName, "w", encoding='utf-8', newline='') as outFile:

Write the header

      outFile.write(c_license_header)
      outFile.write('/// \\brief Define device-specific mappings from Rxy to RPy, ANn, and CNm pins.\n\n#if 0\n')

Walk through the file

      while True:

Read three rows

          try:
              RPy = csv_dict_reader.__next__()
              ANn = csv_dict_reader.__next__()
              CNm = csv_dict_reader.__next__()
          except StopIteration:
              break

Gather processor information. Make sure all three rows refer to the same processors and to the expected ports.

          processors, port = splitProcessorNames(RPy)
          assert port == 'RPy'

Check: all processors in this group should be unique.

          if len(processors) > len(set(processors)):
              print(" ".join(processors) + " contains duplicates.")
              assert False
          processors_temp, port = splitProcessorNames(ANn)
          assert processors == processors_temp
          assert port == 'ANn'
          processors_temp, port = splitProcessorNames(CNm)
          assert processors == processors_temp
          assert port == 'CNm'

Write out processor information.

          outFile.write('\n#elif defined(__' + '__) || \\\n      defined(__'.join(processors) + '__)\n')

Walk through each Rxy on this processor

          for Rxy in portlist:

Get the specific values for this Rxy.

              _RPy = RPy[Rxy]
              _ANn = ANn[Rxy]
              _CNm = CNm[Rxy]
              if _RPy:
                  outFile.write('# define R%s_RP %s\n' % (Rxy, _RPy))
              if _ANn:
                  outFile.write('# define R%s_AN %s\n' % (Rxy, _ANn))
              if _CNm:
                  outFile.write('# define R%s_CN %s\n' % (Rxy, _CNm))

Write footer

      outFile.write('#else\n# error "Port information not defined."\n#endif\n')
 

Builds a .h from a .csv – SCons Builder function formation. Has the proper inputs, outputs and returns value to be registered SCons environment as a builder function.

def csv_template_builder(
Target file for this function to build
  target,
File on which target is built from
  source,
Environment (if needed)
  env):

  s=str(source[0])
  t=str(target[0])
  f=os.path.split(str(target[0]))[-1]
  g=os.path.splitext(f)[0]
  genTablesFromTemplate(s, t)
 

Builds a .h from a template – SCons Builder function formation. Function will build the .c as requested. Has the proper inputs, outputs and returns value to be registered SCons environment as a builder function

def h_template_builder(

Target file for this function to build

  target,

File on which target is built from

  source,

Environment (if needed)

  env):

  s=str(source[0])
  t=str(target[0])
  f=os.path.split(str(target[0]))[-1]
  g=os.path.splitext(f)[0]
  if (g == "pic24_uart"):
    genFromTemplate(s, t, 4)
  if (g == "pic24_i2c"):
    genFromTemplate(s, t, 2)
  if (g == "pic24_ecan"):
    genFromTemplate(s, t, 2)
  if (g == "pic24_ports_config"):
    genConfigFromTemplate(s, t)
 

Define and register a template-driven builder for .c files

cbldr = Builder(action = c_template_builder, suffix='.c', src_suffix='.c-template')
env.Append(BUILDERS = {'CTemplate' : cbldr})
 

Define and register a template-driven builder for .h files

hbldr = Builder(action = h_template_builder, suffix='.h', src_suffix='.h-template')
env.Append(BUILDERS = {'HTemplate' : hbldr})
 

Define and register a CVS-driven builder for .csv files

csvbldr = Builder(action = csv_template_builder, suffix='.h', src_suffix='.csv')
env.Append(BUILDERS = {'CSVTemplate' : csvbldr})
 
 
 

Calls to build templates

 

Specify which files are produced by templates

env.HTemplate('../lib/include/pic24_uart','pic24_uart')
env.HTemplate('../lib/include/pic24_i2c','pic24_i2c')
env.HTemplate('../lib/include/pic24_ecan','pic24_ecan')
env.HTemplate('../lib/include/pic24_ports_config','pic24_ports_config')
env.CSVTemplate('../lib/include/pic24_ports_mapping','pic24_devices')
env.CTemplate('../lib/src/pic24_uart','pic24_uart')
env.CTemplate('../lib/src/pic24_i2c','pic24_i2c')
env.CTemplate('../lib/src/pic24_ecan','pic24_ecan')
env.CTemplate('../lib/src/pic24_spi','pic24_spi')

env.Alias('template-build', ['../lib/include/pic24_uart.h',
                             '../lib/include/pic24_i2c.h',
                             '../lib/include/pic24_ecan.h',
                             '../lib/include/pic24_ports_config.h',
                             '../lib/include/pic24_ports_mapping.h',
                             '../lib/src/pic24_uart.c',
                             '../lib/src/pic24_i2c.c',
                             '../lib/src/pic24_spi.c',
                             '../lib/src/pic24_ecan.c']);
 
 
 
 

Header, containing the license as a C comment.

c_license_header = """/*
 * "Copyright (c) 2017 Robert B. Reese, Bryan A. Jones, J. W. Bruce ("AUTHORS")"
 * All rights reserved.
 * (R. Reese, reese_AT_ece.msstate.edu, Mississippi State University)
 * (B. A. Jones, bjones_AT_ece.msstate.edu, Mississippi State University)
 * (J. W. Bruce, jwbruce_AT_ece.msstate.edu, Mississippi State University)
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the authors appear in all copies of this software.
 *
 * IN NO EVENT SHALL THE "AUTHORS" BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE "AUTHORS"
 * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * THE "AUTHORS" SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE "AUTHORS" HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
 *
 * Please maintain this header in its entirety when copying/modifying
 * these files.
 *
 *
 */

/// \\file
"""