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, see <https://www.gnu.org/licenses/>.
18 */
19
21
22#include <boost/uuid/name_generator_sha1.hpp>
23#include <boost/uuid/string_generator.hpp>
24#include <boost/uuid/uuid_io.hpp>
25
26#include <project.h>
27#include <set>
28#include <wx/fileconf.h>
29#include <wx/filename.h>
30#include <wx/log.h>
31
32
38static wxString absoluteProjectPath( const wxString& aPrjPcbPath )
39{
40 wxFileName fn( aPrjPcbPath );
41 fn.MakeAbsolute();
42
43 return fn.GetFullPath();
44}
45
46
47KIID AltiumUniqueIdToKiid( const wxString& aUniqueId )
48{
49 // Fixed namespace so a given id always maps to the same KIID. Must never change or
50 // previously stamped footprint paths would stop matching.
51 static const boost::uuids::uuid s_namespace =
52 boost::uuids::string_generator()( "6f9619ff-8b86-d011-b42d-00cf4fc964ff" );
53
54 boost::uuids::name_generator_sha1 generator( s_namespace );
55 boost::uuids::uuid uuid = generator( aUniqueId.utf8_string() );
56
57 return KIID( wxString::FromUTF8( boost::uuids::to_string( uuid ) ) );
58}
59
60
62{
64
65 // AlternatePart is the last named field on the line; everything after it belongs to the
66 // alternate part definition rather than to the component itself
67 bool inAlternatePart = false;
68
69 // UniqueId targets are backslash-delimited paths, so wxSplit's default '\' escape would
70 // merge the field that follows one ending in a separator
71 for( const wxString& token : wxSplit( aValue, '|', '\0' ) )
72 {
73 int eqPos = token.Find( '=' );
74
75 if( eqPos == wxNOT_FOUND )
76 continue;
77
78 wxString key = token.Left( eqPos ).Trim().Trim( false );
79 wxString val = token.Mid( eqPos + 1 ).Trim().Trim( false );
80
81 if( key.CmpNoCase( wxS( "AlternatePart" ) ) == 0 )
82 {
83 inAlternatePart = true;
84
85 // Typically empty for Kind=1 and a library reference for Kind=0
86 if( !val.empty() )
87 entry.alternateFields[wxS( "LibReference" )] = val;
88 }
89 else if( inAlternatePart )
90 {
91 if( !val.empty() )
92 entry.alternateFields[key] = val;
93 }
94 else if( key.CmpNoCase( wxS( "Designator" ) ) == 0 )
95 {
96 entry.designator = val;
97 }
98 else if( key.CmpNoCase( wxS( "UniqueId" ) ) == 0 )
99 {
100 // The target is a backslash-delimited path; only the final segment is the
101 // component's own unique id, which is what symbol and footprint records store.
102 int sep = val.Find( '\\', true );
103
104 if( sep != wxNOT_FOUND )
105 entry.uniqueId = val.Mid( sep + 1 );
106 else
107 entry.uniqueId = val;
108 }
109 else if( key.CmpNoCase( wxS( "Kind" ) ) == 0 )
110 {
111 long k = 0;
112 val.ToLong( &k );
113 entry.kind = static_cast<int>( k );
114 }
115 }
116
117 return entry;
118}
119
120
121std::map<wxString, wxString> ParseAltiumProjectParameters( const wxString& aPrjPcbPath )
122{
123 std::map<wxString, wxString> parameters;
124
125 wxFileConfig config( wxEmptyString, wxEmptyString, wxEmptyString, absoluteProjectPath( aPrjPcbPath ),
126 wxCONFIG_USE_NO_ESCAPE_CHARACTERS );
127
128 wxString groupname;
129 long groupid;
130
131 for( bool more = config.GetFirstGroup( groupname, groupid ); more;
132 more = config.GetNextGroup( groupname, groupid ) )
133 {
134 if( !groupname.StartsWith( wxS( "Parameter" ) ) )
135 continue;
136
137 // Only numbered [ParameterN] sections hold the project parameters; reject look-alikes
138 // such as [ParameterEngine] by requiring a trailing integer.
139 wxString numStr = groupname.Mid( 9 );
140 long num;
141
142 if( !numStr.ToLong( &num ) )
143 continue;
144
145 wxString name = config.Read( groupname + wxS( "/Name" ), wxEmptyString );
146
147 if( name.empty() )
148 continue;
149
150 wxString value = config.Read( groupname + wxS( "/Value" ), wxEmptyString );
151
152 // KiCad variable references are matched case-insensitively by resolving to upper case,
153 // which is what AltiumPcbSpecialStringsToKiCadStrings emits, so key on the upper-cased
154 // name to keep ${PCB_REVISION} and friends resolvable.
155 name.UpperCase();
156
157 parameters[name] = value;
158 }
159
160 return parameters;
161}
162
163
164bool AltiumPowerPortNamesTakePriority( const wxString& aPrjPcbPath )
165{
166 wxFileConfig config( wxEmptyString, wxEmptyString, wxEmptyString, absoluteProjectPath( aPrjPcbPath ),
167 wxCONFIG_USE_NO_ESCAPE_CHARACTERS );
168
169 return config.ReadLong( wxS( "Design/PowerPortNamesTakePriority" ), 0 ) != 0;
170}
171
172
174 const std::map<wxString, wxString>& aParameters )
175{
176 if( !aProject )
177 return;
178
179 // Names KiCad resolves contextually (board fields, title block, special strings). Importing
180 // them as project variables would shadow nothing useful and could surprise the user, so they
181 // are left to native resolution.
182 static const std::set<wxString> reserved = {
183 wxS( "LAYER" ), wxS( "FILENAME" ), wxS( "FILEPATH" ),
184 wxS( "PROJECTNAME" ), wxS( "VARIANT" ), wxS( "VARIANT_DESC" ),
185 wxS( "ISSUE_DATE" ), wxS( "CURRENT_DATE" ), wxS( "CURRENT_TIME_LOCALE" ),
186 wxS( "CURRENT_TIME_HH_MM_SS" ), wxS( "REVISION" ), wxS( "TITLE" ),
187 wxS( "COMPANY" ), wxS( "COMMENT1" ), wxS( "COMMENT2" ),
188 wxS( "COMMENT3" ), wxS( "COMMENT4" ), wxS( "COMMENT5" ),
189 wxS( "COMMENT6" ), wxS( "COMMENT7" ), wxS( "COMMENT8" ),
190 wxS( "COMMENT9" ), wxS( "VCSHASH" ), wxS( "VCSSHORTHASH" ),
191 wxS( "KICAD_VERSION" ), wxS( "PAPER" ), wxS( "SHEETNAME" ),
192 wxS( "SHEETPATH" ), wxS( "DRC_WARNING" ), wxS( "DRC_ERROR" ),
193 wxS( "ERC_WARNING" ), wxS( "ERC_ERROR" )
194 };
195
196 std::map<wxString, wxString>& textVars = aProject->GetTextVars();
197 bool added = false;
198
199 for( const auto& [name, value] : aParameters )
200 {
201 if( reserved.count( name ) )
202 continue;
203
204 // Don't clobber a variable the user (or a prior import step) already set.
205 if( textVars.count( name ) )
206 continue;
207
208 textVars[name] = value;
209 added = true;
210 }
211
212 if( added )
213 aProject->IncrementTextVarsTicker();
214}
215
216
217std::vector<ALTIUM_PROJECT_VARIANT> ParseAltiumProjectVariants( const wxString& aPrjPcbPath )
218{
219 std::vector<ALTIUM_PROJECT_VARIANT> variants;
220
221 wxFileConfig config( wxEmptyString, wxEmptyString, wxEmptyString, absoluteProjectPath( aPrjPcbPath ),
222 wxCONFIG_USE_NO_ESCAPE_CHARACTERS );
223
224 wxString groupname;
225 long groupid;
226
227 for( bool more = config.GetFirstGroup( groupname, groupid ); more;
228 more = config.GetNextGroup( groupname, groupid ) )
229 {
230 if( !groupname.StartsWith( wxS( "ProjectVariant" ) ) )
231 continue;
232
233 wxString numStr = groupname.Mid( 14 );
234 long num;
235
236 if( !numStr.ToLong( &num ) )
237 continue;
238
240
241 pv.description = config.Read( groupname + wxS( "/Description" ), wxEmptyString );
242 pv.name = pv.description;
243
244 if( pv.name.empty() )
245 pv.name = groupname;
246
247 long variationCount = 0;
248 config.Read( groupname + wxS( "/VariationCount" ), &variationCount, 0 );
249
250 for( long vi = 1; vi <= variationCount; ++vi )
251 {
252 wxString key = wxString::Format( wxS( "%s/Variation%ld" ), groupname, vi );
253 wxString value = config.Read( key, wxEmptyString );
254
255 if( value.empty() )
256 continue;
257
259
260 if( !entry.designator.empty() )
261 pv.variations.push_back( std::move( entry ) );
262 }
263
264 if( !pv.variations.empty() )
265 variants.push_back( std::move( pv ) );
266 }
267
268 return variants;
269}
const char * name
std::vector< ALTIUM_PROJECT_VARIANT > ParseAltiumProjectVariants(const wxString &aPrjPcbPath)
Parse all [ProjectVariantN] sections from an Altium .PrjPcb project file.
static wxString absoluteProjectPath(const wxString &aPrjPcbPath)
wxFileConfig resolves a relative local filename against the user's home directory rather than the wor...
std::map< wxString, wxString > ParseAltiumProjectParameters(const wxString &aPrjPcbPath)
Parse all [ParameterN] sections from an Altium .PrjPcb project file.
ALTIUM_VARIANT_ENTRY ParseVariationString(const wxString &aValue)
Parse a single pipe-delimited Altium "Variation" string into its component entry.
bool AltiumPowerPortNamesTakePriority(const wxString &aPrjPcbPath)
Read the project's Power Port Names Take Priority netlist option.
KIID AltiumUniqueIdToKiid(const wxString &aUniqueId)
Derive a stable KIID from an Altium component unique id.
void ApplyAltiumProjectParametersToProject(PROJECT *aProject, const std::map< wxString, wxString > &aParameters)
Register Altium project parameters as KiCad project text variables so that imported board text refere...
Definition kiid.h:46
Container for project specific data.
Definition project.h:63
void IncrementTextVarsTicker()
Definition project.h:112
virtual std::map< wxString, wxString > & GetTextVars() const
Definition project.cpp:222
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