WIP: provide a recipe registration facility for LVS rerun

This commit is contained in:
Matthias Koefferlein 2019-08-25 18:03:27 +02:00
parent 444e10d32f
commit 515b68b76f
7 changed files with 399 additions and 3 deletions

View File

@ -27,6 +27,7 @@
#include "tlProgress.h"
#include "tlExpression.h"
#include "tlGlobPattern.h"
#include "tlRecipe.h"
// ----------------------------------------------------------------
// Logger binding
@ -640,4 +641,83 @@ Class<tl::GlobPattern> decl_GlobPattern ("tl", "GlobPattern",
"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> &params) 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."
);
}

View File

@ -45,7 +45,8 @@ SOURCES = \
tlUniqueId.cc \
tlList.cc \
tlEquivalenceClusters.cc \
tlUniqueName.cc
tlUniqueName.cc \
tlRecipe.cc
HEADERS = \
tlAlgorithm.h \
@ -100,7 +101,8 @@ HEADERS = \
tlUniqueId.h \
tlList.h \
tlEquivalenceClusters.h \
tlUniqueName.h
tlUniqueName.h \
tlRecipe.h
equals(HAVE_CURL, "1") {

87
src/tl/tl/tlRecipe.cc Normal file
View File

@ -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> &params)
{
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

132
src/tl/tl/tlRecipe.h Normal file
View File

@ -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> &params, 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> &params);
/**
* @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> &params) 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

View File

@ -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> &params) 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);
}

View File

@ -38,7 +38,8 @@ SOURCES = \
tlListTests.cc \
tlEquivalenceClustersTests.cc \
tlUniqueNameTests.cc \
tlGlobPatternTests.cc
tlGlobPatternTests.cc \
tlRecipeTests.cc
!equals(HAVE_QT, "0") {

View File

@ -262,6 +262,42 @@ class Tl_TestClass < TestBase
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
load("test_epilogue.rb")