QA: Centralise BOARD reading functions in pcbnew_utils

Several pcbnew_tools utilities read a file from the command line.
Instead of replicating this code, centralise the code in
qa_pcbnew_utils, which allows simpler reuse.

THe utilities are:

* polygon_triangulation
* polygon_generator
* drc_tool

pcb_parser keeps its own function, as that is the focus of the tool,
and its likely to have its own instrumention.

This also adds the ability to read from stdin for the above tools,
which means fuzz testers could theoretically work with them, and it
also can make life easier if you can pipe a board to the executable
directly.
This commit is contained in:
John Beard 2019-01-24 14:56:55 +00:00 committed by Wayne Stambaugh
parent adddc41bc5
commit 9bd7ea815c
6 changed files with 120 additions and 118 deletions

View File

@ -30,12 +30,7 @@
#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>
#include <pcbnew_utils/board_file_utils.h>
// DRC
#include <drc/courtyard_overlap.h>
@ -45,36 +40,6 @@
#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;
/**
@ -330,31 +295,14 @@ int drc_main_func( int argc, char** argv )
const bool verbose = cl_parser.Found( "verbose" );
const auto file_count = cl_parser.GetParamCount();
std::string filename;
std::unique_ptr<BOARD> board;
if( file_count == 0 )
if( cl_parser.GetParamCount() )
{
// Parse the file provided on stdin - used by AFL to drive the
// program
// while (__AFL_LOOP(2))
{
board = parse( std::cin );
}
filename = cl_parser.GetParam( 0 ).ToStdString();
}
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 );
}
std::unique_ptr<BOARD> board = KI_TEST::ReadBoardFromFileOrStream( filename );
if( !board )
return PARSER_RET_CODES::PARSE_FAILED;

View File

@ -29,8 +29,7 @@
#include <geometry/shape_line_chain.h>
#include <geometry/shape_poly_set.h>
#include <io_mgr.h>
#include <kicad_plugin.h>
#include <pcbnew_utils/board_file_utils.h>
#include <class_board.h>
#include <class_drawsegment.h>
@ -39,26 +38,6 @@
#include <class_track.h>
#include <class_zone.h>
BOARD* loadBoard( const std::string& filename )
{
PLUGIN::RELEASER pi( new PCB_IO );
BOARD* brd = nullptr;
try
{
brd = pi->Load( wxString( filename.c_str() ), NULL, NULL );
}
catch( const IO_ERROR& ioe )
{
wxString msg = wxString::Format( _( "Error loading board.\n%s" ), ioe.Problem() );
printf( "%s\n", (const char*) msg.mb_str() );
return nullptr;
}
return brd;
}
void process( const BOARD_CONNECTED_ITEM* item, int net )
{
@ -79,19 +58,32 @@ void process( const BOARD_CONNECTED_ITEM* item, int net )
}
enum POLY_GEN_RET_CODES
{
LOAD_FAILED = KI_TEST::RET_CODES::TOOL_SPECIFIC,
};
int polygon_gererator_main( int argc, char* argv[] )
{
if( argc < 2 )
{
printf( "A sample tool for dumping board geometry as a set of polygons.\n" );
printf( "usage : %s board_file.kicad_pcb\n\n", argv[0] );
return -1;
printf( "Usage : %s board_file.kicad_pcb\n\n", argv[0] );
return KI_TEST::RET_CODES::BAD_CMDLINE;
}
std::unique_ptr<BOARD> brd( loadBoard( argv[1] ) );
std::string filename;
if( argc > 1 )
filename = argv[1];
auto brd = KI_TEST::ReadBoardFromFileOrStream( filename );
if( !brd )
return -1;
{
return POLY_GEN_RET_CODES::LOAD_FAILED;
}
for( unsigned net = 0; net < brd->GetNetCount(); net++ )
{
@ -112,7 +104,7 @@ int polygon_gererator_main( int argc, char* argv[] )
printf( "endnet\n" );
}
return 0;
return KI_TEST::RET_CODES::OK;
}
/*

View File

@ -24,11 +24,10 @@
#include "polygon_triangulation.h"
#include <geometry/shape_poly_set.h>
#include <geometry/shape_line_chain.h>
#include <geometry/shape_poly_set.h>
#include <io_mgr.h>
#include <kicad_plugin.h>
#include <pcbnew_utils/board_file_utils.h>
#include <class_board.h>
#include <class_zone.h>
@ -200,26 +199,6 @@ aResult->clear();
std::swap( (*aResult) [0], (*aResult)[outline] );
}
BOARD* loadBoardTri( const std::string& filename )
{
PLUGIN::RELEASER pi( new PCB_IO );
BOARD* brd = nullptr;
try
{
brd = pi->Load( wxString( filename.c_str() ), NULL, NULL );
}
catch( const IO_ERROR& ioe )
{
wxString msg = wxString::Format( _( "Error loading board.\n%s" ),
ioe.Problem() );
printf( "%s\n", (const char*) msg.mb_str() );
return nullptr;
}
return brd;
}
enum POLY_TRI_RET_CODES
{
@ -229,7 +208,12 @@ enum POLY_TRI_RET_CODES
int polygon_triangulation_main( int argc, char *argv[] )
{
auto brd = loadBoardTri( argc > 1 ? argv[1] : "../../../../tests/dp.kicad_pcb" );
std::string filename;
if( argc > 1 )
filename = argv[1];
auto brd = KI_TEST::ReadBoardFromFileOrStream( filename );
if( !brd )
return POLY_TRI_RET_CODES::LOAD_FAILED;
@ -244,8 +228,7 @@ int polygon_triangulation_main( int argc, char *argv[] )
size_t parallelThreadCount = std::max<size_t>( std::thread::hardware_concurrency(), 2 );
for( size_t ii = 0; ii < parallelThreadCount; ++ii )
{
std::thread t = std::thread( [brd, &zonesToTriangulate, &threadsFinished] ()
{
std::thread t = std::thread( [&brd, &zonesToTriangulate, &threadsFinished]() {
for( size_t areaId = zonesToTriangulate.fetch_add( 1 );
areaId < static_cast<size_t>( brd->GetAreaCount() );
areaId = zonesToTriangulate.fetch_add( 1 ) )
@ -257,7 +240,7 @@ int polygon_triangulation_main( int argc, char *argv[] )
(void) poly;
printf("zone %zu/%d\n", ( areaId + 1 ), brd->GetAreaCount() );
#if 0
#if 0
PROF_COUNTER unfrac("unfrac");
poly.Unfracture( SHAPE_POLY_SET::PM_FAST );
unfrac.Show();
@ -269,7 +252,7 @@ int polygon_triangulation_main( int argc, char *argv[] )
poly.triangulatePoly( &poly.Polygon(i) );
}
triangulate.Show();
#endif
#endif
}
threadsFinished++;
@ -281,13 +264,9 @@ int polygon_triangulation_main( int argc, char *argv[] )
while( threadsFinished < parallelThreadCount )
std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
cnt.Show();
delete brd;
return KI_TEST::RET_CODES::OK;
}
/*

View File

@ -54,7 +54,9 @@ target_include_directories( qa_pcbnew_utils PUBLIC
${INC_AFTER}
)
# target_link_libraries( qa_pcbnew_utils PUBLIC
target_link_libraries( qa_pcbnew_utils PUBLIC
qa_utils
)
# 3d-viewer
# connectivity
# pcbcommon

View File

@ -30,6 +30,8 @@
#include <class_board.h>
#include <stdstream_line_reader.h>
namespace KI_TEST
{
@ -50,4 +52,50 @@ std::unique_ptr<BOARD_ITEM> ReadBoardItemFromFile( const std::string& aFilename
return std::unique_ptr<BOARD_ITEM>( parser.Parse() );
}
std::unique_ptr<BOARD_ITEM> ReadBoardItemFromStream( std::istream& aStream )
{
// Take input from stdin
STDISTREAM_LINE_READER reader;
reader.SetStream( aStream );
PCB_PARSER parser;
parser.SetLineReader( &reader );
std::unique_ptr<BOARD_ITEM> board;
try
{
board.reset( parser.Parse() );
}
catch( const IO_ERROR& parse_error )
{
std::cerr << parse_error.Problem() << std::endl;
std::cerr << parse_error.Where() << std::endl;
}
return board;
}
std::unique_ptr<BOARD> ReadBoardFromFileOrStream(
const std::string& aFilename, std::istream& aFallback )
{
std::istream* in_stream = nullptr;
std::ifstream file_stream;
if( aFilename.empty() )
{
// no file, read stdin
in_stream = &aFallback;
}
else
{
file_stream.open( aFilename );
in_stream = &file_stream;
}
return ReadItemFromStream<BOARD>( *in_stream );
}
} // namespace KI_TEST

View File

@ -25,6 +25,7 @@
#ifndef QA_PCBNEW_UTILS_BOARD_FILE_UTILS__H
#define QA_PCBNEW_UTILS_BOARD_FILE_UTILS__H
#include <iostream>
#include <memory>
#include <string>
@ -60,7 +61,39 @@ void DumpBoardToFile( BOARD& aBoard, const std::string& aFilename );
* @param aFilename the file to read in
* @returns a new #BOARD_ITEM, which is nullptr if the read or parse failed.
*/
std::unique_ptr<BOARD_ITEM> ReadBoardItemFromFile( const std::string& aFilename );
std::unique_ptr<BOARD_ITEM> ReadBoardItemFromStream( std::istream& aStream );
/**
* Read a specific kind of #BOARD_ITEM from a stream
*
* @tparam ITEM the item type to return (probably a #MODULE or #BOARD)
* @param aStream the stream to read from.
*/
template <typename ITEM> std::unique_ptr<ITEM> ReadItemFromStream( std::istream& aStream )
{
auto bi_ptr = ReadBoardItemFromStream( aStream );
std::unique_ptr<ITEM> downcast_ptr;
// if it's the right type, downcast and "steal" (and we'll return ownership)
ITEM* const tmp = dynamic_cast<ITEM*>( bi_ptr.get() );
if( tmp != nullptr )
{
bi_ptr.release();
downcast_ptr.reset( tmp );
}
return downcast_ptr;
}
/**
* Read a board from a file, or another stream, as appropriate
*
* @param aFilename The file to read, or the fallback if empty
* @param aFallback: the fallback stream
* @return a #BOARD, if successful
*/
std::unique_ptr<BOARD> ReadBoardFromFileOrStream(
const std::string& aFilename, std::istream& aFallback = std::cin );
} // namespace KI_TEST