KiCad PCB EDA Suite
Loading...
Searching...
No Matches
altium_project_variants.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
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU 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, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
25
26#include <boost/uuid/name_generator_sha1.hpp>
27#include <boost/uuid/string_generator.hpp>
28#include <boost/uuid/uuid_io.hpp>
29
30#include <wx/fileconf.h>
31#include <wx/log.h>
32
33
34KIID AltiumUniqueIdToKiid( const wxString& aUniqueId )
35{
36 // Fixed namespace so a given id always maps to the same KIID. Must never change or
37 // previously stamped footprint paths would stop matching.
38 static const boost::uuids::uuid s_namespace =
39 boost::uuids::string_generator()( "6f9619ff-8b86-d011-b42d-00cf4fc964ff" );
40
41 boost::uuids::name_generator_sha1 generator( s_namespace );
42 boost::uuids::uuid uuid = generator( aUniqueId.utf8_string() );
43
44 return KIID( wxString::FromUTF8( boost::uuids::to_string( uuid ) ) );
45}
46
47
48static ALTIUM_VARIANT_ENTRY ParseVariationString( const wxString& aValue )
49{
51
52 for( const wxString& token : wxSplit( aValue, '|' ) )
53 {
54 int eqPos = token.Find( '=' );
55
56 if( eqPos == wxNOT_FOUND )
57 continue;
58
59 wxString key = token.Left( eqPos ).Trim().Trim( false );
60 wxString val = token.Mid( eqPos + 1 ).Trim().Trim( false );
61
62 if( key.CmpNoCase( wxS( "Designator" ) ) == 0 )
63 {
64 entry.designator = val;
65 }
66 else if( key.CmpNoCase( wxS( "UniqueId" ) ) == 0 )
67 {
68 // The target is a backslash-delimited path; only the final segment is the
69 // component's own unique id, which is what symbol and footprint records store.
70 int sep = val.Find( '\\', true );
71
72 if( sep != wxNOT_FOUND )
73 entry.uniqueId = val.Mid( sep + 1 );
74 else
75 entry.uniqueId = val;
76 }
77 else if( key.CmpNoCase( wxS( "Kind" ) ) == 0 )
78 {
79 long k = 0;
80 val.ToLong( &k );
81 entry.kind = static_cast<int>( k );
82 }
83 else if( key.CmpNoCase( wxS( "AlternatePart" ) ) == 0 )
84 {
85 if( val.empty() )
86 continue;
87
88 // AlternatePart contains sub-fields in the same pipe-delimited format, but since
89 // it appears at the end of the line, the remaining tokens are its sub-fields.
90 // We've already split on '|', so we just keep accumulating from here.
91 // However, wxSplit already split everything. The AlternatePart sub-fields are the
92 // remaining tokens after this one. We handle this below after the loop.
93 }
94 }
95
96 // Parse AlternatePart sub-fields. In the raw line, AlternatePart= is followed by
97 // pipe-separated key=value pairs that are part of the alternate part definition.
98 // Since we already split on '|', find the AlternatePart token and treat everything
99 // after it as sub-fields.
100 bool inAlternatePart = false;
101
102 for( const wxString& token : wxSplit( aValue, '|' ) )
103 {
104 int eqPos = token.Find( '=' );
105
106 if( eqPos == wxNOT_FOUND )
107 continue;
108
109 wxString key = token.Left( eqPos ).Trim().Trim( false );
110 wxString val = token.Mid( eqPos + 1 ).Trim().Trim( false );
111
112 if( key.CmpNoCase( wxS( "AlternatePart" ) ) == 0 )
113 {
114 inAlternatePart = true;
115
116 // The value after AlternatePart= might itself be the first sub-field value
117 // In practice it's typically empty for Kind=1, or a lib reference for Kind=0
118 if( !val.empty() )
119 entry.alternateFields[wxS( "LibReference" )] = val;
120
121 continue;
122 }
123
124 if( inAlternatePart && !val.empty() )
125 entry.alternateFields[key] = val;
126 }
127
128 return entry;
129}
130
131
132std::map<wxString, wxString> ParseAltiumProjectParameters( const wxString& aPrjPcbPath )
133{
134 std::map<wxString, wxString> parameters;
135
136 wxFileConfig config( wxEmptyString, wxEmptyString, wxEmptyString, aPrjPcbPath,
137 wxCONFIG_USE_NO_ESCAPE_CHARACTERS );
138
139 wxString groupname;
140 long groupid;
141
142 for( bool more = config.GetFirstGroup( groupname, groupid ); more;
143 more = config.GetNextGroup( groupname, groupid ) )
144 {
145 if( !groupname.StartsWith( wxS( "Parameter" ) ) )
146 continue;
147
148 // Only numbered [ParameterN] sections hold the project parameters; reject look-alikes
149 // such as [ParameterEngine] by requiring a trailing integer.
150 wxString numStr = groupname.Mid( 9 );
151 long num;
152
153 if( !numStr.ToLong( &num ) )
154 continue;
155
156 wxString name = config.Read( groupname + wxS( "/Name" ), wxEmptyString );
157
158 if( name.empty() )
159 continue;
160
161 wxString value = config.Read( groupname + wxS( "/Value" ), wxEmptyString );
162
163 // KiCad variable references are matched case-insensitively by resolving to upper case,
164 // which is what AltiumPcbSpecialStringsToKiCadStrings emits, so key on the upper-cased
165 // name to keep ${PCB_REVISION} and friends resolvable.
166 name.UpperCase();
167
168 parameters[name] = value;
169 }
170
171 return parameters;
172}
173
174
175std::vector<ALTIUM_PROJECT_VARIANT> ParseAltiumProjectVariants( const wxString& aPrjPcbPath )
176{
177 std::vector<ALTIUM_PROJECT_VARIANT> variants;
178
179 wxFileConfig config( wxEmptyString, wxEmptyString, wxEmptyString, aPrjPcbPath,
180 wxCONFIG_USE_NO_ESCAPE_CHARACTERS );
181
182 wxString groupname;
183 long groupid;
184
185 for( bool more = config.GetFirstGroup( groupname, groupid ); more;
186 more = config.GetNextGroup( groupname, groupid ) )
187 {
188 if( !groupname.StartsWith( wxS( "ProjectVariant" ) ) )
189 continue;
190
191 wxString numStr = groupname.Mid( 14 );
192 long num;
193
194 if( !numStr.ToLong( &num ) )
195 continue;
196
198
199 pv.description = config.Read( groupname + wxS( "/Description" ), wxEmptyString );
200 pv.name = pv.description;
201
202 if( pv.name.empty() )
203 pv.name = groupname;
204
205 long variationCount = 0;
206 config.Read( groupname + wxS( "/VariationCount" ), &variationCount, 0 );
207
208 for( long vi = 1; vi <= variationCount; ++vi )
209 {
210 wxString key = wxString::Format( wxS( "%s/Variation%ld" ), groupname, vi );
211 wxString value = config.Read( key, wxEmptyString );
212
213 if( value.empty() )
214 continue;
215
217
218 if( !entry.designator.empty() )
219 pv.variations.push_back( std::move( entry ) );
220 }
221
222 if( !pv.variations.empty() )
223 variants.push_back( std::move( pv ) );
224 }
225
226 return variants;
227}
const char * name
std::vector< ALTIUM_PROJECT_VARIANT > ParseAltiumProjectVariants(const wxString &aPrjPcbPath)
Parse all [ProjectVariantN] sections from an Altium .PrjPcb project file.
std::map< wxString, wxString > ParseAltiumProjectParameters(const wxString &aPrjPcbPath)
Parse all [ParameterN] sections from an Altium .PrjPcb project file.
KIID AltiumUniqueIdToKiid(const wxString &aUniqueId)
Derive a stable KIID from an Altium component unique id.
static ALTIUM_VARIANT_ENTRY ParseVariationString(const wxString &aValue)
Definition kiid.h:48
A project-level assembly variant parsed from an Altium .PrjPcb file.
std::vector< ALTIUM_VARIANT_ENTRY > variations
A single component variation within an Altium project variant.
int kind
wxString uniqueId
wxString designator
std::map< wxString, wxString > alternateFields