mirror of https://github.com/KLayout/klayout.git
WIP: provide a recipe registration facility for LVS rerun
This commit is contained in:
parent
444e10d32f
commit
515b68b76f
|
|
@ -27,6 +27,7 @@
|
||||||
#include "tlProgress.h"
|
#include "tlProgress.h"
|
||||||
#include "tlExpression.h"
|
#include "tlExpression.h"
|
||||||
#include "tlGlobPattern.h"
|
#include "tlGlobPattern.h"
|
||||||
|
#include "tlRecipe.h"
|
||||||
|
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
// Logger binding
|
// Logger binding
|
||||||
|
|
@ -640,4 +641,83 @@ Class<tl::GlobPattern> decl_GlobPattern ("tl", "GlobPattern",
|
||||||
"This class has been introduced in version 0.26."
|
"This class has been introduced in version 0.26."
|
||||||
);
|
);
|
||||||
|
|
||||||
|
class Recipe_Impl
|
||||||
|
: public tl::Recipe
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Recipe_Impl (const std::string &name, const std::string &description)
|
||||||
|
: tl::Recipe (name, description)
|
||||||
|
{
|
||||||
|
// .. nothing yet ..
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual tl::Variant execute (const std::map<std::string, tl::Variant> ¶ms) const
|
||||||
|
{
|
||||||
|
if (execute_cb.can_issue ()) {
|
||||||
|
return execute_cb.issue<tl::Recipe, tl::Variant, const std::map<std::string, tl::Variant> &> (&tl::Recipe::execute, params);
|
||||||
|
} else {
|
||||||
|
return tl::Variant ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gsi::Callback execute_cb;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace tl
|
||||||
|
{
|
||||||
|
template <> struct type_traits<gsi::Recipe_Impl> : public type_traits<tl::Recipe> { };
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace gsi
|
||||||
|
{
|
||||||
|
|
||||||
|
static Recipe_Impl *make_recipe (const std::string &name, const std::string &description)
|
||||||
|
{
|
||||||
|
return new Recipe_Impl (name, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
Class<Recipe_Impl> decl_Recipe_Impl ("tl", "Recipe",
|
||||||
|
gsi::constructor ("new", &make_recipe, gsi::arg ("name"), gsi::arg ("description", std::string ()),
|
||||||
|
"@brief Creates a new recipe object with the given name and (optional) description"
|
||||||
|
) +
|
||||||
|
gsi::method ("name", &Recipe_Impl::name,
|
||||||
|
"@brief Gets the name of the recipe."
|
||||||
|
) +
|
||||||
|
gsi::method ("description", &Recipe_Impl::description,
|
||||||
|
"@brief Gets the description of the recipe."
|
||||||
|
) +
|
||||||
|
gsi::method ("make", &Recipe_Impl::make, gsi::arg ("generator"),
|
||||||
|
"@brief Executes the recipe given by the generator string.\n"
|
||||||
|
"The generator string is the one delivered with \\generator."
|
||||||
|
) +
|
||||||
|
gsi::method ("generator", &Recipe_Impl::generator, gsi::arg ("params"),
|
||||||
|
"@brief Delivers the generator string from the given parameters.\n"
|
||||||
|
"The generator string can be used with \\make to re-run the recipe."
|
||||||
|
) +
|
||||||
|
gsi::callback ("execute", &Recipe_Impl::execute, &Recipe_Impl::execute_cb,
|
||||||
|
"@brief Reimplement this method to provide the functionality of the recipe.\n"
|
||||||
|
"This method is supposed to re-run the recipe with the given parameters and deliver the "
|
||||||
|
"the intended output object."
|
||||||
|
),
|
||||||
|
"@brief A facility for providing reproducable recipes\n"
|
||||||
|
"The idea of this facility is to provide a service by which an object\n"
|
||||||
|
"can be reproduced in a parametrized way. The intended use case is a \n"
|
||||||
|
"DRC report for example, where the DRC script is the generator.\n"
|
||||||
|
"\n"
|
||||||
|
"In this use case, the DRC engine will register a recipe. It will \n"
|
||||||
|
"put the serialized version of the recipe into the DRC report. If the \n"
|
||||||
|
"user requests a re-run of the DRC, the recipe will be called and \n"
|
||||||
|
"the implementation is supposed to deliver a new database.\n"
|
||||||
|
"\n"
|
||||||
|
"To register a recipe, reimplement tl::Recipe and create a singleton\n"
|
||||||
|
"instance. To serialize a recipe, use \"generator\", to execute the\n"
|
||||||
|
"recipe, use \"make\". \n"
|
||||||
|
"\n"
|
||||||
|
"Parameters are kept as a generic key/value map.\n"
|
||||||
|
"\n"
|
||||||
|
"This class has been introduced in version 0.26."
|
||||||
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,8 @@ SOURCES = \
|
||||||
tlUniqueId.cc \
|
tlUniqueId.cc \
|
||||||
tlList.cc \
|
tlList.cc \
|
||||||
tlEquivalenceClusters.cc \
|
tlEquivalenceClusters.cc \
|
||||||
tlUniqueName.cc
|
tlUniqueName.cc \
|
||||||
|
tlRecipe.cc
|
||||||
|
|
||||||
HEADERS = \
|
HEADERS = \
|
||||||
tlAlgorithm.h \
|
tlAlgorithm.h \
|
||||||
|
|
@ -100,7 +101,8 @@ HEADERS = \
|
||||||
tlUniqueId.h \
|
tlUniqueId.h \
|
||||||
tlList.h \
|
tlList.h \
|
||||||
tlEquivalenceClusters.h \
|
tlEquivalenceClusters.h \
|
||||||
tlUniqueName.h
|
tlUniqueName.h \
|
||||||
|
tlRecipe.h
|
||||||
|
|
||||||
equals(HAVE_CURL, "1") {
|
equals(HAVE_CURL, "1") {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
KLayout Layout Viewer
|
||||||
|
Copyright (C) 2006-2019 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 "tlRecipe.h"
|
||||||
|
#include "tlString.h"
|
||||||
|
|
||||||
|
namespace tl
|
||||||
|
{
|
||||||
|
|
||||||
|
Recipe::Recipe (const std::string &name, const std::string &description)
|
||||||
|
: tl::RegisteredClass<tl::Recipe> (this, 0, name.c_str (), false)
|
||||||
|
{
|
||||||
|
m_name = name;
|
||||||
|
m_description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Recipe::generator (const std::map<std::string, tl::Variant> ¶ms)
|
||||||
|
{
|
||||||
|
std::string g;
|
||||||
|
g += tl::to_word_or_quoted_string (name ());
|
||||||
|
g += ": ";
|
||||||
|
|
||||||
|
for (std::map<std::string, tl::Variant>::const_iterator p = params.begin (); p != params.end (); ++p) {
|
||||||
|
if (p != params.begin ()) {
|
||||||
|
g += ",";
|
||||||
|
}
|
||||||
|
g += tl::to_word_or_quoted_string (p->first);
|
||||||
|
g += "=";
|
||||||
|
g += p->second.to_parsable_string ();
|
||||||
|
}
|
||||||
|
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
tl::Variant Recipe::make (const std::string &generator)
|
||||||
|
{
|
||||||
|
tl::Extractor ex (generator.c_str ());
|
||||||
|
|
||||||
|
std::string recipe;
|
||||||
|
ex.read_word_or_quoted (recipe);
|
||||||
|
ex.test (":");
|
||||||
|
|
||||||
|
std::map<std::string, tl::Variant> params;
|
||||||
|
while (! ex.at_end ()) {
|
||||||
|
std::string key;
|
||||||
|
ex.read_word_or_quoted (key);
|
||||||
|
ex.test ("=");
|
||||||
|
tl::Variant v;
|
||||||
|
ex.read (v);
|
||||||
|
ex.test (",");
|
||||||
|
params.insert (std::make_pair (key, v));
|
||||||
|
}
|
||||||
|
|
||||||
|
tl::Recipe *recipe_obj = 0;
|
||||||
|
for (tl::Registrar<tl::Recipe>::iterator r = tl::Registrar<tl::Recipe>::begin (); r != tl::Registrar<tl::Recipe>::end (); ++r) {
|
||||||
|
if (r->name () == recipe) {
|
||||||
|
recipe_obj = r.operator-> ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! recipe_obj) {
|
||||||
|
return tl::Variant ();
|
||||||
|
} else {
|
||||||
|
return recipe_obj->execute (params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace tl
|
||||||
|
|
@ -0,0 +1,132 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
KLayout Layout Viewer
|
||||||
|
Copyright (C) 2006-2019 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_tlRecipe
|
||||||
|
#define HDR_tlRecipe
|
||||||
|
|
||||||
|
#include "tlCommon.h"
|
||||||
|
#include "tlVariant.h"
|
||||||
|
#include "tlTypeTraits.h"
|
||||||
|
#include "tlClassRegistry.h"
|
||||||
|
|
||||||
|
namespace tl
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A facility for providing reproducable recipes
|
||||||
|
*
|
||||||
|
* The idea of this facility is to provide a service by which an object
|
||||||
|
* can be reproduced in a parametrized way. The intended use case is a
|
||||||
|
* DRC report for example, where the DRC script is the generator.
|
||||||
|
*
|
||||||
|
* In this use case, the DRC engine will register a recipe. It will
|
||||||
|
* put the serialized version of the recipe into the DRC report. If the
|
||||||
|
* user requests a re-run of the DRC, the recipe will be called and
|
||||||
|
* the implementation is supposed to deliver a new database.
|
||||||
|
*
|
||||||
|
* To register a recipe, reimplement tl::Recipe and create a singleton
|
||||||
|
* instance. To serialize a recipe, use "generator", to execute the
|
||||||
|
* recipe, use "make".
|
||||||
|
*
|
||||||
|
* Parameters are kept as a generic key/value map.
|
||||||
|
*/
|
||||||
|
class TL_PUBLIC Recipe
|
||||||
|
: public tl::RegisteredClass<Recipe>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief @brief Creates a new recipe object
|
||||||
|
*/
|
||||||
|
Recipe (const std::string &name, const std::string &description = std::string ());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Destructor
|
||||||
|
*/
|
||||||
|
virtual ~Recipe () { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the recipes name (a unique identifier)
|
||||||
|
*/
|
||||||
|
const std::string &name () const
|
||||||
|
{
|
||||||
|
return m_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the description text
|
||||||
|
*/
|
||||||
|
const std::string &description () const
|
||||||
|
{
|
||||||
|
return m_description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An utility function to get a parameters
|
||||||
|
*/
|
||||||
|
template <class T>
|
||||||
|
static T get_value (const std::map<std::string, tl::Variant> ¶ms, const std::string &pname, const T &def_value)
|
||||||
|
{
|
||||||
|
std::map<std::string, tl::Variant>::const_iterator p = params.find (pname);
|
||||||
|
if (p != params.end ()) {
|
||||||
|
const tl::Variant &v = p->second;
|
||||||
|
return v.to<T> ();
|
||||||
|
} else {
|
||||||
|
return def_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Serializes the given recipe
|
||||||
|
*/
|
||||||
|
std::string generator (const std::map<std::string, tl::Variant> ¶ms);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Executes the recipe from the generator
|
||||||
|
*
|
||||||
|
* Returns nil if the recipe can't be executed, e.g. because the recipe isn't known.
|
||||||
|
*/
|
||||||
|
static tl::Variant make (const std::string &generator);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Recipe interface: executes the recipe with the given parameters
|
||||||
|
*/
|
||||||
|
virtual tl::Variant execute (const std::map<std::string, tl::Variant> ¶ms) const = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Recipe (const Recipe &) : tl::RegisteredClass<tl::Recipe> (this) { }
|
||||||
|
Recipe &operator= (const Recipe &) { return *this; }
|
||||||
|
|
||||||
|
std::string m_name;
|
||||||
|
std::string m_description;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct type_traits<tl::Recipe> : public type_traits<void>
|
||||||
|
{
|
||||||
|
typedef tl::false_tag has_copy_constructor;
|
||||||
|
typedef tl::false_tag has_default_constructor;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace tl
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
KLayout Layout Viewer
|
||||||
|
Copyright (C) 2006-2019 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 "tlRecipe.h"
|
||||||
|
#include "tlUnitTest.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class MyRecipe : public tl::Recipe
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MyRecipe () : tl::Recipe ("test_recipe", "description") { }
|
||||||
|
|
||||||
|
tl::Variant execute (const std::map<std::string, tl::Variant> ¶ms) const
|
||||||
|
{
|
||||||
|
int a = get_value (params, "A", 0);
|
||||||
|
double b = get_value (params, "B", 0.0);
|
||||||
|
return tl::Variant (b * a);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static MyRecipe my_recipe;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// basic abilities
|
||||||
|
TEST(1)
|
||||||
|
{
|
||||||
|
std::map<std::string, tl::Variant> params;
|
||||||
|
params["A"] = tl::Variant (7);
|
||||||
|
params["B"] = tl::Variant (6.0);
|
||||||
|
std::string g = my_recipe.generator (params);
|
||||||
|
EXPECT_EQ (g, "test_recipe: A=#7,B=##6");
|
||||||
|
|
||||||
|
tl::Variant res = tl::Recipe::make (g);
|
||||||
|
EXPECT_EQ (res.to_double (), 42.0);
|
||||||
|
}
|
||||||
|
|
@ -38,7 +38,8 @@ SOURCES = \
|
||||||
tlListTests.cc \
|
tlListTests.cc \
|
||||||
tlEquivalenceClustersTests.cc \
|
tlEquivalenceClustersTests.cc \
|
||||||
tlUniqueNameTests.cc \
|
tlUniqueNameTests.cc \
|
||||||
tlGlobPatternTests.cc
|
tlGlobPatternTests.cc \
|
||||||
|
tlRecipeTests.cc
|
||||||
|
|
||||||
!equals(HAVE_QT, "0") {
|
!equals(HAVE_QT, "0") {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -262,6 +262,42 @@ class Tl_TestClass < TestBase
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class MyRecipe < RBA::Recipe
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
super("test_recipe", "description")
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute(params)
|
||||||
|
a = params["A"] || 0
|
||||||
|
b = params["B"] || 0.0
|
||||||
|
b * a
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
# Recipe
|
||||||
|
def test_4_Recipe
|
||||||
|
|
||||||
|
# make sure there isn't a second instance
|
||||||
|
GC.start
|
||||||
|
|
||||||
|
my_recipe = MyRecipe::new
|
||||||
|
my_recipe._create # makes debugging easier
|
||||||
|
|
||||||
|
assert_equal(my_recipe.name, "test_recipe")
|
||||||
|
assert_equal(my_recipe.description, "description")
|
||||||
|
|
||||||
|
g = my_recipe.generator("A" => 6, "B" => 7.0)
|
||||||
|
assert_equal(g, "test_recipe: A=#6,B=##7")
|
||||||
|
assert_equal("%g" % RBA::Recipe::make(g).to_s, "42")
|
||||||
|
|
||||||
|
my_recipe._destroy
|
||||||
|
my_recipe = nil
|
||||||
|
GC.start
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
load("test_epilogue.rb")
|
load("test_epilogue.rb")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue