blob: c226313395143dfc6d229c5487e6f59773be8fed [file] [log] [blame]
Tim Edwards5ed94ff2022-06-16 15:07:53 -04001#!/usr/bin/env python3
2#--------------------------------------------------------------------
3#
4# save_commit_refs.py
5#
6# This file is used to annotate a standard tech information JSON
7# file. Each JSON file should have a section called "reference"
8# (if not, this script exits without doing anything). The
9# "reference" section contains the commit hashes of repositories
10# used by the developer for generating the PDK, creating a
11# reference against which issues can be compared. The "reference"
12# section is fixed and is not modified by replacement like the
13# rest of the JSON file. It is the duty of the PDK developer to
14# update the references section periodically by running "make
Tim Edwardscc0029b2022-08-01 18:11:30 -040015# reference".
Tim Edwards5ed94ff2022-06-16 15:07:53 -040016#
17#--------------------------------------------------------------------
18# Usage:
19#
20# save_commit_refs.py <filename> [-D<variable> ...]
21#
22# Where <filename> is the name of a technology JSON file, and
23# <variable> may be a <keyword>=<hash_value> pair only.
24# <keyword> corresponds to the variable name in a technology Makefile
25# for a commit hash that appears somewhere in <filename>. <value>
26# is the commit hash value corresponding to <keyword>.
27#
28# The JSON file "reference" section is expected to have entries of
29# the form
30# "<name>": "<commit_hash>"
31#
32# where <name> must also appear elsewhere in <filename> in an entry
33# of the form
34# "<name>": "<keyword>"
35#
36# Then, where "-D<keyword>=<hash_value>" has been passed to the
37# script on the command line, the entry in the "reference" section
38# is changed by replacing "<commit_hash>" with "<hash_value>".
39#
40# The output of the script overwrites <filename> with the modified
41# contents.
42#
43# Example:
Tim Edwardscc0029b2022-08-01 18:11:30 -040044# sky130.json has an entry in "reference":
Tim Edwards5ed94ff2022-06-16 15:07:53 -040045# "magic": "fe2eb6d3906ed15ade0e7a51daea80dd4e3846e2"
46# reflecting the git commit number of the program "magic" at
47# the time the developer last ran save_commit_refs.py.
48# Elsewhere in sky130.json, the following line appears:
49# "magic": "MAGIC_COMMIT"
50# If save_commit_refs.py is called as:
51# save_commit_refs.py sky130.json -DMAGIC_COMMIT=abcdef
Tim Edwardscc0029b2022-08-01 18:11:30 -040052# then the line in "reference" will be changed to:
Tim Edwards5ed94ff2022-06-16 15:07:53 -040053# "magic": "abcdef"
54#
55#--------------------------------------------------------------------
56
57import os
58import re
59import sys
60
61def runsubs(keys, defines, inputfile, outputfile):
62
63 ifile = False
64 ifile = open(inputfile, 'r')
65 if not ifile:
66 print("Error: Cannot open file " + inputfile + " for reading.\n", file=sys.stderr)
67 return
68
69 indist = False
70
71 filetext = ifile.readlines()
72 lastline = []
73
74 # Input file has been read, so close it.
75 ifile.close()
76
77 # Open output file
78 if not outputfile:
79 outputfile = inputfile
80
81 ofile = open(outputfile, 'w')
82 if not ofile:
83 print("Error: Cannot open file " + outputfile + " for writing.\n", file=sys.stderr)
84 return
85
86 keyrex = re.compile('[ \t]*"([^"]+)":[ \t]*"[^"]+"')
87 valuerex = re.compile('[ \t]*"[^"]+":[ \t]*"([a-z0-9]+)"')
88 distdefs = {}
89 defs = []
90
91 for line in filetext:
92 newline = line
93
94 if indist == False:
Tim Edwardscc0029b2022-08-01 18:11:30 -040095 if '"reference":' in line:
Tim Edwards5ed94ff2022-06-16 15:07:53 -040096 indist = True
97 else:
98 # Find values matching keywords
99 for key in keys:
100 if key in line:
101 kmatch = keyrex.match(line)
102 if kmatch:
103 print('Found match "' + kmatch.group(1) + '" for key "' + key + '"')
104 distdefs[kmatch.group(1)] = defines[key]
105 defs.append(kmatch.group(1))
106 else:
107 # Find definitions matching keywords
108 for defx in defs:
109 if defx in line:
110 vmatch = valuerex.match(line)
111 if vmatch:
112 value = distdefs[defx]
113 newline = line.replace(vmatch.group(1), value)
114
115 print(newline, file=ofile, end='')
116
117 ofile.close()
118 return
119
120def printusage(progname):
121 print('Usage: ' + progname + ' filename [-options]')
122 print(' Options are:')
123 print(' -help Print this help text.')
124 print(' -quiet Stop without error if input file is not found.')
125 print(' -ccomm Remove C comments in /* ... */ delimiters.')
126 print(' -D<def> Define word <def> and set its value to 1.')
127 print(' -D<def>=<val> Define word <def> and set its value to <val>.')
128 print(' -I<dir> Add <dir> to search path for input files.')
129 return
130
131if __name__ == '__main__':
132
133 # Parse command line for options and arguments
134 options = []
135 arguments = []
136 for item in sys.argv[1:]:
137 if item.find('-', 0) == 0:
138 options.append(item)
139 else:
140 arguments.append(item)
141
142 if len(arguments) > 0:
143 inputfile = arguments[0]
144 if len(arguments) > 1:
145 outputfile = arguments[1]
146 else:
147 outputfile = []
148 else:
149 printusage(sys.argv[0])
150 sys.exit(0)
151
152 defines = {}
153 keys = []
154 for item in options:
155 result = item.split('=')
156 if result[0] == '-help':
157 printusage(sys.argv[0])
158 sys.exit(0)
159 elif result[0][0:2] == '-D':
160 keyword = result[0][2:]
161 value = result[1]
162 defines[keyword] = value
163 keys.append(keyword)
164 else:
165 print('Bad option ' + item + ', options are -help, -D<def>\n')
166 sys.exit(1)
167
168 if not os.path.isfile(inputfile):
169 if not quiet:
170 print("Error: No input file " + inputfile + " found.")
171 else:
172 sys.exit(0)
173
174 runsubs(keys, defines, inputfile, outputfile)
175
176 # Set mode of outputfile to be equal to that of inputfile (if not stdout)
177 if outputfile:
178 statinfo = os.stat(inputfile)
179 mode = statinfo.st_mode
180 os.chmod(outputfile, mode)
181
182 sys.exit(0)