Equipped strmclip with the new command line parser

Plus: added repeated arguments (in addition to the array
arguments that have been there before).
This commit is contained in:
Matthias Koefferlein 2017-08-19 00:36:45 +02:00
parent e54cadef99
commit 681c255e50
8 changed files with 205 additions and 147 deletions

View File

@ -229,20 +229,25 @@ void GenericReaderOptions::set_dbu (double dbu)
}
void
GenericReaderOptions::configure (db::LoadLayoutOptions &load_options)
GenericReaderOptions::configure (db::LoadLayoutOptions &load_options) const
{
m_common_reader_options.layer_map = m_layer_map;
m_common_reader_options.create_other_layers = m_create_other_layers;
m_dxf_reader_options.layer_map = m_layer_map;
m_dxf_reader_options.create_other_layers = m_create_other_layers;
m_cif_reader_options.layer_map = m_layer_map;
m_cif_reader_options.create_other_layers = m_create_other_layers;
db::CommonReaderOptions common_reader_options = m_common_reader_options;
common_reader_options.layer_map = m_layer_map;
common_reader_options.create_other_layers = m_create_other_layers;
load_options.set_options (m_common_reader_options);
db::DXFReaderOptions dxf_reader_options = m_dxf_reader_options;
dxf_reader_options.layer_map = m_layer_map;
dxf_reader_options.create_other_layers = m_create_other_layers;
db::CIFReaderOptions cif_reader_options = m_cif_reader_options;
cif_reader_options.layer_map = m_layer_map;
cif_reader_options.create_other_layers = m_create_other_layers;
load_options.set_options (common_reader_options);
load_options.set_options (m_gds2_reader_options);
load_options.set_options (m_oasis_reader_options);
load_options.set_options (m_cif_reader_options);
load_options.set_options (m_dxf_reader_options);
load_options.set_options (cif_reader_options);
load_options.set_options (dxf_reader_options);
}
}

View File

@ -65,7 +65,7 @@ public:
/**
* @brief Configures the reader options object with the options stored in this object
*/
void configure (db::LoadLayoutOptions &load_options);
void configure (db::LoadLayoutOptions &load_options) const;
/**
* @brief Sets the option prefix for the short option name

View File

@ -53,7 +53,7 @@ GenericWriterOptions::add_options (tl::CommandLineOptions &cmd, const std::strin
"given factor."
);
if (format == gds2_format_name || format == gds2text_format_name || format == oasis_format_name) {
if (format.empty () || format == gds2_format_name || format == gds2text_format_name || format == oasis_format_name) {
cmd << tl::arg (group +
"-od|--dbu-out=dbu", &m_dbu, "Uses the specified database unit",
"Specifies the database unit to save the layout in. The database unit is given "
@ -68,7 +68,7 @@ GenericWriterOptions::add_options (tl::CommandLineOptions &cmd, const std::strin
"If given, empty cells won't be written. See --keep-instances for more options."
);
if (format == gds2_format_name || format == gds2text_format_name) {
if (format.empty () || format == gds2_format_name || format == gds2text_format_name) {
cmd << tl::arg (group +
"#--keep-instances", &m_keep_instances, "Keeps instances of dropped cells",
"If given, instances of dropped cell's won't be removed. Hence, ghost cells are "
@ -79,7 +79,7 @@ GenericWriterOptions::add_options (tl::CommandLineOptions &cmd, const std::strin
);
}
if (format == gds2_format_name || format == gds2text_format_name || format == oasis_format_name) {
if (format.empty () || format == gds2_format_name || format == gds2text_format_name || format == oasis_format_name) {
cmd << tl::arg (group +
"#--write-context-info", &m_write_context_info, "Writes context information",
"Include context information for PCell instances and other information in a format-specific "
@ -289,7 +289,7 @@ static void get_selected_cells (tl::Extractor &ex, const db::Layout &layout, std
}
void
GenericWriterOptions::configure (db::SaveLayoutOptions &save_options, const db::Layout &layout)
GenericWriterOptions::configure (db::SaveLayoutOptions &save_options, const db::Layout &layout) const
{
save_options.set_scale_factor (m_scale_factor);
save_options.set_dbu (m_dbu);

View File

@ -62,7 +62,7 @@ public:
* The format string gives a hint about the target format. Certain options will be
* suppressed if they are known to be unavailable for the given format.
*/
void add_options (tl::CommandLineOptions &cmd, const std::string &format);
void add_options (tl::CommandLineOptions &cmd, const std::string &format = std::string ());
/**
* @brief Adds the generic options to the command line parser object for the GDS2 format
@ -100,7 +100,7 @@ public:
* @brief Configures the writer options object with the options stored in this object
* The layout is required in order to derive the cell and layer ID's.
*/
void configure (db::SaveLayoutOptions &save_options, const db::Layout &layout);
void configure (db::SaveLayoutOptions &save_options, const db::Layout &layout) const;
private:
double m_scale_factor;

View File

@ -20,42 +20,62 @@
*/
#include "bdInit.h"
#include "bdReaderOptions.h"
#include "bdWriterOptions.h"
#include "dbClip.h"
#include "dbLayout.h"
#include "dbGDS2Writer.h"
#include "dbOASISWriter.h"
#include "dbReader.h"
#include "tlLog.h"
#include "tlCommandLineParser.h"
struct ClipData
{
ClipData ()
: file_in (), file_out (), clip_layer (),
oasis (false), gzip (false)
: file_in (), file_out (), clip_layer ()
{ }
bd::GenericReaderOptions reader_options;
bd::GenericWriterOptions writer_options;
std::string file_in;
std::string file_out;
db::LayerProperties clip_layer;
bool oasis;
bool gzip;
std::vector <db::DBox> clip_boxes;
std::string result;
std::string top;
void add_box (const std::string &spec)
{
tl::Extractor ex (spec.c_str ());
double l = 0.0, b = 0.0, r = 0.0, t = 0.0;
ex >> l >> "," >> b >> "," >> r >> "," >> t >> tl::Extractor::end ();
clip_boxes.push_back (db::DBox (l, b, r, t));
}
void set_clip_layer (const std::string &spec)
{
tl::Extractor ex (spec.c_str ());
clip_layer = db::LayerProperties ();
clip_layer.read (ex);
}
};
void clip (const ClipData &data)
{
db::Manager m;
db::Layout layout (&m);
db::Layout target_layout (&m);
db::Layout layout;
db::Layout target_layout;
{
db::LoadLayoutOptions load_options;
data.reader_options.configure (load_options);
tl::InputStream stream (data.file_in);
db::Reader reader (stream);
reader.read (layout);
reader.read (layout, load_options);
}
// create the layers in the target layout as well
@ -135,123 +155,61 @@ void clip (const ClipData &data)
}
// write the layout
tl::OutputStreamBase *out_file = 0;
try {
tl::OutputStream stream (data.file_out, data.gzip ? tl::OutputStream::OM_Zlib : tl::OutputStream::OM_Plain);
db::SaveLayoutOptions save_options;
save_options.set_format_from_filename (data.file_out);
data.writer_options.configure (save_options, target_layout);
if (data.oasis) {
db::OASISWriter writer;
writer.write (target_layout, stream, db::SaveLayoutOptions ());
} else {
db::GDS2Writer writer;
writer.write (target_layout, stream, db::SaveLayoutOptions ());
}
delete out_file;
} catch (...) {
if (out_file) {
delete out_file;
}
throw;
}
tl::OutputStream stream (data.file_out);
db::Writer writer (save_options);
writer.write (target_layout, stream);
}
void print_syntax ()
BD_MAIN_FUNC
{
printf ("Syntax: strmclip [<options>] <infile> <outfile>\n");
printf ("\n");
printf ("Options are:\n");
printf (" -l 'l/d' take clip regions from layer l, datatype d\n");
printf (" -o produce oasis output\n");
printf (" -g produce gds output\n");
printf (" -z gzip output\n");
printf (" -t 'cell' use this cell from input (default: determine top cell automatically)\n");
printf (" -x 'name' use this cell as top cell in output\n");
printf (" -r 'l,b,r,t' explicitly specify a clip rectangle (can be present multiple times)\n");
}
ClipData data;
int
main (int argc, char *argv [])
{
try {
bd::init ();
ClipData data;
tl::CommandLineOptions cmd;
data.reader_options.add_options (cmd);
data.writer_options.add_options (cmd);
for (int n = 1; n < argc; ++n) {
cmd << tl::arg ("input", &data.file_in, "The input file",
"The input file can be any supported format. It can be gzip compressed and will "
"be uncompressed automatically in this case."
)
<< tl::arg ("output", &data.file_out, "The output file",
"The output format is determined from the suffix of the file. If the suffix indicates "
"gzip compression, the file will be compressed on output. Examples for recognized suffixes are "
"\".oas\", \".gds.gz\", \".dxf\" or \"gds2\"."
)
<< tl::arg ("-l|--clip-layer=spec", &data, &ClipData::set_clip_layer, "Specifies a layer to take the clip regions from",
"If this option is given, the clip rectangles are taken from the given layer."
"The layer specification is of the \"layer/datatype\" form or a plain layer name if named layers "
"are available."
)
<< tl::arg ("-t|--top-in=cellname", &data.top, "Specifies the top cell for input",
"If this option is given, it specifies the cell to use as top cell from the input."
)
<< tl::arg ("-x|--top-out=cellname", &data.result, "Specifies the top cell for output",
"If given, this name will be used as the top cell name in the output file. "
"By default the output's top cell will be \"CLIPPED_\" plus the input's top cell name."
)
<< tl::arg ("*-r|--clip-box=l,b,r,t", &data, &ClipData::add_box, "Specifies a clip box",
"This option specifies the box to clip in micrometer units. The box is given "
"by left, bottom, right and top coordinates. This option can be used multiple times "
"to produce a clip covering more than one rectangle."
)
;
if (std::string (argv [n]) == "-h") {
print_syntax ();
return 0;
} else if (std::string (argv [n]) == "-o") {
data.oasis = true;
} else if (std::string (argv [n]) == "-g") {
data.oasis = false;
} else if (std::string (argv [n]) == "-z") {
data.gzip = true;
} else if (std::string (argv [n]) == "-x") {
if (n < argc + 1) {
++n;
data.result = argv [n];
}
} else if (std::string (argv [n]) == "-t") {
if (n < argc + 1) {
++n;
data.top = argv [n];
}
} else if (std::string (argv [n]) == "-r") {
if (n < argc + 1) {
++n;
tl::Extractor ex (argv [n]);
double l = 0.0, b = 0.0, r = 0.0, t = 0.0;
ex.read (l); ex.expect (",");
ex.read (b); ex.expect (",");
ex.read (r); ex.expect (",");
ex.read (t); ex.expect_end ();
data.clip_boxes.push_back (db::DBox (l, b, r, t));
}
} else if (std::string (argv [n]) == "-l") {
if (n < argc + 1) {
++n;
tl::Extractor ex (argv[n]);
db::LayerProperties lp;
lp.read (ex);
data.clip_layer = lp;
}
} else if (argv [n][0] == '-') {
print_syntax ();
throw tl::Exception ("Unknown option: " + std::string (argv [n]));
} else if (data.file_in.empty ()) {
data.file_in = argv [n];
} else if (data.file_out.empty ()) {
data.file_out = argv [n];
} else {
print_syntax ();
throw tl::Exception ("Superfluous command element: " + std::string (argv [n]));
}
cmd.brief ("This program will produce clips from an input layout and writes them to another layout");
}
cmd.parse (argc, argv);
if (data.file_in.empty () || data.file_out.empty ()) {
print_syntax ();
throw tl::Exception ("Input or output file name missing");
}
clip (data);
} catch (std::exception &ex) {
tl::error << ex.what ();
return 1;
} catch (tl::Exception &ex) {
tl::error << ex.msg ();
return 1;
} catch (...) {
tl::error << "unspecific error";
return 1;
}
clip (data);
return 0;
}
BD_MAIN

View File

@ -32,7 +32,7 @@ namespace tl
// ArgBase implementation
ArgBase::ParsedOption::ParsedOption (const std::string &option)
: optional (false), inverted (false), advanced (false), non_advanced (false)
: optional (false), inverted (false), advanced (false), non_advanced (false), repeated (false)
{
tl::Extractor ex (option.c_str ());
@ -42,6 +42,12 @@ ArgBase::ParsedOption::ParsedOption (const std::string &option)
advanced = true;
} else if (ex.test ("/")) {
non_advanced = true;
} else if (ex.test ("*")) {
repeated = true;
} else if (ex.test ("!")) {
inverted = true;
} else if (ex.test ("?")) {
optional = true;
} else if (ex.test ("[")) {
const char *t = ex.get ();
while (! ex.at_end () && *ex != ']') {
@ -55,10 +61,6 @@ ArgBase::ParsedOption::ParsedOption (const std::string &option)
}
if (ex.test ("!")) {
inverted = true;
}
while (! ex.at_end ()) {
if (ex.test ("--")) {
optional = true;
@ -73,7 +75,6 @@ ArgBase::ParsedOption::ParsedOption (const std::string &option)
ex.read_word (name);
}
} else {
optional = ex.test ("?");
ex.read_word (name);
}
ex.test("|");
@ -523,13 +524,20 @@ CommandLineOptions::parse (int argc, char *argv[])
throw tl::Exception (tl::to_string (QObject::tr ("Unknown command line component %1 - no further plain argument expected (use -h for help)").arg (tl::to_qstring (arg_as_utf8))));
}
arg = *next_plain_arg++;
arg = *next_plain_arg;
if (! arg->option ().repeated) {
++next_plain_arg;
}
}
if (arg->wants_value ()) {
if (! arg->is_option ()) {
if (! arg->is_option () || ex.test ("=")) {
arg->take_value (ex);
} else if (arg->wants_value ()) {
if (ex.test ("=")) {
arg->take_value (ex);
} else {
@ -557,7 +565,7 @@ CommandLineOptions::parse (int argc, char *argv[])
}
// Exection the action if there is one
// Execute the action if there is one
arg->action (this);
}

View File

@ -56,7 +56,7 @@ public:
*/
ParsedOption (const std::string &option);
bool optional, inverted, advanced, non_advanced;
bool optional, inverted, advanced, non_advanced, repeated;
std::string long_option, short_option, name;
std::string group;
};
@ -80,7 +80,11 @@ public:
* "-o|--long-option=value" - A short/long option with a value
* "[group]..." - List the option under this group (group = group title)
* "#..." - Advanced option - listed with --help-all only
* "/..." - Non-ddvanced option - listed with -h|--help only
* "/..." - Non-advanced option - listed with -h|--help only
* "*..." - Multiple occurances allowed - values needs to be
* an array and values are accumulated. Without *, the
* value string is evaluated to a comma-separated list.
* "*" means one occurance at least unless combined with "?".
*/
ArgBase (const std::string &option, const std::string &brief_doc, const std::string &long_doc);
@ -187,6 +191,11 @@ inline void extract (tl::Extractor &ex, std::string &t, bool for_list = false)
ex.read (t, ",");
} else {
t = ex.get ();
// TODO: there should be a tl::Extractor method either to
// read all remaining text or to move the pointer to the end.
while (! ex.at_end ()) {
++ex;
}
}
}
@ -194,12 +203,14 @@ inline void extract (tl::Extractor &ex, std::string &t, bool for_list = false)
* @brief A specialization for a list of any type (vector)
*/
template <class T>
inline void extract (tl::Extractor &ex, std::vector<T> &t, bool /*for_list*/ = false)
inline void extract (tl::Extractor &ex, std::vector<T> &t, bool for_list = false)
{
while (! ex.at_end ()) {
t.push_back (T ());
extract (ex, t.back (), true);
ex.test (",");
extract (ex, t.back (), for_list);
if (for_list) {
ex.test (",");
}
}
}
@ -276,7 +287,7 @@ public:
virtual void take_value (tl::Extractor &ex)
{
extract (ex, *mp_value);
extract (ex, *mp_value, !option ().repeated);
}
virtual void mark_present (bool inverted)
@ -316,7 +327,7 @@ public:
{
typedef typename type_without_const_ref<T>::inner_type inner_type;
inner_type t = inner_type ();
extract (ex, t);
extract (ex, t, !option ().repeated);
(mp_object->*mp_setter) (t);
}

View File

@ -266,6 +266,7 @@ TEST(2)
EXPECT_EQ (v.f, "bar");
}
// Array arguments
TEST(3)
{
std::vector<std::string> a;
@ -318,3 +319,78 @@ TEST(3)
EXPECT_EQ (b[0], -13);
EXPECT_EQ (b[1], 21);
}
// Repeated array arguments
TEST(4)
{
std::vector<std::string> a;
std::vector<int> b;
tl::CommandLineOptions cmd;
cmd << tl::arg ("*-a|--along", &a, "")
<< tl::arg ("*-b|--blong", &b, "");
{
char *argv[] = { "x", "-a", "r,u,v" };
cmd.parse (sizeof (argv) / sizeof (argv[0]), argv);
}
EXPECT_EQ (int (a.size ()), 1);
EXPECT_EQ (a[0], "r,u,v");
EXPECT_EQ (b.empty (), true);
a.clear ();
b.clear ();
{
char *argv[] = { "x", "-b", "1", "-a=r", "-a", "u", "--along=v", "--blong=2" };
cmd.parse (sizeof (argv) / sizeof (argv[0]), argv);
}
EXPECT_EQ (int (a.size ()), 3);
EXPECT_EQ (int (b.size ()), 2);
EXPECT_EQ (a[0], "r");
EXPECT_EQ (a[1], "u");
EXPECT_EQ (a[2], "v");
EXPECT_EQ (b[0], 1);
EXPECT_EQ (b[1], 2);
}
// Repeated and non-repeated plain arguments
TEST(5)
{
std::string a;
std::vector<std::string> b;
std::vector<std::string> c;
tl::CommandLineOptions cmd;
cmd << tl::arg ("a", &a, "")
<< tl::arg ("b", &b, "")
<< tl::arg ("?*c", &c, "");
{
char *argv[] = { "x", "y", "r,u,v" };
cmd.parse (sizeof (argv) / sizeof (argv[0]), argv);
}
EXPECT_EQ (int (b.size ()), 3);
EXPECT_EQ (int (c.size ()), 0);
EXPECT_EQ (a, "y");
EXPECT_EQ (b[0], "r");
EXPECT_EQ (b[1], "u");
EXPECT_EQ (b[2], "v");
a.clear ();
b.clear ();
c.clear ();
{
char *argv[] = { "x", "y", "r,u,v", "a,b", "c", "d" };
cmd.parse (sizeof (argv) / sizeof (argv[0]), argv);
}
EXPECT_EQ (int (b.size ()), 3);
EXPECT_EQ (int (c.size ()), 3);
EXPECT_EQ (a, "y");
EXPECT_EQ (b[0], "r");
EXPECT_EQ (b[1], "u");
EXPECT_EQ (b[2], "v");
EXPECT_EQ (c[0], "a,b");
EXPECT_EQ (c[1], "c");
EXPECT_EQ (c[2], "d");
}