mirror of https://github.com/KLayout/klayout.git
Added tests and two convenience methods
The two convenience methods are Library#library_from_file Library#library_from_files Both create and register a Library object tied to a file. This object supports proper reloading and re-mapping on "refresh".
This commit is contained in:
parent
ca3505b872
commit
f501f039c0
|
|
@ -34,6 +34,7 @@ SOURCES = \
|
||||||
dbEdgeProcessor.cc \
|
dbEdgeProcessor.cc \
|
||||||
dbEdges.cc \
|
dbEdges.cc \
|
||||||
dbEdgesLocalOperations.cc \
|
dbEdgesLocalOperations.cc \
|
||||||
|
dbFileBasedLibrary.cc \
|
||||||
dbFillTool.cc \
|
dbFillTool.cc \
|
||||||
dbFuzzyCellMapping.cc \
|
dbFuzzyCellMapping.cc \
|
||||||
dbGenericShapeIterator.cc \
|
dbGenericShapeIterator.cc \
|
||||||
|
|
@ -274,6 +275,7 @@ HEADERS = \
|
||||||
dbEdges.h \
|
dbEdges.h \
|
||||||
dbEdgesLocalOperations.h \
|
dbEdgesLocalOperations.h \
|
||||||
dbEdgesToContours.h \
|
dbEdgesToContours.h \
|
||||||
|
dbFileBasedLibrary.h \
|
||||||
dbFillTool.h \
|
dbFillTool.h \
|
||||||
dbFuzzyCellMapping.h \
|
dbFuzzyCellMapping.h \
|
||||||
dbGenericShapeIterator.h \
|
dbGenericShapeIterator.h \
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,120 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
KLayout Layout Viewer
|
||||||
|
Copyright (C) 2006-2026 Matthias Koefferlein
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "dbFileBasedLibrary.h"
|
||||||
|
#include "dbReader.h"
|
||||||
|
#include "dbCellMapping.h"
|
||||||
|
|
||||||
|
#include "tlFileUtils.h"
|
||||||
|
#include "tlStream.h"
|
||||||
|
|
||||||
|
namespace db
|
||||||
|
{
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
FileBasedLibrary::FileBasedLibrary (const std::string &path, const std::string &name)
|
||||||
|
: db::Library (), m_name (name), m_path (path), m_is_loaded (false)
|
||||||
|
{
|
||||||
|
set_description (tl::filename (path));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FileBasedLibrary::merge_with_other_layout (const std::string &path)
|
||||||
|
{
|
||||||
|
m_other_paths.push_back (path);
|
||||||
|
if (m_is_loaded) {
|
||||||
|
merge_impl (path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
FileBasedLibrary::load ()
|
||||||
|
{
|
||||||
|
if (! m_is_loaded) {
|
||||||
|
return reload ();
|
||||||
|
} else {
|
||||||
|
return get_name ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
FileBasedLibrary::reload ()
|
||||||
|
{
|
||||||
|
std::string name = m_name.empty () ? tl::basename (m_path) : m_name;
|
||||||
|
|
||||||
|
layout ().clear ();
|
||||||
|
|
||||||
|
tl::InputStream stream (m_path);
|
||||||
|
db::Reader reader (stream);
|
||||||
|
reader.read (layout ());
|
||||||
|
|
||||||
|
// Use the libname if there is one
|
||||||
|
if (m_name.empty ()) {
|
||||||
|
db::Layout::meta_info_name_id_type libname_name_id = layout ().meta_info_name_id ("libname");
|
||||||
|
for (db::Layout::meta_info_iterator m = layout ().begin_meta (); m != layout ().end_meta (); ++m) {
|
||||||
|
if (m->first == libname_name_id && ! m->second.value.is_nil ()) {
|
||||||
|
name = m->second.value.to_string ();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto p = m_other_paths.begin (); p != m_other_paths.end (); ++p) {
|
||||||
|
merge_impl (*p);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_is_loaded = true;
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FileBasedLibrary::merge_impl (const std::string &path)
|
||||||
|
{
|
||||||
|
db::Layout ly;
|
||||||
|
|
||||||
|
tl::InputStream stream (path);
|
||||||
|
db::Reader reader (stream);
|
||||||
|
reader.read (ly);
|
||||||
|
|
||||||
|
std::vector<db::cell_index_type> target_cells, source_cells;
|
||||||
|
|
||||||
|
// collect the cells to pull in (all top cells of the library layout)
|
||||||
|
// NOTE: cells are not overwritten - the first layout wins, in terms
|
||||||
|
// of cell names and also in terms of database unit.
|
||||||
|
for (auto c = ly.begin_top_down (); c != ly.end_top_cells (); ++c) {
|
||||||
|
std::string cn = ly.cell_name (*c);
|
||||||
|
if (! layout ().has_cell (cn.c_str ())) {
|
||||||
|
source_cells.push_back (*c);
|
||||||
|
target_cells.push_back (layout ().add_cell (cn.c_str ()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
db::CellMapping cm;
|
||||||
|
cm.create_multi_mapping_full (layout (), target_cells, ly, source_cells);
|
||||||
|
layout ().copy_tree_shapes (ly, cm);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,105 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
KLayout Layout Viewer
|
||||||
|
Copyright (C) 2006-2026 Matthias Koefferlein
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef HDR_dbFileBasedLibrary
|
||||||
|
#define HDR_dbFileBasedLibrary
|
||||||
|
|
||||||
|
#include "dbLibrary.h"
|
||||||
|
#include "dbCommon.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
namespace db
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A Library specialization that ties a library to a file
|
||||||
|
*
|
||||||
|
* This object supports loading a library from multiple files and merging them into
|
||||||
|
* a single library (e.g. for loading a directory of cell files).
|
||||||
|
*/
|
||||||
|
class DB_PUBLIC FileBasedLibrary
|
||||||
|
: public db::Library
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Creates a file-based library object
|
||||||
|
*
|
||||||
|
* @param path The file path
|
||||||
|
* @param name The library name
|
||||||
|
*
|
||||||
|
* If the library name is an empty string, the library name is taken from
|
||||||
|
* a GDS LIBNAME or from the file name in the path.
|
||||||
|
*
|
||||||
|
* Note that you need to call "load" in order to actually load the file.
|
||||||
|
*/
|
||||||
|
FileBasedLibrary (const std::string &path, const std::string &name = std::string ());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Merges another file into this library
|
||||||
|
*
|
||||||
|
* If the library was not loaded already, the merge requests are postponed
|
||||||
|
* until "load" is called.
|
||||||
|
*/
|
||||||
|
void merge_with_other_layout (const std::string &path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Loads the files
|
||||||
|
*
|
||||||
|
* If the files are already loaded, this method does nothing.
|
||||||
|
* It returns the name of the library derived from the first file
|
||||||
|
* or the name given in the constructor. The constructor name has
|
||||||
|
* priority.
|
||||||
|
*/
|
||||||
|
std::string load ();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Implements the reload feature
|
||||||
|
*/
|
||||||
|
virtual std::string reload ();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the paths
|
||||||
|
* This method is provided for test purposes only.
|
||||||
|
*/
|
||||||
|
void set_paths (const std::string &path, const std::list<std::string> &other_paths = std::list<std::string> ())
|
||||||
|
{
|
||||||
|
m_path = path;
|
||||||
|
m_other_paths = other_paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_name;
|
||||||
|
std::string m_path;
|
||||||
|
std::list<std::string> m_other_paths;
|
||||||
|
bool m_is_loaded;
|
||||||
|
|
||||||
|
void merge_impl (const std::string &path);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -177,8 +177,6 @@ Library::refresh ()
|
||||||
void
|
void
|
||||||
Library::remap_to (db::Library *other, db::Layout *original_layout)
|
Library::remap_to (db::Library *other, db::Layout *original_layout)
|
||||||
{
|
{
|
||||||
tl_assert (other != this || original_layout != 0);
|
|
||||||
|
|
||||||
if (! original_layout) {
|
if (! original_layout) {
|
||||||
original_layout = &layout ();
|
original_layout = &layout ();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@
|
||||||
#include "dbPCellDeclaration.h"
|
#include "dbPCellDeclaration.h"
|
||||||
#include "dbLibrary.h"
|
#include "dbLibrary.h"
|
||||||
#include "dbLibraryManager.h"
|
#include "dbLibraryManager.h"
|
||||||
|
#include "dbFileBasedLibrary.h"
|
||||||
#include "tlLog.h"
|
#include "tlLog.h"
|
||||||
|
|
||||||
namespace gsi
|
namespace gsi
|
||||||
|
|
@ -156,11 +157,63 @@ static LibraryImpl *new_lib ()
|
||||||
return new LibraryImpl ();
|
return new LibraryImpl ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static db::Library *library_from_file (const std::string &path, const std::string &name)
|
||||||
|
{
|
||||||
|
std::unique_ptr<db::FileBasedLibrary> lib (new db::FileBasedLibrary (path, name));
|
||||||
|
|
||||||
|
std::string n = lib->load ();
|
||||||
|
db::Library *ret = lib.get ();
|
||||||
|
register_lib (lib.release (), n);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static db::Library *library_from_files (const std::vector<std::string> &paths, const std::string &name)
|
||||||
|
{
|
||||||
|
if (paths.empty ()) {
|
||||||
|
throw tl::Exception (tl::to_string (tr ("At least one path must be given")));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<db::FileBasedLibrary> lib (new db::FileBasedLibrary (paths.front (), name));
|
||||||
|
for (auto i = paths.begin () + 1; i != paths.end (); ++i) {
|
||||||
|
lib->merge_with_other_layout (*i);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string n = lib->load ();
|
||||||
|
db::Library *ret = lib.get ();
|
||||||
|
register_lib (lib.release (), n);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A basic implementation of the library
|
* @brief A basic implementation of the library
|
||||||
*/
|
*/
|
||||||
|
|
||||||
LibraryClass<db::Library> decl_Library ("db", "LibraryBase",
|
LibraryClass<db::Library> decl_Library ("db", "LibraryBase",
|
||||||
|
gsi::method ("library_from_file", &library_from_file, gsi::arg ("path"), gsi::arg ("name", std::string (), "auto"),
|
||||||
|
"@brief Creates a library from a file\n"
|
||||||
|
"@param path The path to the file from which to create the library from.\n"
|
||||||
|
"@param name The name of the library. If empty, the name will be derived from the GDS LIBNAME or the file name.\n"
|
||||||
|
"@return The library object created. It is already registered with the name given or derived from the file.\n"
|
||||||
|
"\n"
|
||||||
|
"This method will create a \\Library object which is tied to a specific file. This object supports "
|
||||||
|
"automatic reloading when the \\Library#refresh method is called.\n"
|
||||||
|
"\n"
|
||||||
|
"This convenience method has been added in version 0.30.8.\n"
|
||||||
|
) +
|
||||||
|
gsi::method ("library_from_files", &library_from_files, gsi::arg ("paths"), gsi::arg ("name", std::string (), "auto"),
|
||||||
|
"@brief Creates a library from a set of files\n"
|
||||||
|
"@param paths The paths to the files from which to create the library from. At least one file needs to be given.\n"
|
||||||
|
"@param name The name of the library. If empty, the name will be derived from the GDS LIBNAME or the file name.\n"
|
||||||
|
"@return The library object created. It is already registered with the name given or derived from the file.\n"
|
||||||
|
"\n"
|
||||||
|
"This method will create a \\Library object which is tied to several files. This object supports "
|
||||||
|
"automatic reloading when the \\Library#refresh method is called. The content of the files is merged "
|
||||||
|
"into the library. This is useful for example to create one library from a collection of files.\n"
|
||||||
|
"\n"
|
||||||
|
"This convenience method has been added in version 0.30.8.\n"
|
||||||
|
) +
|
||||||
gsi::method ("library_by_name", &library_by_name, gsi::arg ("name"), gsi::arg ("for_technology", std::string (), "unspecific"),
|
gsi::method ("library_by_name", &library_by_name, gsi::arg ("name"), gsi::arg ("for_technology", std::string (), "unspecific"),
|
||||||
"@brief Gets a library by name\n"
|
"@brief Gets a library by name\n"
|
||||||
"Returns the library object for the given name. If the name is not a valid library name, nil is returned.\n"
|
"Returns the library object for the given name. If the name is not a valid library name, nil is returned.\n"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
KLayout Layout Viewer
|
||||||
|
Copyright (C) 2006-2026 Matthias Koefferlein
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "dbLibraryManager.h"
|
||||||
|
#include "dbLibrary.h"
|
||||||
|
#include "dbFileBasedLibrary.h"
|
||||||
|
#include "tlUnitTest.h"
|
||||||
|
#include "tlFileUtils.h"
|
||||||
|
|
||||||
|
// map library to different file (aka reload/refresh)
|
||||||
|
TEST(1)
|
||||||
|
{
|
||||||
|
std::string pa = tl::testsrc () + "/testdata/gds/lib_a.gds";
|
||||||
|
std::string pb = tl::testsrc () + "/testdata/gds/lib_b.gds";
|
||||||
|
|
||||||
|
db::FileBasedLibrary *lib = new db::FileBasedLibrary (pa);
|
||||||
|
lib->load ();
|
||||||
|
lib->set_name ("LIB");
|
||||||
|
db::LibraryManager::instance ().register_lib (lib);
|
||||||
|
|
||||||
|
db::Layout l;
|
||||||
|
auto top_cell_index = l.add_cell ("TOP");
|
||||||
|
auto lib_proxy = l.get_lib_proxy (lib, lib->layout ().cell_by_name ("A").second);
|
||||||
|
|
||||||
|
l.cell (top_cell_index).insert (db::CellInstArray (db::CellInst (lib_proxy), db::Trans ()));
|
||||||
|
|
||||||
|
EXPECT_EQ (l.cell (top_cell_index).bbox ().to_string (), "(0,0;2000,2000)");
|
||||||
|
|
||||||
|
// switch to a different file and refresh
|
||||||
|
lib->set_paths (pb);
|
||||||
|
lib->refresh ();
|
||||||
|
|
||||||
|
EXPECT_EQ (l.cell (top_cell_index).bbox ().to_string (), "(-1000,-1000;1000,1000)");
|
||||||
|
|
||||||
|
db::LibraryManager::instance ().delete_lib (lib);
|
||||||
|
}
|
||||||
|
|
||||||
|
// merge multiple files into one library
|
||||||
|
TEST(2)
|
||||||
|
{
|
||||||
|
std::string pa = tl::testsrc () + "/testdata/gds/lib_a.gds";
|
||||||
|
std::string pb = tl::testsrc () + "/testdata/gds/lib_b.gds";
|
||||||
|
|
||||||
|
db::FileBasedLibrary *lib = new db::FileBasedLibrary (pa);
|
||||||
|
lib->merge_with_other_layout (pb);
|
||||||
|
lib->load ();
|
||||||
|
lib->set_name ("LIB");
|
||||||
|
db::LibraryManager::instance ().register_lib (lib);
|
||||||
|
|
||||||
|
{
|
||||||
|
db::Layout l;
|
||||||
|
auto top_cell_index = l.add_cell ("TOP");
|
||||||
|
auto lib_proxy = l.get_lib_proxy (lib, lib->layout ().cell_by_name ("A").second);
|
||||||
|
|
||||||
|
l.cell (top_cell_index).insert (db::CellInstArray (db::CellInst (lib_proxy), db::Trans ()));
|
||||||
|
|
||||||
|
// A comes from the first layout
|
||||||
|
EXPECT_EQ (l.cell (top_cell_index).bbox ().to_string (), "(0,0;2000,2000)");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
db::Layout l;
|
||||||
|
auto top_cell_index = l.add_cell ("TOP");
|
||||||
|
auto lib_proxy = l.get_lib_proxy (lib, lib->layout ().cell_by_name ("Z").second);
|
||||||
|
|
||||||
|
l.cell (top_cell_index).insert (db::CellInstArray (db::CellInst (lib_proxy), db::Trans ()));
|
||||||
|
|
||||||
|
// Z comes from the second layout
|
||||||
|
EXPECT_EQ (l.cell (top_cell_index).bbox ().to_string (), "(0,0;100,100)");
|
||||||
|
}
|
||||||
|
|
||||||
|
db::LibraryManager::instance ().delete_lib (lib);
|
||||||
|
}
|
||||||
|
|
@ -11,6 +11,7 @@ SOURCES = \
|
||||||
dbCompoundOperationTests.cc \
|
dbCompoundOperationTests.cc \
|
||||||
dbEdgeNeighborhoodTests.cc \
|
dbEdgeNeighborhoodTests.cc \
|
||||||
dbFillToolTests.cc \
|
dbFillToolTests.cc \
|
||||||
|
dbLibraryTests.cc \
|
||||||
dbLogTests.cc \
|
dbLogTests.cc \
|
||||||
dbObjectWithPropertiesTests.cc \
|
dbObjectWithPropertiesTests.cc \
|
||||||
dbPLCConvexDecompositionTests.cc \
|
dbPLCConvexDecompositionTests.cc \
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,7 @@
|
||||||
#include "layQtTools.h"
|
#include "layQtTools.h"
|
||||||
#include "dbLibraryManager.h"
|
#include "dbLibraryManager.h"
|
||||||
#include "dbLibrary.h"
|
#include "dbLibrary.h"
|
||||||
#include "dbReader.h"
|
#include "dbFileBasedLibrary.h"
|
||||||
#include "dbCellMapping.h"
|
|
||||||
#include "tlLog.h"
|
#include "tlLog.h"
|
||||||
#include "tlStream.h"
|
#include "tlStream.h"
|
||||||
#include "tlFileUtils.h"
|
#include "tlFileUtils.h"
|
||||||
|
|
@ -42,81 +41,6 @@ namespace lay
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
class FileBasedLibrary
|
|
||||||
: public db::Library
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
FileBasedLibrary (const std::string &path)
|
|
||||||
: db::Library (), m_path (path)
|
|
||||||
{
|
|
||||||
set_description (tl::filename (path));
|
|
||||||
}
|
|
||||||
|
|
||||||
void merge_with_other_layout (const std::string &path)
|
|
||||||
{
|
|
||||||
m_other_paths.push_back (path);
|
|
||||||
merge_impl (path);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual std::string reload ()
|
|
||||||
{
|
|
||||||
std::string name = tl::basename (m_path);
|
|
||||||
|
|
||||||
layout ().clear ();
|
|
||||||
|
|
||||||
tl::InputStream stream (m_path);
|
|
||||||
db::Reader reader (stream);
|
|
||||||
reader.read (layout ());
|
|
||||||
|
|
||||||
// Use the libname if there is one
|
|
||||||
db::Layout::meta_info_name_id_type libname_name_id = layout ().meta_info_name_id ("libname");
|
|
||||||
for (db::Layout::meta_info_iterator m = layout ().begin_meta (); m != layout ().end_meta (); ++m) {
|
|
||||||
if (m->first == libname_name_id && ! m->second.value.is_nil ()) {
|
|
||||||
name = m->second.value.to_string ();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto p = m_other_paths.begin (); p != m_other_paths.end (); ++p) {
|
|
||||||
merge_impl (*p);
|
|
||||||
}
|
|
||||||
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string m_path;
|
|
||||||
std::list<std::string> m_other_paths;
|
|
||||||
|
|
||||||
void merge_impl (const std::string &path)
|
|
||||||
{
|
|
||||||
db::Layout ly;
|
|
||||||
|
|
||||||
tl::InputStream stream (path);
|
|
||||||
db::Reader reader (stream);
|
|
||||||
reader.read (ly);
|
|
||||||
|
|
||||||
std::vector<db::cell_index_type> target_cells, source_cells;
|
|
||||||
|
|
||||||
// collect the cells to pull in (all top cells of the library layout)
|
|
||||||
// NOTE: cells are not overwritten - the first layout wins, in terms
|
|
||||||
// of cell names and also in terms of database unit.
|
|
||||||
for (auto c = ly.begin_top_down (); c != ly.end_top_cells (); ++c) {
|
|
||||||
std::string cn = ly.cell_name (*c);
|
|
||||||
if (! layout ().has_cell (cn.c_str ())) {
|
|
||||||
source_cells.push_back (*c);
|
|
||||||
target_cells.push_back (layout ().add_cell (cn.c_str ()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
db::CellMapping cm;
|
|
||||||
cm.create_multi_mapping_full (layout (), target_cells, ly, source_cells);
|
|
||||||
layout ().copy_tree_shapes (ly, cm);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
LibraryController::LibraryController ()
|
LibraryController::LibraryController ()
|
||||||
: m_file_watcher (0),
|
: m_file_watcher (0),
|
||||||
dm_sync_files (this, &LibraryController::sync_files)
|
dm_sync_files (this, &LibraryController::sync_files)
|
||||||
|
|
@ -279,7 +203,7 @@ LibraryController::sync_files ()
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
std::map<std::string, FileBasedLibrary *> libs_by_name_here;
|
std::map<std::string, db::FileBasedLibrary *> libs_by_name_here;
|
||||||
|
|
||||||
// Reload all files
|
// Reload all files
|
||||||
for (QStringList::const_iterator im = libs.begin (); im != libs.end (); ++im) {
|
for (QStringList::const_iterator im = libs.begin (); im != libs.end (); ++im) {
|
||||||
|
|
@ -289,13 +213,13 @@ LibraryController::sync_files ()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
std::unique_ptr<FileBasedLibrary> lib (new FileBasedLibrary (lib_path));
|
std::unique_ptr<db::FileBasedLibrary> lib (new db::FileBasedLibrary (lib_path));
|
||||||
if (! p->second.empty ()) {
|
if (! p->second.empty ()) {
|
||||||
lib->set_technology (p->second);
|
lib->set_technology (p->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
tl::log << "Reading library '" << lib_path << "'";
|
tl::log << "Reading library '" << lib_path << "'";
|
||||||
std::string libname = lib->reload ();
|
std::string libname = lib->load ();
|
||||||
|
|
||||||
// merge with existing lib if there is already one in this folder with the right name
|
// merge with existing lib if there is already one in this folder with the right name
|
||||||
auto il = libs_by_name_here.find (libname);
|
auto il = libs_by_name_here.find (libname);
|
||||||
|
|
|
||||||
|
|
@ -265,6 +265,70 @@ class DBLibrary_TestClass < TestBase
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_8_file_based_library
|
||||||
|
|
||||||
|
# clean up before
|
||||||
|
[ "RBA-unit-test" ].each do |name|
|
||||||
|
l = RBA::Library::library_by_name(name)
|
||||||
|
l && l.unregister
|
||||||
|
end
|
||||||
|
|
||||||
|
lylib = RBA::Layout::new
|
||||||
|
lylib.read(File.join($ut_testsrc, "testdata", "gds", "lib_a.gds"))
|
||||||
|
|
||||||
|
tmp = File::join($ut_testtmp, "rba_dbLibrary_8.gds")
|
||||||
|
lylib.write(tmp)
|
||||||
|
|
||||||
|
lib = RBA::Library::library_from_file(tmp, "RBA-unit-test")
|
||||||
|
|
||||||
|
ly = RBA::Layout::new
|
||||||
|
top = ly.create_cell("TOP")
|
||||||
|
a = ly.create_cell("A", "RBA-unit-test")
|
||||||
|
top.insert(RBA::CellInstArray::new(a, RBA::Trans::new))
|
||||||
|
|
||||||
|
assert_equal(top.dbbox.to_s, "(0,0;2,2)")
|
||||||
|
|
||||||
|
lylib.clear
|
||||||
|
lylib.read(File.join($ut_testsrc, "testdata", "gds", "lib_b.gds"))
|
||||||
|
lylib.write(tmp)
|
||||||
|
|
||||||
|
lib.refresh
|
||||||
|
|
||||||
|
assert_equal(top.dbbox.to_s, "(-1,-1;1,1)")
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_9_file_based_library_multiple_files
|
||||||
|
|
||||||
|
# clean up before
|
||||||
|
[ "RBA-unit-test" ].each do |name|
|
||||||
|
l = RBA::Library::library_by_name(name)
|
||||||
|
l && l.unregister
|
||||||
|
end
|
||||||
|
|
||||||
|
files = [
|
||||||
|
File.join($ut_testsrc, "testdata", "gds", "lib_a.gds"),
|
||||||
|
File.join($ut_testsrc, "testdata", "gds", "lib_b.gds")
|
||||||
|
]
|
||||||
|
|
||||||
|
lib = RBA::Library::library_from_files(files, "RBA-unit-test")
|
||||||
|
|
||||||
|
ly = RBA::Layout::new
|
||||||
|
top = ly.create_cell("TOP")
|
||||||
|
a = ly.create_cell("A", "RBA-unit-test")
|
||||||
|
top.insert(RBA::CellInstArray::new(a, RBA::Trans::new))
|
||||||
|
|
||||||
|
assert_equal(top.dbbox.to_s, "(0,0;2,2)")
|
||||||
|
|
||||||
|
ly = RBA::Layout::new
|
||||||
|
top = ly.create_cell("TOP")
|
||||||
|
z = ly.create_cell("Z", "RBA-unit-test")
|
||||||
|
top.insert(RBA::CellInstArray::new(z, RBA::Trans::new))
|
||||||
|
|
||||||
|
assert_equal(top.dbbox.to_s, "(0,0;0.1,0.1)")
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
load("test_epilogue.rb")
|
load("test_epilogue.rb")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue