Add cli for "fp" upgrade

This commit is contained in:
Marek Roszko 2022-11-27 21:27:34 -05:00
parent bd40684ecd
commit 25d5defc10
14 changed files with 384 additions and 100 deletions

View File

@ -0,0 +1,42 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2022 Mark Roszko <mark.roszko@gmail.com>
* Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.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 3 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef JOB_FP_UPGRADE_H
#define JOB_FP_UPGRADE_H
#include <wx/string.h>
#include "job.h"
class JOB_FP_UPGRADE : public JOB
{
public:
JOB_FP_UPGRADE( bool aIsCli ) :
JOB( "fpupgrade", aIsCli ),
m_libraryPath(),
m_force( false )
{
}
wxString m_libraryPath;
bool m_force;
};
#endif

View File

@ -24,6 +24,8 @@ set( KICAD_SRCS
cli/command_export_pcb_pos.cpp
cli/command_export_pcb_step.cpp
cli/command_export_pcb_svg.cpp
cli/command_fp.cpp
cli/command_fp_upgrade.cpp
cli/command_pcb.cpp
cli/command_pcb_export.cpp
cli/command_export_sch_bom.cpp

32
kicad/cli/command_fp.cpp Normal file
View File

@ -0,0 +1,32 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2022 Mark Roszko <mark.roszko@gmail.com>
* Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "command_fp.h"
CLI::FP_COMMAND::FP_COMMAND() : COMMAND( "fp" )
{
}
int CLI::FP_COMMAND::Perform( KIWAY& aKiway )
{
std::cout << m_argParser;
return 1;
}

36
kicad/cli/command_fp.h Normal file
View File

@ -0,0 +1,36 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2022 Mark Roszko <mark.roszko@gmail.com>
* Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.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 3 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef COMMAND_FP_H
#define COMMAND_FP_H
#include "command.h"
namespace CLI
{
struct FP_COMMAND : public COMMAND
{
FP_COMMAND();
int Perform( KIWAY& aKiway ) override;
};
}
#endif

View File

@ -0,0 +1,57 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2022 Mark Roszko <mark.roszko@gmail.com>
* Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "command_fp_upgrade.h"
#include <cli/exit_codes.h>
#include "jobs/job_fp_upgrade.h"
#include <kiface_base.h>
#include <layer_ids.h>
#include <wx/crt.h>
#include <wx/dir.h>
#include <macros.h>
#define ARG_FORCE "--force"
CLI::FP_UPGRADE_COMMAND::FP_UPGRADE_COMMAND() : EXPORT_PCB_BASE_COMMAND( "upgrade" )
{
m_argParser.add_argument( ARG_FORCE )
.help( UTF8STDSTR( _( "Forces the footprint to be resaved regardless of versioning" ) ) )
.implicit_value( true )
.default_value( false );
}
int CLI::FP_UPGRADE_COMMAND::Perform( KIWAY& aKiway )
{
std::unique_ptr<JOB_FP_UPGRADE> fpJob = std::make_unique<JOB_FP_UPGRADE>( true );
fpJob->m_libraryPath = FROM_UTF8( m_argParser.get<std::string>( ARG_INPUT ).c_str() );
if( !wxDir::Exists( fpJob->m_libraryPath ) )
{
wxFprintf( stderr, _( "Footprint file does not exist or is not accessible\n" ) );
return EXIT_CODES::ERR_INVALID_INPUT_FILE;
}
int exitCode = aKiway.ProcessJob( KIWAY::FACE_PCB, fpJob.get() );
return exitCode;
}

View File

@ -0,0 +1,37 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2022 Mark Roszko <mark.roszko@gmail.com>
* Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.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 3 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef COMMAND_FP_UPGRADE_H
#define COMMAND_FP_UPGRADE_H
#include "command_export_pcb_base.h"
namespace CLI
{
class FP_UPGRADE_COMMAND : public EXPORT_PCB_BASE_COMMAND
{
public:
FP_UPGRADE_COMMAND();
int Perform( KIWAY& aKiway ) override;
};
} // namespace CLI
#endif

View File

@ -59,6 +59,8 @@
#include "cli/command_export_sch_netlist.h"
#include "cli/command_export_sch_pdf.h"
#include "cli/command_export_sch_svg.h"
#include "cli/command_fp.h"
#include "cli/command_fp_upgrade.h"
#include "cli/command_sch.h"
#include "cli/command_sch_export.h"
#include "cli/exit_codes.h"
@ -123,8 +125,18 @@ static CLI::EXPORT_SCH_BOM_COMMAND exportSchBomCmd{};
static CLI::EXPORT_SCH_NETLIST_COMMAND exportSchNetlistCmd{};
static CLI::EXPORT_SCH_PDF_COMMAND exportSchPdfCmd{};
static CLI::EXPORT_SCH_SVG_COMMAND exportSchSvgCmd{};
static CLI::FP_COMMAND fpCmd{};
static CLI::FP_UPGRADE_COMMAND fpUpgradeCmd{};
static std::vector<COMMAND_ENTRY> commandStack = {
{
&fpCmd,
{
{
&fpUpgradeCmd
}
}
},
{
&pcbCmd,
{

View File

@ -73,6 +73,7 @@ FOOTPRINT::FOOTPRINT( BOARD* parent ) :
m_localSolderPasteMargin = 0;
m_localSolderPasteMarginRatio = 0.0;
m_zoneConnection = ZONE_CONNECTION::INHERITED;
m_fileFormatVersionAtLoad = 0;
// These are special and mandatory text fields
m_reference = new FP_TEXT( this, FP_TEXT::TEXT_is_REFERENCE );
@ -109,6 +110,7 @@ FOOTPRINT::FOOTPRINT( const FOOTPRINT& aFootprint ) :
m_localSolderPasteMarginRatio = aFootprint.m_localSolderPasteMarginRatio;
m_zoneConnection = aFootprint.m_zoneConnection;
m_netTiePadGroups = aFootprint.m_netTiePadGroups;
m_fileFormatVersionAtLoad = aFootprint.m_fileFormatVersionAtLoad;
std::map<BOARD_ITEM*, BOARD_ITEM*> ptrMap;

View File

@ -616,6 +616,9 @@ public:
m_attributes &= ~FP_ALLOW_MISSING_COURTYARD;
}
void SetFileFormatVersionAtLoad( int aVersion ) { m_fileFormatVersionAtLoad = aVersion; }
int GetFileFormatVersionAtLoad() const { return m_fileFormatVersionAtLoad; }
/**
* Return a #PAD with a matching number.
*
@ -834,6 +837,7 @@ private:
LIB_ID m_fpid; // The #LIB_ID of the FOOTPRINT.
int m_attributes; // Flag bits (see FOOTPRINT_ATTR_T)
int m_fpStatus; // For autoplace: flags (LOCKED, FIELDS_AUTOPLACED)
int m_fileFormatVersionAtLoad;
// Bounding box caching strategy:
// While we attempt to notice the low-hanging fruit operations and update the bounding boxes

View File

@ -19,6 +19,7 @@
*/
#include "pcbnew_jobs_handler.h"
#include <jobs/job_fp_upgrade.h>
#include <jobs/job_export_pcb_gerber.h>
#include <jobs/job_export_pcb_drill.h>
#include <jobs/job_export_pcb_dxf.h>
@ -42,6 +43,7 @@
#include <gendrill_Excellon_writer.h>
#include <gendrill_gerber_writer.h>
#include <wildcards_and_files_ext.h>
#include <plugins/kicad/pcb_plugin.h>
#include "pcbnew_scripting_helpers.h"
@ -56,8 +58,8 @@ PCBNEW_JOBS_HANDLER::PCBNEW_JOBS_HANDLER()
std::bind( &PCBNEW_JOBS_HANDLER::JobExportGerber, this, std::placeholders::_1 ) );
Register( "drill",
std::bind( &PCBNEW_JOBS_HANDLER::JobExportDrill, this, std::placeholders::_1 ) );
Register( "pos",
std::bind( &PCBNEW_JOBS_HANDLER::JobExportPos, this, std::placeholders::_1 ) );
Register( "pos", std::bind( &PCBNEW_JOBS_HANDLER::JobExportPos, this, std::placeholders::_1 ) );
Register( "fpupgrade", std::bind( &PCBNEW_JOBS_HANDLER::JobExportFpUpgrade, this, std::placeholders::_1 ) );
}
@ -463,3 +465,62 @@ int PCBNEW_JOBS_HANDLER::JobExportPos( JOB* aJob )
return CLI::EXIT_CODES::OK;
}
extern FOOTPRINT* try_load_footprint( const wxFileName& aFileName, IO_MGR::PCB_FILE_T aFileType,
const wxString& aName );
int PCBNEW_JOBS_HANDLER::JobExportFpUpgrade( JOB* aJob )
{
JOB_FP_UPGRADE* upgradeJob = dynamic_cast<JOB_FP_UPGRADE*>( aJob );
if( upgradeJob == nullptr )
return CLI::EXIT_CODES::ERR_UNKNOWN;
if( aJob->IsCli() )
wxPrintf( _( "Loading board\n" ) );
PCB_PLUGIN pcb_io( CTL_FOR_LIBRARY );
FP_CACHE fpLib( &pcb_io, upgradeJob->m_libraryPath );
try
{
fpLib.Load();
}
catch(...)
{
wxFprintf( stderr, _( "Unable to load library\n" ) );
return CLI::EXIT_CODES::ERR_UNKNOWN;
}
bool shouldSave = upgradeJob->m_force;
for( const auto& footprint : fpLib.GetFootprints() )
{
if( footprint.second->GetFootprint()->GetFileFormatVersionAtLoad() < SEXPR_BOARD_FILE_VERSION )
{
shouldSave = true;
}
}
if( shouldSave )
{
wxPrintf( _( "Saving footprint library\n" ) );
try
{
fpLib.Save();
}
catch( ... )
{
wxFprintf( stderr, _( "Unable to save library\n" ) );
return CLI::EXIT_CODES::ERR_UNKNOWN;
}
}
else
{
wxPrintf( _( "Footprint library was not updated\n" ) );
}
return CLI::EXIT_CODES::OK;
}

View File

@ -34,6 +34,7 @@ public:
int JobExportGerber( JOB* aJob );
int JobExportDrill( JOB* aJob );
int JobExportPos( JOB* aJob );
int JobExportFpUpgrade( JOB* aJob );
};
#endif

View File

@ -3621,6 +3621,7 @@ FOOTPRINT* PCB_PARSER::parseFOOTPRINT_unchecked( wxArrayString* aInitialComments
NeedRIGHT();
m_requiredVersion = std::max( m_requiredVersion, this_version );
m_tooRecent = ( m_requiredVersion > SEXPR_BOARD_FILE_VERSION );
footprint->SetFileFormatVersionAtLoad( this_version );
break;
}

View File

@ -26,7 +26,6 @@
#include <base_units.h>
#include <board.h>
#include <board_design_settings.h>
#include <boost/ptr_container/ptr_map.hpp>
#include <confirm.h>
#include <convert_basic_shapes_to_polygon.h> // for enum RECT_CHAMFER_POSITIONS definition
#include <core/arraydim.h>
@ -54,7 +53,6 @@
#include <wildcards_and_files_ext.h>
#include <wx/dir.h>
#include <wx/log.h>
#include <wx_filename.h>
#include <zone.h>
#include <zones.h>
@ -68,101 +66,12 @@
using namespace PCB_KEYS_T;
/**
* Helper class for creating a footprint library cache.
*
* The new footprint library design is a file path of individual footprint files that contain
* a single footprint per file. This class is a helper only for the footprint portion of the
* PLUGIN API, and only for the #PCB_PLUGIN plugin. It is private to this implementation file so
* it is not placed into a header.
*/
class FP_CACHE_ITEM
{
WX_FILENAME m_filename;
std::unique_ptr<FOOTPRINT> m_footprint;
public:
FP_CACHE_ITEM( FOOTPRINT* aFootprint, const WX_FILENAME& aFileName );
const WX_FILENAME& GetFileName() const { return m_filename; }
const FOOTPRINT* GetFootprint() const { return m_footprint.get(); }
};
FP_CACHE_ITEM::FP_CACHE_ITEM( FOOTPRINT* aFootprint, const WX_FILENAME& aFileName ) :
m_filename( aFileName ),
m_footprint( aFootprint )
{ }
typedef boost::ptr_map< wxString, FP_CACHE_ITEM > FOOTPRINT_MAP;
class FP_CACHE
{
PCB_PLUGIN* m_owner; // Plugin object that owns the cache.
wxFileName m_lib_path; // The path of the library.
wxString m_lib_raw_path; // For quick comparisons.
FOOTPRINT_MAP m_footprints; // Map of footprint filename to FOOTPRINT*.
bool m_cache_dirty; // Stored separately because it's expensive to check
// m_cache_timestamp against all the files.
long long m_cache_timestamp; // A hash of the timestamps for all the footprint
// files.
public:
FP_CACHE( PCB_PLUGIN* aOwner, const wxString& aLibraryPath );
wxString GetPath() const { return m_lib_raw_path; }
bool IsWritable() const { return m_lib_path.IsOk() && m_lib_path.IsDirWritable(); }
bool Exists() const { return m_lib_path.IsOk() && m_lib_path.DirExists(); }
FOOTPRINT_MAP& GetFootprints() { return m_footprints; }
// Most all functions in this class throw IO_ERROR exceptions. There are no
// error codes nor user interface calls from here, nor in any PLUGIN.
// Catch these exceptions higher up please.
/**
* Save the footprint cache or a single footprint from it to disk
*
* @param aFootprint if set, save only this footprint, otherwise, save the full library
*/
void Save( FOOTPRINT* aFootprint = nullptr );
void Load();
void Remove( const wxString& aFootprintName );
/**
* Generate a timestamp representing all source files in the cache (including the
* parent directory).
* Timestamps should not be considered ordered. They either match or they don't.
*/
static long long GetTimestamp( const wxString& aLibPath );
/**
* Return true if the cache is not up-to-date.
*/
bool IsModified();
/**
* Check if \a aPath is the same as the current cache path.
*
* This tests paths by converting \a aPath using the native separators. Internally
* #FP_CACHE stores the current path using native separators. This prevents path
* miscompares on Windows due to the fact that paths can be stored with / instead of \\
* in the footprint library table.
*
* @param aPath is the library path to test against.
* @return true if \a aPath is the same as the cache path.
*/
bool IsPath( const wxString& aPath ) const;
};
FP_CACHE::FP_CACHE( PCB_PLUGIN* aOwner, const wxString& aLibraryPath )
{
m_owner = aOwner;
@ -189,7 +98,7 @@ void FP_CACHE::Save( FOOTPRINT* aFootprint )
m_lib_raw_path ) );
}
for( FOOTPRINT_MAP::iterator it = m_footprints.begin(); it != m_footprints.end(); ++it )
for( FP_CACHE_FOOTPRINT_MAP::iterator it = m_footprints.begin(); it != m_footprints.end(); ++it )
{
if( aFootprint && aFootprint != it->second->GetFootprint() )
continue;
@ -300,7 +209,7 @@ void FP_CACHE::Load()
void FP_CACHE::Remove( const wxString& aFootprintName )
{
FOOTPRINT_MAP::const_iterator it = m_footprints.find( aFootprintName );
FP_CACHE_FOOTPRINT_MAP::const_iterator it = m_footprints.find( aFootprintName );
if( it == m_footprints.end() )
{
@ -2571,8 +2480,8 @@ const FOOTPRINT* PCB_PLUGIN::getFootprint( const wxString& aLibraryPath,
// do nothing with the error
}
FOOTPRINT_MAP& footprints = m_cache->GetFootprints();
FOOTPRINT_MAP::const_iterator it = footprints.find( aFootprintName );
FP_CACHE_FOOTPRINT_MAP& footprints = m_cache->GetFootprints();
FP_CACHE_FOOTPRINT_MAP::const_iterator it = footprints.find( aFootprintName );
if( it == footprints.end() )
return nullptr;
@ -2665,7 +2574,7 @@ void PCB_PLUGIN::FootprintSave( const wxString& aLibraryPath, const FOOTPRINT* a
wxString footprintName = aFootprint->GetFPID().GetLibItemName();
FOOTPRINT_MAP& footprints = m_cache->GetFootprints();
FP_CACHE_FOOTPRINT_MAP& footprints = m_cache->GetFootprints();
// Quietly overwrite footprint and delete footprint file from path for any by same name.
wxFileName fn( aLibraryPath, aFootprint->GetFPID().GetLibItemName(),
@ -2688,7 +2597,7 @@ void PCB_PLUGIN::FootprintSave( const wxString& aLibraryPath, const FOOTPRINT* a
wxString fullPath = fn.GetFullPath();
wxString fullName = fn.GetFullName();
FOOTPRINT_MAP::const_iterator it = footprints.find( footprintName );
FP_CACHE_FOOTPRINT_MAP::const_iterator it = footprints.find( footprintName );
if( it != footprints.end() )
{

View File

@ -28,6 +28,8 @@
#include <io_mgr.h>
#include <string>
#include <layer_ids.h>
#include <boost/ptr_container/ptr_map.hpp>
#include <wx_filename.h>
#include "widgets/report_severity.h"
class BOARD;
@ -51,7 +53,7 @@ class PCB_TEXT;
class PCB_TEXTBOX;
class EDA_TEXT;
class SHAPE_LINE_CHAIN;
class PCB_PLUGIN; // forward decl
/// Current s-expression file format version. 2 was the last legacy format version.
@ -160,6 +162,92 @@ class SHAPE_LINE_CHAIN;
/// a BOARD file underneath IO_MGR.
#define CTL_FOR_BOARD (CTL_OMIT_INITIAL_COMMENTS|CTL_OMIT_FOOTPRINT_VERSION)
/**
* Helper class for creating a footprint library cache.
*
* The new footprint library design is a file path of individual footprint files that contain
* a single footprint per file. This class is a helper only for the footprint portion of the
* PLUGIN API, and only for the #PCB_PLUGIN plugin. It is private to this implementation file so
* it is not placed into a header.
*/
class FP_CACHE_ITEM
{
WX_FILENAME m_filename;
std::unique_ptr<FOOTPRINT> m_footprint;
public:
FP_CACHE_ITEM( FOOTPRINT* aFootprint, const WX_FILENAME& aFileName );
const WX_FILENAME& GetFileName() const { return m_filename; }
const FOOTPRINT* GetFootprint() const { return m_footprint.get(); }
};
typedef boost::ptr_map<wxString, FP_CACHE_ITEM> FP_CACHE_FOOTPRINT_MAP;
class FP_CACHE
{
PCB_PLUGIN* m_owner; // Plugin object that owns the cache.
wxFileName m_lib_path; // The path of the library.
wxString m_lib_raw_path; // For quick comparisons.
FP_CACHE_FOOTPRINT_MAP m_footprints; // Map of footprint filename to FOOTPRINT*.
bool m_cache_dirty; // Stored separately because it's expensive to check
// m_cache_timestamp against all the files.
long long m_cache_timestamp; // A hash of the timestamps for all the footprint
// files.
public:
FP_CACHE( PCB_PLUGIN* aOwner, const wxString& aLibraryPath );
wxString GetPath() const { return m_lib_raw_path; }
bool IsWritable() const { return m_lib_path.IsOk() && m_lib_path.IsDirWritable(); }
bool Exists() const { return m_lib_path.IsOk() && m_lib_path.DirExists(); }
FP_CACHE_FOOTPRINT_MAP& GetFootprints() { return m_footprints; }
// Most all functions in this class throw IO_ERROR exceptions. There are no
// error codes nor user interface calls from here, nor in any PLUGIN.
// Catch these exceptions higher up please.
/**
* Save the footprint cache or a single footprint from it to disk
*
* @param aFootprint if set, save only this footprint, otherwise, save the full library
*/
void Save( FOOTPRINT* aFootprint = nullptr );
void Load();
void Remove( const wxString& aFootprintName );
/**
* Generate a timestamp representing all source files in the cache (including the
* parent directory).
* Timestamps should not be considered ordered. They either match or they don't.
*/
static long long GetTimestamp( const wxString& aLibPath );
/**
* Return true if the cache is not up-to-date.
*/
bool IsModified();
/**
* Check if \a aPath is the same as the current cache path.
*
* This tests paths by converting \a aPath using the native separators. Internally
* #FP_CACHE stores the current path using native separators. This prevents path
* miscompares on Windows due to the fact that paths can be stored with / instead of \\
* in the footprint library table.
*
* @param aPath is the library path to test against.
* @return true if \a aPath is the same as the cache path.
*/
bool IsPath( const wxString& aPath ) const;
};
/**
* A #PLUGIN derivation for saving and loading Pcbnew s-expression formatted files.