Background async library loading workers hold raw LIBRARY_TABLE_ROW
pointers into the LIBRARY_TABLE::m_rows vector. When ProjectChanged()
calls LoadProjectTables(), it destroys the old LIBRARY_TABLE objects
via unique_ptr replacement.
The workers were not aborted until after the tables were destroyed,
creating a window where workers could access freed memory.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/23480
The schemaPath() helper used __FILE__ to navigate from the test source
file up to the repository root, then down to resources/schemas/. This
breaks when dpkg-buildflags injects -ffile-prefix-map which remaps the
absolute source path to a relative one. The Normalize() call then
resolves the relative path against the test working directory (inside
the build tree), producing a bogus path that cannot reach the source
tree's schema files.
Replace the __FILE__ navigation with QA_SRC_ROOT compile definition
Savers now serialize to in-memory HISTORY_FILE_DATA on the UI thread,
then Prettify + file writes + git stage/diff/commit run on a background
thread via GetKiCadThreadPool(). An atomic skip-if-busy flag prevents
overlapping saves; WaitForPendingSave() ensures clean shutdown.
Extracted FormatBoardToFormatter and FormatSchematicToFormatter so
SaveToHistory can serialize without touching disk. PROJECT savers use
sourcePath for file-copy semantics since project files are small.
The old FP backside logic was to flip anything on the back layer.
However this doesn't work for FPs with things on both sides, which
are rare but do exist (BB-AI-64 power socket for example has silk
on both sides).
In particular, there are some parts that have courtyards on the bottom
for top-side SMD fps:
- MainBoard - the U1 courtyard is on the back - A mistake I think,
but the layer class is indeed 0x09/0xfa - PLACE_BOUND_BOTTOM.
- VCU118 - U1 has front and back courtyards too.
- BeagleBone Black has a back CY for a thernal pad
So this test is not universally true. Restrict to a board (EVK
BaseBoard) that hs back SMD parts, but no violations of the
principle.
0x22 is a rare field wedon't see much. BB-AI is a V172 file, but
the 0x22 field has this element, so it must be at least V172, not
V174.
Add a block test to really nail this down (at least for V172!)
This allows BB-AI-64 to load successfully.
saveSelectionToDesignBlock() used Clone() for groups, which creates a
shallow copy where m_items still references original board items. The
code then separately cloned children via RunOnChildren(cloneAndAdd) on
the original group, but never updated the cloned group's m_items to
reference the new clones. This resulted in design blocks with groups
whose member UUIDs referenced nonexistent items. On reload, the group
would have no children.
Additionally, addNetIfNeeded was called on the original group's children
rather than the clones, corrupting net assignments on the real board.
Replace the shallow Clone()+manual child cloning with DeepClone(), which
recursively clones all children with correct group ownership. The deep
clone and its children are then added to the temporary board for
serialization.
Related https://gitlab.com/kicad/code/kicad/-/issues/23356
When multiple top-level sheets in a multi-root schematic reference the
same sub-sheet file, each sub-sheet must share a single SCH_SCREEN
object. Previously, loadHierarchy() only searched the current root
sheet's hierarchy for an existing screen, so screens loaded by earlier
top-level sheets were never found. This caused each top-level sheet to
create its own copy of the shared sub-sheet's screen, leading to data
loss on save as later writes would overwrite earlier ones.
Add m_loadedRootSheets to SCH_IO_KICAD_SEXPR to track root sheets from
prior LoadSchematicFile() calls within the same IO instance. During
loadHierarchy(), after the existing search fails, also search through
previously loaded root sheets. The cache is cleared when the SCHEMATIC
pointer changes, preventing stale references when the IO object is
reused for unrelated loads.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/23403
Extract duplicated helpers from remote provider files into shared
utilities. Split sendRpcMessage into purpose-specific functions.
Implement standard nonce-based exchange for re-auth. Provide
additional models for more efficient model/symbol/fp/etc using
external URLs. This allows us to better handle connection errors,
retries and large data packets.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/23381
The Move command grabbed footprints from the nearest grid point rather
than the footprint anchor. This was caused by the reference point being
grid-aligned at drag start (commit 89be3fd390), which was a workaround
for fractional-nanometer positioning errors with non-page display origin.
Fix the root cause instead: AlignGrid(VECTOR2I, VECTOR2D, VECTOR2D)
relied on implicit VECTOR2D->VECTOR2I truncation to pass grid parameters
to computeNearest. For grid sizes that aren't exact in IEEE 754 double
(e.g., 0.254mm = 10 mil becomes 253999.999... instead of 254000), the
truncation produces incorrect grid sizes. Use KiROUND for the conversion
so all grid operations produce exact grid multiples.
With AlignGrid returning correct positions, the move tool no longer needs
to grid-align its reference point. Restore the anchor-based grab and the
GetMoveWarpsCursor() preference that 89be3fd390 removed.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/23308
This means it can read all supported file types from the command line as
filename. Stream input (for fuzzing) still requires the plugin to be
specified and for the plugin to support a stream interface.
This is useful for putting profilers onto importers without confusing it
with GUI init, libraries, etc.
When generating paths between board edge nodes from different Edge.Cuts
shapes (e.g., corners of two rectangular slots), both parents were added
to the IgnoreForTest list.
For POINT shapes (rectangle/polygon corners), the segments_intersect
function already excludes shared-endpoint intersections, so skipping
the parent shape is unnecessary. Only circle board edge shapes need
their parent in IgnoreForTest because segmentIntersectsCircle has no
endpoint exclusion.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/23364
Zone-to-zone clearance knockouts and higher-priority zone subtraction
happen after the initial deflate/inflate minimum-width enforcement
cycle. These knockouts can carve out material and leave thin copper
strips that violate the zone's minimum width constraint.
Add a second deflate/inflate pass that runs only when knockouts were
actually applied. The result is intersected with the pre-deflate
boundary to prevent re-inflation into cleared areas (keepouts, pad
clearances, etc.).
Fixes https://gitlab.com/kicad/code/kicad/-/issues/23332
This is a tricky function that really needs a test around it.
Also it has several assumptions and traps:
- It will not work if the zones have multiple polygons in the poly sets
- It will not work if any polygon has line chains in a different order
- It is quadratic in number of zones and the compare itself is
superlinear (Simplify is called, evilly in a const function, which is
quadratic) and in a cyclic comparison, sort, which is presumably nlog-n
ish.
However, at least for the quadratic nature, profiling shows this to be
absolutely insignificant even for the VCU118 board.
boost_test_print_type was introduced in 1.64 and we can tidy
up/standardise the old style printers in boost::test_tools::tt_detail
and "normal" stream operators which risk conflicts if one appears in
a header in future.
This isn't a major hotspot, but Flip does show up a little on
a profile of board loading, so it not nothing, and it's quite
easy to maintain the existing geometry here.
Also maintains the hull on rotation.
And add some tests around the courtyard and hull class invariants.
The S-expression serializer only writes variant attributes that differ
from the symbol/sheet default values. When deserializing, the parser
created variants with all-false attribute defaults from the VARIANT
constructor. This meant any variant attribute matching the owner's
default was silently lost on save/reload.
Call InitializeAttributes() on each variant immediately after
construction in the parser so that unparsed attributes inherit the
correct default values from the owning symbol or sheet.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/23347
The PARAM_SCALED lower bound for min_silk_clearance was 0.0mm, which
caused negative values to be reset to the default on project reload.
Users set negative silk clearance to suppress DRC violations for
footprints that intentionally overlap their silkscreen.
Changed the lower bound from 0.0mm to -10.0mm so negative values
survive the JSON round-trip through the project file.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/23327
A local label that only connects to other labels or hierarchical sheet
pins without any component pin on the same sheet serves no local purpose
and is likely mislabeled. Extend the existing bus-member check to all
local labels so that ercCheckLabels() flags these cases as
ERCE_LABEL_NOT_CONNECTED.
The allPins > 1 guard prevents double-reporting with
ERCE_LABEL_SINGLE_PIN which already covers the allPins == 1 case.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/23346
cacheLib() was unconditionally calling Load() for new caches even when
buffering was enabled, introduced by e013691d02 to fix issue #22191.
This broke "Save Library As" because the target file doesn't exist yet,
causing Load() to throw IO_ERROR. The exception was caught but no
symbols were added to the cache, resulting in an empty file
Additionally, the unbuffered library save path was passing the library
nickname instead of the target file path to the plugin's LoadSymbol/
SaveSymbol calls. While this was always wrong, it was masked before
e013691d02 because buffering mode skipped Load() entirely.
Fix cacheLib() to only call Load() for new caches when the library file
actually exists on disk, and fix the unbuffered save path to use the
target file path.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/23337
PADSTACK in NORMAL mode stores one shape for all layers. When front
and back copper have different shapes, the second convertPadShape()
call overwrites the first. Pre-scanning copper layers and switching
to FRONT_INNER_BACK mode when shapes differ fixes this.
PADS stores connection style per-pin, not per-zone. The zone default
was THERMAL; it is now FULL (solid). Per-pad THERMAL overrides are
set from RT/ST thermal relief entries in the pad stack, carrying
over spoke width and angle.
The parser read NET_CLASS names and memberships but skipped per-class
RULE_SET entries for clearance and track width. RULE_SET parsing now
handles non-default rule sets and matches them to netclasses via the
FOR/NET_CLASS declaration.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/23352
The Bom and Avl top-level sections were emitted without corresponding
BomRef/AvlRef elements in the Content section, which IPC-2581C 4.1.1.2
requires as cross-references. Insert them in schema order after
LayerRef and before Dictionary elements.
OtherSideView was appended to Package during courtyard processing,
before Pin elements, violating the IPC-2581C schema sequence. Build
it off-tree and attach after all Pin elements.
Also fix packages without a primary courtyard missing the required
Outline element by always emitting a bounding hull fallback when the
primary courtyard is empty.
Footprints with blank references produced schema-invalid XML with empty
refDes, RefDes/@name, and PinRef/@componentRef attributes. Generate a
stable UUID-based identifier when GetReference() is empty.
The text-based rule editor incorrectly warned about "duplicate conditions"
when two rules shared the same condition expression but had different layer
scopes (e.g., "(layer outer)" vs "(layer inner)"). Include the layer source
in the deduplication key so layer-differentiated rules are not flagged.
The graphical rule editor's save path had three related issues:
generateLayerClause() emitted multi-token "(layer "F.Cu" "B.Cu")" format
that the parser cannot handle (it expects a single token). Now detects
ExternalCuMask/InternalCuMask and emits "(layer outer)"/"(layer inner)".
The merge grouping key only used (ruleName, condition), causing rules with
different layer scopes to be incorrectly merged. Now includes layerSource.
The same-name validation in the graphical editor also excluded layer scope.
Add regression test verifying EvalRules returns correct per-layer clearances
for layer-scoped custom rules and zone fill produces no DRC violations.
Two bugs caused the creepage checker to miss violations through arcs.
segmentIntersectsArc did not exclude shared-endpoint intersections,
so paths starting at arc endpoints were incorrectly rejected as
crossing a board edge, unlike segments_intersect which already had
this exclusion. The temp_nodes filter in testCreepage also had
inverted conditions, leaving ConnectChildren unreachable and
preventing point-on-arc nodes from linking to their parent arc
surface.
Adds a regression test with a slot between HV and GND pads requiring
8mm creepage distance.
Three bugs prevented creepage paths from navigating around board
edge shapes (segments, rectangles, polygons):
1. TransformEdgeToCreepShapes never called SetParent() for SEGMENT,
RECTANGLE, and POLY shapes. Their CREEP_SHAPE parent remained
nullptr, causing GeneratePaths to exclude them from all path
generation since null-parent nodes were filtered out.
2. CU_SHAPE_SEGMENT never set m_pos (defaulting to 0,0) and
GetRadius() returned 0. The distance filter in GeneratePaths
compared center positions with a bounding sphere, so all
segment-to-edge-point pairs were rejected as too far apart.
Set m_pos to the segment midpoint and override GetRadius() to
return half the segment length plus half the width.
3. GeneratePaths only generated work items between nodes of
different parent BOARD_ITEMs. Nodes sharing the same parent
(e.g. multiple edge segments of the same gr_rect slot) were
never paired, so the algorithm could not route around slot
geometry. Added same-parent pairing for nodes with different
CREEP_SHAPE parents.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/20480
Connector pins (e.g. J12-1 through J12-53) were imported as
separate single-pin symbols instead of units of one shared
symbol. Add BuildMultiUnitConnectorSymbol which creates a
multi-unit LIB_SYMBOL where each unit has the connector decal
graphics and one pin with the correct number.
Multi-gate parts like TL082 (U1-A, U1-B) appeared as separate
symbols because AddHierarchicalReference was called with the
unsuffixed reference "U1-A" instead of the stripped "U1".
Also move the connector SetRef call to after ApplyPartAttributes,
which only strips alphabetic gate suffixes and would otherwise
override the base reference back to "J12-1".
LAYER_RANGE enters an infinite loop when constructed with
layer_count < 2. The next_layer() arithmetic underflows,
producing unreachable termination values, so the iterator
increments forever. This happens during board import when a
file produces a board with zero copper layers.
Clamp layer_count to a minimum of 2 in the LAYER_RANGE
constructor since F_Cu and B_Cu are always present.
Add content validation to the Fabmaster plugin, which
previously accepted any .txt file based on extension alone.
CanReadBoard now scans for !-delimited rows containing
known Fabmaster column headers before claiming the file.
This lightens the load when including forwards for PCB_TRACK and
friends as we don't need all the transitive includes. Sometimes
you just need the enums - many users just wanted VIATYPE, or
just needed a forward.
And reduces transitive code churn when something is changed in the
header - this reduces the files affected by a change in
pcb_track.h from nearly 400 to under 200.
Five call sites independently reimplemented the same
time-gated throttle pattern. This consolidates them all
into a single THROTTLE class backed by steady_clock.
Three call sites duplicated the wxWidgets YieldFor workaround
for timer-event backlog (wxWidgets issue #26192). This replaces them
with a shared DrainPendingEvents free function.
Virtual root sheet set symbol and sheet instance projectnames
to be saved as empty strings. SCH_SHEET_PATH::Path()
skips the virtual root, so instance paths never start
with niluuid. Existing root UUID comparison was dead
code.
Use GetTopLevelSheets() to find current project instances
when the root is virtual and do direct root UUID comparison
for non-virtual roots. At save time, populate the project
name from GetProjectName() for current project instances
instead of using the stored (empty) m_ProjectName.
This fixes project rename not updating instance data,
and broken cross-project sheet sharing.
When hierarchical pins connect to global power nets, the multichannel
repeat-layout tool incorrectly reports a topology mismatch. The net
isomorphism algorithm treats global nets as channel-internal, causing
two failures: connection counts differ when global net pads are shared
with external components, and net-consistency checks flag false
contradictions between channels. The fix identifies nets whose
footprints fall outside the channel's footprint set and excludes them
from connection building and net-consistency checks. Global power rails
do not define internal channel routing topology, so this is correct.
Adds a QA regression test and reproduction board.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/21739
A junction placed at the midpoint of a wire (without the wire being
split) now correctly connects all wires meeting at that point. The
connection graph previously only connected wires at their endpoints,
so a horizontal wire passing through a junction as a midpoint was not
linked to the vertical wire terminating at the junction.
The fix adds a special case in updateItemConnectivity() that, when
processing a junction item, queries GetBusesAndWires() with
aIgnoreEndpoints=true to find wires whose midpoints coincide with the
junction. These wires are added to the connection map at the junction
position, mirroring the existing label-at-wire-midpoint handling.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/23143
When the fmt migration (77e42a151b) converted gendrill report writing
from richio OUTPUTFORMATTER to fmt::print, the first argument to
Out.Print() was an indentation level (integer 0), not the FILE*.
The migration preserved the literal 0 as the first argument to
fmt::print(), passing a null FILE* and causing a SIGSEGV the first
time any drill tool was written to the report.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/23289