Merge pull request #396 from KLayout/issue-387

Implemented issue-387 (python version in grain.xml)
This commit is contained in:
Matthias Köfferlein 2019-11-06 01:01:23 +01:00 committed by GitHub
commit 65b0752c5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 297 additions and 10 deletions

View File

@ -279,7 +279,7 @@
</font>
</property>
<property name="text">
<string>API version</string>
<string>API features</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
@ -842,7 +842,7 @@
</font>
</property>
<property name="text">
<string>(API version required - i.e. &quot;0.25&quot;)</string>
<string>(API version and features - e.g. &quot;0.26; ruby 2.0&quot;)</string>
</property>
</widget>
</item>

View File

@ -305,6 +305,33 @@ SaltGrain::valid_name (const std::string &n)
return res == n;
}
bool
SaltGrain::valid_api_version (const std::string &v)
{
tl::Extractor ex (v.c_str ());
while (! ex.at_end ()) {
std::string feature;
ex.try_read_name (feature);
bool first = true;
while (! ex.at_end () && ! ex.test (";")) {
int n = 0;
if (! first && ! ex.test (".")) {
return false;
}
if (! ex.try_read (n)) {
return false;
}
first = false;
}
}
return true;
}
bool
SaltGrain::valid_version (const std::string &v)
{

View File

@ -198,6 +198,11 @@ public:
* The API version is the KLayout version required to run the grain's macros.
* A version string is of the form "x.y..." where x, y and other version
* components are integer numbers.
*
* The version string can also list other features such as ruby version etc.
* The components are separated with a semicolon.
* For example, "0.26; ruby; python 3.0" means: requires KLayout API 0.26,
* ruby (any version) any python (>= 3.0).
*/
const std::string &api_version () const
{
@ -433,6 +438,11 @@ public:
*/
static bool valid_version (const std::string &v);
/**
* @brief Gets a value indicating whether the given version string is a valid API version string
*/
static bool valid_api_version (const std::string &v);
/**
* @brief Checks whether the given string is a valid name
*/

View File

@ -252,7 +252,7 @@ SaltGrainDetailsTextWidget::details_text ()
stream << "<p>";
if (! g->api_version ().empty ()) {
stream << "<b>" << QObject::tr ("API version") << ":</b> " << tl::to_qstring (tl::escaped_to_html (g->api_version ())) << " ";
stream << "<b>" << QObject::tr ("API version and features") << ":</b> " << tl::to_qstring (tl::escaped_to_html (g->api_version ())) << " ";
}
stream << "</p>";

View File

@ -529,8 +529,8 @@ SaltGrainPropertiesDialog::accept ()
// API version
api_version_alert->clear ();
if (! m_grain.api_version ().empty () && ! SaltGrain::valid_version (m_grain.api_version ())) {
api_version_alert->error () << tr ("'%1' is not a valid API version string. An API version string needs to be numeric (like '0.25'').").arg (tl::to_qstring (m_grain.api_version ()));
if (! m_grain.api_version ().empty () && ! SaltGrain::valid_api_version (m_grain.api_version ())) {
api_version_alert->error () << tr ("'%1' is not a valid API version string. An API version string needs to be a semicolon-separated list of features with optional numeric versions (like '0.26' or 'ruby 2.0; python').").arg (tl::to_qstring (m_grain.api_version ()));
}
// doc URL

View File

@ -31,6 +31,9 @@
#include "tlString.h"
#include "tlExceptions.h"
#include "rba.h"
#include "pya.h"
#include <QTextDocument>
#include <QPainter>
#include <QDir>
@ -108,6 +111,176 @@ private:
lay::Salt *mp_salt;
};
// --------------------------------------------------------------------------------------
// SaltAPIVersionCheck
class SaltAPIVersionCheck
{
public:
struct APIFeature
{
APIFeature (const std::string &_name, const std::string &_version, const std::string &_description)
: name (_name), version (_version), description (_description)
{
// .. nothing yet ..
}
std::string name, version, description;
};
SaltAPIVersionCheck ();
bool check (const std::string &api_version);
const std::string &message () const
{
return m_message;
}
private:
std::vector<APIFeature> m_features;
std::string m_message;
void populate_features ();
const APIFeature *find_feature (const std::string &name) const;
std::string feature_list () const;
};
SaltAPIVersionCheck::SaltAPIVersionCheck ()
{
populate_features ();
}
bool
SaltAPIVersionCheck::check (const std::string &api_version)
{
tl::Extractor ex (api_version.c_str ());
bool any_not_available = false;
bool good = true;
m_message.clear ();
while (! ex.at_end ()) {
std::string fname;
ex.try_read_name (fname);
std::string v;
while (! ex.at_end () && ! ex.test (";")) {
int n = 0;
if (ex.try_read (n)) {
v += tl::to_string (n);
} else if (ex.test (".")) {
v += ".";
} else {
m_message = tl::to_string (tr ("API version string malformed - cannot check."));
return false;
}
}
const APIFeature *f = find_feature (fname);
if (!f) {
if (! m_message.empty ()) {
m_message += "\n";
}
m_message += tl::sprintf (tl::to_string (tr ("Feature %s not available.")), fname);
good = false;
any_not_available = true;
} else if (! f->version.empty () && ! v.empty () && SaltGrain::compare_versions (f->version, v) < 0) {
// shorten the version (Python reports "3.6.7 blabla...")
std::vector<std::string> fv = tl::split (f->version, " ");
tl_assert (! fv.empty ());
std::string fv_short = fv.front ();
if (fv.size () > 1) {
fv_short += " ...";
}
if (! m_message.empty ()) {
m_message += "\n";
}
m_message += tl::sprintf (tl::to_string (tr ("%s required with version %s or later (is %s).")), f->description, v, fv_short);
good = false;
}
}
if (any_not_available) {
m_message += tl::sprintf (tl::to_string (tr ("\nAvailable features are: %s.")), feature_list ());
}
return good;
}
std::string
SaltAPIVersionCheck::feature_list () const
{
std::string fl;
for (std::vector<APIFeature>::const_iterator f = m_features.begin (); f != m_features.end (); ++f) {
if (! fl.empty ()) {
fl += ", ";
}
fl += f->name;
}
return fl;
}
const SaltAPIVersionCheck::APIFeature *
SaltAPIVersionCheck::find_feature (const std::string &name) const
{
for (std::vector<APIFeature>::const_iterator f = m_features.begin (); f != m_features.end (); ++f) {
if (f->name == name) {
return f.operator-> ();
}
}
return 0;
}
void
SaltAPIVersionCheck::populate_features ()
{
m_features.push_back (APIFeature (std::string (), lay::Version::version (), "KLayout API"));
if (rba::RubyInterpreter::instance () && rba::RubyInterpreter::instance ()->available ()) {
std::string v = rba::RubyInterpreter::instance ()->version ();
m_features.push_back (APIFeature ("ruby", v, "Ruby"));
if (SaltGrain::compare_versions (v, "2") < 0) {
m_features.push_back (APIFeature ("ruby1", v, "Ruby 1"));
} else if (SaltGrain::compare_versions (v, "3") < 0) {
m_features.push_back (APIFeature ("ruby2", v, "Ruby 2"));
}
}
if (pya::PythonInterpreter::instance () && pya::PythonInterpreter::instance ()->available ()) {
std::string v = pya::PythonInterpreter::instance ()->version ();
m_features.push_back (APIFeature ("python", v, "Python"));
if (SaltGrain::compare_versions (v, "3") < 0) {
m_features.push_back (APIFeature ("python2", v, "Python 2"));
} else if (SaltGrain::compare_versions (v, "4") < 0) {
m_features.push_back (APIFeature ("python3", v, "Python 3"));
}
}
#if defined(HAVE_QTBINDINGS)
m_features.push_back (APIFeature ("qt_binding", std::string (), "Qt Binding for RBA or PYA"));
#endif
#if defined(HAVE_QT)
# if QT_VERSION >= 0x040000 && QT_VERSION < 0x050000
m_features.push_back (APIFeature ("qt4", std::string (), "Qt 4"));
# elif QT_VERSION >= 0x050000 && QT_VERSION < 0x060000
m_features.push_back (APIFeature ("qt5", std::string (), "Qt 5"));
# endif
#endif
#if defined(HAVE_64BIT_COORD)
m_features.push_back (APIFeature ("wide-coords", std::string (), "64 bit coordinates"));
#endif
}
// --------------------------------------------------------------------------------------
// SaltManager implementation
@ -790,6 +963,7 @@ SaltManagerDialog::update_models ()
}
SaltAPIVersionCheck svc;
SaltModel *mine_model;
mine_model = dynamic_cast <SaltModel *> (salt_mine_view_update->model ());
@ -817,8 +991,8 @@ SaltManagerDialog::update_models ()
// Establish a message indicating whether the API version does not match
for (Salt::flat_iterator g = m_salt_mine.begin_flat (); g != m_salt_mine.end_flat (); ++g) {
if (SaltGrain::compare_versions (lay::Version::version (), (*g)->api_version ()) < 0) {
mine_model->set_message ((*g)->name (), SaltModel::Warning, tl::to_string (tr ("This package requires a newer API (%1)").arg (tl::to_qstring ((*g)->api_version ()))));
if (! svc.check ((*g)->api_version ())) {
mine_model->set_message ((*g)->name (), SaltModel::Warning, svc.message ());
mine_model->set_enabled ((*g)->name (), false);
}
}
@ -848,8 +1022,8 @@ SaltManagerDialog::update_models ()
// Establish a message indicating whether the API version does not match
for (Salt::flat_iterator g = m_salt_mine.begin_flat (); g != m_salt_mine.end_flat (); ++g) {
if (SaltGrain::compare_versions (lay::Version::version (), (*g)->api_version ()) < 0) {
mine_model->set_message ((*g)->name (), SaltModel::Warning, tl::to_string (tr ("This package requires a newer API (%1)").arg (tl::to_qstring ((*g)->api_version ()))));
if (! svc.check ((*g)->api_version ())) {
mine_model->set_message ((*g)->name (), SaltModel::Warning, svc.message ());
mine_model->set_enabled ((*g)->name (), false);
}
}

View File

@ -1023,6 +1023,15 @@ Extractor::read_word (std::string &value, const char *non_term)
return *this;
}
Extractor &
Extractor::read_name (std::string &value, const char *non_term)
{
if (! try_read_name (value, non_term)) {
error (tl::to_string (tr ("Expected a name string")));
}
return *this;
}
Extractor &
Extractor::read_word_or_quoted (std::string &value, const char *non_term)
{
@ -1227,6 +1236,31 @@ Extractor::try_read (bool &value)
return false;
}
bool
Extractor::try_read_name (std::string &string, const char *non_term)
{
if (! *skip ()) {
return false;
}
string.clear ();
// first character must not be a digit
if (*m_cp && (safe_isalpha (*m_cp) || strchr (non_term, *m_cp) != NULL)) {
string += *m_cp;
++m_cp;
} else {
return false;
}
while (*m_cp && (safe_isalnum (*m_cp) || strchr (non_term, *m_cp) != NULL)) {
string += *m_cp;
++m_cp;
}
return ! string.empty ();
}
bool
Extractor::try_read_word (std::string &string, const char *non_term)
{
@ -1235,10 +1269,12 @@ Extractor::try_read_word (std::string &string, const char *non_term)
}
string.clear ();
while (*m_cp && (safe_isalnum (*m_cp) || strchr (non_term, *m_cp) != NULL)) {
string += *m_cp;
++m_cp;
}
return ! string.empty ();
}

View File

@ -528,6 +528,13 @@ public:
*/
Extractor &read (std::string &value, const char *term = "");
/**
* @brief Read a name string
*
* Name strings are like words, but for the first character digits are not allowed.
*/
Extractor &read_name (std::string &value, const char *non_term = "_.$");
/**
* @brief Read a string consisting of "word" characters
*
@ -610,6 +617,13 @@ public:
*/
bool try_read (std::string &string, const char *term = "");
/**
* @brief Try to read a name string
*
* Name strings are like words, but for the first character digits are not allowed.
*/
bool try_read_name (std::string &value, const char *non_term = "_.$");
/**
* @brief Try to read a string consisting of "word" characters
*

View File

@ -312,23 +312,49 @@ TEST(8)
x = Extractor ("a_word!");
x.read_word (s);
EXPECT_EQ (s, "a_word");
x = Extractor ("a_word!");
s.clear ();
x.read_name (s);
EXPECT_EQ (s, "a_word");
EXPECT_EQ (x.test ("!"), true);
x = Extractor ("0_word!");
EXPECT_EQ (x.try_read_word (s), true);
x = Extractor ("0_word!");
EXPECT_EQ (x.try_read_name (s), false);
x = Extractor ("a_word!");
EXPECT_EQ (x.try_read_word (s), true);
EXPECT_EQ (s, "a_word");
EXPECT_EQ (x.test ("!"), true);
x = Extractor ("a_word!");
EXPECT_EQ (x.try_read_name (s), true);
EXPECT_EQ (s, "a_word");
EXPECT_EQ (x.test ("!"), true);
x = Extractor ("a_word!");
x.read_word (s, "_!");
EXPECT_EQ (s, "a_word!");
EXPECT_EQ (x.at_end (), true);
x = Extractor ("a_word!");
x.read_name (s, "_!");
EXPECT_EQ (s, "a_word!");
EXPECT_EQ (x.at_end (), true);
x = Extractor ("a_word!");
EXPECT_EQ (x.try_read_word (s, "_!"), true);
EXPECT_EQ (s, "a_word!");
EXPECT_EQ (x.at_end (), true);
x = Extractor ("a_word!");
EXPECT_EQ (x.try_read_name (s, "_!"), true);
EXPECT_EQ (s, "a_word!");
EXPECT_EQ (x.at_end (), true);
x = Extractor ("a_word!");
x.read_word_or_quoted (s);
EXPECT_EQ (s, "a_word");

View File

@ -2,7 +2,7 @@
# This script is sourced to define the main version parameters
# The main version
KLAYOUT_VERSION="0.26"
KLAYOUT_VERSION="0.26.1"
# The build date
KLAYOUT_VERSION_DATE=$(date "+%Y-%m-%d")