11# -*- coding: utf-8 -*-
2- # Copyright (c) 2020-2025 Salvador E. Tropea
3- # Copyright (c) 2020-2025 Instituto Nacional de Tecnología Industrial
2+ # Copyright (c) 2020-2026 Salvador E. Tropea
3+ # Copyright (c) 2020-2026 Instituto Nacional de Tecnología Industrial
44# License: AGPL-3.0
55# Project: KiBot (formerly KiPlot)
66# Drill table contributed by Nguyen Vincent (@nguyen-v)
1111 PLOT_FORMAT_PDF , wxPoint , B_Cu , F_Cu , GERBER_WRITER )
1212from .error import KiPlotConfigurationError
1313from .kicad .drill_info import get_full_holes_list , PLATED_DICT , HOLE_SHAPE_DICT , HOLE_TYPE_DICT
14+ from .kiplot import run_command
1415from .optionable import Optionable
1516from .out_base import VariantOptions
1617from .gs import GS
@@ -166,8 +167,14 @@ def config(self, parent):
166167 else :
167168 map = self .map .type
168169 self ._map_output = self .map .output
169- if GS .ki10 and map == 'hpgl' :
170- raise KiPlotConfigurationError ("KiCad 10+ doesn't support HPGL" )
170+ if GS .ki10 :
171+ if map == 'hpgl' :
172+ raise KiPlotConfigurationError ("KiCad 10+ doesn't support HPGL" )
173+ if map == 'gerber' :
174+ self ._map_cli = 'gerberx2'
175+ else :
176+ self ._map_cli = map
177+ self ._map_type = map
171178 self ._map_ext = self ._map_ext [map ]
172179 self ._map = self ._map_map [map ]
173180 # Solve the report for both cases
@@ -260,12 +267,17 @@ def _get_drill_groups(unified):
260267 groups .extend (list (pairs ))
261268 return groups
262269
263- def get_file_names (self , output_dir ):
270+ def get_file_names (self , output_dir , just_drills = False , tmp_base = None ):
264271 """ Returns a dict containing KiCad names and its replacement.
265- If no replacement is needed the replacement is empty """
272+ If no replacement is needed the replacement is empty.
273+ tmp_base is used to specify the `GS.pcb_basename` for a temporal variant.
274+ I don't really know if variants really apply to drill files, but is currently supported """
266275 filenames = {}
267276 self ._configure_writer (GS .board , wxPoint (0 , 0 ))
268277 files = AnyDrill ._get_drill_groups (self ._unified_output )
278+ force_rename = tmp_base is not None
279+ if not force_rename :
280+ tmp_base = GS .pcb_basename
269281 for d in files :
270282 kicad_id = '-' + d if d else d
271283 kibot_id = self .solve_id (d )
@@ -274,17 +286,22 @@ def get_file_names(self, output_dir):
274286 kicad_id_main += '-drl'
275287 if not GS .ki8 :
276288 kicad_id_map = kicad_id_main
277- if self .generate_drill_files :
278- k_file = self .expand_filename (output_dir , '%f' + kicad_id_main + '.%x' , '' , self ._ext )
289+ if self .generate_drill_files or just_drills :
290+ k_file = self .expand_filename (output_dir , tmp_base + kicad_id_main + '.%x' , '' , self ._ext )
279291 file = ''
280292 if self .output :
281293 file = self .expand_filename (output_dir , self .output , kibot_id , self ._ext )
294+ elif force_rename :
295+ # To get the real name instead of the temporal one
296+ file = k_file .replace (tmp_base , GS .pcb_basename )
282297 filenames [k_file ] = file
283- if self ._map is not None :
284- k_file = self .expand_filename (output_dir , '%f' + kicad_id_map + '-drl_map.%x' , '' , self ._map_ext )
298+ if self ._map is not None and not just_drills :
299+ k_file = self .expand_filename (output_dir , tmp_base + kicad_id_map + '-drl_map.%x' , '' , self ._map_ext )
285300 file = ''
286301 if self ._map_output :
287302 file = self .expand_filename (output_dir , self ._map_output , kibot_id + '_map' , self ._map_ext )
303+ elif force_rename :
304+ file = k_file .replace (tmp_base , GS .pcb_basename )
288305 filenames [k_file ] = file
289306 return filenames
290307
@@ -298,43 +315,70 @@ def run(self, output_dir):
298315 offset = GS .get_aux_origin ()
299316 else :
300317 offset = wxPoint (0 , 0 )
301- drill_writer = self ._configure_writer (GS .board , offset )
318+ drill_writer , use_cli = self ._configure_writer (GS .board , offset )
302319
303320 logger .debug ("Generating drill files in " + output_dir )
304321 gen_map = self ._map is not None
305- if gen_map :
306- drill_writer .SetMapFileFormat (self ._map )
307- logger .debug ("Generating drill map type {} in {}" .format (self ._map , output_dir ))
322+
308323 if not self .generate_drill_files and not gen_map and not self ._report and not self ._table_output :
309324 logger .warning (
310325 W_NODRILL +
311326 "Not generating drill files nor drill maps "
312327 "nor report nor drill table on "
313328 f"`{ self ._parent .name } `"
314329 )
315- if GS .ki10 and isinstance (drill_writer , GERBER_WRITER ):
316- # KiCad 10 inconsistency ...
317- drill_writer .CreateDrillandMapFilesSet (output_dir , self .generate_drill_files , gen_map , True )
330+
331+ if use_cli :
332+ # Using kicad-cli
333+ fname = self .save_tmp_board () if self .will_filter_pcb_components () else GS .pcb_file
334+ odir = output_dir # if self.generate_drill_files else tempfile.gettempdir()
335+ cmd = [GS .kicad_cli , 'pcb' , 'export' , 'drill' , '--output' , odir ] + drill_writer
336+ if gen_map :
337+ logger .debug ("Generating drill map type {} in {}" .format (self ._map , output_dir ))
338+ cmd .extend (['--generate-map' , '--map-format' , self ._map_cli ])
339+ if self ._report :
340+ drill_report_file = self .expand_filename (output_dir , self ._report , 'drill_report' , 'txt' )
341+ logger .debug ("Generating drill report: " + drill_report_file )
342+ cmd .extend (['--generate-report' , '--report-path' , drill_report_file ])
343+ run_command (cmd + [fname ])
344+ if self ._files_to_remove :
345+ self .remove_temporals ()
346+ tmp_base = os .path .splitext (os .path .basename (fname ))[0 ]
347+ if not self .generate_drill_files :
348+ # We can't prevent kicad-cli from generating them, so just remove them
349+ files = self .get_file_names (output_dir , tmp_base = tmp_base , just_drills = True )
350+ for f in files .keys ():
351+ os .remove (f )
318352 else :
319- drill_writer .CreateDrillandMapFilesSet (output_dir , self .generate_drill_files , gen_map )
353+ # Using Python API
354+ if gen_map :
355+ drill_writer .SetMapFileFormat (self ._map )
356+ logger .debug ("Generating drill map type {} in {}" .format (self ._map , output_dir ))
357+ if GS .ki10 and isinstance (drill_writer , GERBER_WRITER ):
358+ # KiCad 10 inconsistency ...
359+ drill_writer .CreateDrillandMapFilesSet (output_dir , self .generate_drill_files , gen_map , True )
360+ else :
361+ drill_writer .CreateDrillandMapFilesSet (output_dir , self .generate_drill_files , gen_map )
362+ # Generate the report
363+ if self ._report :
364+ drill_report_file = self .expand_filename (output_dir , self ._report , 'drill_report' , 'txt' )
365+ logger .debug ("Generating drill report: " + drill_report_file )
366+ if GS .ki10 :
367+ logger .error ("No drill reports until KiCad bug https://gitlab.com/kicad/code/kicad/-/work_items/23268 "
368+ "is fixed" )
369+ else :
370+ drill_writer .GenDrillReportFile (drill_report_file )
371+ tmp_base = None
372+
320373 # Rename the files
321- files = self .get_file_names (output_dir )
374+ files = self .get_file_names (output_dir , tmp_base = tmp_base )
322375 for k_f , f in files .items ():
323376 if f :
324377 logger .debug (f"Renaming { k_f } -> { f } " )
325378 if not os .path .isfile (k_f ):
326379 GS .exit_with_error (f"Missing `{ k_f } ` drill file, KiCad bug? please report" , DONT_STOP )
327380 else :
328381 os .replace (k_f , f )
329- # Generate the report
330- if self ._report :
331- drill_report_file = self .expand_filename (output_dir , self ._report , 'drill_report' , 'txt' )
332- logger .debug ("Generating drill report: " + drill_report_file )
333- if GS .ki10 :
334- logger .error ("No drill reports until KiCad bug https://gitlab.com/kicad/code/kicad/-/work_items/23268 "
335- "is fixed" )
336- else :
337- drill_writer .GenDrillReportFile (drill_report_file )
338382 # Generate the drill table
339383 if self ._table_output :
340384
0 commit comments