WIP: some refactoring, debugging output for netlist compare

Abstraction: a central getenv() feature to wrap all the system-specific things

Netlist compare debug and options can be enabled through environment variables:

KLAYOUT_NETLIST_COMPARE_DEBUG_NETCOMPARE=1: print netlist compare debug info
KLAYOUT_NETLIST_COMPARE_DEBUG_NETGRAPH=1: print net grapg
KLAYOUT_NETLIST_COMPARE_CASE_SENSITIVE=1: make netlist compare case sensitive
This commit is contained in:
Matthias Koefferlein 2020-02-25 00:07:48 +01:00
parent 99f22c3221
commit 58de38739a
12 changed files with 358 additions and 236 deletions

View File

@ -27,20 +27,49 @@
#include "tlTimer.h"
#include "tlEquivalenceClusters.h"
#include "tlLog.h"
#include "tlEnv.h"
#include <cstring>
// verbose debug output
// TODO: make this a feature?
// #define PRINT_DEBUG_NETCOMPARE
namespace {
// verbose net graph output
// TODO: make this a feature?
// #define PRINT_DEBUG_NETGRAPH
struct GlobalCompareOptions
{
GlobalCompareOptions ()
{
m_is_initialized = false;
}
// Add this define for case insensitive compare
// (applies to circuits, device classes)
#define COMPARE_CASE_INSENSITIVE
void ensure_initialized ()
{
if (! m_is_initialized) {
// $KLAYOUT_NETLIST_COMPARE_DEBUG_NETCOMPARE
debug_netcompare = tl::app_flag ("netlist-compare-debug-netcompare");
// $KLAYOUT_NETLIST_COMPARE_DEBUG_NETGRAPH
debug_netgraph = tl::app_flag ("netlist-compare-debug-netgraph");
// $KLAYOUT_NETLIST_COMPARE_CASE_SENSITIVE
compare_case_sensitive = tl::app_flag ("netlist-compare-case-sensitive");
m_is_initialized = true;
}
}
bool debug_netcompare;
bool debug_netgraph;
bool compare_case_sensitive;
private:
bool m_is_initialized;
};
}
static GlobalCompareOptions *options ()
{
// TODO: thread safe?
static GlobalCompareOptions s_options;
s_options.ensure_initialized ();
return &s_options;
}
// A constant indicating a failed match
const size_t failed_match = std::numeric_limits<size_t>::max ();
@ -63,28 +92,25 @@ namespace db
static int name_compare (const std::string &n1, const std::string &n2)
{
// TODO: unicode support?
#if defined(COMPARE_CASE_INSENSITIVE)
if (options ()->compare_case_sensitive) {
return strcmp (n1.c_str (), n2.c_str ());
} else {
#if defined(_WIN32)
return _stricmp (n1.c_str (), n2.c_str ());
return _stricmp (n1.c_str (), n2.c_str ());
#else
return strcasecmp (n1.c_str (), n2.c_str ());
#endif
#else
return strcmp (n1.c_str (), n2.c_str ());
return strcasecmp (n1.c_str (), n2.c_str ());
#endif
}
}
#if defined(COMPARE_CASE_INSENSITIVE)
static std::string normalize_name (const std::string &n)
static inline std::string normalize_name (const std::string &n)
{
return tl::to_upper_case (n);
if (options ()->compare_case_sensitive) {
return n;
} else {
return tl::to_upper_case (n);
}
}
#else
static inline const std::string &normalize_name (const std::string &n)
{
return n;
}
#endif
// --------------------------------------------------------------------------------------------------------------------
// DeviceCompare definition and implementation
@ -1515,11 +1541,12 @@ NetGraph::build (const db::Circuit *c, DeviceCategorizer &device_categorizer, Ci
for (std::vector<NetGraphNode>::iterator i = m_nodes.begin (); i != m_nodes.end (); ++i) {
i->apply_net_index (m_net_index);
}
#if defined(PRINT_DEBUG_NETGRAPH)
for (std::vector<NetGraphNode>::iterator i = m_nodes.begin (); i != m_nodes.end (); ++i) {
tl::info << i->to_string () << tl::noendl;
if (options ()->debug_netgraph) {
for (std::vector<NetGraphNode>::iterator i = m_nodes.begin (); i != m_nodes.end (); ++i) {
tl::info << i->to_string () << tl::noendl;
}
}
#endif
// create subcircuit virtual nodes
@ -1543,11 +1570,12 @@ NetGraph::build (const db::Circuit *c, DeviceCategorizer &device_categorizer, Ci
for (std::map<const db::SubCircuit *, NetGraphNode>::iterator i = m_virtual_nodes.begin (); i != m_virtual_nodes.end (); ++i) {
i->second.apply_net_index (m_net_index);
}
#if defined(PRINT_DEBUG_NETGRAPH)
for (std::map<const db::SubCircuit *, NetGraphNode>::iterator i = m_virtual_nodes.begin (); i != m_virtual_nodes.end (); ++i) {
tl::info << i->second.to_string () << tl::noendl;
if (options ()->debug_netgraph) {
for (std::map<const db::SubCircuit *, NetGraphNode>::iterator i = m_virtual_nodes.begin (); i != m_virtual_nodes.end (); ++i) {
tl::info << i->second.to_string () << tl::noendl;
}
}
#endif
}
size_t
@ -1560,34 +1588,48 @@ NetGraph::derive_node_identities_for_edges (NetGraphNode::edge_iterator e, NetGr
std::vector<const NetGraphNode *> other_nodes;
other_nodes.reserve (ee - e);
#if defined(PRINT_DEBUG_NETCOMPARE)
tl::info << indent(depth) << "consider transitions:";
#endif
if (options ()->debug_netcompare) {
tl::info << indent(depth) << "considering transitions:";
}
bool first = true;
for (NetGraphNode::edge_iterator i = e; i != ee; ++i) {
if (i->second.first != net_index) {
const NetGraphNode *nn = &node (i->second.first);
#if defined(PRINT_DEBUG_NETCOMPARE)
tl::info << indent(depth) << "here: " << (nn->net() ? nn->net()->expanded_name().c_str() : "(null)") << " via";
for (std::vector<NetGraphNode::Transition>::const_iterator t = i->first.begin (); t != i->first.end(); ++t) {
tl::info << indent(depth) << " " << t->to_string();
if (options ()->debug_netcompare) {
if (first) {
tl::info << indent (depth) << " here: " << (node (net_index).net () ? node (net_index).net ()->expanded_name ().c_str () : "(null)") << " ->";
first = false;
}
tl::info << indent (depth) << " " << (nn->net () ? nn->net ()->expanded_name ().c_str() : "(null)") << " via: " << tl::noendl;
for (std::vector<NetGraphNode::Transition>::const_iterator t = i->first.begin (); t != i->first.end(); ++t) {
tl::info << (t != i->first.begin () ? "; " : "") << t->to_string() << tl::noendl;
}
tl::info << "";
}
#endif
nodes.push_back (nn);
}
}
if (! nodes.empty ()) { // if non-ambiguous, non-assigned
first = true;
for (NetGraphNode::edge_iterator i = e_other; i != ee_other; ++i) {
if (i->second.first != other_net_index) {
const NetGraphNode *nn = &data->other->node (i->second.first);
#if defined(PRINT_DEBUG_NETCOMPARE)
tl::info << indent(depth) << "there: " << (nn->net() ? nn->net()->expanded_name().c_str() : "(null)") << " via";
for (std::vector<NetGraphNode::Transition>::const_iterator t = i->first.begin (); t != i->first.end(); ++t) {
tl::info << indent(depth) << " " << t->to_string();
if (options ()->debug_netcompare) {
if (first) {
tl::info << indent (depth) << " there: " << (data->other->node (other_net_index).net () ? data->other->node (other_net_index).net ()->expanded_name ().c_str () : "(null)") << " ->";
first = false;
}
tl::info << indent(depth) << " " << (nn->net() ? nn->net()->expanded_name().c_str() : "(null)") << " via: " << tl::noendl;
for (std::vector<NetGraphNode::Transition>::const_iterator t = i->first.begin (); t != i->first.end(); ++t) {
tl::info << (t != i->first.begin () ? "; " : "") << t->to_string() << tl::noendl;
}
tl::info << "";
}
#endif
other_nodes.push_back (nn);
}
}
@ -1604,9 +1646,9 @@ NetGraph::derive_node_identities_for_edges (NetGraphNode::edge_iterator e, NetGr
if (tentative) {
if (nodes.size () != other_nodes.size ()) {
#if defined(PRINT_DEBUG_NETCOMPARE)
tl::info << indent(depth) << "! rejected branch.";
#endif
if (options ()->debug_netcompare) {
tl::info << indent(depth) << "=> rejected branch.";
}
return failed_match;
}
@ -1614,9 +1656,9 @@ NetGraph::derive_node_identities_for_edges (NetGraphNode::edge_iterator e, NetGr
if (nodes.size () > 1 || other_nodes.size () > 1) {
for (size_t i = 0; i < nodes.size (); ++i) {
if (! (*nodes[i] == *other_nodes[i])) {
#if defined(PRINT_DEBUG_NETCOMPARE)
tl::info << indent(depth) << "! rejected branch.";
#endif
if (options ()->debug_netcompare) {
tl::info << indent(depth) << "=> rejected branch.";
}
return failed_match;
}
}
@ -1631,9 +1673,9 @@ NetGraph::derive_node_identities_for_edges (NetGraphNode::edge_iterator e, NetGr
if (bt_count == failed_match) {
if (tentative) {
#if defined(PRINT_DEBUG_NETCOMPARE)
tl::info << indent(depth) << "! rejected branch.";
#endif
if (options ()->debug_netcompare) {
tl::info << indent(depth) << "=> rejected branch.";
}
return bt_count;
}
} else {
@ -1642,11 +1684,11 @@ NetGraph::derive_node_identities_for_edges (NetGraphNode::edge_iterator e, NetGr
}
#if defined(PRINT_DEBUG_NETCOMPARE)
if (! new_nodes) {
tl::info << indent(depth) << "! no updates.";
if (options ()->debug_netcompare) {
if (! new_nodes) {
tl::info << indent(depth) << "=> no updates.";
}
}
#endif
return new_nodes;
}
@ -1671,13 +1713,13 @@ NetGraph::derive_node_identities (size_t net_index, size_t depth, size_t n_branc
size_t other_net_index = n->other_net_index ();
NetGraphNode *n_other = & data->other->node (other_net_index);
#if defined(PRINT_DEBUG_NETCOMPARE)
if (! tentative) {
tl::info << indent(depth) << "deducing from pair: " << n->net ()->expanded_name () << " vs. " << n_other->net ()->expanded_name ();
} else {
tl::info << indent(depth) << "tentatively deducing from pair: " << n->net ()->expanded_name () << " vs. " << n_other->net ()->expanded_name ();
if (options ()->debug_netcompare) {
if (! tentative) {
tl::info << indent(depth) << "deducing from pair: " << n->net ()->expanded_name () << " vs. " << n_other->net ()->expanded_name ();
} else {
tl::info << indent(depth) << "tentatively deducing from pair: " << n->net ()->expanded_name () << " vs. " << n_other->net ()->expanded_name ();
}
}
#endif
size_t new_nodes = 0;
@ -1722,9 +1764,9 @@ NetGraph::derive_node_identities (size_t net_index, size_t depth, size_t n_branc
size_t bt_count = derive_node_identities_for_edges (e, ee, e_other, ee_other, net_index, other_net_index, depth, n_branch, tentative, with_ambiguous, data);
if (bt_count == failed_match) {
#if defined(PRINT_DEBUG_NETCOMPARE)
tl::info << indent(depth) << "! rejected pair.";
#endif
if (options ()->debug_netcompare) {
tl::info << indent(depth) << "=> rejected pair.";
}
return bt_count;
} else {
new_nodes += bt_count;
@ -1733,9 +1775,9 @@ NetGraph::derive_node_identities (size_t net_index, size_t depth, size_t n_branc
} else if (tentative) {
// in tentative mode an exact match is required: no having the same edges for a node disqualifies the node
// as matching.
#if defined(PRINT_DEBUG_NETCOMPARE)
tl::info << indent(depth) << "! rejected pair for missing edge.";
#endif
if (options ()->debug_netcompare) {
tl::info << indent(depth) << "=> rejected pair for missing edge.";
}
return failed_match;
}
@ -1758,9 +1800,9 @@ NetGraph::derive_node_identities (size_t net_index, size_t depth, size_t n_branc
NetGraphNode::edge_iterator e = n->find_edge (e_other->first);
if (e == n->end ()) {
#if defined(PRINT_DEBUG_NETCOMPARE)
tl::info << indent(depth) << "! rejected pair for missing edge.";
#endif
if (options ()->debug_netcompare) {
tl::info << indent(depth) << "=> rejected pair for missing edge.";
}
return failed_match;
}
@ -1770,11 +1812,11 @@ NetGraph::derive_node_identities (size_t net_index, size_t depth, size_t n_branc
}
#if defined(PRINT_DEBUG_NETCOMPARE)
if (! tentative && new_nodes > 0) {
tl::info << indent(depth) << "finished pair deduction: " << n->net ()->expanded_name () << " vs. " << n_other->net ()->expanded_name () << " with " << new_nodes << " new pairs";
if (options ()->debug_netcompare) {
if (! tentative && new_nodes > 0) {
tl::info << indent(depth) << "=> finished pair deduction: " << n->net ()->expanded_name () << " vs. " << n_other->net ()->expanded_name () << " with " << new_nodes << " new pairs";
}
}
#endif
return new_nodes;
}
@ -1855,17 +1897,18 @@ static bool net_names_are_different (const db::Net *a, const db::Net *b)
size_t
NetGraph::derive_node_identities_from_node_set (std::vector<const NetGraphNode *> &nodes, std::vector<const NetGraphNode *> &other_nodes, size_t depth, size_t n_branch, TentativeNodeMapping *tentative, bool with_ambiguous, CompareData *data)
{
#if defined(PRINT_DEBUG_NETCOMPARE)
std::string indent_s = indent (depth);
indent_s += "*" + tl::to_string (n_branch) + " ";
#endif
std::string indent_s;
if (options ()->debug_netcompare) {
indent_s = indent (depth);
indent_s += "*" + tl::to_string (n_branch) + " ";
}
size_t new_nodes = 0;
if (depth > data->max_depth) {
#if defined(PRINT_DEBUG_NETCOMPARE)
tl::info << indent_s << "max. depth exhausted (" << depth + 1 << ">" << data->max_depth << ")";
#endif
if (options ()->debug_netcompare) {
tl::info << indent_s << "max. depth exhausted (" << depth + 1 << ">" << data->max_depth << ")";
}
return failed_match;
}
@ -1881,9 +1924,9 @@ NetGraph::derive_node_identities_from_node_set (std::vector<const NetGraphNode *
TentativeNodeMapping::map_pair (tentative, this, ni, data->other, other_ni);
#if defined(PRINT_DEBUG_NETCOMPARE)
tl::info << indent_s << "deduced match (singular): " << nodes.front ()->net ()->expanded_name () << " vs. " << other_nodes.front ()->net ()->expanded_name ();
#endif
if (options ()->debug_netcompare) {
tl::info << indent_s << "deduced match (singular): " << nodes.front ()->net ()->expanded_name () << " vs. " << other_nodes.front ()->net ()->expanded_name ();
}
if (data->logger && ! tentative) {
if (! (node (ni) == data->other->node (other_ni))) {
// this is a mismatch but we continue, because that is the only candidate
@ -2030,9 +2073,9 @@ NetGraph::derive_node_identities_from_node_set (std::vector<const NetGraphNode *
// their names differ -> this favors net matching by name
if (tentative && ! data->dont_consider_net_names && net_names_are_different ((*nr->n1)->net (), (*nr->n2)->net ())) {
#if defined(PRINT_DEBUG_NETCOMPARE)
tl::info << indent_s << "rejecting pair as names are not identical: " << (*nr->n1)->net ()->expanded_name () << " vs. " << (*nr->n2)->net ()->expanded_name ();
#endif
if (options ()->debug_netcompare) {
tl::info << indent_s << "rejecting pair as names are not identical: " << (*nr->n1)->net ()->expanded_name () << " vs. " << (*nr->n2)->net ()->expanded_name ();
}
return failed_match;
}
@ -2044,9 +2087,9 @@ NetGraph::derive_node_identities_from_node_set (std::vector<const NetGraphNode *
TentativeNodeMapping::map_pair (tentative, this, ni, data->other, other_ni);
#if defined(PRINT_DEBUG_NETCOMPARE)
tl::info << indent_s << "deduced match (singular): " << (*nr->n1)->net ()->expanded_name () << " vs. " << (*nr->n2)->net ()->expanded_name ();
#endif
if (options ()->debug_netcompare) {
tl::info << indent_s << "deduced match (singular): " << (*nr->n1)->net ()->expanded_name () << " vs. " << (*nr->n2)->net ()->expanded_name ();
}
if (data->logger && ! tentative) {
if (! (node (ni) == data->other->node (other_ni))) {
// this is a mismatch, but we continue with this
@ -2086,16 +2129,16 @@ NetGraph::derive_node_identities_from_node_set (std::vector<const NetGraphNode *
} else if (nr->num * n_branch > data->max_n_branch) {
#if defined(PRINT_DEBUG_NETCOMPARE)
tl::info << indent_s << "max. complexity exhausted (" << nr->num << "*" << n_branch << ">" << data->max_n_branch << ") - mismatch.";
#endif
if (options ()->debug_netcompare) {
tl::info << indent_s << "max. complexity exhausted (" << nr->num << "*" << n_branch << ">" << data->max_n_branch << ") - mismatch.";
}
return failed_match;
} else {
#if defined(PRINT_DEBUG_NETCOMPARE)
tl::info << indent_s << "analyzing ambiguity group with " << nr->num << " members";
#endif
if (options ()->debug_netcompare) {
tl::info << indent_s << "analyzing ambiguity group with " << nr->num << " members";
}
// sort the ambiguity group such that net names match best
@ -2152,16 +2195,16 @@ NetGraph::derive_node_identities_from_node_set (std::vector<const NetGraphNode *
TentativeNodeMapping::map_pair_from_unknown (&tn, this, ni, data->other, other_ni);
// try this candidate in tentative mode
#if defined(PRINT_DEBUG_NETCOMPARE)
tl::info << indent_s << "trying in tentative mode: " << (*i1)->net ()->expanded_name () << " vs. " << (*i2)->net ()->expanded_name ();
#endif
if (options ()->debug_netcompare) {
tl::info << indent_s << "trying in tentative mode: " << (*i1)->net ()->expanded_name () << " vs. " << (*i2)->net ()->expanded_name ();
}
size_t bt_count = derive_node_identities (ni, depth + 1, nr->num * n_branch, &tn, with_ambiguous, data);
if (bt_count != failed_match) {
#if defined(PRINT_DEBUG_NETCOMPARE)
tl::info << indent_s << "match found";
#endif
if (options ()->debug_netcompare) {
tl::info << indent_s << "match found";
}
// we have a match ...
if (any) {
@ -2185,9 +2228,9 @@ NetGraph::derive_node_identities_from_node_set (std::vector<const NetGraphNode *
}
if (! any && tentative) {
#if defined(PRINT_DEBUG_NETCOMPARE)
tl::info << indent_s << "mismatch.";
#endif
if (options ()->debug_netcompare) {
tl::info << indent_s << "mismatch.";
}
// a mismatch - stop here.
return failed_match;
}
@ -2207,13 +2250,13 @@ NetGraph::derive_node_identities_from_node_set (std::vector<const NetGraphNode *
TentativeNodeMapping::map_pair (tentative, this, ni, data->other, other_ni);
#if defined(PRINT_DEBUG_NETCOMPARE)
if (equivalent_other_nodes.has_attribute (p->second)) {
tl::info << indent_s << "deduced ambiguous match: " << p->first->net ()->expanded_name () << " vs. " << p->second->net ()->expanded_name ();
} else {
tl::info << indent_s << "deduced match: " << p->first->net ()->expanded_name () << " vs. " << p->second->net ()->expanded_name ();
if (options ()->debug_netcompare) {
if (equivalent_other_nodes.has_attribute (p->second)) {
tl::info << indent_s << "deduced ambiguous match: " << p->first->net ()->expanded_name () << " vs. " << p->second->net ()->expanded_name ();
} else {
tl::info << indent_s << "deduced match: " << p->first->net ()->expanded_name () << " vs. " << p->second->net ()->expanded_name ();
}
}
#endif
if (data->logger) {
bool ambiguous = equivalent_other_nodes.has_attribute (p->second);
if (ambiguous) {
@ -2249,9 +2292,9 @@ NetGraph::derive_node_identities_from_node_set (std::vector<const NetGraphNode *
}
#if defined(PRINT_DEBUG_NETCOMPARE)
tl::info << indent_s << "finished analysis of ambiguity group with " << nr->num << " members";
#endif
if (options ()->debug_netcompare) {
tl::info << indent_s << "finished analysis of ambiguity group with " << nr->num << " members";
}
}
@ -2516,9 +2559,9 @@ NetlistComparer::compare (const db::Netlist *a, const db::Netlist *b) const
if (all_subcircuits_verified (ca, verified_circuits_a) && all_subcircuits_verified (cb, verified_circuits_b)) {
#if defined(PRINT_DEBUG_NETCOMPARE)
tl::info << "treating circuit: " << ca->name () << " vs. " << cb->name ();
#endif
if (options ()->debug_netcompare) {
tl::info << "treating circuit: " << ca->name () << " vs. " << cb->name ();
}
if (mp_logger) {
mp_logger->begin_circuit (ca, cb);
}
@ -2835,9 +2878,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
g2.identify (ni2, ni1);
}
#if defined(PRINT_DEBUG_NETCOMPARE)
int iter = 0;
#endif
// two passes: one without ambiguities, the second one with
@ -2845,20 +2886,20 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
for (int pass = 0; pass < 2; ++pass) {
#if defined(PRINT_DEBUG_NETCOMPARE)
if (pass > 0) {
tl::info << "including ambiguous nodes now.";
if (options ()->debug_netcompare) {
if (pass > 0) {
tl::info << "including ambiguous nodes now.";
}
}
#endif
good = true;
while (true) {
#if defined(PRINT_DEBUG_NETCOMPARE)
++iter;
tl::info << "new compare iteration #" << iter;
tl::info << "deducing from present nodes ...";
#endif
if (options ()->debug_netcompare) {
tl::info << "new compare iteration #" << iter;
tl::info << "deducing from present nodes ...";
}
size_t new_identities = 0;
@ -2877,18 +2918,18 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
size_t ni = g1.derive_node_identities (i1 - g1.begin (), 0, 1, 0 /*not tentative*/, pass > 0 /*with ambiguities*/, &data);
if (ni > 0 && ni != failed_match) {
new_identities += ni;
#if defined(PRINT_DEBUG_NETCOMPARE)
tl::info << ni << " new identities.";
#endif
if (options ()->debug_netcompare) {
tl::info << ni << " new identities.";
}
}
}
}
#if defined(PRINT_DEBUG_NETCOMPARE)
tl::info << "checking topological identity ...";
#endif
if (options ()->debug_netcompare) {
tl::info << "checking topological identity ...";
}
// derive new identities through topology: first collect all nets with the same topological signature
@ -2934,9 +2975,9 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
size_t ni = g1.derive_node_identities_from_node_set (nodes, other_nodes, 0, 1, 0 /*not tentative*/, pass > 0 /*with ambiguities*/, &data);
if (ni > 0 && ni != failed_match) {
new_identities += ni;
#if defined(PRINT_DEBUG_NETCOMPARE)
tl::info << ni << " new identities.";
#endif
if (options ()->debug_netcompare) {
tl::info << ni << " new identities.";
}
}
if (new_identities == 0) {

View File

@ -1283,14 +1283,6 @@ ApplicationBase::get_config_names () const
return names;
}
bool
ApplicationBase::special_app_flag (const std::string &name)
{
// TODO: some more elaborate scheme?
const char *env = getenv (("KLAYOUT_" + name).c_str ());
return (env && *env);
}
// --------------------------------------------------------------------------------
// GuiApplication implementation

View File

@ -198,16 +198,6 @@ public:
*/
std::vector<std::string> get_config_names () const;
/**
* @brief Gets a value indicating whether the give special application flag is set
*
* Special application flags are ways to introduce debug or flags for special
* use cases. Such flags have a name and currently are controlled externally by
* an environment variable called "KLAYOUT_x" where x is the name. If that
* variable is set and the value is non-empty, the flag is regarded set.
*/
bool special_app_flag (const std::string &name);
/**
* @brief Return a reference to the Ruby interpreter
*/

View File

@ -24,6 +24,7 @@
#include "laySystemPaths.h"
#include "tlFileUtils.h"
#include "tlString.h"
#include "tlEnv.h"
#include <QDir>
#include <QFileInfo>
@ -31,32 +32,17 @@
#ifdef _WIN32
# include <windows.h>
#elif __APPLE__
# include <libproc.h>
# include <unistd.h>
#else
# include <unistd.h>
#endif
#include <cstdlib>
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));
if (tl::has_env ("KLAYOUT_HOME")) {
return tl::get_env ("KLAYOUT_HOME");
}
#else
char *env = getenv ("KLAYOUT_HOME");
if (env) {
return (tl::system_to_string (env));
}
#endif
QDir appdata_dir = QDir::homePath ();
QString appdata_folder;
@ -138,23 +124,14 @@ get_klayout_path ()
// generate the klayout path: the first component is always the appdata path
klayout_path.push_back (get_appdata_path ());
#ifdef _WIN32
wchar_t *env = _wgetenv (L"KLAYOUT_PATH");
if (env) {
split_path (tl::to_string (QString ((const QChar *) env)), klayout_path);
std::string env = tl::get_env ("KLAYOUT_PATH");
if (! env.empty ()) {
split_path (env, klayout_path);
} else {
get_other_system_paths (klayout_path);
klayout_path.push_back (tl::get_inst_path ());
}
#else
char *env = getenv ("KLAYOUT_PATH");
if (env) {
split_path (tl::system_to_string (env), klayout_path);
} else {
get_other_system_paths (klayout_path);
klayout_path.push_back (tl::get_inst_path ());
}
#endif
return klayout_path;
@ -164,23 +141,7 @@ get_klayout_path ()
std::string
salt_mine_url ()
{
const std::string default_url ("http://sami.klayout.org/repository.xml");
#ifdef _WIN32
wchar_t *env = _wgetenv (L"KLAYOUT_SALT_MINE");
if (env) {
return tl::to_string (QString ((const QChar *) env));
} else {
return default_url;
}
#else
char *env = getenv ("KLAYOUT_SALT_MINE");
if (env) {
return (tl::system_to_string (env));
} else {
return default_url;
}
#endif
return tl::get_env ("KLAYOUT_SALT_MINE", "http://sami.klayout.org/repository.xml");
}
}

View File

@ -35,6 +35,7 @@
#include "tlTimer.h"
#include "tlProgress.h"
#include "tlThreadedWorkers.h"
#include "tlEnv.h"
#include "tlExceptions.h"
#include "tlMath.h"
#include "layCellView.h"
@ -1085,7 +1086,7 @@ XORToolDialog::run_xor ()
bool summarize = mp_ui->summarize_cb->isChecked ();
// TODO: make this a user interface feature later
bool process_el = lay::ApplicationBase::instance ()->special_app_flag ("ALWAYS_DO_XOR");
bool process_el = tl::app_flag ("always-do-xor");
int cv_index_a = mp_ui->layouta->current_cv_index ();
int cv_index_b = mp_ui->layoutb->current_cv_index ();

View File

@ -46,7 +46,8 @@ SOURCES = \
tlList.cc \
tlEquivalenceClusters.cc \
tlUniqueName.cc \
tlRecipe.cc
tlRecipe.cc \
tlEnv.cc
HEADERS = \
tlAlgorithm.h \
@ -103,7 +104,8 @@ HEADERS = \
tlEquivalenceClusters.h \
tlUniqueName.h \
tlRecipe.h \
tlSelect.h
tlSelect.h \
tlEnv.h
equals(HAVE_CURL, "1") {

85
src/tl/tl/tlEnv.cc Normal file
View File

@ -0,0 +1,85 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 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 "tlEnv.h"
#include "tlString.h"
#include <string>
#ifdef _WIN32
# include <windows.h>
#elif __APPLE__
# include <libproc.h>
# include <unistd.h>
#else
# include <unistd.h>
#endif
namespace tl
{
std::string get_env (const std::string &name, const std::string &def_value)
{
#ifdef _WIN32
std::wstring wname = tl::to_wstring (name);
wchar_t *env = _wgetenv (wname.c_str ());
if (env) {
return tl::to_string (QString ((const QChar *) env));
} else {
return def_value;
}
#else
char *env = getenv (name.c_str ());
if (env) {
return tl::system_to_string (env);
} else {
return def_value;
}
#endif
}
bool has_env (const std::string &name)
{
#ifdef _WIN32
std::wstring wname = tl::to_wstring (name);
wchar_t *env = _wgetenv (wname.c_str ());
return env != 0;
#else
char *env = getenv (name.c_str ());
return env != 0;
#endif
}
bool app_flag (const std::string &name)
{
std::string env_name = std::string ("KLAYOUT_") + tl::replaced (tl::to_upper_case (name), "-", "_");
int v = 0;
std::string vs = get_env (env_name);
tl::Extractor ex (vs.c_str ());
return ex.try_read (v) && v != 0;
}
} // namespace tl

65
src/tl/tl/tlEnv.h Normal file
View File

@ -0,0 +1,65 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 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_tlAppFlags
#define HDR_tlAppFlags
#include "tlCommon.h"
#include <string>
namespace tl
{
/**
* @brief Gets the value for the given environment variable
*
* If the environment variable is not set, the default value will be returned.
*/
std::string TL_PUBLIC get_env (const std::string &name, const std::string &def_value = std::string ());
/**
* @brief Gets the value if the given environment variable is set
*/
bool TL_PUBLIC has_env (const std::string &name);
/**
* @brief Gets an application flag with the given name
*
* This feature is supposed to deliver special flags for debugging etc.
* By using a central access point for these settings, it will be easy to provide
* different implementations later.
*
* Currently, application flags are derived by checking whether a corresponding
* environment variable exists. Names like "a-b" are translated into "KLAYOUT_A_B"
* for the environment variable.
*
* If the corresponding variable exists and does not have a value of "0", true
* is returned from this function.
*/
bool TL_PUBLIC app_flag (const std::string &name);
}
#endif

View File

@ -26,6 +26,7 @@
#include "tlString.h"
#include "tlGlobPattern.h"
#include "tlFileUtils.h"
#include "tlEnv.h"
#include <map>
#include <vector>
@ -35,7 +36,6 @@
#define _USE_MATH_DEFINES // for MSVC
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
// Suggestions for further functions:
@ -2800,9 +2800,9 @@ env_f (const ExpressionParserContext &context, tl::Variant &out, const std::vect
throw EvalError (tl::to_string (tr ("'env' function expects exactly two arguments")), context);
}
const char *env = getenv (vv [0].to_string ());
if (env) {
out = env;
const char *vn = vv [0].to_string ();
if (tl::has_env (vn)) {
out = tl::get_env (vn);
} else {
out = tl::Variant ();
}

View File

@ -23,16 +23,12 @@
#include "tlLog.h"
#include "tlString.h"
#include "tlEnv.h"
#include <stdio.h>
#if !defined(_MSC_VER)
# include <unistd.h>
#endif
#include <stdlib.h>
#if defined(_WIN32)
# include <windows.h>
#endif
namespace tl
{
@ -42,21 +38,9 @@ namespace tl
static int default_verbosity ()
{
const char *verbosity_str = 0;
#if defined(_WIN32)
const wchar_t *verbosity_wstr = _wgetenv (L"KLAYOUT_VERBOSITY");
std::string vs;
if (verbosity_wstr) {
vs = tl::to_string (std::wstring (verbosity_wstr));
verbosity_str = vs.c_str ();
}
#else
verbosity_str = getenv ("KLAYOUT_VERBOSITY");
#endif
int verbosity = 0;
if (verbosity_str) {
std::string verbosity_str = tl::get_env ("KLAYOUT_VERBOSITY");
if (! verbosity_str.empty ()) {
try {
tl::from_string (verbosity_str, verbosity);
} catch (...) {

View File

@ -24,6 +24,7 @@
#include "tlFileUtils.h"
#include "tlTimer.h"
#include "tlStream.h"
#include "tlEnv.h"
#include <cmath>
@ -85,8 +86,8 @@ void set_debug_mode (bool f)
std::string testsrc ()
{
const char *ts = getenv ("TESTSRC");
if (! ts) {
std::string ts = tl::get_env ("TESTSRC");
if (ts.empty ()) {
throw tl::Exception ("TESTSRC undefined");
}
return ts;
@ -104,8 +105,8 @@ std::string testsrc_private ()
std::string testtmp ()
{
// Ensures the test temp directory is present
const char *tt = getenv ("TESTTMP");
if (! tt) {
std::string tt = tl::get_env ("TESTTMP");
if (tt.empty ()) {
throw tl::Exception ("TESTTMP undefined");
}
return tt;

View File

@ -24,8 +24,8 @@
#include "tlExpression.h"
#include "tlVariantUserClasses.h"
#include "tlUnitTest.h"
#include "tlEnv.h"
#include <stdlib.h>
#define _USE_MATH_DEFINES // for MSVC
#include <math.h>
@ -834,7 +834,7 @@ TEST(6)
v = e.parse ("env('HJASK')").execute ();
EXPECT_EQ (v.to_string (), std::string ("nil"));
v = e.parse ("env('PATH')").execute ();
EXPECT_EQ (v.to_string (), std::string (getenv ("PATH")));
EXPECT_EQ (v.to_string (), tl::get_env ("PATH"));
v = e.parse ("absolute_path('./x.gds')").execute ();
// EXPECT_EQ (v.to_string (), std::string ()); // not universal
v = e.parse ("absolute_file_path('./x.gds')").execute ();