From 7f098579387bb16c775ab28490be2f347b05e51b Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Thu, 15 Sep 2016 00:37:46 +0200 Subject: Adding CMake commands: kicad_pcb_plot() that plots the PCB in PDF, PS and SVG formats. kicad_gerber() that generates GERBER files from a PCB. Can optionally create a ZIP file with all the files. Supports old (protel) naming of files. --- cmake/KicadUtilsConfig.cmake | 2 + cmake/kicad_gerber.cmake | 88 ++++++++++++++++++++++++ cmake/kicad_schematic_plot.cmake | 46 +++++++++++++ examples/CMakeLists.txt | 3 + py/kicad_gerber.py | 145 +++++++++++++++++++++++++++++++++++++++ py/kicad_pcb_plot.py | 33 +++++---- 6 files changed, 300 insertions(+), 17 deletions(-) create mode 100644 cmake/kicad_gerber.cmake create mode 100644 cmake/kicad_schematic_plot.cmake create mode 100755 py/kicad_gerber.py diff --git a/cmake/KicadUtilsConfig.cmake b/cmake/KicadUtilsConfig.cmake index a2e6419..f8b91b7 100644 --- a/cmake/KicadUtilsConfig.cmake +++ b/cmake/KicadUtilsConfig.cmake @@ -1,5 +1,7 @@ set(kicad_generate_header_cmd KicadUtils::generate-header) include("${CMAKE_CURRENT_LIST_DIR}/kicad_generate_header.cmake") include("${CMAKE_CURRENT_LIST_DIR}/kicad_pcb_plot.cmake") +#include("${CMAKE_CURRENT_LIST_DIR}/kicad_schematic_plot.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/kicad_gerber.cmake") include("${CMAKE_CURRENT_LIST_DIR}/KicadUtilsTargets.cmake") set(KicadUtilsPyDir "${CMAKE_CURRENT_LIST_DIR}/../../../share/kicad-utils/py") diff --git a/cmake/kicad_gerber.cmake b/cmake/kicad_gerber.cmake new file mode 100644 index 0000000..330c5bd --- /dev/null +++ b/cmake/kicad_gerber.cmake @@ -0,0 +1,88 @@ +function(kicad_gerber) + set(options ALL) + set(one_value_args TARGET PCB_FILE DIR ZIP_FILE NAMING_STYLE) + set(multi_value_args) + cmake_parse_arguments(ARGS "${options}" "${one_value_args}" "${multi_value_args}" ${ARGN}) + + if (NOT ARGS_PCB_FILE) + message(SEND_ERROR "Missing required argument: PCB_FILE") + return() + endif () + + if (NOT ARGS_NAMING_STYLE) + set(ARGS_NAMING_STYLE MODERN) + endif () + + if (ARGS_NAMING_STYLE STREQUAL MODERN) + elseif (ARGS_NAMING_STYLE STREQUAL PROTEL) + set(protel --protel-extension) + else () + message(SEND_ERROR "Unsupported NAMING_STYLE: ${ARGS_NAMING_STYLE}") + return() + endif () + + set(pcb_file "${ARGS_PCB_FILE}") + get_filename_component(pcb_file "${ARGS_PCB_FILE}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + get_filename_component(basename "${pcb_file}" NAME_WE) + + if (NOT ARGS_TARGET) + set(target "${basename}-gerber") + else () + set(target "${ARGS_TARGET}") + endif () + + if (NOT ARGS_DIR) + message(SEND_ERROR "Missing required argument: DIR") + return() + endif () + + set(out_dir "${ARGS_DIR}") + set(prefix "${out_dir}/${basename}") + + execute_process( + COMMAND "${KicadUtilsPyDir}/kicad_gerber.py" --pcb "${pcb_file}" --output-directory "${out_dir}" ${protel} --detect-files-only + OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/${target}-gerber-index.txt + OUTPUT_VARIABLE out + ERROR_VARIABLE err + RESULT_VARIABLE res) + + file(STRINGS ${CMAKE_CURRENT_BINARY_DIR}/${target}-gerber-index.txt outputs) + + if (NOT res EQUAL 0) + message(SEND_ERROR "kicad_gerber.py failed: ${out} ${err}") + return() + endif () + +# foreach (o ${out}) +# list(APPEND outputs ${prefix}${o}) +# endforeach () + + # message("out=${out}") + message("outputs=${outputs}") + add_custom_command( + OUTPUT ${outputs} + COMMAND cmake -E make_directory "${out_dir}" + COMMAND "${KicadUtilsPyDir}/kicad_gerber.py" --pcb "${pcb_file}" --output-directory "${out_dir}" ${protel} + MAIN_DEPENDENCY "${pcb_file}") + + if (ARGS_ALL) + set(all ALL) + endif () + + if (ARGS_ZIP_FILE) + if (NOT IS_ABSOLUTE ARGS_ZIP_FILE) + set(ARGS_ZIP_FILE ${CMAKE_CURRENT_BINARY_DIR}/${ARGS_ZIP_FILE}) + endif () + + add_custom_command( + OUTPUT ${ARGS_ZIP_FILE} + MAIN_DEPENDENCY "${pcb_file}" + DEPENDS ${outputs} + COMMAND cmake -E remove -f ${ARGS_ZIP_FILE} + COMMAND zip -q ${ARGS_ZIP_FILE} -j ${outputs}) + endif () + + add_custom_target( + ${target} ${all} + DEPENDS ${outputs} ${ARGS_ZIP_FILE}) +endfunction() diff --git a/cmake/kicad_schematic_plot.cmake b/cmake/kicad_schematic_plot.cmake new file mode 100644 index 0000000..d4b77f9 --- /dev/null +++ b/cmake/kicad_schematic_plot.cmake @@ -0,0 +1,46 @@ +function(kicad_schematic_plot) + set(options ALL) + set(one_value_args TARGET SCHEMATIC_FILE DIR) + set(multi_value_args) + cmake_parse_arguments(kicad_schematic_plot "${options}" "${one_value_args}" "${multi_value_args}" ${ARGN}) + + if (NOT kicad_schematic_plot_SCHEMATIC_FILE) + message(SEND_ERROR "Missing required argument: SCHEMATIC_FILE") + return() + endif () + + set(schematic_file "${kicad_schematic_plot_SCHEMATIC_FILE}") + get_filename_component(schematic_file "${kicad_schematic_plot_SCHEMATIC_FILE}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + get_filename_component(basename "${schematic_file}" NAME_WE) + + if (NOT kicad_schematic_plot_TARGET) + set(target "${basename}-plots") + else () + set(target "${kicad_schematic_plot_TARGET}") + endif () + + if (NOT kicad_schematic_plot_DIR) + message(SEND_ERROR "Missing required argument: DIR") + return() + endif () + + set(out_dir "${kicad_schematic_plot_DIR}") + set(pdf_prefix "${out_dir}/${basename}") + + set(outputs + "${pdf_prefix}.pdf") + + add_custom_command( + OUTPUT ${outputs} + COMMAND cmake -E make_directory "${out_dir}" + COMMAND "${KicadUtilsPyDir}/kicad_schematic_plot.py" "${schematic_file}" "${out_dir}" + MAIN_DEPENDENCY "${schematic_file}") + + if (kicad_schematic_plot_ALL) + set(all ALL) + endif () + + add_custom_target( + ${target} ${all} + DEPENDS ${outputs}) +endfunction() diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index daa2b4d..7d45496 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -9,7 +9,10 @@ if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) message(FATAL_ERROR "The KicadUtils CMake package was not found. Did you pass the correct value for KicadUtils_DIR?\nIt should probably be something like KicadUtils_DIR=$HOME/opt/kicad-utils/lib/cmake/KicadUtils") endif () else () + set(KicadUtilsPyDir "${CMAKE_CURRENT_LIST_DIR}/../py") include(../cmake/kicad_generate_header.cmake) + include(../cmake/kicad_pcb_plot.cmake) +# include(../cmake/kicad_schematic_plot.cmake) list(APPEND KICAD_GEN_TEMPLATE_LIBS ${CMAKE_CURRENT_SOURCE_DIR}/../templates) set(kicad_generate_header_cmd generate-header) endif () diff --git a/py/kicad_gerber.py b/py/kicad_gerber.py new file mode 100755 index 0000000..41ae3af --- /dev/null +++ b/py/kicad_gerber.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python +import sys +import os +import argparse +from pcbnew import * + +parser = argparse.ArgumentParser(description='KiCAD PCB to GERBER converter') + +parser.add_argument('--pcb', + required=True, + dest='pcb', + action='store', + help='A foo.kicad_pcb file') + +parser.add_argument('--output-directory', + required=True, + dest='output_directory', + action='store', + help='Directory to store output files') + +parser.add_argument('--detect-files-only', + dest='detect_files_only', + action='store_true', + help='Don\'t create the GERBER files, just list the files to be created') + +parser.add_argument('--create-drill-map-file', + dest='create_drill_map_file', + action='store_true', + help='Create drill map file') + +parser.add_argument('--protel-extensions', + dest='protel_extensions', + action='store_true', + help='Use Protel filename extensions') + +args = parser.parse_args() + +filename = args.pcb + +board = LoadBoard(filename) + +plot_plan = [ + ("GTO", board.GetLayerName(F_SilkS), F_SilkS, "Silk front"), + ("GTS", board.GetLayerName(F_Mask), F_Mask, "Mask front"), + ("GBO", board.GetLayerName(B_SilkS), B_SilkS, "Silk bottom"), + ("GBS", board.GetLayerName(B_Mask), B_Mask, "Mask bottom"), + ("GBR", board.GetLayerName(Edge_Cuts), Edge_Cuts, "Edges"), +] + +layers = board.GetEnabledLayers() +for i in layers.CuStack(): + name = board.GetLayerName(i) + plot_plan.append(("", name, i, "Copper Layer " + name)) + +pctl = PLOT_CONTROLLER(board) +popt = pctl.GetPlotOptions() +popt.SetOutputDirectory(args.output_directory) + +# A nasty hack to get the base filename +pctl.SetLayer(F_Cu) +pctl.OpenPlotfile("", PLOT_FORMAT_GERBER, "") +filename = pctl.GetPlotFileName() +try: + os.remove(filename) +except: + pass +pctl.ClosePlot() + +basename = filename.replace('.gbr', '') +drlFile = basename + ".drl" +drlFileProtel = basename + ".txt" + +if args.protel_extensions: + popt.SetUseGerberProtelExtensions(True) + +if args.detect_files_only: + for layer_info in plot_plan: + filenamePostfix = layer_info[1] + layer_num = layer_info[2] + pctl.SetLayer(layer_num) + + pctl.OpenPlotfile(filenamePostfix, PLOT_FORMAT_GERBER, layer_info[3]) + filename = pctl.GetPlotFileName() + print filename + try: + os.remove(filename) + except: + pass + + pctl.ClosePlot() + if args.protel_extensions: + print drlFileProtel + else: + print drlFile + sys.exit(0) + +# Set some important plot options: +popt.SetPlotFrameRef(False) +popt.SetLineWidth(FromMM(0.35)) + +popt.SetAutoScale(False) +popt.SetScale(1) +popt.SetMirror(False) +popt.SetUseGerberAttributes(True) +popt.SetExcludeEdgeLayer(False) +popt.SetScale(1) +popt.SetUseAuxOrigin(True) + +# This by gerbers only (also the name is truly horrid!) +popt.SetSubtractMaskFromSilk(False) + +for layer_info in plot_plan: + filenamePostfix = layer_info[1] + layer_num = layer_info[2] + pctl.SetLayer(layer_num) + + pctl.OpenPlotfile(filenamePostfix, PLOT_FORMAT_GERBER, layer_info[3]) + pctl.PlotLayer() + pctl.ClosePlot() + +popt.SetDrillMarksType(PCB_PLOT_PARAMS.FULL_DRILL_SHAPE) + +drlwriter = EXCELLON_WRITER(board) +# drlwriter.SetMapFileFormat(PLOT_FORMAT_PDF) +drlwriter.SetMapFileFormat(PLOT_FORMAT_GERBER) + +mirror = False +minimalHeader = False +offset = wxPoint(0, 0) +# False to generate 2 separate drill files (one for plated holes, one for non plated holes) +# True to generate only one drill file +mergeNPTH = False +drlwriter.SetOptions(mirror, minimalHeader, offset, mergeNPTH) + +metricFmt = True +drlwriter.SetFormat(metricFmt) + +genDrl = True +genMap = args.create_drill_map_file + +drlwriter.CreateDrillandMapFilesSet(pctl.GetPlotDirName(), genDrl, genMap) + +if args.protel_extensions: + os.rename(drlFile, basename + ".txt") + pass diff --git a/py/kicad_pcb_plot.py b/py/kicad_pcb_plot.py index ef62cb2..d6f123d 100755 --- a/py/kicad_pcb_plot.py +++ b/py/kicad_pcb_plot.py @@ -53,7 +53,8 @@ PLOT_FORMAT_GERBER import sys from pcbnew import * -filename=sys.argv[1] #e.g left-main/left-main.kicad_pcb + +filename = sys.argv[1] # e.g left-main/left-main.kicad_pcb board = LoadBoard(filename) @@ -64,8 +65,6 @@ popt = pctl.GetPlotOptions() # popt.SetOutputDirectory("plot/") popt.SetOutputDirectory(sys.argv[2]) - - # Set some important plot options: popt.SetPlotFrameRef(False) popt.SetLineWidth(FromMM(0.35)) @@ -102,16 +101,16 @@ pctl.PlotLayer() # param 0 is a string added to the file base name to identify the drawing # param 1 is the layer ID plot_plan = [ - ( "F.Cu", F_Cu, "Front layer" ), - ( "B.Cu", B_Cu, "Bottom layer" ), - ( "F.Paste", F_Paste, "Paste front" ), - ( "B.Paste", B_Paste, "Paste bottom" ), - ( "F.Silk", F_SilkS, "Silk front" ), - ( "B.Silk", B_SilkS, "Silk bottom" ), - ( "F.Mask", F_Mask, "Mask front" ), - ( "B.Mask", B_Mask, "Mask bottom" ), - ( "Edge.Cuts", Edge_Cuts, "Edges" ), - ( "Cmts.User", Cmts_User, "User comments" ), + ("F.Cu", F_Cu, "Front layer"), + ("B.Cu", B_Cu, "Bottom layer"), + ("F.Paste", F_Paste, "Paste front"), + ("B.Paste", B_Paste, "Paste bottom"), + ("F.Silk", F_SilkS, "Silk front"), + ("B.Silk", B_SilkS, "Silk bottom"), + ("F.Mask", F_Mask, "Mask front"), + ("B.Mask", B_Mask, "Mask bottom"), + ("Edge.Cuts", Edge_Cuts, "Edges"), + ("Cmts.User", Cmts_User, "User comments"), ] for layer_info in plot_plan: @@ -180,7 +179,7 @@ pctl.SetLayer(Cmts_User) pctl.PlotLayer() # Bottom mask as lines only, in red -#popt.SetMode(LINE) +# popt.SetMode(LINE) popt.SetColor(RED) pctl.SetLayer(B_Mask) pctl.PlotLayer() @@ -192,7 +191,7 @@ pctl.PlotLayer() # Top paste in light blue, filled popt.SetColor(BLUE) -#popt.SetMode(FILLED) +# popt.SetMode(FILLED) pctl.SetLayer(F_Paste) pctl.PlotLayer() @@ -229,7 +228,7 @@ popt.SetReferenceColor(19) popt.SetValueColor(21) popt.SetColor(0) -#popt.SetMode(LINE) +# popt.SetMode(LINE) pctl.SetLayer(B_SilkS) pctl.PlotLayer() popt.SetColor(14) @@ -258,7 +257,7 @@ pctl.PlotLayer() # better anyway... popt.SetColor(17) -#popt.SetMode(FILLED) +# popt.SetMode(FILLED) popt.SetDrillMarksType(PCB_PLOT_PARAMS.FULL_DRILL_SHAPE) pctl.SetLayer(B_Cu) pctl.PlotLayer() -- cgit v1.2.3