aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ee/__init__.py18
-rw-r--r--src/ee/kicad/functions.py105
-rw-r--r--src/ee/kicad/make_bom.py164
-rw-r--r--src/ee/kicad/model.py24
-rw-r--r--src/ee/kicad/sch_fact_types.py18
-rw-r--r--src/ee/part/__init__.py52
-rw-r--r--src/ee/part/common_fact_types.py7
-rw-r--r--src/ee/part/fact_keys.py1
-rw-r--r--src/ee/part/requirement.py58
-rw-r--r--src/ee/tools/kicad_make_bom.py4
-rw-r--r--src/ee/tools/part_apply_function.py67
-rw-r--r--src/ee/tools/part_find_requirements.py39
-rw-r--r--src/ee/tools/templates/build.ninja.j221
-rw-r--r--src/ee/xml/uris.py2
14 files changed, 407 insertions, 173 deletions
diff --git a/src/ee/__init__.py b/src/ee/__init__.py
index 93c73f7..2ba113e 100644
--- a/src/ee/__init__.py
+++ b/src/ee/__init__.py
@@ -8,6 +8,11 @@ from ee.formatting import eng_str
__all__ = [
"EeException",
"EeVal",
+ "EeValType",
+ "resistance_type",
+ "capacitance_type",
+ "inductance_type",
+ "power_type",
]
@@ -15,6 +20,18 @@ class EeException(Exception):
pass
+class EeValType(object):
+ def __init__(self, symbol, alternate_symbols):
+ self.symbol = symbol
+ self.alternate_symbols = alternate_symbols
+
+
+resistance_type = EeValType("\u2126", ["ohm"])
+capacitance_type = EeValType("F", [])
+inductance_type = EeValType("H", [])
+power_type = EeValType("W", [])
+
+
@total_ordering
class EeVal(object):
units = ['F',
@@ -102,4 +119,3 @@ class EeVal(object):
def __float__(self):
return self._value * math.pow(10, self._exp)
-
diff --git a/src/ee/kicad/functions.py b/src/ee/kicad/functions.py
new file mode 100644
index 0000000..39614b6
--- /dev/null
+++ b/src/ee/kicad/functions.py
@@ -0,0 +1,105 @@
+import re
+
+import ee.kicad.model
+import ee.kicad.sch_fact_types as kicad_ft
+from ee.kicad import sch_fact_types
+from ee.part import Part
+from ee.part import common_fact_types
+from ee.xml import uris
+
+
+def part_type_from_footprint(lib):
+ if lib == "Capacitor_SMD":
+ return uris.CAPACITOR
+ elif lib == "Resistor_SMD":
+ return uris.RESISTOR
+ elif lib == "Diode_SMD":
+ return uris.DIODE
+ elif lib == "Inductor_SMD":
+ return uris.INDUCTOR
+ elif lib == "Crystal":
+ return uris.CRYSTAL
+
+
+def part_type_from_ref_type(ref_type):
+ if ref_type == "C":
+ return uris.CAPACITOR
+ elif ref_type == "R":
+ return uris.RESISTOR
+ elif ref_type == "D":
+ return uris.DIODE
+ elif ref_type == "L":
+ return uris.INDUCTOR
+ elif ref_type == "X":
+ return uris.CRYSTAL
+ elif ref_type == "Q":
+ return uris.TRANSISTOR
+
+
+def part_type_strategy(part: Part) -> Part:
+ pt = None
+
+ fp_lib = part.facts.get_value(kicad_ft.footprint_library)
+ if fp_lib is not None:
+ pt = part_type_from_footprint(fp_lib)
+
+ ref = part.get_only_schematic_reference()
+ if ref:
+ ref_type, ref_num = ee.kicad.model.split_ref(ref.referenceProp)
+
+ if ref_type:
+ pt = part_type_from_ref_type(ref_type)
+
+ if pt is not None:
+ part.facts.add(common_fact_types.ee_component_type, pt)
+
+ return part
+
+
+def fix_value_strategy(part: Part) -> Part:
+ ref = part.get_only_schematic_reference()
+
+ if not ref:
+ return part
+
+ ref_type, ref_num = ee.kicad.model.split_ref(ref.referenceProp)
+
+ if not ref_num:
+ return part
+
+ v = part.facts.get_value(sch_fact_types.value)
+ if not v:
+ return part
+
+ symbol_name = part.facts.get_value(sch_fact_types.symbol_name)
+
+ if ref_type in ("D", "R", "L", "C") and v == symbol_name:
+ part.remove_fact(uris.make_fact_key("value"))
+
+ if ref_type == "Q" and v == symbol_name and re.match("^Q_[NP]MOS_[DSG]{3}$", symbol_name):
+ part.remove_fact(uris.make_fact_key("value"))
+
+ return part
+
+
+def mpn_strategy(part: Part) -> Part:
+ for field in part.facts.all(sch_fact_types.field):
+
+ k, v = re.split(":", field.value, 1)
+ if k == "mpn":
+ part.add_mpn(v)
+
+ return part
+
+
+def default(part: Part) -> Part:
+ functions = [
+ fix_value_strategy,
+ mpn_strategy,
+ part_type_strategy,
+ ]
+
+ for f in functions:
+ part = f(part)
+
+ return part
diff --git a/src/ee/kicad/make_bom.py b/src/ee/kicad/make_bom.py
index dcdf96b..13b2157 100644
--- a/src/ee/kicad/make_bom.py
+++ b/src/ee/kicad/make_bom.py
@@ -1,145 +1,19 @@
-import pydoc
-import re
from pathlib import Path
-from typing import Optional, List, Callable, Mapping
from xml.dom import minidom
from xml.etree import ElementTree
-from ee import EeException
-from ee.kicad.model import Component
+from ee.kicad import sch_fact_types
from ee.kicad.read_schematic import read_schematics
from ee.kicad.to_bom import to_bom, to_bom_xml
from ee.part import PartDb, save_db, Part
-from ee.xml import types, uris
+from ee.xml import types
__all__ = [
- "StrategyCallable",
- "MakeBomStrategy",
- "apply_strategies",
- "mpn_strategy",
- "part_type_strategy",
- "dpn_strategy_factory",
"make_bom",
]
-StrategyCallable = Callable[[Component, Part], Optional[Part]]
-
-def apply_strategies(c: Component, part: Part, strategies: List[StrategyCallable]):
- for strategy in strategies:
- part = strategy(c, part)
-
- if part is None:
- return
-
- if not isinstance(part, Part):
- raise EeException("Values returned from strategy must be a Part, got {}".format(type(part)))
-
- return part
-
-
-def part_type_from_footprint(fp):
- lib, part_name = fp.split(":")
-
- if lib == "Capacitor_SMD":
- return uris.CAPACITOR
- elif lib == "Resistor_SMD":
- return uris.RESISTOR
- elif lib == "Diode_SMD":
- return uris.DIODE
- elif lib == "Inductor_SMD":
- return uris.INDUCTOR
- elif lib == "Crystal":
- return uris.CRYSTAL
-
-
-def part_type_from_ref_type(ref_type):
- if ref_type == "C":
- return uris.CAPACITOR
- elif ref_type == "R":
- return uris.RESISTOR
- elif ref_type == "D":
- return uris.DIODE
- elif ref_type == "L":
- return uris.INDUCTOR
- elif ref_type == "X":
- return uris.CRYSTAL
- elif ref_type == "Q":
- return uris.TRANSISTOR
-
-
-def part_type_strategy(component: Component, part: Part) -> Part:
- pt = None
-
- fp = component.footprint
- if fp is not None:
- pt = part_type_from_footprint(fp)
-
- if pt is None and component.has_ref_num:
- pt = part_type_from_ref_type(component.ref_type)
-
- if pt is not None:
- part.get_facts().append(types.Fact(uris.make_fact_key("type"), value=pt))
-
- return part
-
-
-def fix_value_strategy(component: Component, part: Part) -> Part:
- if not component.has_ref_num:
- return part
-
- rt = component.ref_type
- v = component.value
-
- if rt in ("D", "R", "L", "C") and v == component.symbol.name:
- part.remove_fact(uris.make_fact_key("value"))
-
- if rt == "Q" and v == component.symbol.name and re.match("^Q_[NP]MOS_[DSG]{3}$", component.symbol.name):
- part.remove_fact(uris.make_fact_key("value"))
-
- return part
-
-
-def mpn_strategy(component: Component, part: Part) -> Part:
- mpn = component.get_field("mpn")
- if mpn is not None:
- part.add_mpn(mpn.value)
-
- return part
-
-
-def dpn_strategy_factory(dpn_mappings: Mapping[str, str]) -> StrategyCallable:
- def dpn_strategy(component: Component, part: Part) -> Part:
- for field_name, distributor in dpn_mappings:
- s = component.get_field(field_name)
- if s is None:
- continue
-
- part.add_spn(s.value)
-
- return part
-
- return dpn_strategy
-
-
-class MakeBomStrategy():
- def __init__(self):
- self.dpn_mappings = {}
- self.default_strategies = [
- fix_value_strategy,
- mpn_strategy,
- part_type_strategy,
- dpn_strategy_factory(self.dpn_mappings),
- ]
-
- def process_part(self, component: Component, part: Part):
- return self.default_process_part(component, part)
-
- def default_process_part(self, component: Component, part: Part):
- return apply_strategies(component, part, self.default_strategies)
-
-
-def work(sch, out: Path, strategy: MakeBomStrategy, new_mode, pretty):
+def work(sch, out: Path, new_mode, pretty):
def strip(s):
s = (s or "").strip()
@@ -165,39 +39,31 @@ def work(sch, out: Path, strategy: MakeBomStrategy, new_mode, pretty):
value = strip(c.value)
if value:
- part.facts.add(uris.make_fact_key("value"), value)
+ part.facts.add(sch_fact_types.value, value)
+
+ if c.symbol.library:
+ part.facts.add(sch_fact_types.symbol_library, c.symbol.library)
+ part.facts.add(sch_fact_types.symbol_name, c.symbol.name)
footprint = strip(c.footprint)
if footprint:
- part.facts.add(uris.make_fact_key("footprint"), footprint)
+ part.facts.add(sch_fact_types.footprint, footprint)
i = footprint.find(":")
if i >= 0:
lib, footprint = footprint.split(":")
- part.facts.add(uris.make_fact_key("kicad-schematic-library"), lib)
- part.facts.add(uris.make_fact_key("kicad-schematic-footprint"), footprint)
+ part.facts.add(sch_fact_types.footprint_library, lib)
+ part.facts.add(sch_fact_types.footprint_name, footprint)
- part = strategy.process_part(c, part)
-
- if part is None:
- continue
+ for f in c.named_fields:
+ part.facts.add(sch_fact_types.field, "{}:{}".format(f.name, f.value))
parts.add_entry(part, True)
save_db(out, parts)
-def make_bom(sch_file: Path, out_dir: Path, strategy_name: str, new_mode: bool, pretty: bool):
+def make_bom(sch_file: Path, out_dir: Path, new_mode: bool, pretty: bool):
sch = read_schematics(str(sch_file))
- make_bom_strategy_factory = pydoc.locate(strategy_name)
-
- if not callable(make_bom_strategy_factory):
- raise EeException("Not a callable: {}, is a {}".format(strategy_name, type(make_bom_strategy_factory)))
-
- make_bom_strategy = make_bom_strategy_factory() # type: MakeBomStrategy
-
- if not isinstance(make_bom_strategy, MakeBomStrategy):
- raise EeException("Not a MakeBomStrategy: {}, is a {}".format(strategy_name, type(make_bom_strategy)))
-
- work(sch, out_dir, make_bom_strategy, new_mode, pretty)
+ work(sch, out_dir, new_mode, pretty)
diff --git a/src/ee/kicad/model.py b/src/ee/kicad/model.py
index 295a40a..1f6cf96 100644
--- a/src/ee/kicad/model.py
+++ b/src/ee/kicad/model.py
@@ -1,6 +1,6 @@
import re
from functools import total_ordering
-from typing import List, Set
+from typing import List, Set, Tuple, Optional
from ee import EeException
@@ -62,7 +62,16 @@ class Symbol(object):
self.name = symbol
else:
self.library = symbol[0:i]
- self.name = symbol[i+1:]
+ self.name = symbol[i + 1:]
+
+
+def split_ref(ref: str) -> Tuple[Optional[str], Optional[int]]:
+ r = re.compile("([^0-9]+)(.+)")
+ try:
+ parts = r.split(ref)
+ return parts[1], int(parts[2])
+ except ValueError:
+ return None, None
@total_ordering
@@ -76,14 +85,7 @@ class Component(object):
self._ref = ref
self._fields = fields # type List[ComponentField]
- r = re.compile("([^0-9]+)(.+)")
- try:
- parts = r.split(self._ref)
- self._ref_type = parts[1]
- self._ref_num = int(parts[2])
- except ValueError:
- self._ref_type = None
- self._ref_num = None
+ self._ref_type, self._ref_num = split_ref(self._ref)
def __eq__(self, o: object) -> bool:
other = o # type: Component
@@ -157,7 +159,7 @@ class Component(object):
return list(self._fields)
@property
- def named_fields(self):
+ def named_fields(self) -> List[ComponentField]:
return [f for f in self._fields if f.name]
def get_field(self, name) -> ComponentField:
diff --git a/src/ee/kicad/sch_fact_types.py b/src/ee/kicad/sch_fact_types.py
new file mode 100644
index 0000000..c9ea918
--- /dev/null
+++ b/src/ee/kicad/sch_fact_types.py
@@ -0,0 +1,18 @@
+from ee.part import FactType
+
+
+def make_fact_key(key: str):
+ return "http://purl.org/ee/kicad-sch-fact-type#{}".format(key)
+
+
+value = FactType(make_fact_key("value"), "Value")
+component = FactType(make_fact_key("component"), "Value")
+
+symbol_library = FactType(make_fact_key("symbol-library"), "Value")
+symbol_name = FactType(make_fact_key("symbol-name"), "Value")
+
+footprint = FactType(make_fact_key("footprint"), "Value")
+footprint_library = FactType(make_fact_key("footprint-library"), "Value")
+footprint_name = FactType(make_fact_key("footprint-name"), "Value")
+
+field = FactType(make_fact_key("field"), "Value")
diff --git a/src/ee/part/__init__.py b/src/ee/part/__init__.py
index ff6c7f1..a2face8 100644
--- a/src/ee/part/__init__.py
+++ b/src/ee/part/__init__.py
@@ -1,11 +1,13 @@
from pathlib import Path
from typing import List, Optional, Iterator, Union
-from ee import EeException
+from ee import EeException, EeValType
from ee.money import Money
from ee.xml import types
__all__ = [
+ "FactType",
+ "EeValueFactType",
"Part",
"PartDb",
"load_db",
@@ -13,6 +15,18 @@ __all__ = [
]
+class FactType(object):
+ def __init__(self, uri: str, label: str):
+ self.uri = uri
+ self.label = label
+
+
+class EeValueFactType(FactType):
+ def __init__(self, uri: str, label, ee_type: EeValType):
+ super().__init__(uri, label)
+ self.ee_type = ee_type
+
+
class Reference(object):
pass
@@ -324,12 +338,42 @@ class Part(object):
self.xml.factsProp.fact = [f for f in self.xml.factsProp.fact if f.keyProp != key]
+class Fact(object):
+ def __init__(self, xml: types.Fact):
+ self.xml = xml
+
+ @property
+ def key(self) -> str:
+ return self.xml.keyProp
+
+ @property
+ def value(self) -> str:
+ return self.xml.valueProp
+
+
class Facts(object):
- def __init__(self, part):
+ def __init__(self, part: Part):
self.part = part
- def add(self, key: str, value: str, label=None):
- self.part.get_facts().append(types.Fact(key=key, label=label, value=value))
+ @staticmethod
+ def _get_key(key: Union[str, FactType]):
+ return key if isinstance(key, str) else key.uri
+
+ def add(self, key: Union[str, FactType], value: str, label=None):
+ k = self._get_key(key)
+ self.part.get_facts().append(types.Fact(key=k, label=label, value=value))
+
+ def get(self, key: Union[str, FactType]) -> Fact:
+ k = self._get_key(key)
+ return next((Fact(f) for f in self.part.get_facts() if f.keyProp == k), None)
+
+ def all(self, key: Union[str, FactType]) -> List[Fact]:
+ k = self._get_key(key)
+ return [Fact(f) for f in self.part.get_facts() if f.keyProp == k]
+
+ def get_value(self, key: Union[str, FactType]) -> Optional[str]:
+ k = self._get_key(key)
+ return next((f.valueProp for f in self.part.get_facts() if f.keyProp == k), None)
class Entry(object):
diff --git a/src/ee/part/common_fact_types.py b/src/ee/part/common_fact_types.py
new file mode 100644
index 0000000..12c099e
--- /dev/null
+++ b/src/ee/part/common_fact_types.py
@@ -0,0 +1,7 @@
+import ee
+from ee.part import EeValueFactType, fact_keys
+
+resistance = EeValueFactType(fact_keys.resistance, "Resistance", ee.resistance_type)
+capacitance = EeValueFactType(fact_keys.capacitance, "Capacitance", ee.capacitance_type)
+
+ee_component_type = EeValueFactType(fact_keys.ee_component_type, "EE component type", ee.capacitance_type)
diff --git a/src/ee/part/fact_keys.py b/src/ee/part/fact_keys.py
index f1f02c5..65dc699 100644
--- a/src/ee/part/fact_keys.py
+++ b/src/ee/part/fact_keys.py
@@ -1,3 +1,4 @@
+ee_component_type = "http://purl.org/ee/fact-type/ee-component-type"
capacitance = "http://purl.org/ee/fact-type/capacitance"
max_voltage = "http://purl.org/ee/fact-type/voltage"
# https://en.wikipedia.org/wiki/Ceramic_capacitor#Class_2_ceramic_capacitors
diff --git a/src/ee/part/requirement.py b/src/ee/part/requirement.py
new file mode 100644
index 0000000..a9381df
--- /dev/null
+++ b/src/ee/part/requirement.py
@@ -0,0 +1,58 @@
+from typing import List
+
+from ee.part import Part, fact_keys, FactType, common_fact_types
+
+
+class Requirement(object):
+ def __init__(self, part: Part):
+ self.part = part
+
+
+class EqualRequirement(Requirement):
+ def __init__(self, part: Part, fact_type: FactType, value: str):
+ super().__init__(part)
+ self.fact_type = fact_type
+ self.value = value
+
+ def __str__(self):
+ return "{}.{} == {}".format(self.part.printable_reference, self.fact_type, self.value)
+
+
+class MinRequirement(Requirement):
+ def __init__(self, part: Part, fact_type: FactType, value: str):
+ super().__init__(part)
+ self.fact_type = fact_type
+ self.value = value
+
+ def __str__(self):
+ return "{}.{} == {}".format(self.part.printable_reference, self.fact_type, self.value)
+
+
+class MaxRequirement(Requirement):
+ def __init__(self, part: Part, fact_type: FactType, value: str):
+ super().__init__(part)
+ self.fact_type = fact_type
+ self.value = value
+
+ def __str__(self):
+ return "{}.{} == {}".format(self.part.printable_reference, self.fact_type, self.value)
+
+
+class PartAnalysis(object):
+ def __init__(self, part: Part, requirements: List[Requirement]):
+ self.part = part
+ self.requirements = requirements
+
+
+def analyze_requirements(part: Part) -> PartAnalysis:
+ rs = []
+
+ resistance = part.find_fact(common_fact_types.resistance.uri)
+ if resistance:
+ rs.append(EqualRequirement(part, common_fact_types.resistance, resistance.valueProp))
+
+ capacitance = part.find_fact(common_fact_types.capacitance.uri)
+ if capacitance:
+ rs.append(EqualRequirement(part, common_fact_types.capacitance, capacitance.valueProp))
+
+ return PartAnalysis(part, rs)
diff --git a/src/ee/tools/kicad_make_bom.py b/src/ee/tools/kicad_make_bom.py
index 60509a3..0e36c6d 100644
--- a/src/ee/tools/kicad_make_bom.py
+++ b/src/ee/tools/kicad_make_bom.py
@@ -22,6 +22,4 @@ args = parser.parse_args()
new_mode = True
-strategy = args.strategy if args.strategy else "ee.kicad.make_bom.MakeBomStrategy"
-
-make_bom(Path(args.sch), Path(args.out), strategy, new_mode, pretty)
+make_bom(Path(args.sch), Path(args.out), new_mode, pretty)
diff --git a/src/ee/tools/part_apply_function.py b/src/ee/tools/part_apply_function.py
new file mode 100644
index 0000000..bece364
--- /dev/null
+++ b/src/ee/tools/part_apply_function.py
@@ -0,0 +1,67 @@
+import argparse
+import pydoc
+from pathlib import Path
+from typing import List
+
+from ee import tools, EeException
+from ee.part import Part, PartDb, load_db, save_db
+from ee.project import Project
+
+
+def load_functions(function_names):
+ functions = []
+ for fn in function_names:
+ f = pydoc.locate(fn)
+
+ if f is None:
+ raise EeException("Could not load function: {}".format(fn))
+
+ if not callable(f):
+ raise EeException("Not a callable: {}, is a {}".format(fn, type(f)))
+
+ functions.append((fn, f))
+ return functions
+
+
+def work(in_path: Path, out_path: Path, report_path: Path, function_names: List[str]):
+ functions = load_functions(function_names)
+
+ in_parts = load_db(in_path)
+
+ parts = PartDb()
+
+ tools.mk_parents(report_path)
+ with report_path.open("w") as rpt:
+ for xml in in_parts.iterparts():
+ part = Part(xml)
+
+ for name, f in functions:
+ part = f(part)
+
+ save_db(out_path, in_parts)
+
+
+parser = argparse.ArgumentParser()
+
+parser.add_argument("--in",
+ dest="in_path",
+ required=True,
+ metavar="PART DB")
+
+parser.add_argument("--out",
+ required=True,
+ metavar="REQUIREMENTS")
+
+parser.add_argument("--function",
+ required=True,
+ nargs="*",
+ metavar="FUNCTION")
+
+parser.add_argument("--execution",
+ default="default")
+
+args = parser.parse_args()
+
+project = Project.load()
+report = project.report_dir / "apply-function" / (args.execution + ".rst")
+work(Path(args.in_path), Path(args.out), report, args.function)
diff --git a/src/ee/tools/part_find_requirements.py b/src/ee/tools/part_find_requirements.py
new file mode 100644
index 0000000..032db02
--- /dev/null
+++ b/src/ee/tools/part_find_requirements.py
@@ -0,0 +1,39 @@
+import argparse
+from pathlib import Path
+
+from ee.part import requirement, Part, PartDb, load_db, save_db
+
+
+def work(in_path: Path, out_path: Path):
+ in_parts = load_db(in_path)
+
+ with out_path.open("w") as f:
+ print("<root>", file=f)
+ for xml in in_parts.iterparts():
+ part = Part(xml)
+
+ analysis = requirement.analyze_requirements(part)
+
+ print("Part: {}. Found {} requirements".format(analysis.part.printable_reference,
+ len(analysis.requirements)), file=f)
+
+ for r in analysis.requirements:
+ print(" {}".format(r), file=f)
+
+ print("</root>", file=f)
+
+
+parser = argparse.ArgumentParser()
+
+parser.add_argument("--in",
+ dest="in_path",
+ required=True,
+ metavar="PART DB")
+
+parser.add_argument("--out",
+ required=True,
+ metavar="REQUIREMENTS")
+
+args = parser.parse_args()
+
+work(Path(args.in_path), Path(args.out))
diff --git a/src/ee/tools/templates/build.ninja.j2 b/src/ee/tools/templates/build.ninja.j2
index 76da4cf..1f9ba35 100644
--- a/src/ee/tools/templates/build.ninja.j2
+++ b/src/ee/tools/templates/build.ninja.j2
@@ -19,12 +19,19 @@ rule kicad-gerber
rule kicad-make-bom
description = kicad-make-bom $out
- command = $ee kicad-make-bom --sch $sch --out $out $strategy
+ command = $ee kicad-make-bom --sch $sch --out $out
rule pn-part-search-list
description = pn-part-search-list supplier: $supplier
command = $ee pn-part-search-list --in $in --out $out --supplier $supplier
+rule part-apply-function
+ command = $ee part-apply-function --in $in --out $out $functions
+
+rule part-find-requirements
+ description = part-find-requirements
+ command = $ee part-find-requirements --in $in --out $out
+
rule digikey-search-parts
description = digikey-search-parts
command = $ee digikey-search-parts --in $in --out $out
@@ -69,11 +76,17 @@ build {{ gerber_zip }}: kicad-gerber $pcb
{%- endif %}
{% if sch is defined -%}
-build ee/sch.xml: kicad-make-bom $sch
-{%- if project.cfg["kicad-project"]["strategy"] %}
- strategy = --strategy {{ project.cfg["kicad-project"]["strategy"] }}
+build ee/kicad-sch.xml: kicad-make-bom $sch
+build ee/sch.xml: part-apply-function ee/kicad-sch.xml
+{%- if project.cfg["kicad-project"]["functions"] %}
+ functions = --function {{ project.cfg["kicad-project"]["functions"] }}
+{%- else %}
+ functions = --function ee.kicad.functions.default
{%- endif %}
{%- endif %}
+
+build ee/requirements.xml: part-find-requirements ee/sch.xml
+
{% for s in distributors %}
# Supplier {{ s }}
build ee/{{ s }}/pn-part-search-list.xml: pn-part-search-list ee/sch.xml
diff --git a/src/ee/xml/uris.py b/src/ee/xml/uris.py
index f168c59..bb961df 100644
--- a/src/ee/xml/uris.py
+++ b/src/ee/xml/uris.py
@@ -1,6 +1,6 @@
from typing import Optional
-# Values for `..#type` facts
+# Values for `..#ee-component-type` facts
CAPACITOR = "http://purl.org/ee/part-type#capacitor"
RESISTOR = "http://purl.org/ee/part-type#resistor"
DIODE = "http://purl.org/ee/part-type#diode"