diff --git a/common/io/eagle/eagle_parser.cpp b/common/io/eagle/eagle_parser.cpp index 723638d619..4fda1a9287 100644 --- a/common/io/eagle/eagle_parser.cpp +++ b/common/io/eagle/eagle_parser.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -213,8 +214,6 @@ size_t GetNodeCount( const wxXmlNode* aNode ) return cnt; } - - template<> template<> OPTIONAL_XML_ATTRIBUTE::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 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( const wxString& aCoord ) } +template<> +EURN Convert( 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( aElement, "y" ); // optional - library_urn = parseOptionalAttribute( aElement, "library_urn" ); + library_urn = parseOptionalAttribute( aElement, "library_urn" ); locked = parseOptionalAttribute( aElement, "locked" ); smashed = parseOptionalAttribute( aElement, "smashed" ); rot = parseOptionalAttribute( aElement, "rot" ); @@ -1471,7 +1526,7 @@ EPART::EPART( wxXmlNode* aPart, IO_BASE* aIo ) : */ name = parseRequiredAttribute( aPart, "name" ); library = parseRequiredAttribute( aPart, "library" ); - libraryUrn = parseOptionalAttribute( aPart, "library_urn" ); + libraryUrn = parseOptionalAttribute( aPart, "library_urn" ); deviceset = parseRequiredAttribute( aPart, "deviceset" ); device = parseRequiredAttribute( aPart, "device" ); package3d_urn = parseOptionalAttribute( aPart, "package3d_urn" ); @@ -1717,7 +1772,7 @@ EDEVICE_SET::EDEVICE_SET( wxXmlNode* aDeviceSet, IO_BASE* aIo ) : * inside boards or schematics --> */ name = parseRequiredAttribute( aDeviceSet, "name" ); - urn = parseOptionalAttribute( aDeviceSet, "urn" ); + urn = parseOptionalAttribute( aDeviceSet, "urn" ); locally_modified = parseOptionalAttribute( aDeviceSet, "locally_modified" ); prefix = parseOptionalAttribute( aDeviceSet, "prefix" ); uservalue = parseOptionalAttribute( aDeviceSet, "uservalue" ); @@ -2216,7 +2271,7 @@ EPACKAGE::EPACKAGE( wxXmlNode* aPackage, IO_BASE* aIo ) : * inside boards or schematics --> */ name = parseRequiredAttribute( aPackage, "name" ); - urn = parseOptionalAttribute( aPackage, "urn" ); + urn = parseOptionalAttribute( aPackage, "urn" ); locally_modified = parseOptionalAttribute( aPackage, "locally_modified" ); library_version = parseOptionalAttribute( aPackage, "library_version" ); library_locally_modified = parseOptionalAttribute( aPackage, "library_locally_modified" ); @@ -2349,7 +2404,7 @@ ESYMBOL::ESYMBOL( wxXmlNode* aSymbol, IO_BASE* aIo ) : */ name = parseRequiredAttribute( aSymbol, "name" ); - urn = parseOptionalAttribute( aSymbol, "urn" ); + urn = parseOptionalAttribute( aSymbol, "urn" ); locally_modified = parseOptionalAttribute( aSymbol, "locally_modified" ); library_version = parseOptionalAttribute( aSymbol, "library_version" ); library_locally_modified = parseOptionalAttribute( aSymbol, "library_locally_modified" ); @@ -2420,7 +2475,7 @@ ELIBRARY::ELIBRARY( wxXmlNode* aLibrary, IO_BASE* aIo ) : if( parentNodeName == "libraries" ) { name = parseRequiredAttribute( aLibrary, "name" ); - urn = parseOptionalAttribute( aLibrary, "urn" ); + urn = parseOptionalAttribute( 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 tmp = std::make_unique( 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 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 ); } } diff --git a/common/io/eagle/eagle_parser.h b/common/io/eagle/eagle_parser.h index 5b676ae27b..513fd3d65c 100644 --- a/common/io/eagle/eagle_parser.h +++ b/common/io/eagle/eagle_parser.h @@ -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 opt_wxString; -typedef OPTIONAL_XML_ATTRIBUTE opt_int; -typedef OPTIONAL_XML_ATTRIBUTE opt_double; -typedef OPTIONAL_XML_ATTRIBUTE opt_bool; -typedef OPTIONAL_XML_ATTRIBUTE opt_erot; -typedef OPTIONAL_XML_ATTRIBUTE opt_ecoord; +typedef OPTIONAL_XML_ATTRIBUTE opt_int; +typedef OPTIONAL_XML_ATTRIBUTE opt_double; +typedef OPTIONAL_XML_ATTRIBUTE opt_bool; +typedef OPTIONAL_XML_ATTRIBUTE opt_erot; +typedef OPTIONAL_XML_ATTRIBUTE opt_ecoord; +typedef OPTIONAL_XML_ATTRIBUTE 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: + * + * */ 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 * */ wxString name; - opt_wxString urn; + opt_eurn urn; std::optional description; std::map> packages; @@ -2123,6 +2164,12 @@ struct ELIBRARY : public EAGLE_BASE std::map> symbols; std::map> devicesets; + /** + * Fetch the fully unique library name. + * + * @return the unique library name. + */ + wxString GetName() const; ELIBRARY( wxXmlNode* aLibrary, IO_BASE* aIo = nullptr ); }; diff --git a/eeschema/sch_io/eagle/sch_io_eagle.cpp b/eeschema/sch_io/eagle/sch_io_eagle.cpp index 87d55608ac..9b6bdf68b5 100644 --- a/eeschema/sch_io/eagle/sch_io_eagle.cpp +++ b/eeschema/sch_io/eagle/sch_io_eagle.cpp @@ -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& 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& 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; } diff --git a/pcbnew/pcb_io/eagle/pcb_io_eagle.cpp b/pcbnew/pcb_io/eagle/pcb_io_eagle.cpp index 17fb5590bb..c4c6cfc8d3 100644 --- a/pcbnew/pcb_io/eagle/pcb_io_eagle.cpp +++ b/pcbnew/pcb_io/eagle/pcb_io_eagle.cpp @@ -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 );