1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2004 Jean-Pierre Charras, jp.charras ar wanadoo.fr
5  * Copyright (C) 2008 Wayne Stambaugh <stambaughw@gmail.com>
6  * Copyright (C) 2004-2021 KiCad Developers, see AUTHORS.txt for contributors.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
24  */
25 
26 /**
27  * @file libarch.cpp
28  * @brief Module for generation of symbol archive files.
29  */
30 
31 #include <confirm.h>
32 #include <wildcards_and_files_ext.h>
33 
34 #include <sch_edit_frame.h>
35 #include <symbol_lib_table.h>
36 #include <symbol_library.h>
37 #include <sch_symbol.h>
38 #include <sch_sheet.h>
39 #include <schematic.h>
40 
41 
CreateArchiveLibraryCacheFile(bool aUseCurrentSheetFilename)42 bool SCH_EDIT_FRAME::CreateArchiveLibraryCacheFile( bool aUseCurrentSheetFilename )
43 {
44     wxFileName fn;
45 
46     if( aUseCurrentSheetFilename )
47         fn = GetScreen()->GetFileName();
48     else
49         fn = Schematic().RootScreen()->GetFileName();
50 
51     fn.SetName( fn.GetName() + "-cache" );
52     fn.SetExt( LegacySymbolLibFileExtension );
53 
54     bool success = CreateArchiveLibrary( fn.GetFullPath() );
55 
56     // Update the schematic symbol library links.
57     // because the lib cache has changed
58     SCH_SCREENS schematic( Schematic().Root() );
59     schematic.UpdateSymbolLinks();
60 
61     return success;
62 }
63 
64 
CreateArchiveLibrary(const wxString & aFileName)65 bool SCH_EDIT_FRAME::CreateArchiveLibrary( const wxString& aFileName )
66 {
67     wxString          tmp;
68     wxString          errorMsg;
69     SCH_SCREENS       screens( Schematic().Root() );
70 
71     // Create a new empty library to archive symbols:
72     std::unique_ptr<SYMBOL_LIB> archLib = std::make_unique<SYMBOL_LIB>( SCH_LIB_TYPE::LT_EESCHEMA,
73                                                                     aFileName );
74 
75     // Save symbols to file only when the library will be fully filled
76     archLib->EnableBuffering();
77 
78     /* Examine all screens (not hierarchical sheets) used in the schematic and build a
79      * library of unique symbols found in all screens.  Complex hierarchies are not a
80      * problem because we just want to know the library symbols used in the schematic
81      * not their reference.
82      */
83     for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
84     {
85 
86         for( SCH_ITEM* aItem : screen->Items().OfType( SCH_SYMBOL_T ) )
87         {
88             LIB_SYMBOL* libSymbol = nullptr;
89             SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( aItem );
90 
91             try
92             {
93                 if( archLib->FindSymbol( symbol->GetLibId() ) )
94                     continue;
95 
96                 libSymbol = GetLibSymbol( symbol->GetLibId(), true );
97             }
98             catch( const IO_ERROR& )
99             {
100                 // Queue up error messages for later.
101                 tmp.Printf( _( "Failed to add symbol %s to library file '%s'." ),
102                             symbol->GetLibId().GetUniStringLibItemName(),
103                             aFileName );
104 
105                 // Don't bail out here.  Attempt to add as many of the symbols to the library
106                 // as possible.
107             }
108             catch( ... )
109             {
110                 tmp = _( "Unexpected exception occurred." );
111             }
112 
113             if( libSymbol )
114             {
115                 std::unique_ptr<LIB_SYMBOL> flattenedSymbol = libSymbol->Flatten();
116 
117                 // Use the full LIB_ID as the symbol name to prevent symbol name collisions.
118                 flattenedSymbol->SetName( symbol->GetLibId().GetUniStringLibId() );
119 
120                 // AddSymbol() does first clone the symbol before adding.
121                 archLib->AddSymbol( flattenedSymbol.get() );
122             }
123             else
124             {
125                 tmp.Printf( _( "Symbol %s not found in any library or cache." ),
126                             symbol->GetLibId().GetUniStringLibId() );
127             }
128 
129             if( !tmp.empty() && !errorMsg.Contains( symbol->GetLibId().GetUniStringLibId() ) )
130             {
131                 if( errorMsg.empty() )
132                     errorMsg += tmp;
133                 else
134                     errorMsg += "\n" + tmp;
135             }
136         }
137     }
138 
139     if( !errorMsg.empty() )
140     {
141         tmp.Printf( _( "Errors occurred creating symbol library %s." ), aFileName );
142         DisplayErrorMessage( this, tmp, errorMsg );
143     }
144 
145     archLib->EnableBuffering( false );
146 
147     try
148     {
149         archLib->Save( false );
150     }
151     catch( const IO_ERROR& ioe )
152     {
153         errorMsg.Printf( _( "Failed to save symbol library file '%s'." ), aFileName );
154         DisplayErrorMessage( this, errorMsg, ioe.What() );
155         return false;
156     }
157     catch( std::exception& error )
158     {
159         errorMsg.Printf( _( "Failed to save symbol library file '%s'." ), aFileName );
160         DisplayErrorMessage( this, errorMsg, error.what() );
161         return false;
162     }
163 
164     return true;
165 }
166