KiCad PCB EDA Suite
Loading...
Searching...
No Matches
project_downgrade.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
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include "project_downgrade.h"
21
22#include <filesystem>
23#include <system_error>
24#include <utility>
25
26#include <json_common.h>
27
28#include <wx/dir.h>
29#include <wx/ffile.h>
30#include <wx/filefn.h>
31#include <wx/filename.h>
32
33#include <downgrade_scan.h>
34#include <downgrade_target.h>
35#include <drc_rules_downgrade.h>
38#include <kiway.h>
42
43
44// Collects the errors a job reports, so a failure message can say why instead of dead-ending
45// on a generic line.
47{
48public:
49 REPORTER& Report( const wxString& aText, SEVERITY aSeverity = RPT_SEVERITY_UNDEFINED ) override
50 {
51 if( aSeverity == RPT_SEVERITY_ERROR )
52 m_errors += aText;
53
54 return *this;
55 }
56
57 bool HasMessage() const override { return !m_errors.IsEmpty(); }
58
60 wxString Explain( const wxString& aMessage ) const
61 {
62 if( m_errors.IsEmpty() )
63 return aMessage;
64
65 wxString detail = m_errors;
66 detail.Trim();
67
68 return aMessage + wxT( "\n\n" ) + detail;
69 }
70
71private:
72 wxString m_errors;
73};
74
75
76static bool quoteLegacyLibraryTableValue( const std::string& aValue, bool aPreTen, std::string& aQuoted )
77{
78 if( aValue.find( '\0' ) != std::string::npos )
79 return false;
80
81 if( !aPreTen )
82 {
83 if( aValue.find( '"' ) == std::string::npos )
84 {
85 // Frozen 10.0 takes quoted contents literally, including slashes and line breaks.
86 aQuoted = '"' + aValue + '"';
87 return true;
88 }
89
90 // Its bare TOKEN grammar accepts internal quotes, but cannot start with one.
91 if( !aValue.empty() && aValue.front() != '"' && aValue.find_first_of( "() \t\n\r" ) == std::string::npos )
92 {
93 aQuoted = aValue;
94 return true;
95 }
96
97 return false;
98 }
99
100 // Frozen 9.0 uses DSNLEXER and decodes escape sequences.
101 aQuoted = '"';
102
103 for( char c : aValue )
104 {
105 switch( c )
106 {
107 case '\\': aQuoted += "\\\\"; break;
108 case '"': aQuoted += "\\\""; break;
109 case '\n': aQuoted += "\\n"; break;
110 case '\r': aQuoted += "\\r"; break;
111 case '\t': aQuoted += "\\t"; break;
112 default: aQuoted += c; break;
113 }
114 }
115
116 aQuoted += '"';
117 return true;
118}
119
120
121static bool legacyLibraryTables( const wxString& aDir, const DOWNGRADE_TARGET& aTarget,
122 std::vector<std::pair<wxString, wxString>>& aTables, wxString& aError )
123{
124 wxArrayString files;
125 wxDir::GetAllFiles( aDir, &files );
126
127 for( const wxString& path : files )
128 {
129 wxFileName filename( path );
130
132 || ( filename.GetFullName() != wxT( "sym-lib-table" ) && filename.GetFullName() != wxT( "fp-lib-table" ) ) )
133 {
134 continue;
135 }
136
137 wxFFile in( path, wxT( "rb" ) );
138 wxString text;
139
140 if( !in.IsOpened() || !in.ReadAll( &text ) )
141 {
142 aError = wxString::Format( _( "Could not read library table '%s'." ), path );
143 return false;
144 }
145
146 if( text.IsEmpty() )
147 continue;
148
150 auto parsed = parser.ParseBuffer( text.ToStdString( wxConvUTF8 ) );
151
152 if( !parsed )
153 {
154 aError = wxString::Format( _( "Library table '%s' is invalid: %s" ), path, parsed.error().description );
155 return false;
156 }
157
158 std::string output = filename.GetFullName() == wxT( "sym-lib-table" ) ? "(sym_lib_table" : "(fp_lib_table";
159 output += " (version 7)\n";
160
161 for( const LIBRARY_TABLE_ROW_IR& row : parsed->rows )
162 {
163 if( row.type == "Table" && aTarget.m_boardVersion < 20260206 )
164 {
165 aError = wxString::Format( _( "Library table '%s' uses a nested table that %s cannot open." ), path,
166 aTarget.m_name );
167 return false;
168 }
169
170 output += " (lib";
171
172 for( const auto& [key, value] : { std::pair{ "name", row.nickname },
173 { "type", row.type },
174 { "uri", row.uri },
175 { "options", row.options },
176 { "descr", row.description } } )
177 {
178 // Keep an unspecified plugin type absent, matching the source's default.
179 if( std::string( key ) == "type" && value.empty() )
180 continue;
181
182 std::string quoted;
183
184 if( !quoteLegacyLibraryTableValue( value, aTarget.m_boardVersion < 20260206, quoted ) )
185 {
186 aError = wxString::Format(
187 _( "Library table '%s' contains a quoted value that %s cannot represent exactly." ), path,
188 aTarget.m_name );
189 return false;
190 }
191
192 output += " (" + std::string( key ) + ' ' + quoted + ')';
193 }
194
195 if( row.disabled )
196 output += " (disabled)";
197
198 if( row.hidden )
199 output += " (hidden)";
200
201 output += ")\n";
202 }
203
204 output += ")\n";
205 aTables.emplace_back( path, wxString::FromUTF8( output.c_str() ) );
206 }
207
208 return true;
209}
210
211
212// Parse a project file, migrate it down, and write it back. wx file IO throughout, so
213// non-ASCII paths work on every platform.
214static bool downgradeProjectFileOnDisk( const wxString& aPath, const DOWNGRADE_TARGET& aTarget,
215 COMPATIBILITY_REPORT& aReport )
216{
217 wxString text;
218
219 {
220 wxFFile in( aPath, wxT( "rb" ) );
221
222 if( !in.IsOpened() || !in.ReadAll( &text ) )
223 return false;
224 }
225
226 nlohmann::json doc;
227
228 try
229 {
230 doc = nlohmann::json::parse( text.ToStdString( wxConvUTF8 ) );
231 DowngradeProjectFileJson( doc, aTarget, aReport );
232
233 if( aReport.IsBlocked() )
234 return false;
235 }
236 catch( ... )
237 {
238 return false;
239 }
240
241 wxFFile out( aPath, wxT( "wb" ) );
242
243 return out.IsOpened() && out.Write( wxString::FromUTF8( ( doc.dump( 2 ) + "\n" ).c_str() ) );
244}
245
246
247// Read the (version N) stamp of a KiCad s-expression file. Returns 0 if not found. The header
248// is ASCII, so the scan works on raw bytes and a chunk cut inside a multibyte character cannot
249// blind it. Quoted strings are stripped so text content cannot shadow the header.
250static int readSexprVersion( const wxString& aPath )
251{
252 wxFFile file( aPath, wxT( "rb" ) );
253
254 if( !file.IsOpened() )
255 return 0;
256
257 char buf[8192];
258 size_t n = file.Read( buf, sizeof( buf ) - 1 );
259
260 std::string head = StripSexprStrings( std::string( buf, n ) );
261 size_t idx = head.find( "(version " );
262
263 if( idx == std::string::npos )
264 return 0;
265
266 return atoi( head.c_str() + idx + 9 );
267}
268
269
270// Final backstop over the exported copy. Anything still stamped newer than the target would not
271// open there. Returns the first such file, or empty if all are safe.
272static wxString firstTooNewFile( const wxString& aDir, const DOWNGRADE_TARGET& aTarget )
273{
274 auto scan = [&]( const wxString& aPattern, int aMaxVersion ) -> wxString
275 {
276 wxArrayString files;
277 wxDir::GetAllFiles( aDir, &files, aPattern );
278
279 for( const wxString& f : files )
280 {
281 if( !IsDowngradeWorkingArtifact( f ) && readSexprVersion( f ) > aMaxVersion )
282 return f;
283 }
284
285 return wxEmptyString;
286 };
287
288 wxString bad = scan( wxT( "*.kicad_sym" ), aTarget.m_symLibVersion );
289
290 if( !bad.IsEmpty() )
291 return bad;
292
293 bad = scan( wxT( "*.kicad_mod" ), aTarget.m_boardVersion );
294
295 if( !bad.IsEmpty() )
296 return bad;
297
298 bad = scan( wxT( "*.kicad_pcb" ), aTarget.m_boardVersion );
299
300 if( !bad.IsEmpty() )
301 return bad;
302
303 return scan( wxT( "*.kicad_sch" ), aTarget.m_schVersion );
304}
305
306
307bool ClassifyProjectForDowngrade( KIWAY& aKiway, const wxString& aProjectDir, const wxString& aProjectName,
308 const wxString& aTargetName, COMPATIBILITY_REPORT& aReport, wxString& aError,
309 std::vector<wxString>* aFoundVariants, bool aDropInsteadOfApproximate,
310 const wxString& aVariant )
311{
312 wxFileName boardFn( aProjectDir, aProjectName, FILEEXT::KiCadPcbFileExtension );
313 wxFileName schFn( aProjectDir, aProjectName, FILEEXT::KiCadSchematicFileExtension );
314
315 if( !boardFn.FileExists() && !schFn.FileExists() )
316 {
317 aError = _( "This project has no board or schematic to export." );
318 return false;
319 }
320
321 const DOWNGRADE_TARGET* target = FindDowngradeTarget( aTargetName );
322 wxFileName proFn( aProjectDir, aProjectName, FILEEXT::ProjectFileExtension );
323
324 // Settings guards run before native jobs can reinterpret a future project schema.
325 if( target && proFn.FileExists() )
326 {
327 wxString text;
328 wxFFile in( proFn.GetFullPath(), wxT( "rb" ) );
329
330 if( in.IsOpened() && in.ReadAll( &text ) )
331 {
332 try
333 {
334 nlohmann::json doc = nlohmann::json::parse( text.ToStdString( wxConvUTF8 ) );
335 DowngradeProjectFileJson( doc, *target, aReport );
336 }
337 catch( ... )
338 {
339 // Native readers diagnose malformed source projects.
340 }
341 }
342
343 if( aReport.IsBlocked() )
344 return true;
345 }
346
347 {
350 job.m_filename = boardFn.FileExists() ? boardFn.GetFullPath() : wxString();
351 job.m_target = aTargetName;
352 job.m_libraryDir = aProjectDir;
353 job.m_variant = boardFn.FileExists() ? aVariant : wxString();
354 job.m_dryRun = true;
355 job.m_dropInsteadOfApproximate = aDropInsteadOfApproximate;
356
357 if( aKiway.ProcessJob( KIWAY::FACE_PCB, &job, &reporter ) != 0 )
358 {
359 aError = reporter.Explain( _( "Could not read the board to check compatibility." ) );
360 return false;
361 }
362
363 aReport.Merge( job.m_report );
364 }
365
366 {
369 job.m_filename = schFn.FileExists() ? schFn.GetFullPath() : wxString();
370 job.m_target = aTargetName;
371 job.m_libraryDir = aProjectDir;
372 job.m_variant = schFn.FileExists() ? aVariant : wxString();
373 job.m_dryRun = true;
374 job.m_dropInsteadOfApproximate = aDropInsteadOfApproximate;
375
376 if( aKiway.ProcessJob( KIWAY::FACE_SCH, &job, &reporter ) != 0 )
377 {
378 aError = reporter.Explain( _( "Could not read the schematic to check compatibility." ) );
379 return false;
380 }
381
382 aReport.Merge( job.m_report );
383
384 if( aFoundVariants )
385 *aFoundVariants = job.m_foundVariants;
386 }
387
388 wxFileName druFn( aProjectDir, aProjectName, FILEEXT::DesignRulesFileExtension );
389
390 if( target )
391 {
392 std::vector<std::pair<wxString, wxString>> tables;
393 wxString tableError;
394
395 if( !legacyLibraryTables( aProjectDir, *target, tables, tableError ) )
396 aReport.Add( DOWNGRADE_BUCKET::BLOCK, _( "Project library tables" ), tableError );
397 }
398
399 if( target && druFn.FileExists() )
400 {
401 wxString rulesText;
402 wxFFile file( druFn.GetFullPath(), wxT( "rb" ) );
403
404 if( file.IsOpened() && file.ReadAll( &rulesText ) )
405 {
406 DRC_RULES_FILTER_RESULT filtered = FilterDrcRulesForTarget( rulesText, *target );
407
408 if( !filtered.m_error.IsEmpty() )
409 aReport.Add( DOWNGRADE_BUCKET::BLOCK, _( "Invalid custom DRC rules" ), filtered.m_error );
410
411 for( const wxString& name : filtered.m_dropped )
412 {
413 wxString shown = name.IsEmpty() ? _( "unnamed" ) : name;
414
415 aReport.Add( DOWNGRADE_BUCKET::DROP, wxString::Format( _( "Custom DRC rule '%s'" ), shown ),
416 _( "The target cannot evaluate it with the same meaning. The rule is removed." ), 1 );
417 }
418 }
419 }
420
421 return true;
422}
423
424
425bool ExportProjectForDowngrade( KIWAY& aKiway, const wxString& aProjectDir, const wxString& aProjectName,
426 const wxString& aTargetName, const wxString& aDestDir, wxString& aError,
427 const wxString& aVariant, bool aDropInsteadOfApproximate )
428{
429 const DOWNGRADE_TARGET* target = FindDowngradeTarget( aTargetName );
430
431 if( !target )
432 {
433 aError = wxString::Format( _( "Unknown target version '%s'. Valid targets: %s" ), aTargetName,
435 return false;
436 }
437
438 std::vector<std::pair<wxString, wxString>> tables;
439
440 if( !legacyLibraryTables( aProjectDir, *target, tables, aError ) )
441 return false;
442
443 std::error_code ec;
444 std::filesystem::path srcPath = std::filesystem::weakly_canonical( aProjectDir.ToStdWstring(), ec );
445 std::filesystem::path destPath = std::filesystem::weakly_canonical( aDestDir.ToStdWstring(), ec );
446
447 // The destination must not be the project folder or live inside it, or the copy would feed
448 // on its own output. Existing ancestors are compared by identity, not by spelling, so a
449 // case or UNC variation of the same folder cannot slip past.
450 for( std::filesystem::path p = destPath; !p.empty() && p != p.parent_path(); p = p.parent_path() )
451 {
452 std::error_code cmpEc;
453
454 if( p == srcPath || ( std::filesystem::exists( p, cmpEc ) && std::filesystem::equivalent( p, srcPath, cmpEc ) ) )
455 {
456 aError = _( "Choose a destination outside the current project." );
457 return false;
458 }
459 }
460
461 bool created = std::filesystem::create_directories( destPath, ec );
462
463 if( ec )
464 {
465 aError = wxString::Format( _( "Could not create the folder '%s'." ), aDestDir );
466 return false;
467 }
468
469 if( !created && !std::filesystem::is_empty( destPath, ec ) )
470 {
471 aError = wxString::Format( _( "The folder '%s' is not empty. Choose a new or empty folder." ), aDestDir );
472 return false;
473 }
474
475 // Take away only what the export put there. A folder the user already had stays, because the
476 // export did not create it. Report a cleanup that did not finish rather than claiming the
477 // destination is untouched.
478 auto failClosed = [&]( const wxString& aMessage ) -> bool
479 {
480 std::error_code cleanupEc;
481
482 if( created )
483 {
484 std::filesystem::remove_all( destPath, cleanupEc );
485 }
486 else
487 {
488 std::vector<std::filesystem::path> entries;
489
490 for( const auto& entry : std::filesystem::directory_iterator( destPath, cleanupEc ) )
491 entries.push_back( entry.path() );
492
493 for( const std::filesystem::path& entry : entries )
494 std::filesystem::remove_all( entry, cleanupEc );
495 }
496
497 aError = aMessage;
498
499 if( cleanupEc )
500 {
501 aError << wxS( " " )
502 << wxString::Format( _( "Some files could not be removed from '%s'." ), aDestDir );
503 }
504
505 return false;
506 };
507
508 // Design files only. Datasheets, gerbers, UI state and other working files stay behind.
509 {
510 wxArrayString files;
511 wxDir::GetAllFiles( aProjectDir, &files );
512
513 for( const wxString& file : files )
514 {
515 if( IsDowngradeWorkingArtifact( file ) || !IsDowngradeDesignFile( file ) )
516 continue;
517
518 wxFileName rel( file );
519 rel.MakeRelativeTo( aProjectDir );
520
521 wxFileName out( aDestDir + wxFileName::GetPathSeparator() + rel.GetFullPath() );
522
523 if( !out.DirExists() && !wxFileName::Mkdir( out.GetPath(), wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
524 return failClosed( _( "Could not copy the project." ) );
525
526 if( !wxCopyFile( file, out.GetFullPath() ) )
527 return failClosed( _( "Could not copy the project." ) );
528 }
529 }
530
531 wxFileName boardFn( aProjectDir, aProjectName, FILEEXT::KiCadPcbFileExtension );
532 wxFileName schFn( aProjectDir, aProjectName, FILEEXT::KiCadSchematicFileExtension );
533
534 // Consent was collected against the classification, so the jobs run forced.
535 {
536 wxFileName destBoard( aDestDir, boardFn.GetFullName() );
537
540 job.m_filename = boardFn.FileExists() ? destBoard.GetFullPath() : wxString();
541 job.m_inPlace = true;
542 job.m_outputFile = destBoard.GetFullPath();
543 job.m_target = aTargetName;
544 job.m_libraryDir = aDestDir;
545 job.m_variant = boardFn.FileExists() ? aVariant : wxString();
546 job.m_force = true;
547 job.m_dropInsteadOfApproximate = aDropInsteadOfApproximate;
548
549 if( aKiway.ProcessJob( KIWAY::FACE_PCB, &job, &reporter ) != 0 )
550 {
551 return failClosed( reporter.Explain( _( "Export failed: the board could not be converted. Nothing "
552 "was written." ) ) );
553 }
554 }
555
556 {
557 wxFileName destSch( aDestDir, schFn.GetFullName() );
558
561 job.m_filename = schFn.FileExists() ? destSch.GetFullPath() : wxString();
562 job.m_inPlace = true;
563 job.m_target = aTargetName;
564 job.m_libraryDir = aDestDir;
565 job.m_variant = schFn.FileExists() ? aVariant : wxString();
566 job.m_force = true;
567 job.m_dropInsteadOfApproximate = aDropInsteadOfApproximate;
568
569 if( aKiway.ProcessJob( KIWAY::FACE_SCH, &job, &reporter ) != 0 )
570 {
571 return failClosed( reporter.Explain( _( "Export failed: the schematic could not be converted. Nothing "
572 "was written." ) ) );
573 }
574 }
575
576 wxFileName destPro( aDestDir, aProjectName, FILEEXT::ProjectFileExtension );
577
578 if( destPro.FileExists() )
579 {
580 COMPATIBILITY_REPORT ignored;
581
582 if( !downgradeProjectFileOnDisk( destPro.GetFullPath(), *target, ignored ) )
583 return failClosed( _( "Export failed: could not migrate the project file." ) );
584 }
585
586 // The old rules parsers are all-or-nothing, so unknown rules must not reach the copy.
587 wxFileName destDru( aDestDir, aProjectName, FILEEXT::DesignRulesFileExtension );
588
589 if( destDru.FileExists() )
590 {
591 wxString rulesText;
592
593 {
594 wxFFile file( destDru.GetFullPath(), wxT( "rb" ) );
595
596 if( !file.IsOpened() || !file.ReadAll( &rulesText ) )
597 return failClosed( _( "Export failed: could not read the design rules file." ) );
598 }
599
600 DRC_RULES_FILTER_RESULT filtered = FilterDrcRulesForTarget( rulesText, *target );
601
602 if( !filtered.m_error.IsEmpty() )
603 return failClosed( wxString::Format( _( "Export failed: %s" ), filtered.m_error ) );
604
605 wxFFile out( destDru.GetFullPath(), wxT( "wb" ) );
606
607 if( !out.IsOpened() || !out.Write( filtered.m_text ) )
608 return failClosed( _( "Export failed: could not write the design rules file." ) );
609 }
610
611 // Convert table quoting only after the native jobs have finished using the source grammar.
612 for( const auto& [source, text] : tables )
613 {
614 wxFileName relative( source );
615 relative.MakeRelativeTo( aProjectDir );
616 wxFileName destination( aDestDir + wxFileName::GetPathSeparator() + relative.GetFullPath() );
617 wxFFile out( destination.GetFullPath(), wxT( "wb" ) );
618
619 if( !out.IsOpened() || !out.Write( text ) )
620 return failClosed( _( "Export failed: could not write the library table." ) );
621 }
622
623 // Final backstop: nothing left in the copy may still be too new for the target to open.
624 wxString tooNew = firstTooNewFile( aDestDir, *target );
625
626 if( !tooNew.IsEmpty() )
627 {
628 // Point at the source path, not the deleted copy, so the user can act on it.
629 wxFileName relative( tooNew );
630 relative.MakeRelativeTo( aDestDir );
631 wxString source = wxFileName( aProjectDir, wxEmptyString ).GetPathWithSep() + relative.GetFullPath();
632
633 return failClosed( wxString::Format( _( "Export failed: '%s' is not handled by the export and %s cannot "
634 "open it. Remove or update the file and try again. Nothing was "
635 "written." ),
636 source, target->m_name ) );
637 }
638
639 return true;
640}
const char * name
What an export to a given target will keep, approximate, drop, or block.
void Add(DOWNGRADE_BUCKET aBucket, const wxString &aFeature, const wxString &aDetail=wxEmptyString, int aCount=1)
void Merge(const COMPATIBILITY_REPORT &aOther)
bool IsBlocked() const
True if some feature makes the target impossible. The user cannot export anyway.
wxString Explain(const wxString &aMessage) const
The collected error text appended to aMessage, or aMessage alone when there is none.
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
bool HasMessage() const override
Returns true if any messages were reported.
wxString m_outputFile
Where to write the downgraded copy.
bool m_dropInsteadOfApproximate
Omit features that would otherwise be approximated.
wxString m_filename
Input board file (may be empty to downgrade only libraries)
wxString m_libraryDir
If set, downgrade every .kicad_mod footprint under this folder too.
COMPATIBILITY_REPORT m_report
Filled by the handler so the caller can show it.
wxString m_variant
Flatten this variant into the export. Empty means the base design.
bool m_inPlace
Internal project export operating only on its prepared copy.
wxString m_target
Target release name, e.g. "KiCad 10.0". Empty means the newest.
bool m_dryRun
Only classify and fill m_report, do not write anything.
bool m_force
Export even when features must be dropped or approximated.
std::vector< wxString > m_foundVariants
Variant names found in the schematic when the target drops variants.
wxString m_variant
Flatten this variant into the export. Empty means the base design.
wxString m_libraryDir
If set, downgrade every .kicad_sym symbol library under this folder too.
bool m_inPlace
Internal only: convert the coordinator's already-copied export files.
bool m_dryRun
Only classify and fill m_report, do not write anything.
wxString m_filename
Root schematic file (may be empty to downgrade only libraries)
COMPATIBILITY_REPORT m_report
Filled by the handler so the caller can show it.
wxString m_target
Target release name or id, e.g. "KiCad 10.0" or "10.0". Empty means the newest.
bool m_dropInsteadOfApproximate
Omit features that would otherwise be approximated.
bool m_force
Export even when features must be dropped or approximated.
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition kiway.h:348
int ProcessJob(KIWAY::FACE_T aFace, JOB *aJob, REPORTER *aReporter=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr)
Definition kiway.cpp:740
@ FACE_SCH
eeschema DSO
Definition kiway.h:355
@ FACE_PCB
pcbnew DSO
Definition kiway.h:356
tl::expected< LIBRARY_TABLE_IR, LIBRARY_PARSE_ERROR > ParseBuffer(const std::string &aBuffer)
REPORTER()
Definition reporter.h:75
@ BLOCK
Cannot be represented at all, export to this target is not possible.
@ DROP
No equivalent, left out.
bool IsDowngradeWorkingArtifact(const wxString &aPath)
Editor working state (autosaves, local history, backups) holds current-format copies nothing downgrad...
bool IsDowngradeDesignFile(const wxString &aPath)
Files the project export carries over.
std::string StripSexprStrings(const std::string &aText)
Drop quoted strings so a token inside a text value cannot cause a false refusal.
const DOWNGRADE_TARGET * FindDowngradeTarget(const wxString &aNameOrId)
Match a user-supplied target string against name or stable id, case-insensitively.
wxString DowngradeTargetNames()
The valid target choices as one user-facing list, for error messages.
DRC_RULES_FILTER_RESULT FilterDrcRulesForTarget(const wxString &aRulesText, const DOWNGRADE_TARGET &aTarget)
Filter a .kicad_dru file for the target.
#define _(s)
static const std::string ProjectFileExtension
static const std::string KiCadSchematicFileExtension
static const std::string DesignRulesFileExtension
static const std::string KiCadPcbFileExtension
std::string source
static int readSexprVersion(const wxString &aPath)
bool ExportProjectForDowngrade(KIWAY &aKiway, const wxString &aProjectDir, const wxString &aProjectName, const wxString &aTargetName, const wxString &aDestDir, wxString &aError, const wxString &aVariant, bool aDropInsteadOfApproximate)
Export a downgraded copy of the project into aDestDir.
static bool downgradeProjectFileOnDisk(const wxString &aPath, const DOWNGRADE_TARGET &aTarget, COMPATIBILITY_REPORT &aReport)
static wxString firstTooNewFile(const wxString &aDir, const DOWNGRADE_TARGET &aTarget)
bool ClassifyProjectForDowngrade(KIWAY &aKiway, const wxString &aProjectDir, const wxString &aProjectName, const wxString &aTargetName, COMPATIBILITY_REPORT &aReport, wxString &aError, std::vector< wxString > *aFoundVariants, bool aDropInsteadOfApproximate, const wxString &aVariant)
Run the board and schematic classification for a whole project without writing anything.
static bool quoteLegacyLibraryTableValue(const std::string &aValue, bool aPreTen, std::string &aQuoted)
static bool legacyLibraryTables(const wxString &aDir, const DOWNGRADE_TARGET &aTarget, std::vector< std::pair< wxString, wxString > > &aTables, wxString &aError)
void DowngradeProjectFileJson(nlohmann::json &aDoc, const DOWNGRADE_TARGET &aTarget, COMPATIBILITY_REPORT &aReport)
Migrate a .kicad_pro document down to what the target release expects.
SEVERITY
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_UNDEFINED
An older KiCad release we can export a project back to.
int m_schVersion
SEXPR_SCHEMATIC_FILE_VERSION for the target.
int m_boardVersion
SEXPR_BOARD_FILE_VERSION for the target.
int m_symLibVersion
SEXPR_SYMBOL_LIB_FILE_VERSION for the target.
wxString m_name
User-facing name, e.g. "KiCad 10.0".
wxString m_text
The filtered rules file text.
wxString m_error
Invalid source syntax; do not write m_text.
std::vector< wxString > m_dropped
Names of the rules the target cannot parse.
std::string path
IbisParser parser & reporter
Definition of file extensions used in Kicad.