From 681c255e50631bd1865c35e9c9125aef832cae63 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 19 Aug 2017 00:36:45 +0200 Subject: [PATCH] Equipped strmclip with the new command line parser Plus: added repeated arguments (in addition to the array arguments that have been there before). --- src/buddies/bd/bdReaderOptions.cc | 25 ++-- src/buddies/bd/bdReaderOptions.h | 2 +- src/buddies/bd/bdWriterOptions.cc | 8 +- src/buddies/bd/bdWriterOptions.h | 4 +- src/buddies/strmclip/strmclip.cc | 184 ++++++++++---------------- src/tl/tlCommandLineParser.cc | 28 ++-- src/tl/tlCommandLineParser.h | 25 +++- src/unit_tests/tlCommandLineParser.cc | 76 +++++++++++ 8 files changed, 205 insertions(+), 147 deletions(-) diff --git a/src/buddies/bd/bdReaderOptions.cc b/src/buddies/bd/bdReaderOptions.cc index 0309a760e..7f5aaa114 100644 --- a/src/buddies/bd/bdReaderOptions.cc +++ b/src/buddies/bd/bdReaderOptions.cc @@ -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); } } diff --git a/src/buddies/bd/bdReaderOptions.h b/src/buddies/bd/bdReaderOptions.h index 93dfaa4d7..fc69daee3 100644 --- a/src/buddies/bd/bdReaderOptions.h +++ b/src/buddies/bd/bdReaderOptions.h @@ -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 diff --git a/src/buddies/bd/bdWriterOptions.cc b/src/buddies/bd/bdWriterOptions.cc index 8da187e3d..bad4efa62 100644 --- a/src/buddies/bd/bdWriterOptions.cc +++ b/src/buddies/bd/bdWriterOptions.cc @@ -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); diff --git a/src/buddies/bd/bdWriterOptions.h b/src/buddies/bd/bdWriterOptions.h index 527df6917..f1bf9c6d8 100644 --- a/src/buddies/bd/bdWriterOptions.h +++ b/src/buddies/bd/bdWriterOptions.h @@ -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; diff --git a/src/buddies/strmclip/strmclip.cc b/src/buddies/strmclip/strmclip.cc index 4b728a98a..24ee33d0a 100644 --- a/src/buddies/strmclip/strmclip.cc +++ b/src/buddies/strmclip/strmclip.cc @@ -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 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 [] \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 diff --git a/src/tl/tlCommandLineParser.cc b/src/tl/tlCommandLineParser.cc index 78b515811..6aaacd963 100644 --- a/src/tl/tlCommandLineParser.cc +++ b/src/tl/tlCommandLineParser.cc @@ -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); } diff --git a/src/tl/tlCommandLineParser.h b/src/tl/tlCommandLineParser.h index 35af88ea9..bd9583f8a 100644 --- a/src/tl/tlCommandLineParser.h +++ b/src/tl/tlCommandLineParser.h @@ -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 -inline void extract (tl::Extractor &ex, std::vector &t, bool /*for_list*/ = false) +inline void extract (tl::Extractor &ex, std::vector &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::inner_type inner_type; inner_type t = inner_type (); - extract (ex, t); + extract (ex, t, !option ().repeated); (mp_object->*mp_setter) (t); } diff --git a/src/unit_tests/tlCommandLineParser.cc b/src/unit_tests/tlCommandLineParser.cc index 303a828e8..b0beddabe 100644 --- a/src/unit_tests/tlCommandLineParser.cc +++ b/src/unit_tests/tlCommandLineParser.cc @@ -266,6 +266,7 @@ TEST(2) EXPECT_EQ (v.f, "bar"); } +// Array arguments TEST(3) { std::vector a; @@ -318,3 +319,78 @@ TEST(3) EXPECT_EQ (b[0], -13); EXPECT_EQ (b[1], 21); } + +// Repeated array arguments +TEST(4) +{ + std::vector a; + std::vector 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 b; + std::vector 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"); +}