kicad/qa/pcbnew_tools/tools/drc_tool/drc_tool.cpp

394 lines
9.8 KiB
C++
Raw Normal View History

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2018 KiCad Developers, see CHANGELOG.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "drc_tool.h"
#include <cstdio>
#include <string>
#include <common.h>
#include <wx/cmdline.h>
// Parsing
#include <class_board.h>
#include <class_board_item.h>
#include <kicad_plugin.h>
#include <pcb_parser.h>
#include <richio.h>
// DRC
#include <drc/courtyard_overlap.h>
#include <drc/drc_marker_factory.h>
#include <scoped_timer.h>
#include <stdstream_line_reader.h>
/**
* Parse a PCB from the given stream
*
* @param aStream the input stream to read from
*/
std::unique_ptr<BOARD> parse( std::istream& aStream )
{
// Take input from stdin
STDISTREAM_LINE_READER reader;
reader.SetStream( aStream );
PCB_PARSER parser;
parser.SetLineReader( &reader );
std::unique_ptr<BOARD> board;
try
{
board.reset( dynamic_cast<BOARD*>( parser.Parse() ) );
}
catch( const IO_ERROR& parse_error )
{
std::cerr << parse_error.Problem() << std::endl;
std::cerr << parse_error.Where() << std::endl;
}
return board;
}
using DRC_DURATION = std::chrono::microseconds;
/**
* DRC runner: provides a simple framework to run some DRC checks on #BOARDS.
* The DRC_RUNNER can be set up as needed to instantiate a #DRC_PROVIDER to
* perform the desired DRC on the #BOARD and provide some basic information
* about what happened.
*/
class DRC_RUNNER
{
public:
/**
* How the DRC runner behaves (e.g. what is printed and how)
*/
struct EXECUTION_CONTEXT
{
bool m_verbose;
bool m_print_times;
bool m_print_markers;
};
DRC_RUNNER( const EXECUTION_CONTEXT& aExecCtx ) : m_exec_context( aExecCtx )
{
}
void Execute( BOARD& aBoard )
{
if( m_exec_context.m_verbose )
{
std::cout << "Running DRC check: " << getRunnerIntro() << std::endl;
}
aBoard.SetDesignSettings( getDesignSettings() );
std::vector<std::unique_ptr<MARKER_PCB>> markers;
auto marker_handler = [&]( MARKER_PCB* aMarker ) {
markers.push_back( std::unique_ptr<MARKER_PCB>( aMarker ) );
};
std::unique_ptr<DRC_PROVIDER> drc_prov = createDrcProvider( aBoard, marker_handler );
DRC_DURATION duration;
{
SCOPED_TIMER<DRC_DURATION> timer( duration );
drc_prov->RunDRC( aBoard );
}
// report results
if( m_exec_context.m_print_times )
reportDuration( duration );
if( m_exec_context.m_print_markers )
reportMarkers( markers );
}
protected:
const DRC_MARKER_FACTORY& getMarkerFactory() const
{
return m_marker_factory;
}
private:
/**
* Get the introduction text for this DRC runner
*/
virtual std::string getRunnerIntro() const = 0;
/**
* Get suitable design settings for this DRC runner
*/
virtual BOARD_DESIGN_SETTINGS getDesignSettings() const = 0;
virtual std::unique_ptr<DRC_PROVIDER> createDrcProvider(
BOARD& aBoard, DRC_PROVIDER::MARKER_HANDLER aHandler ) = 0;
void reportDuration( const DRC_DURATION& aDuration ) const
{
std::cout << "Took: " << aDuration.count() << "us" << std::endl;
}
void reportMarkers( const std::vector<std::unique_ptr<MARKER_PCB>>& aMarkers ) const
{
std::cout << "DRC markers: " << aMarkers.size() << std::endl;
int index = 0;
for( const auto& m : aMarkers )
{
std::cout << index++ << ": " << m->GetReporter().ShowReport( EDA_UNITS_T::MILLIMETRES );
}
if( index )
{
std::cout << std::endl;
}
}
const EXECUTION_CONTEXT m_exec_context;
DRC_MARKER_FACTORY m_marker_factory;
};
/**
* DRC runner to run only DRC courtyard-overlap checks
*/
class DRC_COURTYARD_OVERLAP_RUNNER : public DRC_RUNNER
{
public:
DRC_COURTYARD_OVERLAP_RUNNER( const EXECUTION_CONTEXT& aCtx ) : DRC_RUNNER( aCtx )
{
}
private:
std::string getRunnerIntro() const override
{
return "Courtyard overlap";
}
BOARD_DESIGN_SETTINGS getDesignSettings() const override
{
BOARD_DESIGN_SETTINGS des_settings;
des_settings.m_RequireCourtyards = false;
des_settings.m_ProhibitOverlappingCourtyards = true;
return des_settings;
}
std::unique_ptr<DRC_PROVIDER> createDrcProvider(
BOARD& aBoard, DRC_PROVIDER::MARKER_HANDLER aHandler ) override
{
return std::make_unique<DRC_COURTYARD_OVERLAP>( getMarkerFactory(), aHandler );
}
};
/**
* DRC runner to run only DRC courtyard-missing checks
*/
class DRC_COURTYARD_MISSING_RUNNER : public DRC_RUNNER
{
public:
DRC_COURTYARD_MISSING_RUNNER( const EXECUTION_CONTEXT& aCtx ) : DRC_RUNNER( aCtx )
{
}
private:
std::string getRunnerIntro() const override
{
return "Courtyard missing";
}
BOARD_DESIGN_SETTINGS getDesignSettings() const override
{
BOARD_DESIGN_SETTINGS des_settings;
des_settings.m_RequireCourtyards = true;
des_settings.m_ProhibitOverlappingCourtyards = false;
return des_settings;
}
std::unique_ptr<DRC_PROVIDER> createDrcProvider(
BOARD& aBoard, DRC_PROVIDER::MARKER_HANDLER aHandler ) override
{
return std::make_unique<DRC_COURTYARD_OVERLAP>( getMarkerFactory(), aHandler );
}
};
static const wxCmdLineEntryDesc g_cmdLineDesc[] = {
{
wxCMD_LINE_SWITCH,
"h",
"help",
_( "displays help on the command line parameters" ).mb_str(),
wxCMD_LINE_VAL_NONE,
wxCMD_LINE_OPTION_HELP,
},
{
wxCMD_LINE_SWITCH,
"v",
"verbose",
_( "print parsing information" ).mb_str(),
},
{
wxCMD_LINE_SWITCH,
"t",
"timings",
_( "print DRC timings" ).mb_str(),
},
{
wxCMD_LINE_SWITCH,
"m",
"print-markers",
_( "print DRC marker information" ).mb_str(),
},
{
wxCMD_LINE_SWITCH,
"A",
"all-checks",
_( "perform all available DRC checks" ).mb_str(),
},
{
wxCMD_LINE_SWITCH,
"C",
"courtyard-overlap",
_( "perform courtyard-overlap (and malformation) checking" ).mb_str(),
},
{
wxCMD_LINE_SWITCH,
"c",
"courtyard-missing",
_( "perform courtyard-missing checking" ).mb_str(),
},
{
wxCMD_LINE_PARAM,
nullptr,
nullptr,
_( "input file" ).mb_str(),
wxCMD_LINE_VAL_STRING,
wxCMD_LINE_PARAM_OPTIONAL,
},
{ wxCMD_LINE_NONE }
};
/**
* Tool=specific return codes
*/
enum PARSER_RET_CODES
{
PARSE_FAILED = RET_CODES::TOOL_SPECIFIC,
};
int drc_main_func( int argc, char** argv )
{
#ifdef __AFL_COMPILER
__AFL_INIT();
#endif
wxMessageOutput::Set( new wxMessageOutputStderr );
wxCmdLineParser cl_parser( argc, argv );
cl_parser.SetDesc( g_cmdLineDesc );
cl_parser.AddUsageText(
_( "This program runs DRC tools on given PCB files. "
"This can be used for debugging, fuzz testing or development, etc." ) );
int cmd_parsed_ok = cl_parser.Parse();
if( cmd_parsed_ok != 0 )
{
// Help and invalid input both stop here
return ( cmd_parsed_ok == -1 ) ? RET_CODES::OK : RET_CODES::BAD_CMDLINE;
}
const bool verbose = cl_parser.Found( "verbose" );
const auto file_count = cl_parser.GetParamCount();
std::unique_ptr<BOARD> board;
if( file_count == 0 )
{
// Parse the file provided on stdin - used by AFL to drive the
// program
// while (__AFL_LOOP(2))
{
board = parse( std::cin );
}
}
else
{
const auto filename = cl_parser.GetParam( 0 ).ToStdString();
if( verbose )
std::cout << "Parsing: " << filename << std::endl;
std::ifstream fin;
fin.open( filename );
board = parse( fin );
}
if( !board )
return PARSER_RET_CODES::PARSE_FAILED;
DRC_RUNNER::EXECUTION_CONTEXT exec_context{
verbose,
cl_parser.Found( "timings" ),
cl_parser.Found( "print-markers" ),
};
const bool all = cl_parser.Found( "all-checks" );
// Run the DRC on the board
if( all || cl_parser.Found( "courtyard-overlap" ) )
{
DRC_COURTYARD_OVERLAP_RUNNER runner( exec_context );
runner.Execute( *board );
}
if( all || cl_parser.Found( "courtyard-missing" ) )
{
DRC_COURTYARD_MISSING_RUNNER runner( exec_context );
runner.Execute( *board );
}
return RET_CODES::OK;
}
/*
* Define the tool interface
*/
UTILITY_PROGRAM drc_tool = {
"drc",
"Run selected DRC function on a PCB",
drc_main_func,
};