mirror of https://github.com/KLayout/klayout.git
Some refactoring of package manager, new features
* Moved tlSystemPaths into lay namespace where it belongs * Doc updates * New command line switch -y and -yd for unattended installation * Download URL's can be relative to salt.mine URL * KLAYOUT_HOME environment variable to make ~/.klayout configurable * Better error messages on XML parser on file/stream read errors (specifically from http/https)
This commit is contained in:
parent
407c967de4
commit
c077feb3d5
|
|
@ -26,7 +26,6 @@
|
|||
#include "dbEdgeProcessor.h"
|
||||
#include "dbReader.h"
|
||||
#include "tlStream.h"
|
||||
#include "tlSystemPaths.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QResource>
|
||||
|
|
|
|||
|
|
@ -30,9 +30,12 @@
|
|||
|
||||
<p>
|
||||
Packages are identified by name. A package name needs to be unique in the package universe.
|
||||
You can use a prefixed name like <tt>www.mydomain.org/nameofpackage</tt> to create a non-ambiguous name.
|
||||
The choice of the prefix is entirely up to you. You can use a domain name that is owned by
|
||||
yourself for example.
|
||||
You can use a prefixed name like <tt>www.mydomain.org/nameofpackage</tt> to create a non-ambiguous name.
|
||||
Use a slash to separate the prefix from the actual package name.
|
||||
The choice of the prefix is entirely up to you as long as it contains letters, digits, underscores, hypthens or dots.
|
||||
You can use a domain name that is owned by
|
||||
yourself for example. You can use multiple prefixes to further differentiate the packages
|
||||
inside your namespace.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
|
|
|||
|
|
@ -56,7 +56,8 @@ HEADERS = \
|
|||
laySignalHandler.h \
|
||||
layLibraryController.h \
|
||||
layFontController.h \
|
||||
layNativePlugin.h
|
||||
layNativePlugin.h \
|
||||
laySystemPaths.h
|
||||
|
||||
FORMS = \
|
||||
ClipDialog.ui \
|
||||
|
|
@ -159,7 +160,8 @@ SOURCES = \
|
|||
laySignalHandler.cc \
|
||||
layLibraryController.cc \
|
||||
layFontController.cc \
|
||||
layNativePlugin.cc
|
||||
layNativePlugin.cc \
|
||||
laySystemPaths.cc
|
||||
|
||||
RESOURCES = layBuildInMacros.qrc \
|
||||
layHelpResources.qrc \
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
#include "layMacroController.h"
|
||||
#include "layTechnologyController.h"
|
||||
#include "laySaltController.h"
|
||||
#include "laySystemPaths.h"
|
||||
#include "lymMacro.h"
|
||||
#include "gtf.h"
|
||||
#include "gsiDecl.h"
|
||||
|
|
@ -52,7 +53,6 @@
|
|||
#include "tlAssert.h"
|
||||
#include "tlLog.h"
|
||||
#include "tlString.h"
|
||||
#include "tlSystemPaths.h"
|
||||
#include "tlExpression.h"
|
||||
#include "tlExceptions.h"
|
||||
#include "tlInternational.h"
|
||||
|
|
@ -280,13 +280,13 @@ Application::Application (int &argc, char **argv, bool non_ui_mode)
|
|||
bool gtf_save_incremental = false;
|
||||
|
||||
// get and create the klayout appdata folder if required
|
||||
m_appdata_path = tl::get_appdata_path ();
|
||||
m_appdata_path = lay::get_appdata_path ();
|
||||
|
||||
// get the installation path
|
||||
m_inst_path = tl::get_inst_path ();
|
||||
m_inst_path = lay::get_inst_path ();
|
||||
|
||||
// get the KLayout path
|
||||
m_klayout_path = tl::get_klayout_path ();
|
||||
m_klayout_path = lay::get_klayout_path ();
|
||||
|
||||
if (mp_qapp_gui) {
|
||||
|
||||
|
|
@ -408,6 +408,9 @@ Application::Application (int &argc, char **argv, bool non_ui_mode)
|
|||
std::string tech;
|
||||
std::string tech_file;
|
||||
|
||||
std::vector <std::string> package_inst;
|
||||
bool packages_with_dep = false;
|
||||
|
||||
bool editable_set = false;
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
|
|
@ -593,7 +596,15 @@ Application::Application (int &argc, char **argv, bool non_ui_mode)
|
|||
} else if (a == "-x") {
|
||||
|
||||
m_sync_mode = true;
|
||||
|
||||
|
||||
} else if (a == "-y" && (i + 1) < argc) {
|
||||
|
||||
package_inst.push_back (args [++i]);
|
||||
|
||||
} else if (a == "-yd") {
|
||||
|
||||
packages_with_dep = true;
|
||||
|
||||
} else if (a == "-v") {
|
||||
|
||||
tl::info << lay::Version::name () << " " << lay::Version::version ();
|
||||
|
|
@ -692,7 +703,16 @@ Application::Application (int &argc, char **argv, bool non_ui_mode)
|
|||
sc->add_path (*p);
|
||||
}
|
||||
|
||||
sc->set_salt_mine_url (tl::salt_mine_url ());
|
||||
sc->set_salt_mine_url (lay::salt_mine_url ());
|
||||
|
||||
// Do package installation if requested.
|
||||
if (!package_inst.empty ()) {
|
||||
if (! sc->install_packages (package_inst, packages_with_dep)) {
|
||||
exit (1);
|
||||
} else {
|
||||
exit (0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -1065,8 +1085,11 @@ Application::usage ()
|
|||
r += tl::to_string (QObject::tr (" -v Print program version and exit")) + "\n";
|
||||
r += tl::to_string (QObject::tr (" -wd <name>=<value> Define a variable within expressions")) + "\n";
|
||||
r += tl::to_string (QObject::tr (" -x Synchronous drawing mode")) + "\n";
|
||||
r += tl::to_string (QObject::tr (" -zz Non-GUI mode (database only, implies -nc)")) + "\n";
|
||||
r += tl::to_string (QObject::tr (" -y <package> Package installation: install package(s) and exit - can be used more than once")) + "\n";
|
||||
r += tl::to_string (QObject::tr (" ('package' is a name, an URL and optionally a version in round brackets)")) + "\n";
|
||||
r += tl::to_string (QObject::tr (" -yd With -y: include dependencies")) + "\n";
|
||||
r += tl::to_string (QObject::tr (" -z Non-GUI mode (hidden views)")) + "\n";
|
||||
r += tl::to_string (QObject::tr (" -zz Non-GUI mode (database only, implies -nc)")) + "\n";
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "laySaltController.h"
|
||||
#include "laySaltManagerDialog.h"
|
||||
#include "laySaltDownloadManager.h"
|
||||
#include "layConfig.h"
|
||||
#include "layMainWindow.h"
|
||||
#include "layQtTools.h"
|
||||
|
|
@ -152,12 +153,14 @@ SaltController::show_editor ()
|
|||
void
|
||||
SaltController::sync_file_watcher ()
|
||||
{
|
||||
m_file_watcher->clear ();
|
||||
m_file_watcher->enable (false);
|
||||
for (lay::Salt::flat_iterator g = m_salt.begin_flat (); g != m_salt.end_flat (); ++g) {
|
||||
m_file_watcher->add_file ((*g)->path ());
|
||||
if (m_file_watcher) {
|
||||
m_file_watcher->clear ();
|
||||
m_file_watcher->enable (false);
|
||||
for (lay::Salt::flat_iterator g = m_salt.begin_flat (); g != m_salt.end_flat (); ++g) {
|
||||
m_file_watcher->add_file ((*g)->path ());
|
||||
}
|
||||
m_file_watcher->enable (true);
|
||||
}
|
||||
m_file_watcher->enable (true);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -167,6 +170,54 @@ SaltController::sync_files ()
|
|||
emit salt_changed ();
|
||||
}
|
||||
|
||||
bool
|
||||
SaltController::install_packages (const std::vector<std::string> &packages, bool with_dep)
|
||||
{
|
||||
lay::SaltDownloadManager manager;
|
||||
|
||||
lay::Salt salt_mine;
|
||||
if (! m_salt_mine_url.empty ()) {
|
||||
tl::log << tl::to_string (tr ("Downloading package repository from %1").arg (tl::to_qstring (m_salt_mine_url)));
|
||||
salt_mine.load (m_salt_mine_url);
|
||||
}
|
||||
|
||||
for (std::vector<std::string>::const_iterator p = packages.begin (); p != packages.end (); ++p) {
|
||||
|
||||
if (p->empty ()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string v, n = *p;
|
||||
|
||||
size_t br = p->find ("(");
|
||||
if (br != std::string::npos) {
|
||||
n = std::string (*p, 0, br);
|
||||
v = std::string (*p, br + 1);
|
||||
size_t brr = v.find (")");
|
||||
if (brr != std::string::npos) {
|
||||
v = std::string (v, 0, brr);
|
||||
}
|
||||
}
|
||||
|
||||
if (n.find ("http:") == 0 || n.find ("https:") == 0 || n.find ("file:") == 0 || n[0] == '/' || n[0] == '\\') {
|
||||
// it's a URL
|
||||
manager.register_download (std::string (), n, v);
|
||||
} else {
|
||||
// it's a plain name
|
||||
manager.register_download (n, std::string (), v);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (with_dep) {
|
||||
manager.compute_dependencies (m_salt, salt_mine);
|
||||
} else {
|
||||
manager.compute_packages (m_salt, salt_mine);
|
||||
}
|
||||
|
||||
return manager.execute (0, m_salt);
|
||||
}
|
||||
|
||||
void
|
||||
SaltController::add_path (const std::string &path)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -121,11 +121,35 @@ public:
|
|||
*/
|
||||
void add_path (const std::string &path);
|
||||
|
||||
/**
|
||||
* @brief Installs the packages from the given list
|
||||
*
|
||||
* The list is a list of names or URL's, optionally with a version in round brackets.
|
||||
* If a package with the given name or URL is installed already, it is skipped.
|
||||
* Otherwise, it's downloaded and installed. If URL's are given, the URL is
|
||||
* used for download. If names are given, the URL's are taken from the
|
||||
* salt mine index.
|
||||
*
|
||||
* If "with_dep" is used, dependencies are also installed as taken from the
|
||||
* salt mine index.
|
||||
*
|
||||
* The method returns true if all packages could be installed successfully.
|
||||
*/
|
||||
bool install_packages (const std::vector<std::string> &packages, bool with_dep);
|
||||
|
||||
/**
|
||||
* @brief Specifies the salt mine (package repository) URL
|
||||
*/
|
||||
void set_salt_mine_url (const std::string &url);
|
||||
|
||||
/**
|
||||
* @brief Gets the salt mine (package repository) URL
|
||||
*/
|
||||
const std::string &salt_mine_url () const
|
||||
{
|
||||
return m_salt_mine_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the salt
|
||||
*/
|
||||
|
|
@ -163,7 +187,7 @@ private:
|
|||
lay::SaltManagerDialog *mp_salt_dialog;
|
||||
lay::MainWindow *mp_mw;
|
||||
std::string m_salt_mine_url;
|
||||
lay::Salt m_salt, m_salt_mine;
|
||||
lay::Salt m_salt;
|
||||
tl::FileSystemWatcher *m_file_watcher;
|
||||
tl::DeferredMethod<SaltController> dm_sync_file_watcher;
|
||||
tl::DeferredMethod<SaltController> dm_sync_files;
|
||||
|
|
|
|||
|
|
@ -121,47 +121,65 @@ SaltDownloadManager::SaltDownloadManager ()
|
|||
void
|
||||
SaltDownloadManager::register_download (const std::string &name, const std::string &url, const std::string &version)
|
||||
{
|
||||
m_registry.insert (std::make_pair (name, Descriptor (url, version)));
|
||||
m_registry.push_back (Descriptor (name, url, version));
|
||||
}
|
||||
|
||||
void
|
||||
SaltDownloadManager::compute_dependencies (const lay::Salt &salt, const lay::Salt &salt_mine)
|
||||
{
|
||||
compute_list (salt, salt_mine, true);
|
||||
}
|
||||
|
||||
void
|
||||
SaltDownloadManager::compute_packages (const lay::Salt &salt, const lay::Salt &salt_mine)
|
||||
{
|
||||
compute_list (salt, salt_mine, false);
|
||||
}
|
||||
|
||||
void
|
||||
SaltDownloadManager::compute_list (const lay::Salt &salt, const lay::Salt &salt_mine, bool with_dep)
|
||||
{
|
||||
tl::AbsoluteProgress progress (tl::to_string (QObject::tr ("Computing package dependencies ..")));
|
||||
|
||||
std::map<std::string, Descriptor> registry;
|
||||
|
||||
// remove those registered entries which don't need to be updated
|
||||
|
||||
registry = m_registry;
|
||||
for (std::map<std::string, Descriptor>::const_iterator p = registry.begin (); p != registry.end (); ++p) {
|
||||
const SaltGrain *g = salt.grain_by_name (p->first);
|
||||
if (g && SaltGrain::compare_versions (p->second.version, g->version ()) == 0 && p->second.url == g->url ()) {
|
||||
m_registry.erase (p->first);
|
||||
}
|
||||
}
|
||||
|
||||
// add further entries as derived from the dependencies
|
||||
|
||||
while (needs_iteration ()) {
|
||||
|
||||
fetch_missing (salt_mine, progress);
|
||||
fetch_missing (salt, salt_mine, progress);
|
||||
|
||||
registry = m_registry;
|
||||
for (std::map<std::string, Descriptor>::const_iterator p = registry.begin (); p != registry.end (); ++p) {
|
||||
if (! with_dep) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (std::vector<SaltGrain::Dependency>::const_iterator d = p->second.grain.dependencies ().begin (); d != p->second.grain.dependencies ().end (); ++d) {
|
||||
std::map<std::string, size_t> reg_by_name;
|
||||
for (std::vector<Descriptor>::const_iterator p = m_registry.begin (); p != m_registry.end (); ++p) {
|
||||
reg_by_name.insert (std::make_pair (p->name, p - m_registry.begin ()));
|
||||
}
|
||||
|
||||
std::map<std::string, Descriptor>::iterator r = m_registry.find (d->name);
|
||||
if (r != m_registry.end ()) {
|
||||
size_t n = m_registry.size ();
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
|
||||
if (SaltGrain::compare_versions (r->second.version, d->version) < 0) {
|
||||
const Descriptor &p = m_registry [i];
|
||||
|
||||
for (std::vector<SaltGrain::Dependency>::const_iterator d = p.grain.dependencies ().begin (); d != p.grain.dependencies ().end (); ++d) {
|
||||
|
||||
std::map<std::string, size_t>::iterator r = reg_by_name.find (d->name);
|
||||
if (r != reg_by_name.end ()) {
|
||||
|
||||
// Dependency is already scheduled for installation - check if we need a newer package
|
||||
|
||||
Descriptor &pd = m_registry [r->second];
|
||||
|
||||
if (SaltGrain::compare_versions (pd.version, d->version) < 0) {
|
||||
|
||||
// Grain is present, but too old -> update version and reload in the next iteration
|
||||
r->second.downloaded = false;
|
||||
r->second.version = d->version;
|
||||
r->second.url = d->url;
|
||||
r->second.downloaded = false;
|
||||
if (tl::verbosity() >= 20) {
|
||||
tl::log << "Upgrading installation request as required by package " << p.name << ": " << d->name << " (" << d->version << ") with URL " << d->url;
|
||||
}
|
||||
|
||||
pd.downloaded = false;
|
||||
pd.version = d->version;
|
||||
pd.url = d->url;
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -172,11 +190,25 @@ SaltDownloadManager::compute_dependencies (const lay::Salt &salt, const lay::Sal
|
|||
|
||||
// Grain is installed already, but too old -> register for update
|
||||
if (SaltGrain::compare_versions (g->version (), d->version) < 0) {
|
||||
register_download (d->name, d->url, d->version);
|
||||
|
||||
if (tl::verbosity() >= 20) {
|
||||
tl::log << "Considering for update as dependency: " << d->name << " (" << d->version << ") with URL " << d->url;
|
||||
}
|
||||
m_registry.push_back (Descriptor (d->name, d->url, d->version));
|
||||
|
||||
} else {
|
||||
if (tl::verbosity() >= 20) {
|
||||
tl::log << "Dependency already satisfied: " << d->name << "(" << d->version << ")";
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
register_download (d->name, d->url, d->version);
|
||||
|
||||
if (tl::verbosity() >= 20) {
|
||||
tl::log << "Considering for download as dependency: " << d->name << " (" << d->version << ") with URL " << d->url;
|
||||
}
|
||||
m_registry.push_back (Descriptor (d->name, d->url, d->version));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -191,8 +223,8 @@ SaltDownloadManager::compute_dependencies (const lay::Salt &salt, const lay::Sal
|
|||
bool
|
||||
SaltDownloadManager::needs_iteration ()
|
||||
{
|
||||
for (std::map<std::string, Descriptor>::const_iterator p = m_registry.begin (); p != m_registry.end (); ++p) {
|
||||
if (! p->second.downloaded) {
|
||||
for (std::vector<Descriptor>::const_iterator p = m_registry.begin (); p != m_registry.end (); ++p) {
|
||||
if (! p->downloaded) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -200,35 +232,95 @@ SaltDownloadManager::needs_iteration ()
|
|||
}
|
||||
|
||||
void
|
||||
SaltDownloadManager::fetch_missing (const lay::Salt &salt_mine, tl::AbsoluteProgress &progress)
|
||||
SaltDownloadManager::fetch_missing (const lay::Salt &salt, const lay::Salt &salt_mine, tl::AbsoluteProgress &progress)
|
||||
{
|
||||
for (std::map<std::string, Descriptor>::iterator p = m_registry.begin (); p != m_registry.end (); ++p) {
|
||||
std::vector<Descriptor> registry;
|
||||
|
||||
if (! p->second.downloaded) {
|
||||
// drop entries with same name but lower version
|
||||
|
||||
registry.swap (m_registry);
|
||||
std::sort (registry.begin (), registry.end ());
|
||||
|
||||
for (std::vector<Descriptor>::const_iterator p = registry.begin (); p != registry.end (); ++p) {
|
||||
if (p + 1 != registry.end ()) {
|
||||
if (p->name != p[1].name) {
|
||||
m_registry.push_back (*p);
|
||||
}
|
||||
} else {
|
||||
m_registry.push_back (*p);
|
||||
}
|
||||
}
|
||||
|
||||
// download the items that need to be downloaded
|
||||
|
||||
for (std::vector<Descriptor>::iterator p = m_registry.begin (); p != m_registry.end (); ++p) {
|
||||
|
||||
if (! p->downloaded) {
|
||||
|
||||
++progress;
|
||||
|
||||
// If no URL is given, utilize the salt mine to fetch it
|
||||
if (p->second.url.empty ()) {
|
||||
const lay::SaltGrain *g = salt_mine.grain_by_name (p->first);
|
||||
if (SaltGrain::compare_versions (g->version (), p->second.version) < 0) {
|
||||
throw tl::Exception (tl::to_string (QObject::tr ("Package '%1': package in repository is too old (%2) to satisfy requirements (%3)").arg (tl::to_qstring (p->first)).arg (tl::to_qstring (g->version ())).arg (tl::to_qstring (p->second.version))));
|
||||
if (p->url.empty ()) {
|
||||
|
||||
tl_assert (! p->name.empty ());
|
||||
|
||||
const lay::SaltGrain *g = salt_mine.grain_by_name (p->name);
|
||||
if (! g) {
|
||||
throw tl::Exception (tl::to_string (QObject::tr ("Package '%1' not found in index - cannot resolve download URL").arg (tl::to_qstring (p->name))));
|
||||
}
|
||||
p->second.version = g->version ();
|
||||
p->second.url = g->url ();
|
||||
|
||||
if (tl::verbosity() >= 20) {
|
||||
tl::log << "Resolved package URL for package " << p->name << ": " << g->url ();
|
||||
}
|
||||
|
||||
p->url = g->url ();
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
p->second.grain = SaltGrain::from_url (p->second.url);
|
||||
p->grain = SaltGrain::from_url (p->url);
|
||||
} catch (tl::Exception &ex) {
|
||||
throw tl::Exception (tl::to_string (QObject::tr ("Error fetching spec file for package '%1': %2").arg (tl::to_qstring (p->first)).arg (tl::to_qstring (ex.msg ()))));
|
||||
throw tl::Exception (tl::to_string (QObject::tr ("Error fetching spec file for package '%1': %2").arg (tl::to_qstring (p->name)).arg (tl::to_qstring (ex.msg ()))));
|
||||
}
|
||||
|
||||
p->second.downloaded = true;
|
||||
if (p->version.empty ()) {
|
||||
p->version = p->grain.version ();
|
||||
}
|
||||
|
||||
p->name = p->grain.name ();
|
||||
p->downloaded = true;
|
||||
|
||||
if (SaltGrain::compare_versions (p->grain.version (), p->version) < 0) {
|
||||
throw tl::Exception (tl::to_string (QObject::tr ("Package '%1': package in repository is too old (%2) to satisfy requirements (%3)").arg (tl::to_qstring (p->name)).arg (tl::to_qstring (p->grain.version ())).arg (tl::to_qstring (p->version))));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// remove those registered entries which don't need to be updated (we do this after download since now the
|
||||
// names should be known when only the URL is given)
|
||||
|
||||
registry.clear ();
|
||||
registry.swap (m_registry);
|
||||
|
||||
for (std::vector<Descriptor>::const_iterator p = registry.begin (); p != registry.end (); ++p) {
|
||||
const SaltGrain *g = salt.grain_by_name (p->name);
|
||||
if (g) {
|
||||
if (SaltGrain::compare_versions (p->version, g->version ()) <= 0 && p->url == g->url ()) {
|
||||
if (tl::verbosity() >= 20) {
|
||||
tl::log << "Package already present with sufficient version - not installed again: " << p->name << " (" << p->version << ")";
|
||||
}
|
||||
} else {
|
||||
if (tl::verbosity() >= 20) {
|
||||
tl::log << "Considering package for upgrade or URL switch: " << p->name << ", from " << g->url () << "(" << g->version () << ") to " << p->url << "(" << p->version << ")";
|
||||
}
|
||||
m_registry.push_back (*p);
|
||||
}
|
||||
} else {
|
||||
m_registry.push_back (*p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lay::ConfirmationDialog *
|
||||
|
|
@ -236,20 +328,22 @@ SaltDownloadManager::make_confirmation_dialog (QWidget *parent, const lay::Salt
|
|||
{
|
||||
lay::ConfirmationDialog *dialog = new lay::ConfirmationDialog (parent);
|
||||
|
||||
std::sort (m_registry.begin (), m_registry.end ());
|
||||
|
||||
// First the packages to update
|
||||
for (std::map<std::string, Descriptor>::const_iterator p = m_registry.begin (); p != m_registry.end (); ++p) {
|
||||
const lay::SaltGrain *g = salt.grain_by_name (p->first);
|
||||
for (std::vector<Descriptor>::const_iterator p = m_registry.begin (); p != m_registry.end (); ++p) {
|
||||
const lay::SaltGrain *g = salt.grain_by_name (p->name);
|
||||
if (g) {
|
||||
// \342\206\222 is UTF-8 "right arrow"
|
||||
dialog->add_info (p->first, true, g->version () + " \342\206\222 " + p->second.version, p->second.url);
|
||||
dialog->add_info (p->name, true, g->version () + " \342\206\222 " + p->version, p->url);
|
||||
}
|
||||
}
|
||||
|
||||
// Then the packages to install
|
||||
for (std::map<std::string, Descriptor>::const_iterator p = m_registry.begin (); p != m_registry.end (); ++p) {
|
||||
const lay::SaltGrain *g = salt.grain_by_name (p->first);
|
||||
for (std::vector<Descriptor>::const_iterator p = m_registry.begin (); p != m_registry.end (); ++p) {
|
||||
const lay::SaltGrain *g = salt.grain_by_name (p->name);
|
||||
if (!g) {
|
||||
dialog->add_info (p->first, false, p->second.version, p->second.url);
|
||||
dialog->add_info (p->name, false, p->version, p->url);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -259,52 +353,78 @@ SaltDownloadManager::make_confirmation_dialog (QWidget *parent, const lay::Salt
|
|||
bool
|
||||
SaltDownloadManager::execute (QWidget *parent, lay::Salt &salt)
|
||||
{
|
||||
// Stop with a warning if there is nothing to do
|
||||
if (m_registry.empty()) {
|
||||
QMessageBox::warning (parent, tr ("Nothing to do"), tr ("No packages need update or are marked for installation"));
|
||||
return true;
|
||||
}
|
||||
|
||||
std::auto_ptr<lay::ConfirmationDialog> dialog (make_confirmation_dialog (parent, salt));
|
||||
|
||||
dialog->setModal (true);
|
||||
dialog->show ();
|
||||
|
||||
while (! dialog->is_confirmed ()) {
|
||||
QCoreApplication::processEvents (QEventLoop::AllEvents | QEventLoop::WaitForMoreEvents, 100);
|
||||
if (dialog->is_cancelled () || ! dialog->isVisible ()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
dialog->start ();
|
||||
|
||||
bool result = true;
|
||||
|
||||
for (std::map<std::string, Descriptor>::const_iterator p = m_registry.begin (); p != m_registry.end (); ++p) {
|
||||
if (parent) {
|
||||
|
||||
lay::SaltGrain target;
|
||||
target.set_name (p->first);
|
||||
lay::SaltGrain *g = salt.grain_by_name (p->first);
|
||||
if (g) {
|
||||
target.set_path (g->path ());
|
||||
// Stop with a warning if there is nothing to do
|
||||
if (m_registry.empty()) {
|
||||
QMessageBox::warning (parent, tr ("Nothing to do"), tr ("No packages need update or are marked for installation"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (! salt.create_grain (p->second.grain, target)) {
|
||||
dialog->mark_error (p->first);
|
||||
result = false;
|
||||
} else {
|
||||
dialog->mark_success (p->first);
|
||||
std::auto_ptr<lay::ConfirmationDialog> dialog (make_confirmation_dialog (parent, salt));
|
||||
|
||||
dialog->setModal (true);
|
||||
dialog->show ();
|
||||
|
||||
while (! dialog->is_confirmed ()) {
|
||||
QCoreApplication::processEvents (QEventLoop::AllEvents | QEventLoop::WaitForMoreEvents, 100);
|
||||
if (dialog->is_cancelled () || ! dialog->isVisible ()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
dialog->separator ();
|
||||
dialog->start ();
|
||||
|
||||
}
|
||||
std::sort (m_registry.begin (), m_registry.end ());
|
||||
|
||||
dialog->finish ();
|
||||
for (std::vector<Descriptor>::const_iterator p = m_registry.begin (); p != m_registry.end (); ++p) {
|
||||
|
||||
lay::SaltGrain target;
|
||||
target.set_name (p->name);
|
||||
lay::SaltGrain *g = salt.grain_by_name (p->name);
|
||||
if (g) {
|
||||
target.set_path (g->path ());
|
||||
}
|
||||
|
||||
if (! salt.create_grain (p->grain, target)) {
|
||||
dialog->mark_error (p->name);
|
||||
result = false;
|
||||
} else {
|
||||
dialog->mark_success (p->name);
|
||||
}
|
||||
|
||||
dialog->separator ();
|
||||
|
||||
}
|
||||
|
||||
dialog->finish ();
|
||||
|
||||
while (! dialog->is_closed () && dialog->isVisible ()) {
|
||||
QCoreApplication::processEvents (QEventLoop::AllEvents | QEventLoop::WaitForMoreEvents, 100);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
for (std::vector<Descriptor>::const_iterator p = m_registry.begin (); p != m_registry.end (); ++p) {
|
||||
|
||||
lay::SaltGrain target;
|
||||
target.set_name (p->name);
|
||||
lay::SaltGrain *g = salt.grain_by_name (p->name);
|
||||
if (g) {
|
||||
target.set_path (g->path ());
|
||||
}
|
||||
|
||||
if (! salt.create_grain (p->grain, target)) {
|
||||
tl::error << tl::to_string (QObject::tr ("Installation failed for package %1").arg (tl::to_qstring (target.name ())));
|
||||
result = false;
|
||||
} else {
|
||||
tl::log << tl::to_string (QObject::tr ("Package %1 installed successfully").arg (tl::to_qstring (target.name ())));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
while (! dialog->is_closed () && dialog->isVisible ()) {
|
||||
QCoreApplication::processEvents (QEventLoop::AllEvents | QEventLoop::WaitForMoreEvents, 100);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -110,35 +110,67 @@ public:
|
|||
*/
|
||||
void compute_dependencies (const lay::Salt &salt, const Salt &salt_mine);
|
||||
|
||||
/**
|
||||
* @brief Computes the list of packages after all required packages have been registered
|
||||
*
|
||||
* This method will removes packages that are already installed and satisfy the version requirements.
|
||||
* Packages not present in the list of packages ("salt" argument), will be scheduled for download too.
|
||||
* No dependencies are checked in this version.
|
||||
*/
|
||||
void compute_packages (const lay::Salt &salt, const Salt &salt_mine);
|
||||
|
||||
/**
|
||||
* @brief Actually execute the downloads
|
||||
*
|
||||
* This method will show a confirmation dialog and start installation
|
||||
* if this dialog is confirmed. It will return false if
|
||||
* the dialog was cancelled and an exception if something goes
|
||||
* wrong.
|
||||
* It will return true if the packages were installed successfully.
|
||||
* If parent is non-null, this method will show a confirmation dialog and start installation
|
||||
* if this dialog is confirmed. It will return false if the dialog was cancelled and an exception
|
||||
* if something goes wrong.
|
||||
*
|
||||
* If parent is null, no confirmation dialog will be shown and installation happens in non-GUI
|
||||
* mode.
|
||||
*
|
||||
* The return value will be true if the packages were installed successfully.
|
||||
*/
|
||||
bool execute (QWidget *parent, lay::Salt &salt);
|
||||
|
||||
private:
|
||||
struct Descriptor
|
||||
{
|
||||
Descriptor (const std::string &_url, const std::string &_version)
|
||||
: url (_url), version (_version), downloaded (false)
|
||||
Descriptor (const std::string &_name, const std::string &_url, const std::string &_version)
|
||||
: name (_name), url (_url), version (_version), downloaded (false)
|
||||
{ }
|
||||
|
||||
bool operator< (const Descriptor &other) const
|
||||
{
|
||||
if (name != other.name) {
|
||||
return name < other.name;
|
||||
} else {
|
||||
return lay::SaltGrain::compare_versions (version, other.version) < 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool operator== (const Descriptor &other) const
|
||||
{
|
||||
if (name != other.name) {
|
||||
return false;
|
||||
} else {
|
||||
return lay::SaltGrain::compare_versions (version, other.version) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::string name;
|
||||
std::string url;
|
||||
std::string version;
|
||||
bool downloaded;
|
||||
lay::SaltGrain grain;
|
||||
};
|
||||
|
||||
std::map<std::string, Descriptor> m_registry;
|
||||
std::vector<Descriptor> m_registry;
|
||||
|
||||
bool needs_iteration ();
|
||||
void fetch_missing (const lay::Salt &salt_mine, tl::AbsoluteProgress &progress);
|
||||
void fetch_missing (const lay::Salt &salt, const lay::Salt &salt_mine, tl::AbsoluteProgress &progress);
|
||||
lay::ConfirmationDialog *make_confirmation_dialog (QWidget *parent, const lay::Salt &salt);
|
||||
void compute_list (const lay::Salt &salt, const lay::Salt &salt_mine, bool with_dep);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
*/
|
||||
|
||||
#include "laySaltGrain.h"
|
||||
#include "laySaltController.h"
|
||||
#include "tlString.h"
|
||||
#include "tlXMLParser.h"
|
||||
#include "tlHttpStream.h"
|
||||
|
|
@ -259,6 +260,11 @@ SaltGrain::valid_name (const std::string &n)
|
|||
|
||||
tl::Extractor ex (n);
|
||||
|
||||
// a package name must not start with a dot.
|
||||
if (ex.test (".")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string s;
|
||||
if (! ex.try_read_word (s, "_.")) {
|
||||
return false;
|
||||
|
|
@ -269,6 +275,10 @@ SaltGrain::valid_name (const std::string &n)
|
|||
if (! ex.test ("/")) {
|
||||
return false;
|
||||
}
|
||||
// a prefix must not start with a dot.
|
||||
if (ex.test (".")) {
|
||||
return false;
|
||||
}
|
||||
if (! ex.try_read_word (s, "_.")) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -464,6 +474,11 @@ SaltGrain::from_url (const std::string &url)
|
|||
std::auto_ptr<tl::InputStream> stream;
|
||||
std::string spec_url = SaltGrain::spec_url (url);
|
||||
|
||||
// base relative URL's on the salt mine URL
|
||||
if (spec_url.find ("http:") != 0 && spec_url.find ("https:") != 0 && spec_url.find ("file:") != 0 && !spec_url.empty() && spec_url[0] != '/' && spec_url[0] != '\\' && lay::SaltController::instance ()) {
|
||||
spec_url = lay::SaltController::instance ()->salt_mine_url () + "/" + spec_url;
|
||||
}
|
||||
|
||||
if (spec_url.find ("http:") == 0 || spec_url.find ("https:") == 0) {
|
||||
stream.reset (tl::WebDAVObject::download_item (spec_url));
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -511,7 +511,8 @@ BEGIN_PROTECTED
|
|||
--i;
|
||||
QModelIndex index = model->index (i, 0, QModelIndex ());
|
||||
SaltGrain *g = model->grain_from_index (index);
|
||||
if (g && model->is_marked (g->name ())) {
|
||||
// NOTE: checking for valid_name prevents bad entries inside the download list
|
||||
if (g && model->is_marked (g->name ()) && SaltGrain::valid_name (g->name ())) {
|
||||
manager.register_download (g->name (), g->url (), g->version ());
|
||||
any = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
*/
|
||||
|
||||
|
||||
#include "tlSystemPaths.h"
|
||||
#include "laySystemPaths.h"
|
||||
#include "tlString.h"
|
||||
|
||||
#include <QDir>
|
||||
|
|
@ -36,12 +36,24 @@
|
|||
|
||||
#include <cstdlib>
|
||||
|
||||
namespace tl
|
||||
namespace lay
|
||||
{
|
||||
|
||||
std::string
|
||||
get_appdata_path ()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
wchar_t *env = _wgetenv (L"KLAYOUT_HOME");
|
||||
if (env) {
|
||||
return tl::to_string (QString ((const QChar *) env));
|
||||
}
|
||||
#else
|
||||
char *env = getenv ("KLAYOUT_HOME");
|
||||
if (env) {
|
||||
return (tl::system_to_string (env));
|
||||
}
|
||||
#endif
|
||||
|
||||
QDir appdata_dir = QDir::homePath ();
|
||||
QString appdata_folder;
|
||||
#ifdef _WIN32
|
||||
|
|
@ -50,23 +62,7 @@ get_appdata_path ()
|
|||
appdata_folder = QString::fromUtf8 (".klayout");
|
||||
#endif
|
||||
|
||||
// create the basic folder hierarchy
|
||||
if (! appdata_dir.exists (appdata_folder)) {
|
||||
appdata_dir.mkdir (appdata_folder);
|
||||
}
|
||||
|
||||
QString appdata_klayout_path = appdata_dir.absoluteFilePath (appdata_folder);
|
||||
QDir appdata_klayout_dir (appdata_klayout_path);
|
||||
|
||||
const char *folders[] = { "macros", "drc", "libraries", "tech" };
|
||||
for (size_t i = 0; i < sizeof (folders) / sizeof (folders [0]); ++i) {
|
||||
QString folder = QString::fromUtf8 (folders [i]);
|
||||
if (! appdata_klayout_dir.exists (folder)) {
|
||||
appdata_klayout_dir.mkdir (folder);
|
||||
}
|
||||
}
|
||||
|
||||
return tl::to_string (appdata_klayout_path);
|
||||
return tl::to_string (appdata_dir.absoluteFilePath (appdata_folder));
|
||||
}
|
||||
|
||||
static std::string
|
||||
|
|
@ -21,55 +21,61 @@
|
|||
*/
|
||||
|
||||
|
||||
#ifndef HDR_tlSystemPaths
|
||||
#define HDR_tlSystemPaths
|
||||
#ifndef HDR_laySystemPaths
|
||||
#define HDR_laySystemPaths
|
||||
|
||||
#include "tlCommon.h"
|
||||
#include "layCommon.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace tl
|
||||
namespace lay
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Gets the application data path
|
||||
* The application data path is the path where the application stores it's
|
||||
* data for each user.
|
||||
* By default this is HOME/.klayout or HOME/KLayout (Windows). The value
|
||||
* can be overridden by the KLAYOUT_HOME environment variable.
|
||||
*/
|
||||
TL_PUBLIC std::string get_appdata_path ();
|
||||
LAY_PUBLIC std::string get_appdata_path ();
|
||||
|
||||
/**
|
||||
* @brief Gets the installation path
|
||||
* The installation path is the path where the current application is installed.
|
||||
*/
|
||||
TL_PUBLIC std::string get_inst_path ();
|
||||
LAY_PUBLIC std::string get_inst_path ();
|
||||
|
||||
/**
|
||||
* @brief Gets the KLayout path
|
||||
* This is a path (in the sense of a search path - i.e. multiple paths)
|
||||
* where KLayout (and derived applications) looks for configuration files.
|
||||
* The path is created from the appdata and inst path by default. It can
|
||||
* be overridden by the KLAYOUT_PATH enviroment variable.
|
||||
*/
|
||||
TL_PUBLIC std::vector<std::string> get_klayout_path ();
|
||||
LAY_PUBLIC std::vector<std::string> get_klayout_path ();
|
||||
|
||||
/**
|
||||
* @brief Sets the KLayout path
|
||||
* This method is mainly used for test purposes. It will force the application
|
||||
* is use a specific KLAYOUT_PATH. Use reset_klayout_path to restore the
|
||||
* to use a specific path. Use reset_klayout_path to restore the
|
||||
* default behavior.
|
||||
*/
|
||||
TL_PUBLIC void set_klayout_path (const std::vector<std::string> &path);
|
||||
LAY_PUBLIC void set_klayout_path (const std::vector<std::string> &path);
|
||||
|
||||
/**
|
||||
* @brief Resets the KLayout path
|
||||
* See "set_klayout_path" for a description.
|
||||
*/
|
||||
TL_PUBLIC void reset_klayout_path ();
|
||||
LAY_PUBLIC void reset_klayout_path ();
|
||||
|
||||
/**
|
||||
* @brief Gets the package manager URL
|
||||
* The default value can be overridden by the KLAYOUT_SALT_MINE environment
|
||||
* variable.
|
||||
*/
|
||||
TL_PUBLIC std::string salt_mine_url ();
|
||||
LAY_PUBLIC std::string salt_mine_url ();
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -164,10 +164,14 @@ TEST (2)
|
|||
EXPECT_EQ (lay::SaltGrain::valid_version ("1.2x"), false);
|
||||
EXPECT_EQ (lay::SaltGrain::valid_name (""), false);
|
||||
EXPECT_EQ (lay::SaltGrain::valid_name ("x"), true);
|
||||
EXPECT_EQ (lay::SaltGrain::valid_name (".x"), false);
|
||||
EXPECT_EQ (lay::SaltGrain::valid_name (".."), false);
|
||||
EXPECT_EQ (lay::SaltGrain::valid_name ("x1"), true);
|
||||
EXPECT_EQ (lay::SaltGrain::valid_name ("x1 "), false);
|
||||
EXPECT_EQ (lay::SaltGrain::valid_name ("x$1"), false);
|
||||
EXPECT_EQ (lay::SaltGrain::valid_name ("x/y"), true);
|
||||
EXPECT_EQ (lay::SaltGrain::valid_name ("x/.y"), false);
|
||||
EXPECT_EQ (lay::SaltGrain::valid_name ("x/.."), false);
|
||||
EXPECT_EQ (lay::SaltGrain::valid_name ("x_y"), true);
|
||||
EXPECT_EQ (lay::SaltGrain::compare_versions ("", ""), 0);
|
||||
EXPECT_EQ (lay::SaltGrain::compare_versions ("1", "2"), -1);
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@
|
|||
#include "tlLog.h"
|
||||
#include "tlTimer.h"
|
||||
#include "tlExpression.h"
|
||||
#include "tlSystemPaths.h"
|
||||
|
||||
#include "rba.h"
|
||||
#include "rbaInspector.h"
|
||||
|
|
|
|||
128
src/tl/tl/tl.pro
128
src/tl/tl/tl.pro
|
|
@ -9,35 +9,34 @@ DEFINES += MAKE_TL_LIBRARY
|
|||
LIBS += -lz
|
||||
|
||||
FORMS = \
|
||||
PasswordDialog.ui
|
||||
PasswordDialog.ui
|
||||
|
||||
SOURCES = \
|
||||
tlAssert.cc \
|
||||
tlClassRegistry.cc \
|
||||
tlDataMapping.cc \
|
||||
tlDeferredExecution.cc \
|
||||
tlDeflate.cc \
|
||||
tlException.cc \
|
||||
tlExceptions.cc \
|
||||
tlExpression.cc \
|
||||
tlEvents.cc \
|
||||
tlGlobPattern.cc \
|
||||
tlHeap.cc \
|
||||
tlHttpStream.cc \
|
||||
tlInternational.cc \
|
||||
tlLog.cc \
|
||||
tlObject.cc \
|
||||
tlProgress.cc \
|
||||
tlScriptError.cc \
|
||||
tlStaticObjects.cc \
|
||||
tlStream.cc \
|
||||
tlString.cc \
|
||||
tlSystemPaths.cc \
|
||||
tlThreadedWorkers.cc \
|
||||
tlTimer.cc \
|
||||
tlVariant.cc \
|
||||
tlXMLParser.cc \
|
||||
tlXMLWriter.cc \
|
||||
tlAssert.cc \
|
||||
tlClassRegistry.cc \
|
||||
tlDataMapping.cc \
|
||||
tlDeferredExecution.cc \
|
||||
tlDeflate.cc \
|
||||
tlException.cc \
|
||||
tlExceptions.cc \
|
||||
tlExpression.cc \
|
||||
tlEvents.cc \
|
||||
tlGlobPattern.cc \
|
||||
tlHeap.cc \
|
||||
tlHttpStream.cc \
|
||||
tlInternational.cc \
|
||||
tlLog.cc \
|
||||
tlObject.cc \
|
||||
tlProgress.cc \
|
||||
tlScriptError.cc \
|
||||
tlStaticObjects.cc \
|
||||
tlStream.cc \
|
||||
tlString.cc \
|
||||
tlThreadedWorkers.cc \
|
||||
tlTimer.cc \
|
||||
tlVariant.cc \
|
||||
tlXMLParser.cc \
|
||||
tlXMLWriter.cc \
|
||||
tlFileSystemWatcher.cc \
|
||||
tlFileUtils.cc \
|
||||
tlWebDAV.cc \
|
||||
|
|
@ -46,44 +45,43 @@ SOURCES = \
|
|||
tlUnitTest.cc
|
||||
|
||||
HEADERS = \
|
||||
tlAlgorithm.h \
|
||||
tlAssert.h \
|
||||
tlClassRegistry.h \
|
||||
tlDataMapping.h \
|
||||
tlDeferredExecution.h \
|
||||
tlDeflate.h \
|
||||
tlException.h \
|
||||
tlExceptions.h \
|
||||
tlExpression.h \
|
||||
tlEvents.h \
|
||||
tlFixedVector.h \
|
||||
tlGlobPattern.h \
|
||||
tlHeap.h \
|
||||
tlHttpStream.h \
|
||||
tlInternational.h \
|
||||
tlIntervalMap.h \
|
||||
tlIntervalSet.h \
|
||||
tlKDTree.h \
|
||||
tlLog.h \
|
||||
tlObject.h \
|
||||
tlObjectCollection.h \
|
||||
tlProgress.h \
|
||||
tlReuseVector.h \
|
||||
tlScriptError.h \
|
||||
tlStableVector.h \
|
||||
tlStaticObjects.h \
|
||||
tlStream.h \
|
||||
tlString.h \
|
||||
tlSystemPaths.h \
|
||||
tlThreadedWorkers.h \
|
||||
tlTimer.h \
|
||||
tlTypeTraits.h \
|
||||
tlUtils.h \
|
||||
tlVariant.h \
|
||||
tlVariantUserClasses.h \
|
||||
tlVector.h \
|
||||
tlXMLParser.h \
|
||||
tlXMLWriter.h \
|
||||
tlAlgorithm.h \
|
||||
tlAssert.h \
|
||||
tlClassRegistry.h \
|
||||
tlDataMapping.h \
|
||||
tlDeferredExecution.h \
|
||||
tlDeflate.h \
|
||||
tlException.h \
|
||||
tlExceptions.h \
|
||||
tlExpression.h \
|
||||
tlEvents.h \
|
||||
tlFixedVector.h \
|
||||
tlGlobPattern.h \
|
||||
tlHeap.h \
|
||||
tlHttpStream.h \
|
||||
tlInternational.h \
|
||||
tlIntervalMap.h \
|
||||
tlIntervalSet.h \
|
||||
tlKDTree.h \
|
||||
tlLog.h \
|
||||
tlObject.h \
|
||||
tlObjectCollection.h \
|
||||
tlProgress.h \
|
||||
tlReuseVector.h \
|
||||
tlScriptError.h \
|
||||
tlStableVector.h \
|
||||
tlStaticObjects.h \
|
||||
tlStream.h \
|
||||
tlString.h \
|
||||
tlThreadedWorkers.h \
|
||||
tlTimer.h \
|
||||
tlTypeTraits.h \
|
||||
tlUtils.h \
|
||||
tlVariant.h \
|
||||
tlVariantUserClasses.h \
|
||||
tlVector.h \
|
||||
tlXMLParser.h \
|
||||
tlXMLWriter.h \
|
||||
tlFileSystemWatcher.h \
|
||||
tlCommon.h \
|
||||
tlMath.h \
|
||||
|
|
|
|||
|
|
@ -59,14 +59,16 @@ class StreamIODevice
|
|||
public:
|
||||
StreamIODevice (tl::InputStream &stream)
|
||||
: m_stream (stream),
|
||||
mp_progress (0)
|
||||
mp_progress (0),
|
||||
m_has_error (false)
|
||||
{
|
||||
open (QIODevice::ReadOnly);
|
||||
}
|
||||
|
||||
StreamIODevice (tl::InputStream &stream, const std::string &progress_message)
|
||||
: m_stream (stream),
|
||||
mp_progress (new AbsoluteProgress (progress_message, 100))
|
||||
mp_progress (new AbsoluteProgress (progress_message, 100)),
|
||||
m_has_error (false)
|
||||
{
|
||||
mp_progress->set_format (tl::to_string (QObject::tr ("%.0f MB")));
|
||||
mp_progress->set_unit (1024 * 1024);
|
||||
|
|
@ -106,16 +108,49 @@ public:
|
|||
|
||||
return n0 - n;
|
||||
|
||||
} catch (tl::Exception &) {
|
||||
// TODO: is there another way of reporting errors? This reports an "unexpected EOF" when the "Cancel" button is pressed.
|
||||
// However, throwing a simple tl::Exception does not pass through the Qt library.
|
||||
return 0;
|
||||
} catch (tl::Exception &ex) {
|
||||
setErrorString (tl::to_qstring (ex.msg ()));
|
||||
m_has_error = true;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool has_error () const
|
||||
{
|
||||
return m_has_error;
|
||||
}
|
||||
|
||||
private:
|
||||
tl::InputStream &m_stream;
|
||||
tl::AbsoluteProgress *mp_progress;
|
||||
bool m_has_error;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// ErrorAwareQXmlInputSource definition and implementation
|
||||
|
||||
class ErrorAwareQXmlInputSource
|
||||
: public QXmlInputSource
|
||||
{
|
||||
public:
|
||||
ErrorAwareQXmlInputSource (StreamIODevice *dev)
|
||||
: QXmlInputSource (dev), mp_dev (dev)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
virtual void fetchData ()
|
||||
{
|
||||
QXmlInputSource::fetchData ();
|
||||
|
||||
// This feature is actually missing in the original implementation: throw an exception on error
|
||||
if (mp_dev->has_error ()) {
|
||||
throw tl::Exception (tl::to_string (mp_dev->errorString ()));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
StreamIODevice *mp_dev;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
|
@ -124,15 +159,17 @@ private:
|
|||
XMLFileSource::XMLFileSource (const std::string &path, const std::string &progress_message)
|
||||
: mp_source (0), mp_io (0), m_stream (path)
|
||||
{
|
||||
mp_io = new StreamIODevice (m_stream, progress_message);
|
||||
mp_source = new QXmlInputSource (mp_io);
|
||||
StreamIODevice *io = new StreamIODevice (m_stream, progress_message);
|
||||
mp_io = io;
|
||||
mp_source = new ErrorAwareQXmlInputSource (io);
|
||||
}
|
||||
|
||||
XMLFileSource::XMLFileSource (const std::string &path)
|
||||
: mp_source (0), mp_io (0), m_stream (path)
|
||||
{
|
||||
mp_io = new StreamIODevice (m_stream);
|
||||
mp_source = new QXmlInputSource (mp_io);
|
||||
StreamIODevice *io = new StreamIODevice (m_stream);
|
||||
mp_io = io;
|
||||
mp_source = new ErrorAwareQXmlInputSource (io);
|
||||
}
|
||||
|
||||
XMLFileSource::~XMLFileSource ()
|
||||
|
|
@ -148,14 +185,16 @@ XMLFileSource::~XMLFileSource ()
|
|||
|
||||
XMLStreamSource::XMLStreamSource (tl::InputStream &s, const std::string &progress_message)
|
||||
{
|
||||
mp_io = new StreamIODevice (s, progress_message);
|
||||
mp_source = new QXmlInputSource (mp_io);
|
||||
StreamIODevice *io = new StreamIODevice (s, progress_message);
|
||||
mp_io = io;
|
||||
mp_source = new ErrorAwareQXmlInputSource (io);
|
||||
}
|
||||
|
||||
XMLStreamSource::XMLStreamSource (tl::InputStream &s)
|
||||
{
|
||||
mp_io = new StreamIODevice (s);
|
||||
mp_source = new QXmlInputSource (mp_io);
|
||||
StreamIODevice *io = new StreamIODevice (s);
|
||||
mp_io = io;
|
||||
mp_source = new ErrorAwareQXmlInputSource (io);
|
||||
}
|
||||
|
||||
XMLStreamSource::~XMLStreamSource ()
|
||||
|
|
|
|||
|
|
@ -27,9 +27,9 @@
|
|||
#include "tlUnitTest.h"
|
||||
#include "tlStaticObjects.h"
|
||||
#include "tlTimer.h"
|
||||
#include "tlSystemPaths.h"
|
||||
#include "tlCommandLineParser.h"
|
||||
#include "layApplication.h"
|
||||
#include "laySystemPaths.h"
|
||||
#include "rba.h"
|
||||
#include "pya.h"
|
||||
#include "gsiDecl.h"
|
||||
|
|
@ -334,7 +334,7 @@ main_cont (int argc, char **argv)
|
|||
QStringList name_filters;
|
||||
name_filters << QString::fromUtf8 ("*.ut");
|
||||
|
||||
QDir inst_dir (tl::to_qstring (tl::get_inst_path ()));
|
||||
QDir inst_dir (tl::to_qstring (lay::get_inst_path ()));
|
||||
QStringList inst_modules = inst_dir.entryList (name_filters);
|
||||
inst_modules.sort ();
|
||||
|
||||
|
|
@ -366,7 +366,7 @@ main_cont (int argc, char **argv)
|
|||
}
|
||||
|
||||
// No side effects
|
||||
tl::set_klayout_path (std::vector<std::string> ());
|
||||
lay::set_klayout_path (std::vector<std::string> ());
|
||||
|
||||
int ac = 2;
|
||||
static char av0[] = "unit_test";
|
||||
|
|
|
|||
Loading…
Reference in New Issue