mirror of https://github.com/KLayout/klayout.git
Merge pull request #396 from KLayout/issue-387
Implemented issue-387 (python version in grain.xml)
This commit is contained in:
commit
65b0752c5c
|
|
@ -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. "0.25")</string>
|
||||
<string>(API version and features - e.g. "0.26; ruby 2.0")</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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>";
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
Loading…
Reference in New Issue