forked from SchrodingersGat/KiBoM
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathKiBOM_CLI.py
More file actions
executable file
·210 lines (148 loc) · 6.19 KB
/
Copy pathKiBOM_CLI.py
File metadata and controls
executable file
·210 lines (148 loc) · 6.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@package
KiBOM - Bill of Materials generation for KiCad
Generate BOM in xml, csv, txt, tsv, html or xlsx formats.
- Components are automatically grouped into BoM rows (grouping is configurable)
- Component groups count number of components and list component designators
- Rows are automatically sorted by component reference(s)
- Supports board variants
Extended options are available in the "bom.ini" config file in the PCB directory (this file is auto-generated with default options the first time the script is executed).
"""
from __future__ import print_function
import sys
import os
import argparse
from bomlib.columns import ColumnList
from bomlib.netlist_reader import netlist
from bomlib.bom_writer import WriteBoM
from bomlib.preferences import BomPref
try:
import xlsxwriter # noqa: F401
except:
xlsxwriter_available = False
else:
xlsxwriter_available = True
here = os.path.abspath(os.path.dirname(sys.argv[0]))
sys.path.append(here)
sys.path.append(os.path.join(here, "KiBOM"))
verbose = False
def close(*arg):
print(*arg)
sys.exit(0)
def say(*arg):
# Simple debug message handler
if verbose:
print(*arg)
def isExtensionSupported(filename):
result = False
extensions = [".xml", ".csv", ".txt", ".tsv", ".html"]
if xlsxwriter_available:
extensions.append(".xlsx")
for e in extensions:
if filename.endswith(e):
result = True
break
return result
def writeVariant(variant, subdirectory):
if variant is not None:
pref.pcbConfig = variant.strip().split(',')
print("PCB variant: ", ", ".join(pref.pcbConfig))
# Write preference file back out (first run will generate a file with default preferences)
if not have_cfile:
pref.Write(config_file)
say("Writing preferences file %s" % (config_file,))
# Individual components
components = []
# Component groups
groups = []
# Read out the netlist
net = netlist(input_file, prefs=pref)
# Extract the components
components = net.getInterestingComponents()
# Group the components
groups = net.groupComponents(components)
columns = ColumnList(pref.corder)
# Read out all available fields
for g in groups:
for f in g.fields:
columns.AddColumn(f)
# Don't add 'boards' column if only one board is specified
if pref.boards <= 1:
columns.RemoveColumn(ColumnList.COL_GRP_BUILD_QUANTITY)
say("Removing:", ColumnList.COL_GRP_BUILD_QUANTITY)
# Finally, write the BoM out to file
if write_to_bom:
output_file = args.output
if output_file is None:
output_file = input_file.replace(".xml", ".csv")
output_path, output_name = os.path.split(output_file)
output_name, output_ext = os.path.splitext(output_name)
# KiCad BOM dialog by default passes "%O" without an extension. Append our default
if not isExtensionSupported(output_ext):
output_ext = ".csv"
# Make replacements to custom file_name.
file_name = pref.outputFileName
file_name = file_name.replace("%O", output_name)
file_name = file_name.replace("%v", net.getVersion())
if variant is not None:
file_name = file_name.replace("%V", pref.variantFileNameFormat)
file_name = file_name.replace("%V", variant)
else:
file_name = file_name.replace("%V", "")
if args.subdirectory is not None:
output_path = os.path.join(output_path, args.subdirectory)
if not os.path.exists(os.path.abspath(output_path)):
os.makedirs(os.path.abspath(output_path))
output_file = os.path.join(output_path, file_name + output_ext)
output_file = os.path.abspath(output_file)
say("Output:", output_file)
return WriteBoM(output_file, groups, net, columns.columns, pref)
parser = argparse.ArgumentParser(description="KiBOM Bill of Materials generator script")
parser.add_argument("netlist", help='xml netlist file. Use "%%I" when running from within KiCad')
parser.add_argument("output", default="", help='BoM output file name.\nUse "%%O" when running from within KiCad to use the default output name (csv file).\nFor e.g. HTML output, use "%%O.html"')
parser.add_argument("-n", "--number", help="Number of boards to build (default = 1)", type=int, default=None)
parser.add_argument("-v", "--verbose", help="Enable verbose output", action='count')
parser.add_argument("-r", "--variant", help="Board variant(s), used to determine which components are output to the BoM. Comma-separate for multiple.", type=str, default=None)
parser.add_argument("-d", "--subdirectory", help="Subdirectory within which to store the generated BoM files.", type=str, default=None)
parser.add_argument("--cfg", help="BoM config file (script will try to use 'bom.ini' if not specified here)")
parser.add_argument("-s", "--separator", help="CSV Separator (default ',')", type=str, default=None)
args = parser.parse_args()
input_file = args.netlist
if not input_file.endswith(".xml"):
close("{i} is not a valid xml file".format(i=input_file))
verbose = args.verbose is not None
input_file = os.path.abspath(input_file)
say("Input:", input_file)
# Look for a config file!
# bom.ini by default
ini = os.path.abspath(os.path.join(os.path.dirname(input_file), "bom.ini"))
# Default value
config_file = ini
# User can overwrite with a specific config file
if args.cfg:
config_file = args.cfg
# Read preferences from file. If file does not exists, default preferences will be used
pref = BomPref()
have_cfile = os.path.exists(config_file)
if have_cfile:
pref.Read(config_file)
say("Config:", config_file)
# Pass available modules
pref.xlsxwriter_available = xlsxwriter_available
# Pass various command-line options through
pref.verbose = verbose
if args.number is not None:
pref.boards = args.number
pref.separatorCSV = args.separator
write_to_bom = True
if args.variant is not None:
variants = args.variant.split(';')
else:
variants = [None]
for variant in variants:
result = writeVariant(variant, args)
if not result:
sys.exit(-1)
sys.exit(0)