mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-04-21 00:21:25 +00:00
Fix Eagle schematic import issue.
Handle duplicate symbol library names with Eagle URN notation. Fixes https://gitlab.com/kicad/code/kicad/-/issues/19666
This commit is contained in:
parent
5e6795a774
commit
db877e6a00
common/io/eagle
eeschema/sch_io/eagle
pcbnew/pcb_io/eagle
@ -35,6 +35,7 @@
|
||||
#include <trace_helpers.h>
|
||||
#include <wx/log.h>
|
||||
#include <wx/regex.h>
|
||||
#include <wx/tokenzr.h>
|
||||
|
||||
#include <functional>
|
||||
#include <cstdio>
|
||||
@ -213,8 +214,6 @@ size_t GetNodeCount( const wxXmlNode* aNode )
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
|
||||
template<> template<>
|
||||
OPTIONAL_XML_ATTRIBUTE<wxString>::OPTIONAL_XML_ATTRIBUTE( wxString aData )
|
||||
{
|
||||
@ -291,6 +290,55 @@ long long int ECOORD::ConvertToNm( int aValue, enum EAGLE_UNIT aUnit )
|
||||
}
|
||||
|
||||
|
||||
EURN::EURN( const wxString& aUrn )
|
||||
{
|
||||
Parse( aUrn );
|
||||
}
|
||||
|
||||
|
||||
void EURN::Parse( const wxString& aUrn )
|
||||
{
|
||||
wxStringTokenizer tokens( aUrn, ":" );
|
||||
|
||||
host = tokens.GetNextToken();
|
||||
path = tokens.GetNextToken();
|
||||
assetType = tokens.GetNextToken();
|
||||
|
||||
// Split off the version if there is one.
|
||||
wxString tmp = tokens.GetNextToken();
|
||||
|
||||
assetId = tmp.BeforeFirst( '/' );
|
||||
assetVersion = tmp.AfterLast( '/' );
|
||||
}
|
||||
|
||||
|
||||
bool EURN::IsValid() const
|
||||
{
|
||||
if( host != "urn" )
|
||||
return false;
|
||||
|
||||
if( path.IsEmpty() )
|
||||
return false;
|
||||
|
||||
static std::set<wxString> validAssetTypes =
|
||||
{
|
||||
"component",
|
||||
"footprint",
|
||||
"library",
|
||||
"package",
|
||||
"symbol"
|
||||
};
|
||||
|
||||
if( validAssetTypes.count( assetType ) == 0 )
|
||||
return false;
|
||||
|
||||
if( assetId.IsEmpty() )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Template specializations below parse wxString to the used types:
|
||||
// - wxString (preferred)
|
||||
// - string
|
||||
@ -376,6 +424,13 @@ ECOORD Convert<ECOORD>( const wxString& aCoord )
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
EURN Convert<EURN>( const wxString& aUrn )
|
||||
{
|
||||
return EURN( aUrn );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse \a aAttribute of the XML node \a aNode.
|
||||
*
|
||||
@ -1406,7 +1461,7 @@ EELEMENT::EELEMENT( wxXmlNode* aElement, IO_BASE* aIo ) :
|
||||
y = parseRequiredAttribute<ECOORD>( aElement, "y" );
|
||||
|
||||
// optional
|
||||
library_urn = parseOptionalAttribute<wxString>( aElement, "library_urn" );
|
||||
library_urn = parseOptionalAttribute<EURN>( aElement, "library_urn" );
|
||||
locked = parseOptionalAttribute<bool>( aElement, "locked" );
|
||||
smashed = parseOptionalAttribute<bool>( aElement, "smashed" );
|
||||
rot = parseOptionalAttribute<EROT>( aElement, "rot" );
|
||||
@ -1471,7 +1526,7 @@ EPART::EPART( wxXmlNode* aPart, IO_BASE* aIo ) :
|
||||
*/
|
||||
name = parseRequiredAttribute<wxString>( aPart, "name" );
|
||||
library = parseRequiredAttribute<wxString>( aPart, "library" );
|
||||
libraryUrn = parseOptionalAttribute<wxString>( aPart, "library_urn" );
|
||||
libraryUrn = parseOptionalAttribute<EURN>( aPart, "library_urn" );
|
||||
deviceset = parseRequiredAttribute<wxString>( aPart, "deviceset" );
|
||||
device = parseRequiredAttribute<wxString>( aPart, "device" );
|
||||
package3d_urn = parseOptionalAttribute<wxString>( aPart, "package3d_urn" );
|
||||
@ -1717,7 +1772,7 @@ EDEVICE_SET::EDEVICE_SET( wxXmlNode* aDeviceSet, IO_BASE* aIo ) :
|
||||
* inside boards or schematics -->
|
||||
*/
|
||||
name = parseRequiredAttribute<wxString>( aDeviceSet, "name" );
|
||||
urn = parseOptionalAttribute<wxString>( aDeviceSet, "urn" );
|
||||
urn = parseOptionalAttribute<EURN>( aDeviceSet, "urn" );
|
||||
locally_modified = parseOptionalAttribute<bool>( aDeviceSet, "locally_modified" );
|
||||
prefix = parseOptionalAttribute<wxString>( aDeviceSet, "prefix" );
|
||||
uservalue = parseOptionalAttribute<bool>( aDeviceSet, "uservalue" );
|
||||
@ -2216,7 +2271,7 @@ EPACKAGE::EPACKAGE( wxXmlNode* aPackage, IO_BASE* aIo ) :
|
||||
* inside boards or schematics -->
|
||||
*/
|
||||
name = parseRequiredAttribute<wxString>( aPackage, "name" );
|
||||
urn = parseOptionalAttribute<wxString>( aPackage, "urn" );
|
||||
urn = parseOptionalAttribute<EURN>( aPackage, "urn" );
|
||||
locally_modified = parseOptionalAttribute<bool>( aPackage, "locally_modified" );
|
||||
library_version = parseOptionalAttribute<int>( aPackage, "library_version" );
|
||||
library_locally_modified = parseOptionalAttribute<bool>( aPackage, "library_locally_modified" );
|
||||
@ -2349,7 +2404,7 @@ ESYMBOL::ESYMBOL( wxXmlNode* aSymbol, IO_BASE* aIo ) :
|
||||
*/
|
||||
|
||||
name = parseRequiredAttribute<wxString>( aSymbol, "name" );
|
||||
urn = parseOptionalAttribute<wxString>( aSymbol, "urn" );
|
||||
urn = parseOptionalAttribute<EURN>( aSymbol, "urn" );
|
||||
locally_modified = parseOptionalAttribute<bool>( aSymbol, "locally_modified" );
|
||||
library_version = parseOptionalAttribute<int>( aSymbol, "library_version" );
|
||||
library_locally_modified = parseOptionalAttribute<bool>( aSymbol, "library_locally_modified" );
|
||||
@ -2420,7 +2475,7 @@ ELIBRARY::ELIBRARY( wxXmlNode* aLibrary, IO_BASE* aIo ) :
|
||||
if( parentNodeName == "libraries" )
|
||||
{
|
||||
name = parseRequiredAttribute<wxString>( aLibrary, "name" );
|
||||
urn = parseOptionalAttribute<wxString>( aLibrary, "urn" );
|
||||
urn = parseOptionalAttribute<EURN>( aLibrary, "urn" );
|
||||
}
|
||||
|
||||
for( wxXmlNode* child = aLibrary->GetChildren(); child; child = child->GetNext() )
|
||||
@ -2491,6 +2546,24 @@ ELIBRARY::ELIBRARY( wxXmlNode* aLibrary, IO_BASE* aIo ) :
|
||||
}
|
||||
|
||||
|
||||
wxString ELIBRARY::GetName() const
|
||||
{
|
||||
wxString libName = name;
|
||||
|
||||
// Use the name when no library urn exists.
|
||||
if( !urn )
|
||||
return libName;
|
||||
|
||||
// Suffix the library name with the urn library identifier. Eagle schematics can have
|
||||
// mulitple libraries with the same name. The urn library identifier is used to prevent
|
||||
// library name clashes.
|
||||
if( urn->IsValid() )
|
||||
libName += wxS( "_" ) + urn->assetId;
|
||||
|
||||
return libName;
|
||||
}
|
||||
|
||||
|
||||
EAPPROVED::EAPPROVED( wxXmlNode* aApproved, IO_BASE* aIo ) :
|
||||
EAGLE_BASE( aIo )
|
||||
{
|
||||
@ -2533,7 +2606,34 @@ ESCHEMATIC::ESCHEMATIC( wxXmlNode* aSchematic, IO_BASE* aIo ) :
|
||||
if( library->GetName() == "library" )
|
||||
{
|
||||
std::unique_ptr<ELIBRARY> tmp = std::make_unique<ELIBRARY>( library, aIo );
|
||||
libraries[ tmp->name ] = std::move( tmp );
|
||||
|
||||
wxString libName = tmp->GetName();
|
||||
|
||||
// Prevent duplicate library names. This should only happen if the Eagle
|
||||
// file has an invalid format.
|
||||
if( libraries.find( libName ) != libraries.end() )
|
||||
{
|
||||
wxString uniqueName;
|
||||
std::set<wxString> usedNames;
|
||||
|
||||
for( const auto& [setName, setLibrary] : libraries )
|
||||
usedNames.emplace( setName );
|
||||
|
||||
if( usedNames.find( libName ) != usedNames.end() )
|
||||
{
|
||||
int i = 1;
|
||||
|
||||
do
|
||||
{
|
||||
uniqueName.Format( wxS( "%s_%d" ), libName, i );
|
||||
i += 1;
|
||||
} while( usedNames.find( uniqueName ) != usedNames.end() );
|
||||
}
|
||||
|
||||
libName = uniqueName;
|
||||
}
|
||||
|
||||
libraries[ libName ] = std::move( tmp );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -386,13 +386,15 @@ VECTOR2I ConvertArcCenter( const VECTOR2I& aStart, const VECTOR2I& aEnd, double
|
||||
// Pre-declare for typedefs
|
||||
struct EROT;
|
||||
struct ECOORD;
|
||||
struct EURN;
|
||||
|
||||
typedef OPTIONAL_XML_ATTRIBUTE<wxString> opt_wxString;
|
||||
typedef OPTIONAL_XML_ATTRIBUTE<int> opt_int;
|
||||
typedef OPTIONAL_XML_ATTRIBUTE<double> opt_double;
|
||||
typedef OPTIONAL_XML_ATTRIBUTE<bool> opt_bool;
|
||||
typedef OPTIONAL_XML_ATTRIBUTE<EROT> opt_erot;
|
||||
typedef OPTIONAL_XML_ATTRIBUTE<ECOORD> opt_ecoord;
|
||||
typedef OPTIONAL_XML_ATTRIBUTE<int> opt_int;
|
||||
typedef OPTIONAL_XML_ATTRIBUTE<double> opt_double;
|
||||
typedef OPTIONAL_XML_ATTRIBUTE<bool> opt_bool;
|
||||
typedef OPTIONAL_XML_ATTRIBUTE<EROT> opt_erot;
|
||||
typedef OPTIONAL_XML_ATTRIBUTE<ECOORD> opt_ecoord;
|
||||
typedef OPTIONAL_XML_ATTRIBUTE<EURN> opt_eurn;
|
||||
|
||||
|
||||
struct EAGLE_BASE
|
||||
@ -413,6 +415,45 @@ struct EAGLE_BASE
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Container that parses Eagle library file "urn" definitions.
|
||||
*
|
||||
* According to the eagle.dtd, the "urn" definition is as follows:
|
||||
*
|
||||
* <!ENTITY % Urn "%String;"> <!-- of the form "urn:adsk.eagle:<ASSET_TYPE>:<ASSET_ID>/<VERSION>"
|
||||
* - <ASSET_TYPE> is "symbol", "footprint", "package", "component", or "library"
|
||||
* - <ASSET_ID> is an integer
|
||||
* - <VERSION> is an integer
|
||||
*
|
||||
* The "/<VERSION>" is omitted when referencing the asset without specifying a particular version.
|
||||
* For example, "urn:adsk.eagle:component:60986/2" references version 2 of component 60986 and
|
||||
* "urn:adsk.eagle:library:60968" references library 60986 without specifying a version.
|
||||
*/
|
||||
struct EURN : public EAGLE_BASE
|
||||
{
|
||||
EURN() {}
|
||||
|
||||
/// Parse an Eagle "urn" string.
|
||||
EURN( const wxString& aUrn );
|
||||
|
||||
void Parse( const wxString& aUrn );
|
||||
|
||||
/**
|
||||
* Check if the string passed to the ctor was a valid Eagle urn.
|
||||
*
|
||||
* @retval true if the urn string is valid.
|
||||
* @retval false if the urn string is not valid.
|
||||
*/
|
||||
bool IsValid() const;
|
||||
|
||||
wxString host; ///< Should always be "urn".
|
||||
wxString path; ///< Path to the asset type below.
|
||||
wxString assetType; ///< Must be "symbol", "footprint", "package", "component", or "library".
|
||||
wxString assetId; ///< The unique asset identifier for the asset type.
|
||||
wxString assetVersion; ///< May be empty depending on the asset type.
|
||||
};
|
||||
|
||||
|
||||
// All of the 'E'STRUCTS below merely hold Eagle XML information verbatim, in binary.
|
||||
// For maintenance and troubleshooting purposes, it was thought that we'd need to
|
||||
// separate the conversion process into distinct steps. There is no intent to have KiCad
|
||||
@ -1282,7 +1323,7 @@ struct EELEMENT : public EAGLE_BASE
|
||||
|
||||
wxString name;
|
||||
wxString library;
|
||||
opt_wxString library_urn;
|
||||
opt_eurn library_urn;
|
||||
wxString package;
|
||||
opt_wxString package3d_urn;
|
||||
opt_wxString override_package3d_urn;
|
||||
@ -1455,7 +1496,7 @@ struct EPART : public EAGLE_BASE
|
||||
|
||||
wxString name;
|
||||
wxString library;
|
||||
opt_wxString libraryUrn;
|
||||
opt_eurn libraryUrn;
|
||||
wxString deviceset;
|
||||
wxString device;
|
||||
opt_wxString package3d_urn;
|
||||
@ -1588,7 +1629,7 @@ struct EDEVICE_SET : public EAGLE_BASE
|
||||
*/
|
||||
|
||||
wxString name;
|
||||
opt_wxString urn;
|
||||
opt_eurn urn;
|
||||
opt_bool locally_modified;
|
||||
opt_wxString prefix;
|
||||
opt_bool uservalue;
|
||||
@ -2005,7 +2046,7 @@ struct EPACKAGE : public EAGLE_BASE
|
||||
* inside boards or schematics -->
|
||||
*/
|
||||
wxString name;
|
||||
opt_wxString urn;
|
||||
opt_eurn urn;
|
||||
opt_bool locally_modified;
|
||||
opt_int library_version;
|
||||
opt_bool library_locally_modified;
|
||||
@ -2055,7 +2096,7 @@ struct EPACKAGE3D : public EAGLE_BASE
|
||||
* inside boards or schematics -->
|
||||
*/
|
||||
wxString name;
|
||||
wxString urn;
|
||||
EURN urn;
|
||||
wxString type;
|
||||
opt_int library_version;
|
||||
opt_bool library_locally_modified;
|
||||
@ -2084,7 +2125,7 @@ struct ESYMBOL : public EAGLE_BASE
|
||||
*/
|
||||
|
||||
wxString name;
|
||||
opt_wxString urn;
|
||||
opt_eurn urn;
|
||||
opt_bool locally_modified;
|
||||
opt_int library_version;
|
||||
opt_bool library_locally_modified;
|
||||
@ -2115,7 +2156,7 @@ struct ELIBRARY : public EAGLE_BASE
|
||||
* <!-- urn: Only in online libraries used inside boards or schematics -->
|
||||
*/
|
||||
wxString name;
|
||||
opt_wxString urn;
|
||||
opt_eurn urn;
|
||||
|
||||
std::optional<EDESCRIPTION> description;
|
||||
std::map<wxString, std::unique_ptr<EPACKAGE>> packages;
|
||||
@ -2123,6 +2164,12 @@ struct ELIBRARY : public EAGLE_BASE
|
||||
std::map<wxString, std::unique_ptr<ESYMBOL>> symbols;
|
||||
std::map<wxString, std::unique_ptr<EDEVICE_SET>> devicesets;
|
||||
|
||||
/**
|
||||
* Fetch the fully unique library name.
|
||||
*
|
||||
* @return the unique library name.
|
||||
*/
|
||||
wxString GetName() const;
|
||||
ELIBRARY( wxXmlNode* aLibrary, IO_BASE* aIo = nullptr );
|
||||
};
|
||||
|
||||
|
@ -676,19 +676,10 @@ void SCH_IO_EAGLE::loadSchematic( const ESCHEMATIC& aSchematic )
|
||||
{
|
||||
for( const auto& [name, elibrary] : aSchematic.libraries )
|
||||
{
|
||||
wxString libName = elibrary->name;
|
||||
EAGLE_LIBRARY* elib = &m_eagleLibs[elibrary->GetName()];
|
||||
elib->name = elibrary->GetName();
|
||||
|
||||
if( elibrary->urn )
|
||||
{
|
||||
wxString tmp = *elibrary->urn;
|
||||
|
||||
libName += tmp.AfterLast( '/' );
|
||||
}
|
||||
|
||||
EAGLE_LIBRARY* elib = &m_eagleLibs[libName];
|
||||
elib->name = libName;
|
||||
|
||||
loadLibrary( elibrary.get(), &m_eagleLibs[libName] );
|
||||
loadLibrary( elibrary.get(), &m_eagleLibs[elibrary->GetName()] );
|
||||
}
|
||||
|
||||
m_pi->SaveLibrary( getLibFileName().GetFullPath() );
|
||||
@ -1740,11 +1731,7 @@ void SCH_IO_EAGLE::loadInstance( const std::unique_ptr<EINSTANCE>& aInstance,
|
||||
|
||||
// Correctly handle versioned libraries.
|
||||
if( epart->libraryUrn )
|
||||
{
|
||||
wxString tmp = *epart->libraryUrn;
|
||||
|
||||
libName += tmp.AfterLast( '/' );
|
||||
}
|
||||
libName += wxS( "_" ) + epart->libraryUrn->assetId;
|
||||
|
||||
wxString gatename = epart->deviceset + wxS( "_" ) + epart->device + wxS( "_" ) +
|
||||
aInstance->gate;
|
||||
@ -1766,7 +1753,7 @@ void SCH_IO_EAGLE::loadInstance( const std::unique_ptr<EINSTANCE>& aInstance,
|
||||
|
||||
if( libIt == m_eagleLibs.end() )
|
||||
{
|
||||
Report( wxString::Format( wxS( "Eagle library '%s' not found while looking up symbol for"
|
||||
Report( wxString::Format( wxS( "Eagle library '%s' not found while looking up symbol for "
|
||||
"deviceset '%s', device '%s', and gate '%s." ),
|
||||
libName, epart->deviceset, epart->device, aInstance->gate ) );
|
||||
return;
|
||||
@ -2052,7 +2039,7 @@ EAGLE_LIBRARY* SCH_IO_EAGLE::loadLibrary( const ELIBRARY* aLibrary, EAGLE_LIBRAR
|
||||
if( it == aLibrary->symbols.end() )
|
||||
{
|
||||
Report( wxString::Format( wxS( "Eagle symbol '%s' not found in library '%s'." ),
|
||||
egate->symbol, aLibrary->name ) );
|
||||
egate->symbol, aLibrary->GetName() ) );
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -1272,10 +1272,7 @@ void PCB_IO_EAGLE::loadElements( wxXmlNode* aElements )
|
||||
wxString packageName = e.package;
|
||||
|
||||
if( e.library_urn )
|
||||
{
|
||||
wxString libOrdinal = *e.library_urn;
|
||||
packageName = e.package + wxS( "_" ) + libOrdinal.AfterLast( ':' );
|
||||
}
|
||||
packageName = e.package + wxS( "_" ) + e.library_urn->assetId;
|
||||
|
||||
wxString pkg_key = makeKey( e.library, packageName );
|
||||
auto it = m_templates.find( pkg_key );
|
||||
|
Loading…
Reference in New Issue
Block a user