KiCad PCB EDA Suite
Loading...
Searching...
No Matches
command_import.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "command_import.h"
21#include <cli/exit_codes.h>
22#include <jobs/job_pcb_import.h>
23#include <jobs/job_sch_import.h>
25#include <pgm_base.h>
27#include <project.h>
29#include <reporter.h>
30#include <kiid.h>
31#include <string_utils.h>
32#include <macros.h>
34#include <wx/crt.h>
35#include <wx/filename.h>
36
37#include <memory>
38#include <vector>
39
40
41#define ARG_LAYER_MAP "--layer-map"
42
43
45{
46 m_argParser.add_description( UTF8STDSTR(
47 _( "Import non-KiCad board and/or schematic files into a new KiCad project" ) ) );
48
49 m_argParser.add_argument( ARG_INPUT )
50 .help( UTF8STDSTR( _( "Input file(s) to import; each is autodetected as a board or "
51 "schematic" ) ) )
52 .nargs( argparse::nargs_pattern::at_least_one )
53 .metavar( "INPUT_FILES" );
54
55 m_argParser.add_argument( "-o", ARG_OUTPUT )
56 .default_value( std::string() )
57 .help( UTF8STDSTR( _( "Output project path stem; produces <stem>.kicad_pro plus the "
58 "imported <stem>.kicad_pcb and/or <stem>.kicad_sch beside it. "
59 "Defaults to the first input's name in the current directory." ) ) )
60 .metavar( "PROJECT" );
61
62 m_argParser.add_argument( ARG_LAYER_MAP )
63 .default_value( std::string( "" ) )
64 .help( UTF8STDSTR( _( "JSON file mapping source layer names to KiCad layer names for "
65 "imported boards; unmapped layers use the automatic best-guess" ) ) )
66 .metavar( "FILE" );
67}
68
69
71{
72 std::vector<std::string> rawInputs = m_argParser.get<std::vector<std::string>>( ARG_INPUT );
73 wxString outputArg = From_UTF8( m_argParser.get<std::string>( ARG_OUTPUT ).c_str() );
74
75 if( rawInputs.empty() )
76 {
77 wxFprintf( stderr, _( "At least one input file is required\n" ) );
79 }
80
81 std::vector<wxString> inputFiles;
82
83 for( const std::string& raw : rawInputs )
84 inputFiles.push_back( From_UTF8( raw.c_str() ) );
85
86 std::map<wxString, wxString> layerMap;
87 wxString layerMapFile = From_UTF8( m_argParser.get<std::string>( ARG_LAYER_MAP ).c_str() );
88
89 if( !layerMapFile.IsEmpty() )
90 {
91 wxString error;
92
93 if( !LoadLayerMapFile( layerMapFile, layerMap, error ) )
94 {
95 wxFprintf( stderr, wxS( "%s\n" ), error );
97 }
98 }
99
100 wxFileName projectFn;
101
102 if( !outputArg.IsEmpty() )
103 {
104 projectFn.Assign( outputArg );
105 }
106 else
107 {
108 wxFileName firstInput( inputFiles.front() );
109 projectFn.AssignDir( wxFileName::GetCwd() );
110 projectFn.SetName( firstInput.GetName() );
111 }
112
113 projectFn.SetExt( FILEEXT::ProjectFileExtension );
114 projectFn.MakeAbsolute();
115
116 if( !projectFn.DirExists()
117 && !wxFileName::Mkdir( projectFn.GetPath(), wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
118 {
119 wxFprintf( stderr, _( "Could not create output directory: %s\n" ), projectFn.GetPath() );
121 }
122
123 wxString stem = projectFn.GetName();
124 wxString dir = projectFn.GetPath();
125
126 wxFileName boardFn( dir, stem, FILEEXT::KiCadPcbFileExtension );
127 wxFileName schFn( dir, stem, FILEEXT::KiCadSchematicFileExtension );
128
129 // LoadProject returns false for a not-yet-existing file but still registers a fully-defaulted
130 // active project, so success is signalled by GetProject() returning it.
132
133 mgr.LoadProject( projectFn.GetFullPath(), true );
134
135 PROJECT* project = mgr.GetProject( projectFn.GetFullPath() );
136
137 if( !project )
138 {
139 wxFprintf( stderr, _( "Could not create project: %s\n" ), projectFn.GetFullPath() );
141 }
143
144 bool haveBoard = false;
145 bool haveSch = false;
146 int retCode = EXIT_CODES::SUCCESS;
147
148 for( const wxString& input : inputFiles )
149 {
150 // Classify by trial dispatch: a face that does not recognize the file returns the
151 // ERR_UNKNOWN_FILE_FORMAT sentinel before loading, so we fall through to the next face.
152 // Probe a face only while its slot is open so a duplicate input is rejected without
153 // overwriting an earlier output.
154 if( !haveBoard )
155 {
156 std::unique_ptr<JOB_PCB_IMPORT> pcbJob = std::make_unique<JOB_PCB_IMPORT>();
157 pcbJob->m_inputFile = input;
158 pcbJob->m_format = JOB_PCB_IMPORT::FORMAT::AUTO;
159 pcbJob->m_layerMap = layerMap;
160 pcbJob->SetConfiguredOutputPath( boardFn.GetFullPath() );
161
162 int pcbResult = aKiway.ProcessJob( KIWAY::FACE_PCB, pcbJob.get(), &reporter );
163
164 if( pcbResult == EXIT_CODES::SUCCESS )
165 {
166 haveBoard = true;
167 continue;
168 }
169 else if( pcbResult != EXIT_CODES::ERR_UNKNOWN_FILE_FORMAT )
170 {
171 retCode = pcbResult;
172 break;
173 }
174 }
175
176 if( !haveSch )
177 {
178 std::unique_ptr<JOB_SCH_IMPORT> schJob = std::make_unique<JOB_SCH_IMPORT>();
179 schJob->m_inputFile = input;
180 schJob->m_format = JOB_SCH_IMPORT::FORMAT::AUTO;
181 schJob->SetConfiguredOutputPath( schFn.GetFullPath() );
182
183 int schResult = aKiway.ProcessJob( KIWAY::FACE_SCH, schJob.get(), &reporter );
184
185 if( schResult == EXIT_CODES::SUCCESS )
186 {
187 haveSch = true;
188 continue;
189 }
190 else if( schResult != EXIT_CODES::ERR_UNKNOWN_FILE_FORMAT )
191 {
192 retCode = schResult;
193 break;
194 }
195 }
196
197 if( haveBoard && haveSch )
198 {
199 wxFprintf( stderr, _( "Could not import '%s': a board and a schematic have already "
200 "been imported for this project\n" ), input );
201 }
202 else
203 {
204 wxFprintf( stderr, _( "No board or schematic importer recognizes the file: %s\n" ),
205 input );
206 }
207
209 break;
210 }
211
212 if( retCode == EXIT_CODES::SUCCESS && !haveBoard && !haveSch )
213 {
214 wxFprintf( stderr, _( "No inputs could be imported\n" ) );
216 }
217
218 if( retCode == EXIT_CODES::SUCCESS && project )
219 {
220 // The schematic handler already registered any sheets; link the board here.
221 if( haveBoard )
222 {
223 std::vector<FILE_INFO_PAIR>& boards = project->GetProjectFile().GetBoards();
224 boards.clear();
225 boards.emplace_back( std::make_pair( KIID(), boardFn.GetFullName() ) );
226 }
227
228 if( mgr.SaveProject( projectFn.GetFullPath() ) )
229 {
230 reporter.Report( wxString::Format( _( "Created project '%s'\n" ),
231 projectFn.GetFullPath() ),
233 }
234 else
235 {
236 wxFprintf( stderr, _( "Failed to write project file: %s\n" ),
237 projectFn.GetFullPath() );
239 }
240 }
241
242 mgr.UnloadProject( project, false );
243
244 return retCode;
245}
argparse::ArgumentParser m_argParser
Definition command.h:113
COMMAND(const std::string &aName)
Define a new COMMAND instance.
Definition command.cpp:30
int doPerform(KIWAY &aKiway) override
The internal handler that should be overloaded to implement command specific processing and work.
static CLI_REPORTER & GetInstance()
Definition reporter.cpp:157
Definition kiid.h:44
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition kiway.h:311
int ProcessJob(KIWAY::FACE_T aFace, JOB *aJob, REPORTER *aReporter=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr)
Definition kiway.cpp:746
@ FACE_SCH
eeschema DSO
Definition kiway.h:318
@ FACE_PCB
pcbnew DSO
Definition kiway.h:319
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition pgm_base.h:126
Container for project specific data.
Definition project.h:62
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:71
bool SaveProject(const wxString &aFullPath=wxEmptyString, PROJECT *aProject=nullptr)
Save a loaded project.
bool LoadProject(const wxString &aFullPath, bool aSetActive=true)
Load a project or sets up a new project with a specified path.
PROJECT * GetProject(const wxString &aFullPath) const
Retrieve a loaded project by name.
bool UnloadProject(PROJECT *aProject, bool aSave=true)
Save, unload and unregister the given PROJECT.
#define ARG_OUTPUT
Definition command.h:33
#define ARG_INPUT
Definition command.h:34
#define UTF8STDSTR(s)
Definition command.h:27
#define ARG_LAYER_MAP
#define _(s)
static const std::string ProjectFileExtension
static const std::string KiCadSchematicFileExtension
static const std::string KiCadPcbFileExtension
bool LoadLayerMapFile(const wxString &aFile, std::map< wxString, wxString > &aMap, wxString &aError)
Load an explicit layer-mapping file into aMap.
This file contains miscellaneous commonly used macros and functions.
static const int ERR_ARGS
Definition exit_codes.h:31
static const int ERR_INVALID_INPUT_FILE
Definition exit_codes.h:33
static const int SUCCESS
Definition exit_codes.h:29
static const int ERR_INVALID_OUTPUT_CONFLICT
Definition exit_codes.h:34
static const int ERR_UNKNOWN_FILE_FORMAT
No plugin for the requested face recognized the input file format.
Definition exit_codes.h:42
static const int ERR_UNKNOWN
Definition exit_codes.h:32
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
@ RPT_SEVERITY_INFO
wxString From_UTF8(const char *cstring)
IbisParser parser & reporter
std::vector< BOARD_BEST > boards
Definition of file extensions used in Kicad.