mirror of https://github.com/KLayout/klayout.git
WIP: checking of grain properties.
This commit is contained in:
parent
ec415d9251
commit
334fca3f76
|
|
@ -185,6 +185,48 @@ SaltGrain::compare_versions (const std::string &v1, const std::string &v2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
SaltGrain::spec_url (const std::string &url)
|
||||||
|
{
|
||||||
|
std::string res = url;
|
||||||
|
if (! res.empty()) {
|
||||||
|
if (res [res.size () - 1] != '/') {
|
||||||
|
res += "/";
|
||||||
|
}
|
||||||
|
res += grain_filename;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
SaltGrain::valid_name (const std::string &n)
|
||||||
|
{
|
||||||
|
std::string res;
|
||||||
|
|
||||||
|
tl::Extractor ex (n);
|
||||||
|
|
||||||
|
std::string s;
|
||||||
|
if (! ex.try_read_word (s, "_")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
res += s;
|
||||||
|
|
||||||
|
while (! ex.at_end ()) {
|
||||||
|
if (! ex.test ("/")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (! ex.try_read_word (s, "_")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
res += "/";
|
||||||
|
res += s;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this captures the cases where the extractor skips blanks
|
||||||
|
// TODO: the extractor should have a "non-blank-skipping" mode
|
||||||
|
return s == n;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
SaltGrain::valid_version (const std::string &v)
|
SaltGrain::valid_version (const std::string &v)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -357,6 +357,11 @@ public:
|
||||||
*/
|
*/
|
||||||
static bool valid_version (const std::string &v);
|
static bool valid_version (const std::string &v);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks whether the given string is a valid name
|
||||||
|
*/
|
||||||
|
static bool valid_name (const std::string &n);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Detects a grain from the given directory
|
* @brief Detects a grain from the given directory
|
||||||
* This method will return a grain constructed from the given directory.
|
* This method will return a grain constructed from the given directory.
|
||||||
|
|
@ -365,6 +370,11 @@ public:
|
||||||
*/
|
*/
|
||||||
static SaltGrain from_path (const std::string &path);
|
static SaltGrain from_path (const std::string &path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Forms the spec file download URL from a given download URL
|
||||||
|
*/
|
||||||
|
static std::string spec_url (const std::string &url);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns a value indicating whether the given path represents is a grain
|
* @brief Returns a value indicating whether the given path represents is a grain
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@
|
||||||
#include "laySalt.h"
|
#include "laySalt.h"
|
||||||
#include "tlString.h"
|
#include "tlString.h"
|
||||||
#include "tlExceptions.h"
|
#include "tlExceptions.h"
|
||||||
|
#include "tlHttpStream.h"
|
||||||
|
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
|
@ -34,6 +35,8 @@
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
namespace lay
|
namespace lay
|
||||||
{
|
{
|
||||||
|
|
@ -397,29 +400,173 @@ SaltGrainPropertiesDialog::remove_dependency_clicked ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
class DependencyGraph
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DependencyGraph (Salt *salt)
|
||||||
|
{
|
||||||
|
for (lay::Salt::flat_iterator i = salt->begin_flat (); i != salt->end_flat (); ++i) {
|
||||||
|
m_name_to_grain.insert (std::make_pair ((*i)->name (), *i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_valid_name (const std::string &name) const
|
||||||
|
{
|
||||||
|
return m_name_to_grain.find (name) != m_name_to_grain.end ();
|
||||||
|
}
|
||||||
|
|
||||||
|
const lay::SaltGrain *grain_for_name (const std::string &name) const
|
||||||
|
{
|
||||||
|
std::map <std::string, const lay::SaltGrain *>::const_iterator n = m_name_to_grain.find (name);
|
||||||
|
if (n != m_name_to_grain.end ()) {
|
||||||
|
return n->second;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void check_circular (const lay::SaltGrain *current, const lay::SaltGrain *new_dep)
|
||||||
|
{
|
||||||
|
std::vector <const lay::SaltGrain *> path;
|
||||||
|
path.push_back (current);
|
||||||
|
check_circular_follow (new_dep, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::map <std::string, const lay::SaltGrain *> m_name_to_grain;
|
||||||
|
|
||||||
|
void check_circular_follow (const lay::SaltGrain *current, std::vector <const lay::SaltGrain *> &path)
|
||||||
|
{
|
||||||
|
if (! current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
path.push_back (current);
|
||||||
|
|
||||||
|
for (std::vector <const lay::SaltGrain *>::const_iterator p = path.begin (); p != path.end () - 1; ++p) {
|
||||||
|
if (*p == current) {
|
||||||
|
circular_reference_error (path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::vector<SaltGrain::Dependency>::const_iterator d = current->dependencies ().begin (); d != current->dependencies ().end (); ++d) {
|
||||||
|
check_circular_follow (grain_for_name (d->name), path);
|
||||||
|
}
|
||||||
|
|
||||||
|
path.pop_back ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void circular_reference_error (std::vector <const lay::SaltGrain *> &path)
|
||||||
|
{
|
||||||
|
std::string msg = tl::to_string (QObject::tr ("The following path forms a circular dependency: "));
|
||||||
|
for (std::vector <const lay::SaltGrain *>::const_iterator p = path.begin (); p != path.end (); ++p) {
|
||||||
|
if (p != path.begin ()) {
|
||||||
|
msg += "->";
|
||||||
|
}
|
||||||
|
msg += (*p)->name ();
|
||||||
|
}
|
||||||
|
throw tl::Exception (msg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
SaltGrainPropertiesDialog::accept ()
|
SaltGrainPropertiesDialog::accept ()
|
||||||
{
|
{
|
||||||
update_data ();
|
update_data ();
|
||||||
|
|
||||||
// Perform some checks
|
// Perform some checks
|
||||||
|
|
||||||
|
// license
|
||||||
license_alert->clear ();
|
license_alert->clear ();
|
||||||
if (m_grain.license ().empty ()) {
|
if (m_grain.license ().empty ()) {
|
||||||
license_alert->warn () << tr ("License field is empty. Please consider specifying a license model.");
|
license_alert->warn () << tr ("License field is empty. Please consider specifying a license model.") << tl::endl
|
||||||
|
<< tr ("A license model tells users whether and how to use the source code of the package.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// version
|
||||||
version_alert->clear ();
|
version_alert->clear ();
|
||||||
if (m_grain.version ().empty ()) {
|
if (m_grain.version ().empty ()) {
|
||||||
version_alert->warn () << tr ("Version field is empty. Please consider specifying a version number.");
|
version_alert->warn () << tr ("Version field is empty. Please consider specifying a version number.") << tl::endl
|
||||||
|
<< tr ("Versions help the system to apply upgrades if required.");
|
||||||
} else if (! SaltGrain::valid_version (m_grain.version ())) {
|
} else if (! SaltGrain::valid_version (m_grain.version ())) {
|
||||||
version_alert->error () << tr ("'%1' is not a valid version string. A version string needs to be numeric (like '1.2.3' or '4.5'').").arg (tl::to_qstring (m_grain.version ()));
|
version_alert->error () << tr ("'%1' is not a valid version string. A version string needs to be numeric (like '1.2.3' or '4.5'').").arg (tl::to_qstring (m_grain.version ()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// doc URL
|
||||||
doc_url_alert->clear ();
|
doc_url_alert->clear ();
|
||||||
// @@@ TODO
|
if (! m_grain.doc_url ().empty ()) {
|
||||||
|
tl::InputHttpStream stream (m_grain.doc_url ());
|
||||||
|
try {
|
||||||
|
char b;
|
||||||
|
stream.read (&b, 1);
|
||||||
|
} catch (tl::Exception &ex) {
|
||||||
|
doc_url_alert->error () << tr ("Attempt to read documentation URL failed. Error details follow.") << tl::endl
|
||||||
|
<< tr ("URL: ") << m_grain.doc_url () << tl::endl
|
||||||
|
<< tr ("Message: ") << ex.msg ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// dependencies
|
||||||
dependencies_alert->clear ();
|
dependencies_alert->clear ();
|
||||||
// @@@ TODO
|
DependencyGraph dep (mp_salt);
|
||||||
|
std::set <std::string> dep_seen;
|
||||||
|
for (std::vector<SaltGrain::Dependency>::const_iterator d = m_grain.dependencies ().begin (); d != m_grain.dependencies ().end (); ++d) {
|
||||||
|
|
||||||
|
if (! SaltGrain::valid_name (d->name)) {
|
||||||
|
dependencies_alert->error () << tr ("'%1' is not a valid package name").arg (tl::to_qstring (d->name)) << tl::endl
|
||||||
|
<< tr ("Valid package names are words (letters, digits, underscores)") << tl::endl
|
||||||
|
<< tr ("Package groups can be specified in the form 'group/package'");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dep_seen.find (d->name) != dep_seen.end ()) {
|
||||||
|
dependencies_alert->error () << tr ("Duplicate dependency '%1'").arg (tl::to_qstring (d->name)) << tl::endl
|
||||||
|
<< tr ("A package cannot be dependent on the same package twice. Remove on entry.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
dep_seen.insert (d->name);
|
||||||
|
|
||||||
|
if (! dep.is_valid_name (d->name)) {
|
||||||
|
dependencies_alert->warn () << tr ("'%1' is not a name of a package loaded already").arg (tl::to_qstring (d->name)) << tl::endl
|
||||||
|
<< tr ("You need to specify the details (version, URL) manually");
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
dep.check_circular (dep.grain_for_name (m_grain.name ()), dep.grain_for_name (d->name));
|
||||||
|
} catch (tl::Exception &ex) {
|
||||||
|
dependencies_alert->error () << ex.msg () << tl::endl
|
||||||
|
<< tr ("Circular dependency means a package is eventually depending on itself.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d->version.empty ()) {
|
||||||
|
dependencies_alert->warn () << tr ("No version specified for dependency '%1'").arg (tl::to_qstring (d->name)) << tl::endl
|
||||||
|
<< tr ("Versions help checking dependencies.") << tl::endl
|
||||||
|
<< tr ("If the dependency package has a version itself, the version is automatically set to it's current version");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d->url.empty ()) {
|
||||||
|
dependencies_alert->warn () << tr ("No download URL specified for dependency '%1'").arg (tl::to_qstring (d->name)) << tl::endl
|
||||||
|
<< tr ("A download URL should be specified to ensure the package dependencies can be resolved.") << tl::endl
|
||||||
|
<< tr ("If the dependency package was downloaded itself, the URL is automatically set to the download source");
|
||||||
|
} else {
|
||||||
|
std::string spec_url = SaltGrain::spec_url (d->url);
|
||||||
|
tl::InputHttpStream stream (spec_url);
|
||||||
|
try {
|
||||||
|
char b;
|
||||||
|
stream.read (&b, 1);
|
||||||
|
} catch (tl::Exception &ex) {
|
||||||
|
dependencies_alert->error () << tr ("Attempt to read download URL failed. Error details follow.") << tl::endl
|
||||||
|
<< tr ("URL: ") << spec_url << tl::endl
|
||||||
|
<< tr ("Message: ") << ex.msg ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
if (!license_alert->needs_attention () &&
|
if (!license_alert->needs_attention () &&
|
||||||
!doc_url_alert->needs_attention () &&
|
!doc_url_alert->needs_attention () &&
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue