From 00c826688fa59dcc23facb1b8db681891ab39743 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 18 Mar 2026 22:01:26 +0100 Subject: [PATCH 1/6] Added the ability to run DRC and LVS from strmrun --- src/buddies/src/bd/bd.pro | 4 +- src/buddies/src/bd/strmrun.cc | 2 + src/buddies/src/buddy_app.pri | 2 +- src/buddies/unit_tests/bdStrmrunTests.cc | 44 ++++++++++++++++------ src/drc/drc/built-in-macros/_drc_engine.rb | 16 ++++---- 5 files changed, 46 insertions(+), 22 deletions(-) diff --git a/src/buddies/src/bd/bd.pro b/src/buddies/src/bd/bd.pro index d3b3faa09..9d913cffe 100644 --- a/src/buddies/src/bd/bd.pro +++ b/src/buddies/src/bd/bd.pro @@ -41,7 +41,9 @@ INCLUDEPATH += $$RBA_INC DEPENDPATH += $$RBA_INC equals(HAVE_RUBY, "1") { - LIBS += -lklayout_rba + INCLUDEPATH += $$DRC_INC $$LVS_INC + DEPENDPATH += $$DRC_INC $$LVS_INC + LIBS += -lklayout_rba -lklayout_drc -lklayout_lvs } else { LIBS += -lklayout_rbastub } diff --git a/src/buddies/src/bd/strmrun.cc b/src/buddies/src/bd/strmrun.cc index b324ecc88..1bcc83b76 100644 --- a/src/buddies/src/bd/strmrun.cc +++ b/src/buddies/src/bd/strmrun.cc @@ -36,6 +36,8 @@ #include "libForceLink.h" #include "rdbForceLink.h" #include "pexForceLink.h" +#include "drcForceLink.h" +#include "lvsForceLink.h" #include "lymMacro.h" #include "lymMacroCollection.h" diff --git a/src/buddies/src/buddy_app.pri b/src/buddies/src/buddy_app.pri index 58da418bf..d7a96c693 100644 --- a/src/buddies/src/buddy_app.pri +++ b/src/buddies/src/buddy_app.pri @@ -25,7 +25,7 @@ INCLUDEPATH += $$RBA_INC DEPENDPATH += $$RBA_INC equals(HAVE_RUBY, "1") { - LIBS += -lklayout_rba + LIBS += -lklayout_rba -lklayout_drc -lklayout_lvs } else { LIBS += -lklayout_rbastub } diff --git a/src/buddies/unit_tests/bdStrmrunTests.cc b/src/buddies/unit_tests/bdStrmrunTests.cc index 0766c7bd9..c4762915e 100644 --- a/src/buddies/unit_tests/bdStrmrunTests.cc +++ b/src/buddies/unit_tests/bdStrmrunTests.cc @@ -28,10 +28,7 @@ TEST(1) { #if defined(HAVE_PYTHON) - std::string fp (tl::testsrc ()); - fp += "/testdata/bd/strmrun.py"; - - std::string cmd; + std::string cmd_call; #if defined(__APPLE__) // NOTE: because of system integrity, MacOS does not inherit DYLD_LIBRARY_PATH to child @@ -39,19 +36,42 @@ TEST(1) const char *ldpath_name = "DYLD_LIBRARY_PATH"; const char *ldpath = getenv (ldpath_name); if (ldpath) { - cmd += std::string (ldpath_name) + "=\"" + ldpath + "\"; export " + ldpath_name + "; "; + cmd_call += std::string (ldpath_name) + "=\"" + ldpath + "\"; export " + ldpath_name + "; "; } #endif - cmd += tl::combine_path (tl::get_inst_path (), "strmrun ") + fp; - tl::info << cmd; + cmd_call += tl::combine_path (tl::get_inst_path (), "strmrun"); - tl::InputPipe pipe (cmd); - tl::InputStream is (pipe); - std::string data = is.read_all (); - tl::info << data; + { + std::string fp (tl::testsrc ()); + fp += "/testdata/bd/strmrun.py"; + + std::string cmd = cmd_call + " " + fp; + tl::info << cmd; + + tl::InputPipe pipe (cmd); + tl::InputStream is (pipe); + std::string data = is.read_all (); + tl::info << data; + + EXPECT_EQ (data, "Hello, world (0,-42;42,0)!\n"); + } + + { + std::string fp (tl::testsrc ()); + fp += "/testdata/bd/strmrun.drc"; + + std::string cmd = cmd_call + " " + fp; + tl::info << cmd; + + tl::InputPipe pipe (cmd); + tl::InputStream is (pipe); + std::string data = is.read_all (); + tl::info << data; + + EXPECT_EQ (data, "This is DRC.\n"); + } - EXPECT_EQ (data, "Hello, world (0,-42;42,0)!\n"); #endif } diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index e2ff2b7ee..9aee34eca 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -164,7 +164,7 @@ module DRC def initialize - cv = RBA::CellView::active + cv = RBA.const_defined?(:CellView) && RBA::CellView::active @time = Time::now @force_gc = ($drc_force_gc == true) # for testing, $drc_force_gc can be set to true @@ -1444,7 +1444,7 @@ module DRC if arg =~ /^@(\d+)/ n = $1.to_i - 1 - view = RBA::LayoutView::current + view = RBA.const_defined?(:LayoutView) && RBA::LayoutView::current view || raise("No view open") (n >= 0 && view.cellviews > n) || raise("Invalid layout index @#{n + 1}") cv = view.cellview(n) @@ -1543,7 +1543,7 @@ module DRC if arg =~ /^@(\d+)/ n = $1.to_i - 1 - view = RBA::LayoutView::current + view = RBA.const_defined?(:LayoutView) && RBA::LayoutView::current view || raise("No view open") (n >= 0 && view.cellviews > n) || raise("Invalid layout index @#{n + 1}") cv = view.cellview(n) @@ -1608,7 +1608,7 @@ module DRC self._context("report") do # finish what we got so far - view = RBA::LayoutView::current + view = RBA.const_defined?(:LayoutView) && RBA::LayoutView::current @def_output && @def_output.finish(false, view) @def_output = nil @@ -2925,7 +2925,7 @@ CODE def _start(job_description) # clearing the selection avoids some nasty problems - view = RBA::LayoutView::current + view = RBA.const_defined?(:LayoutView) && RBA::LayoutView::current view && view.cancel @total_timer = RBA::Timer::new @@ -2950,7 +2950,7 @@ CODE _flush - view = RBA::LayoutView::current + view = RBA.const_defined?(:LayoutView) && RBA::LayoutView::current @def_output && @def_output.finish(final, view) @@ -3484,7 +3484,7 @@ CODE output_rdb_index = nil - view = RBA::LayoutView::current + view = RBA.const_defined?(:LayoutView) && RBA::LayoutView::current if view if self._rdb_index output_rdb = RBA::ReportDatabase::new("") # reuse existing name @@ -3518,7 +3518,7 @@ CODE if arg =~ /^@(\d+|\+)/ - view = RBA::LayoutView::current + view = RBA.const_defined?(:LayoutView) && RBA::LayoutView::current view || raise("No view open") if $1 == "+" prev_cv = view.active_cellview_index From 2dfcc9293e9dcab0b9fc50cfe2c865e7f529aae1 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 20 Mar 2026 16:30:28 +0100 Subject: [PATCH 2/6] Allowing to explicity specify a format Most tools and functions now allow to explicitly specify a format for input and output. On input, the format is usually detected, but for some cases, this is not possible (e.g. DEF). So you can specify the format attached to the file name in square brackets: e.g. file.defok[def] "def" is the intended suffix, "defok" is the given one. On output, the same is possible, specifically in strmxor and strmclip: strmxor a.gds b.gds out.xor[oas] --- src/buddies/src/bd/strmclip.cc | 4 +- src/buddies/src/bd/strmxor.cc | 4 +- src/buddies/unit_tests/bdConverterTests.cc | 28 ++++++++ src/buddies/unit_tests/bdStrmclipTests.cc | 28 ++++++++ src/buddies/unit_tests/bdStrmxorTests.cc | 46 +++++++++++++ src/db/db/dbEdgePairs.cc | 3 +- src/db/db/dbEdges.cc | 3 +- src/db/db/dbReader.cc | 25 +++++-- src/db/db/dbRegion.cc | 3 +- src/db/db/dbSaveLayoutOptions.cc | 30 +++++++-- src/db/db/dbSaveLayoutOptions.h | 2 +- src/db/db/dbTestSupport.cc | 2 +- src/db/db/dbTexts.cc | 3 +- src/db/db/gsiDeclDbCell.cc | 4 +- src/db/db/gsiDeclDbLayout.cc | 16 +++-- src/db/unit_tests/dbSaveLayoutOptionsTests.cc | 14 ++++ src/lay/lay/layMainWindow.cc | 6 +- .../lefdef/db_plugin/dbLEFDEFPlugin.cc | 40 ++--------- .../lefdef/unit_tests/dbLEFDEFImportTests.cc | 14 +++- src/tl/tl/tlStream.cc | 33 ++++++++- src/tl/tl/tlStream.h | 31 ++++++++- src/tl/unit_tests/tlStreamTests.cc | 36 ++++++++++ testdata/lefdef/issue-489/in.d | 34 ++++++++++ testdata/lefdef/issue-489/in.l | 63 ++++++++++++++++++ testdata/lefdef/strm2oas_small/au.gds | Bin 0 -> 576 bytes testdata/lefdef/strm2oas_small/in.defok | 29 ++++++++ 26 files changed, 425 insertions(+), 76 deletions(-) create mode 100644 testdata/lefdef/issue-489/in.d create mode 100644 testdata/lefdef/issue-489/in.l create mode 100644 testdata/lefdef/strm2oas_small/au.gds create mode 100644 testdata/lefdef/strm2oas_small/in.defok diff --git a/src/buddies/src/bd/strmclip.cc b/src/buddies/src/bd/strmclip.cc index 5ae5bd7a8..3af66f445 100644 --- a/src/buddies/src/bd/strmclip.cc +++ b/src/buddies/src/bd/strmclip.cc @@ -153,10 +153,10 @@ void clip (ClipData &data) // write the layout db::SaveLayoutOptions save_options; - save_options.set_format_from_filename (data.file_out); + std::string of = save_options.set_format_from_filename (data.file_out).second; data.writer_options.configure (save_options, target_layout); - tl::OutputStream stream (data.file_out); + tl::OutputStream stream (of); db::Writer writer (save_options); writer.write (target_layout, stream); } diff --git a/src/buddies/src/bd/strmxor.cc b/src/buddies/src/bd/strmxor.cc index 5845ac6ae..b3437705a 100644 --- a/src/buddies/src/bd/strmxor.cc +++ b/src/buddies/src/bd/strmxor.cc @@ -587,10 +587,10 @@ BD_PUBLIC int strmxor (int argc, char *argv[]) if (output_layout.get ()) { db::SaveLayoutOptions save_options; - save_options.set_format_from_filename (output); + std::string of = save_options.set_format_from_filename (output).second; writer_options.configure (save_options, *output_layout); - tl::OutputStream stream (output); + tl::OutputStream stream (of); db::Writer writer (save_options); writer.write (*output_layout, stream); diff --git a/src/buddies/unit_tests/bdConverterTests.cc b/src/buddies/unit_tests/bdConverterTests.cc index 226142cf5..7324999b9 100644 --- a/src/buddies/unit_tests/bdConverterTests.cc +++ b/src/buddies/unit_tests/bdConverterTests.cc @@ -212,6 +212,34 @@ TEST(7) db::compare_layouts (this, layout, input, db::NoNormalization); } +// Small DEF example with explicit format +TEST(8) +{ + std::string input = tl::testdata (); + input += "/lefdef/strm2oas_small/in.defok[def]"; + + std::string input_au = tl::testdata (); + input_au += "/lefdef/strm2oas_small/au.gds"; + + std::string output = this->tmp_file ("small_def"); + + const char *argv[] = { "x", input.c_str (), output.c_str () }; + + EXPECT_EQ (bd::converter_main (sizeof (argv) / sizeof (argv[0]), (char **) argv, bd::GenericWriterOptions::oasis_format_name), 0); + + db::Layout layout; + + { + tl::InputStream stream (output); + db::LoadLayoutOptions options; + db::Reader reader (stream); + reader.read (layout, options); + EXPECT_EQ (reader.format (), "OASIS"); + } + + db::compare_layouts (this, layout, input_au, db::WriteGDS2); +} + // Large LEF/DEF to OAS converter test TEST(10) { diff --git a/src/buddies/unit_tests/bdStrmclipTests.cc b/src/buddies/unit_tests/bdStrmclipTests.cc index ff2b7f172..8b15f9693 100644 --- a/src/buddies/unit_tests/bdStrmclipTests.cc +++ b/src/buddies/unit_tests/bdStrmclipTests.cc @@ -97,6 +97,34 @@ TEST(2) tl::InputStream stream (output); db::Reader reader (stream); reader.read (layout); + EXPECT_EQ (reader.format (), "GDS2"); + } + + db::compare_layouts (this, layout, au, db::NoNormalization); +} + +// with explicit output format +TEST(3) +{ + std::string input = tl::testdata (); + input += "/bd/strm2clip_in.gds"; + + std::string au = tl::testdata (); + au += "/bd/strm2clip_au2.gds"; + + std::string output = this->tmp_file () + "[oas]"; + + const char *argv[] = { "x", input.c_str (), output.c_str (), "-r=0,-2,9,5", "-t", "INV2", "-x=CLIP_OUT" }; + + EXPECT_EQ (strmclip (sizeof (argv) / sizeof (argv[0]), (char **) argv), 0); + + db::Layout layout; + + { + tl::InputStream stream (output); + db::Reader reader (stream); + reader.read (layout); + EXPECT_EQ (reader.format (), "OASIS"); } db::compare_layouts (this, layout, au, db::NoNormalization); diff --git a/src/buddies/unit_tests/bdStrmxorTests.cc b/src/buddies/unit_tests/bdStrmxorTests.cc index 981574b38..384313d64 100644 --- a/src/buddies/unit_tests/bdStrmxorTests.cc +++ b/src/buddies/unit_tests/bdStrmxorTests.cc @@ -97,6 +97,52 @@ TEST(1A_Flat) tl::InputStream stream (output); db::Reader reader (stream); reader.read (layout); + + EXPECT_EQ (std::string (reader.format ()), "OASIS"); + } + + db::compare_layouts (this, layout, au, db::NormalizationMode (db::NoNormalization | db::AsPolygons)); + EXPECT_EQ (cap.captured_text (), + "Layer 10/0 is not present in first layout, but in second\n" + "Result summary (layers without differences are not shown):\n" + "\n" + " Layer Output Differences (shape count)\n" + " ----------------------------------------------------------------\n" + " 3/0 3/0 30\n" + " 6/0 6/0 41\n" + " 8/1 8/1 1\n" + " 10/0 - (no such layer in first layout)\n" + "\n" + ); +} + +TEST(1A_FlatWithExplicitOutputFormat) +{ + tl::CaptureChannel cap; + + std::string input_a = tl::testdata (); + input_a += "/bd/strmxor_in1.gds"; + + std::string input_b = tl::testdata (); + input_b += "/bd/strmxor_in2.gds"; + + std::string au = tl::testdata (); + au += "/bd/strmxor_au1.oas"; + + std::string output = this->tmp_file ("tmp.xxx[oas]"); + + const char *argv[] = { "x", input_a.c_str (), input_b.c_str (), output.c_str () }; + + EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1); + + db::Layout layout; + + { + tl::InputStream stream (output); + db::Reader reader (stream); + reader.read (layout); + + EXPECT_EQ (std::string (reader.format ()), "OASIS"); } db::compare_layouts (this, layout, au, db::NormalizationMode (db::NoNormalization | db::AsPolygons)); diff --git a/src/db/db/dbEdgePairs.cc b/src/db/db/dbEdgePairs.cc index 9539a5c29..cd43514b8 100644 --- a/src/db/db/dbEdgePairs.cc +++ b/src/db/db/dbEdgePairs.cc @@ -113,9 +113,8 @@ EdgePairs::write (const std::string &fn) const unsigned int li = layout.insert_layer (db::LayerProperties (0, 0)); insert_into (&layout, top.cell_index (), li); - tl::OutputStream os (fn); db::SaveLayoutOptions opt; - opt.set_format_from_filename (fn); + tl::OutputStream os (opt.set_format_from_filename (fn).second); db::Writer writer (opt); writer.write (layout, os); } diff --git a/src/db/db/dbEdges.cc b/src/db/db/dbEdges.cc index e263b7fdd..1cad75949 100644 --- a/src/db/db/dbEdges.cc +++ b/src/db/db/dbEdges.cc @@ -145,9 +145,8 @@ Edges::write (const std::string &fn) const unsigned int li = layout.insert_layer (db::LayerProperties (0, 0)); insert_into (&layout, top.cell_index (), li); - tl::OutputStream os (fn); db::SaveLayoutOptions opt; - opt.set_format_from_filename (fn); + tl::OutputStream os (opt.set_format_from_filename (fn).second); db::Writer writer (opt); writer.write (layout, os); } diff --git a/src/db/db/dbReader.cc b/src/db/db/dbReader.cc index 3bbf08be2..26b4a6518 100644 --- a/src/db/db/dbReader.cc +++ b/src/db/db/dbReader.cc @@ -134,13 +134,26 @@ ReaderBase::check_dbu (double dbu) const Reader::Reader (tl::InputStream &stream) : mp_actual_reader (0), m_stream (stream) { - // Detect the format by asking all reader declarations - for (tl::Registrar::iterator rdr = tl::Registrar::begin (); rdr != tl::Registrar::end () && ! mp_actual_reader; ++rdr) { - m_stream.reset (); - if (rdr->detect (m_stream)) { - m_stream.reset (); - mp_actual_reader = rdr->create_reader (m_stream); + if (stream.is_explicit_suffix ()) { + + // If an explicit suffix is given for the stream, use it to find the reader + for (tl::Registrar::iterator rdr = tl::Registrar::begin (); rdr != tl::Registrar::end () && ! mp_actual_reader; ++rdr) { + if (tl::match_filename_to_format ("." + stream.suffix (), rdr->file_format ())) { + mp_actual_reader = rdr->create_reader (m_stream); + } } + + } else { + + // Detect the format by asking all reader declarations + for (tl::Registrar::iterator rdr = tl::Registrar::begin (); rdr != tl::Registrar::end () && ! mp_actual_reader; ++rdr) { + m_stream.reset (); + if (rdr->detect (m_stream)) { + m_stream.reset (); + mp_actual_reader = rdr->create_reader (m_stream); + } + } + } if (! mp_actual_reader) { diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index 9af221fa4..f290b87d3 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -142,9 +142,8 @@ Region::write (const std::string &fn) const unsigned int li = layout.insert_layer (db::LayerProperties (0, 0)); insert_into (&layout, top.cell_index (), li); - tl::OutputStream os (fn); db::SaveLayoutOptions opt; - opt.set_format_from_filename (fn); + tl::OutputStream os (opt.set_format_from_filename (fn).second); db::Writer writer (opt); writer.write (layout, os); } diff --git a/src/db/db/dbSaveLayoutOptions.cc b/src/db/db/dbSaveLayoutOptions.cc index 4c2e55cf6..688b34704 100644 --- a/src/db/db/dbSaveLayoutOptions.cc +++ b/src/db/db/dbSaveLayoutOptions.cc @@ -27,6 +27,7 @@ #include "tlStream.h" #include "tlExpression.h" #include "tlInternational.h" +#include "tlGlobPattern.h" namespace db { @@ -444,16 +445,33 @@ SaveLayoutOptions::get_cells (const db::Layout &layout, std::set SaveLayoutOptions::set_format_from_filename (const std::string &fn) { - for (tl::Registrar::iterator fmt = tl::Registrar::begin (); fmt != tl::Registrar::end (); ++fmt) { - if (tl::match_filename_to_format (fn, fmt->file_format ())) { - m_format = fmt->format_name (); - return true; + tl::GlobPattern pat ("(*)\\[(*)\\]"); + + std::vector pat_parts; + if (pat.match (fn, pat_parts) && pat_parts.size () == 2) { + + for (tl::Registrar::iterator fmt = tl::Registrar::begin (); fmt != tl::Registrar::end (); ++fmt) { + if (tl::match_filename_to_format ("." + pat_parts[1], fmt->file_format ())) { + m_format = fmt->format_name (); + return std::make_pair (true, pat_parts[0]); + } } + + } else { + + for (tl::Registrar::iterator fmt = tl::Registrar::begin (); fmt != tl::Registrar::end (); ++fmt) { + if (tl::match_filename_to_format (fn, fmt->file_format ())) { + m_format = fmt->format_name (); + return std::make_pair (true, fn); + } + } + } - return false; + + return std::make_pair (false, fn); } } diff --git a/src/db/db/dbSaveLayoutOptions.h b/src/db/db/dbSaveLayoutOptions.h index 2bc0ab2b7..6c04970bd 100644 --- a/src/db/db/dbSaveLayoutOptions.h +++ b/src/db/db/dbSaveLayoutOptions.h @@ -268,7 +268,7 @@ public: * * Returns true, if the suffix indicates a known format. */ - bool set_format_from_filename (const std::string &fn); + std::pair set_format_from_filename(const std::string &fn); /** * @brief Sets specific options for the given format diff --git a/src/db/db/dbTestSupport.cc b/src/db/db/dbTestSupport.cc index de3f9789d..c2f12e19a 100644 --- a/src/db/db/dbTestSupport.cc +++ b/src/db/db/dbTestSupport.cc @@ -71,7 +71,7 @@ void compare_layouts (tl::TestBase *_this, const db::Layout &layout, const std:: } else { // write the temp file in the same format than the au file tmp_file = _this->tmp_file (tl::sprintf ("tmp_%x." + tl::extension (au_file), hash)); - options.set_format_from_filename (tmp_file); + tmp_file = options.set_format_from_filename (tmp_file).second; } if ((norm & NoContext) != 0) { diff --git a/src/db/db/dbTexts.cc b/src/db/db/dbTexts.cc index 29e88b83e..902a5cf2e 100644 --- a/src/db/db/dbTexts.cc +++ b/src/db/db/dbTexts.cc @@ -109,9 +109,8 @@ Texts::write (const std::string &fn) const unsigned int li = layout.insert_layer (db::LayerProperties (0, 0)); insert_into (&layout, top.cell_index (), li); - tl::OutputStream os (fn); db::SaveLayoutOptions opt; - opt.set_format_from_filename (fn); + tl::OutputStream os (opt.set_format_from_filename (fn).second); db::Writer writer (opt); writer.write (layout, os); } diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index 4a53b12df..6e0718fe3 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -1310,10 +1310,10 @@ write_simple (const db::Cell *cell, const std::string &filename) db::SaveLayoutOptions options; options.clear_cells (); options.add_cell (cell->cell_index ()); - options.set_format_from_filename (filename); + std::string fn = options.set_format_from_filename (filename).second; db::Writer writer (options); - tl::OutputStream stream (filename); + tl::OutputStream stream (fn); writer.write (*layout, stream); } diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc index 0c1b8b577..54c97cded 100644 --- a/src/db/db/gsiDeclDbLayout.cc +++ b/src/db/db/gsiDeclDbLayout.cc @@ -690,12 +690,13 @@ static void write_simple (db::Layout *layout, const std::string &filename) { db::SaveLayoutOptions options; - if (! options.set_format_from_filename (filename)) { + auto ff = options.set_format_from_filename (filename); + if (! ff.first) { throw tl::Exception (tl::to_string (tr ("Cannot determine format from filename"))); } db::Writer writer (options); - tl::OutputStream stream (filename); + tl::OutputStream stream (ff.second); writer.write (*layout, stream); } @@ -2868,12 +2869,13 @@ static db::SaveLayoutOptions *new_v () return new db::SaveLayoutOptions (); } -static bool set_format_from_filename (db::SaveLayoutOptions *opt, const std::string &fn) +static std::string set_format_from_filename (db::SaveLayoutOptions *opt, const std::string &fn) { - if (! opt->set_format_from_filename (fn)) { + auto ff = opt->set_format_from_filename (fn); + if (! ff.first) { throw tl::Exception (tl::to_string (tr ("Cannot determine format from filename"))); } - return true; + return ff.second; } Class decl_SaveLayoutOptions ("db", "SaveLayoutOptions", @@ -2893,6 +2895,10 @@ Class decl_SaveLayoutOptions ("db", "SaveLayoutOptions", "Beginning with version 0.23, this method always returns true, since the " "only consumer for the return value, Layout#write, now ignores that " "parameter and automatically determines the compression mode from the file name.\n" + "\n" + "Starting with version 0.30.7, this method allows specifying the desired format's extension in square brackets " + "after the file name (e.g. 'file.txt[def]'). This allows writing files with non-standard extensions. " + "The return value of this function now the is actual file name used without the square brackets ('file.txt' in the example case)." ) + gsi::method ("format=", &db::SaveLayoutOptions::set_format, gsi::arg ("format"), "@brief Select a format\n" diff --git a/src/db/unit_tests/dbSaveLayoutOptionsTests.cc b/src/db/unit_tests/dbSaveLayoutOptionsTests.cc index e68bbeb88..95b1d7be8 100644 --- a/src/db/unit_tests/dbSaveLayoutOptionsTests.cc +++ b/src/db/unit_tests/dbSaveLayoutOptionsTests.cc @@ -102,3 +102,17 @@ TEST(1) EXPECT_EQ (opt.get_option_by_name ("mywriter_value").to_string (), "17"); } +TEST(2) +{ + db::SaveLayoutOptions opt; + auto ff = opt.set_format_from_filename ("/home/xyz/test.def"); + EXPECT_EQ (ff.first, true); + EXPECT_EQ (ff.second, "/home/xyz/test.def"); + EXPECT_EQ (opt.format (), "LEFDEF"); + + opt = db::SaveLayoutOptions (); + ff = opt.set_format_from_filename ("/home/xyz/test.txt[def]"); + EXPECT_EQ (ff.first, true); + EXPECT_EQ (ff.second, "/home/xyz/test.txt"); + EXPECT_EQ (opt.format (), "LEFDEF"); +} diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 0410cbb4d..5d73e541a 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -2270,7 +2270,7 @@ MainWindow::cm_save_current_cell_as () db::SaveLayoutOptions options (cv->save_options ()); options.set_dbu (cv->layout ().dbu ()); - options.set_format_from_filename (fn); + fn = options.set_format_from_filename (fn).second; tl::OutputStream::OutputStreamMode om = tl::OutputStream::OM_Auto; if (mp_layout_save_as_options->get_options (current_view (), cv_index, fn, om, options)) { @@ -2370,7 +2370,7 @@ MainWindow::do_save (bool as) db::SaveLayoutOptions options = get_save_options_from_cv (cv); if (as || options.format ().empty ()) { - options.set_format_from_filename (fn); + fn = options.set_format_from_filename (fn).second; } tl::OutputStream::OutputStreamMode om = tl::OutputStream::OM_Auto; @@ -2419,7 +2419,7 @@ MainWindow::cm_save_all () db::SaveLayoutOptions options = get_save_options_from_cv (cv); if (options.format ().empty ()) { - options.set_format_from_filename (fn); + fn = options.set_format_from_filename (fn).second; } tl::OutputStream::OutputStreamMode om = tl::OutputStream::OM_Auto; diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc index 1a974bc23..ebba33d50 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc @@ -46,38 +46,8 @@ namespace db */ static bool is_lef_format (const std::string &fn) { - static const char *suffixes[] = { ".lef", ".LEF", ".lef.gz", ".LEF.gz" }; - - // NOTE: there is no reliable way of (easily) detecting the format. Hence we use the file - // name's suffix for the format hint. - for (size_t i = 0; i < sizeof (suffixes) / sizeof (suffixes[0]); ++i) { - std::string suffix = suffixes [i]; - if (fn.size () > suffix.size () && fn.find (suffix) == fn.size () - suffix.size ()) { - return true; - } - } - - return false; -} - -/** - * @brief Determines the format of the given stream - * Returns true, if the stream has DEF format - */ -static bool is_def_format (const std::string &fn) -{ - static const char *suffixes[] = { ".def", ".DEF", ".def.gz", ".DEF.gz" }; - - // NOTE: there is no reliable way of (easily) detecting the format. Hence we use the file - // name's suffix for the format hint. - for (size_t i = 0; i < sizeof (suffixes) / sizeof (suffixes[0]); ++i) { - std::string suffix = suffixes [i]; - if (fn.size () > suffix.size () && fn.find (suffix) == fn.size () - suffix.size ()) { - return true; - } - } - - return false; + static std::string lef_format ("LEF files (*.lef *.LEF *.lef.gz *.LEF.gz)"); + return tl::match_filename_to_format (fn, lef_format); } // --------------------------------------------------------------- @@ -92,13 +62,13 @@ LEFDEFReader::LEFDEFReader (tl::InputStream &s) const db::LayerMap & LEFDEFReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) { - return read_lefdef (layout, options, is_lef_format (m_stream.filename ())); + return read_lefdef (layout, options, is_lef_format ("." + m_stream.suffix ())); } const db::LayerMap & LEFDEFReader::read (db::Layout &layout) { - return read_lefdef (layout, db::LoadLayoutOptions (), is_lef_format (m_stream.filename ())); + return read_lefdef (layout, db::LoadLayoutOptions (), is_lef_format ("." + m_stream.suffix ())); } const char * @@ -287,7 +257,7 @@ class LEFDEFFormatDeclaration virtual bool detect (tl::InputStream &stream) const { - return is_lef_format (stream.filename ()) || is_def_format (stream.filename ()); + return tl::match_filename_to_format (stream.filename (), file_format ()); } virtual db::ReaderBase *create_reader (tl::InputStream &s) const diff --git a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc index eb15ee319..e5552338c 100644 --- a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc +++ b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc @@ -467,15 +467,27 @@ TEST(100) run_test (_this, "issue-172", "lef:in.lef+def:in.def", "au.oas.gz", default_options (), false); } -TEST(101) +TEST(101a) { db::LEFDEFReaderOptions opt = default_options (); opt.set_produce_pin_names (true); opt.set_pin_property_name (2); opt.set_cell_outline_layer ("OUTLINE (13/0)"); + run_test (_this, "issue-489", "lef:in.lef+def:in.def", "au.oas", opt, false); } +TEST(101b) +{ + db::LEFDEFReaderOptions opt = default_options (); + opt.set_produce_pin_names (true); + opt.set_pin_property_name (2); + opt.set_cell_outline_layer ("OUTLINE (13/0)"); + + // also with fake suffix + run_test (_this, "issue-489", "lef:'in.l[lef]'+def:'in.d[def]'", "au.oas", opt, false); +} + TEST(102) { db::LEFDEFReaderOptions opt = default_options (); diff --git a/src/tl/tl/tlStream.cc b/src/tl/tl/tlStream.cc index 442218e28..4e52db055 100644 --- a/src/tl/tl/tlStream.cc +++ b/src/tl/tl/tlStream.cc @@ -48,6 +48,7 @@ #include "tlString.h" #include "tlUri.h" #include "tlHttpStream.h" +#include "tlGlobPattern.h" #if defined(HAVE_QT) # include @@ -304,6 +305,9 @@ InputStream::InputStream (InputStreamBase &delegate) m_bcap = 4096; // initial buffer capacity m_blen = 0; mp_buffer = new char [m_bcap]; + + m_explicit_suffix = false; + m_suffix = tl::extension (delegate.filename ()); } InputStream::InputStream (InputStreamBase *delegate) @@ -312,9 +316,14 @@ InputStream::InputStream (InputStreamBase *delegate) m_bcap = 4096; // initial buffer capacity m_blen = 0; mp_buffer = new char [m_bcap]; + + m_explicit_suffix = false; + if (delegate) { + m_suffix = tl::extension (delegate->filename ()); + } } -InputStream::InputStream (const std::string &abstract_path) +InputStream::InputStream (const std::string &abstract_path_in, bool allow_explicit_suffix) : m_pos (0), mp_bptr (0), mp_delegate (0), m_owns_delegate (false), mp_inflate (0), m_inflate_always (false), m_stop_after_inflate (false) { m_bcap = 4096; // initial buffer capacity @@ -323,6 +332,22 @@ InputStream::InputStream (const std::string &abstract_path) bool needs_inflate = false; + std::string abstract_path = abstract_path_in; + m_explicit_suffix = false; + + // extract override suffix if needed + if (allow_explicit_suffix) { + + tl::GlobPattern pat ("(*)\\[(*)\\]"); + std::vector pat_parts; + if (pat.match (abstract_path_in, pat_parts) && pat_parts.size () == 2) { + abstract_path = pat_parts[0]; + m_suffix = pat_parts[1]; + m_explicit_suffix = true; + } + + } + tl::Extractor ex (abstract_path.c_str ()); if (ex.test (":")) { @@ -404,6 +429,10 @@ InputStream::InputStream (const std::string &abstract_path) m_owns_delegate = true; + if (mp_delegate && ! m_explicit_suffix) { + m_suffix = tl::extension (mp_delegate->filename ()); + } + if (needs_inflate) { inflate_always (); } @@ -1696,7 +1725,7 @@ match_filename_to_format (const std::string &fn, const std::string &fmt) while (*fpp && *fpp != ' ' && *fpp != ')') { ++fpp; } - if (fn.size () > (unsigned int) (fpp - fp) && strncmp (fn.c_str () + fn.size () - (fpp - fp), fp, fpp - fp) == 0) { + if (fn.size () >= (unsigned int) (fpp - fp) && strncmp (fn.c_str () + fn.size () - (fpp - fp), fp, fpp - fp) == 0) { return true; } fp = fpp; diff --git a/src/tl/tl/tlStream.h b/src/tl/tl/tlStream.h index 9e2f03b61..70fac833c 100644 --- a/src/tl/tl/tlStream.h +++ b/src/tl/tl/tlStream.h @@ -408,9 +408,14 @@ public: * This will automatically create the appropriate delegate and * delete it later. * - * The abstract path + * @param abstract_path The abstract path (can be "pipe:", "data:", "file:...", "http(s):...". + * @param allow_explicit_suffix If true, extracts the suffix from the abstract path + * + * With explicit suffix, the abstract path can be appended an override suffix in the + * form "path[suffix]" (e.g. "file.any[gds]"). The override suffix can be accessed with + * the "suffix" accessor. If an explicit suffix is given, "is_explicit_suffix" is true. */ - InputStream (const std::string &abstract_path); + InputStream (const std::string &abstract_path, bool allow_explicit_suffix = true); /** * @brief Destructor @@ -528,6 +533,25 @@ public: return mp_delegate->absolute_path (); } + /** + * @brief Gets the suffix + * + * The suffix is either the override suffix or the + * filename's suffix if no override is given. + */ + const std::string &suffix () const + { + return m_suffix; + } + + /** + * @brief Gets a value indicating if an explicit suffix is given + */ + bool is_explicit_suffix () const + { + return m_explicit_suffix; + } + /** * @brief Reset to the initial position */ @@ -593,6 +617,9 @@ private: InputStreamBase *mp_delegate; bool m_owns_delegate; + std::string m_suffix; + bool m_explicit_suffix; + // inflate support InflateFilter *mp_inflate; bool m_inflate_always; diff --git a/src/tl/unit_tests/tlStreamTests.cc b/src/tl/unit_tests/tlStreamTests.cc index 0a5163d77..4396a102f 100644 --- a/src/tl/unit_tests/tlStreamTests.cc +++ b/src/tl/unit_tests/tlStreamTests.cc @@ -187,6 +187,29 @@ TEST(DataInputStream) EXPECT_EQ (tis.get_line (), "separated by a LFCR and CRLF."); EXPECT_EQ (tis.line_number (), size_t (4)); EXPECT_EQ (tis.at_end (), true); + + EXPECT_EQ (is.is_explicit_suffix (), false); + EXPECT_EQ (is.suffix (), ""); +} + +TEST(DataInputStreamWithSuffix) +{ + tl::InputStream is ("data:SGVsbG8sIHdvcmxkIQpXaXRoIGFub3RoZXIgbGluZQoNDQpzZXBhcmF0ZWQgYnkgYSBMRkNSIGFuZCBDUkxGLg==[txt]"); + tl::TextInputStream tis (is); + EXPECT_EQ (tis.get_line (), "Hello, world!"); + EXPECT_EQ (tis.line_number (), size_t (1)); + EXPECT_EQ (tis.get_line (), "With another line"); + EXPECT_EQ (tis.line_number (), size_t (2)); + EXPECT_EQ (tis.peek_char (), '\n'); + EXPECT_EQ (tis.get_line (), ""); + EXPECT_EQ (tis.line_number (), size_t (3)); + EXPECT_EQ (tis.peek_char (), 's'); + EXPECT_EQ (tis.get_line (), "separated by a LFCR and CRLF."); + EXPECT_EQ (tis.line_number (), size_t (4)); + EXPECT_EQ (tis.at_end (), true); + + EXPECT_EQ (is.is_explicit_suffix (), true); + EXPECT_EQ (is.suffix (), "txt"); } namespace @@ -588,3 +611,16 @@ TEST(AbstractPathFunctions) EXPECT_EQ (tl::InputStream::as_file_path ("a\\b\\c"), "a\\b\\c"); tl::file_utils_force_reset (); } + +TEST(MatchFormat) +{ + EXPECT_EQ (tl::match_filename_to_format ("abc.txt", "Text files (*.txt *.TXT)"), true); + EXPECT_EQ (tl::match_filename_to_format ("abc.txt", "Text files (*.txt)"), true); + EXPECT_EQ (tl::match_filename_to_format ("abc.txt", "Text files (*.TXT)"), false); + EXPECT_EQ (tl::match_filename_to_format (".txt", "Text files (*.txt *.TXT)"), true); + EXPECT_EQ (tl::match_filename_to_format ("/home/xyz/abc.txt", "Text files (*.txt *.TXT)"), true); + EXPECT_EQ (tl::match_filename_to_format ("txt", "Text files (*.txt *.TXT)"), false); + EXPECT_EQ (tl::match_filename_to_format ("abc.TXT", "Text files (*.txt *.TXT)"), true); + EXPECT_EQ (tl::match_filename_to_format ("abc.TEXT", "Text files (*.txt *.TXT)"), false); + EXPECT_EQ (tl::match_filename_to_format ("abc.TEXT", "Text files (*)"), true); +} diff --git a/testdata/lefdef/issue-489/in.d b/testdata/lefdef/issue-489/in.d new file mode 100644 index 000000000..6220e6ab0 --- /dev/null +++ b/testdata/lefdef/issue-489/in.d @@ -0,0 +1,34 @@ +VERSION 5.7 ; +DIVIDERCHAR "/" ; +BUSBITCHARS "[]" ; +DESIGN test ; +UNITS DISTANCE MICRONS 2000 ; + +DIEAREA ( 0 0 ) ( 37520 7840 ) ; + +PINS 12 ; +- VDD + NET VDD + SPECIAL + DIRECTION INOUT + USE POWER + + LAYER M2 ( -320 0 ) ( 320 37520 ) ++ FIXED ( 37520 3920 ) W ; +- VSS + NET VSS + SPECIAL + DIRECTION INOUT + USE GROUND + + PORT + + LAYER M2 ( -18760 0 ) ( 18760 640 ) ++ FIXED ( 18760 -320 ) N + + PORT + + LAYER M2 ( -18760 0 ) ( 18760 640 ) ++ FIXED ( 18760 8160 ) S + ; +END PINS + +SPECIALNETS 4 ; +- VSS ( * VSS ) ++ ROUTED M2 640 + SHAPE FOLLOWPIN ( 0 0 ) ( 37520 * ) +NEW M2 640 + SHAPE FOLLOWPIN ( 0 7840 ) ( 37520 * ) + + USE GROUND + ; +- VDD ( * VDD ) ++ ROUTED M2 640 + SHAPE FOLLOWPIN ( 0 3920 ) ( 37520 * ) + + USE POWER + ; +END SPECIALNETS +END DESIGN diff --git a/testdata/lefdef/issue-489/in.l b/testdata/lefdef/issue-489/in.l new file mode 100644 index 000000000..3e497ca36 --- /dev/null +++ b/testdata/lefdef/issue-489/in.l @@ -0,0 +1,63 @@ +VERSION 5.7 ; + +BUSBITCHARS "[]" ; +DIVIDERCHAR "/" ; +UNITS + DATABASE MICRONS 1000 ; +END UNITS + +MANUFACTURINGGRID 0.002 ; + +USEMINSPACING OBS OFF ; + +LAYER overlap + TYPE OVERLAP ; +END overlap + +LAYER contact + TYPE CUT ; +END contact + +LAYER metal1 + TYPE ROUTING ; + DIRECTION HORIZONTAL ; +END metal1 + +LAYER via1 + TYPE CUT ; +END via1 + +LAYER metal2 + TYPE ROUTING ; + DIRECTION VERTICAL ; +END metal2 + +LAYER via2 + TYPE CUT ; +END via2 + +LAYER metal3 + TYPE ROUTING ; + DIRECTION HORIZONTAL ; +END metal3 + +LAYER via3 + TYPE CUT ; +END via3 + +LAYER metal4 + TYPE ROUTING ; + DIRECTION VERTICAL ; +END metal4 + +LAYER via4 + TYPE CUT ; +END via4 + +LAYER metal5 + TYPE ROUTING ; + DIRECTION HORIZONTAL ; +END metal5 + + +END LIBRARY diff --git a/testdata/lefdef/strm2oas_small/au.gds b/testdata/lefdef/strm2oas_small/au.gds new file mode 100644 index 0000000000000000000000000000000000000000..f6602f376de3776ebe8a9c2ea686fef64ebc0ce8 GIT binary patch literal 576 zcmZQzV_;&6V31*CVt>WJ%pk%bz#zq-fXrs#U}E#}bYfr-VP>^+>@@d2w)}&o%MSeo zv!g;7WLRMv2hj{Hf(&r|%oOTpLDv7V=l}n|F8}|3>|tPF+VlVacNQ2869>^K z_M_+r>4jjBK4c6N$ETlxf#VNY9!Nv<@|D17m^eZ|I|JVE=3-+D_I32}0frP81A~AN sYUuL_Ff%X+cYrZ+Xfh&)#=k57|3feXgN6+" ; +DESIGN SMALL ; +UNITS DISTANCE MICRONS 100 ; + +DIEAREA ( -30 -30 ) ( 1030 1030 ) ; + +VIAS 1 ; +- via1_960x340 ++ VIARULE Via1Array-0 + + CUTSIZE 140 140 + + LAYERS metal1 via1 metal2 + + CUTSPACING 160 160 + + ENCLOSURE 110 100 110 100 + + ROWCOL 1 3 +; +END VIAS + +SPECIALNETS 1 ; +- VDD ( * VDD ) ++ ROUTED metal1 0 + SHAPE STRIPE ( 500 500 ) via1_960x340 ++ USE POWER ; +END SPECIALNETS + +END DESIGN + From 94908162d6890b8744e66f297f2047293bcdd560 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 20 Mar 2026 16:48:35 +0100 Subject: [PATCH 3/6] LEF/DEF suffixes can be configured The way to specify them is through the following environment variables: $KLAYOUT_LEF_FORMAT - LEF suffixes (default: "*.lef *.LEF *.lef.gz *.LEF.gz") $KLAYOUT_DEF_FORMAT - DEF suffixes (default: "*.def *.DEF *.def.gz *.DEF.gz") The string is a space-separated list of simple match pattern. Only "*.xyz"-style pattern are allowed currently. --- .../lefdef/db_plugin/dbLEFDEFPlugin.cc | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc index ebba33d50..ad465aec6 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc @@ -24,6 +24,7 @@ #include "tlTimer.h" #include "tlStream.h" #include "tlFileUtils.h" +#include "tlEnv.h" #include "dbReader.h" #include "dbStream.h" @@ -40,14 +41,31 @@ namespace db // --------------------------------------------------------------- // Plugin for the stream reader +static std::string lef_file_formats () +{ + static std::string s; + if (s.empty ()) { + s = tl::get_env ("KLAYOUT_LEF_FORMAT", "*.lef *.LEF *.lef.gz *.LEF.gz"); + } + return s; +} + +static std::string def_file_formats () +{ + static std::string s; + if (s.empty ()) { + s = tl::get_env ("KLAYOUT_DEF_FORMAT", "*.def *.DEF *.def.gz *.DEF.gz"); + } + return s; +} + /** * @brief Determines the format of the given stream * Returns true, if the stream has LEF format */ static bool is_lef_format (const std::string &fn) { - static std::string lef_format ("LEF files (*.lef *.LEF *.lef.gz *.LEF.gz)"); - return tl::match_filename_to_format (fn, lef_format); + return tl::match_filename_to_format (fn, std::string ("LEF files (") + lef_file_formats () + ")"); } // --------------------------------------------------------------- @@ -253,7 +271,7 @@ class LEFDEFFormatDeclaration virtual std::string format_name () const { return "LEFDEF"; } virtual std::string format_desc () const { return "LEF/DEF"; } virtual std::string format_title () const { return "LEF/DEF (unified reader)"; } - virtual std::string file_format () const { return "LEF/DEF files (*.lef *.LEF *.lef.gz *.LEF.gz *.def *.DEF *.def.gz *.DEF.gz)"; } + virtual std::string file_format () const { return std::string ("LEF/DEF files (") + lef_file_formats () + " " + def_file_formats () + ")"; } virtual bool detect (tl::InputStream &stream) const { From c311906298e0e56cb5af4affae9e5ae5910a6bcf Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 20 Mar 2026 16:58:53 +0100 Subject: [PATCH 4/6] Some refactoring --- src/db/db/db.pro | 1 + src/db/db/gsiDeclDbLayout.cc | 232 --------------------- src/db/db/gsiDeclDbSaveLayoutOptions.cc | 262 ++++++++++++++++++++++++ 3 files changed, 263 insertions(+), 232 deletions(-) create mode 100644 src/db/db/gsiDeclDbSaveLayoutOptions.cc diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 188abf0d8..ce2f3cc3e 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -152,6 +152,7 @@ SOURCES = \ gsiDeclDbRecursiveInstanceIterator.cc \ gsiDeclDbRecursiveShapeIterator.cc \ gsiDeclDbRegion.cc \ + gsiDeclDbSaveLayoutOptions.cc \ gsiDeclDbShape.cc \ gsiDeclDbShapeProcessor.cc \ gsiDeclDbShapes.cc \ diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc index 54c97cded..9d76ae5e9 100644 --- a/src/db/db/gsiDeclDbLayout.cc +++ b/src/db/db/gsiDeclDbLayout.cc @@ -2864,236 +2864,4 @@ Class decl_Layout ("db", "Layout", "@/code\n" ); -static db::SaveLayoutOptions *new_v () -{ - return new db::SaveLayoutOptions (); -} - -static std::string set_format_from_filename (db::SaveLayoutOptions *opt, const std::string &fn) -{ - auto ff = opt->set_format_from_filename (fn); - if (! ff.first) { - throw tl::Exception (tl::to_string (tr ("Cannot determine format from filename"))); - } - return ff.second; -} - -Class decl_SaveLayoutOptions ("db", "SaveLayoutOptions", - gsi::constructor ("new", &new_v, - "@brief Default constructor\n" - "\n" - "This will initialize the scale factor to 1.0, the database unit is set to\n" - "\"same as original\" and all layers are selected as well as all cells.\n" - "The default format is GDS2." - ) + - gsi::method_ext ("set_format_from_filename", &set_format_from_filename, gsi::arg ("filename"), - "@brief Select a format from the given file name\n" - "\n" - "This method will set the format according to the file's extension.\n" - "\n" - "This method has been introduced in version 0.22. " - "Beginning with version 0.23, this method always returns true, since the " - "only consumer for the return value, Layout#write, now ignores that " - "parameter and automatically determines the compression mode from the file name.\n" - "\n" - "Starting with version 0.30.7, this method allows specifying the desired format's extension in square brackets " - "after the file name (e.g. 'file.txt[def]'). This allows writing files with non-standard extensions. " - "The return value of this function now the is actual file name used without the square brackets ('file.txt' in the example case)." - ) + - gsi::method ("format=", &db::SaveLayoutOptions::set_format, gsi::arg ("format"), - "@brief Select a format\n" - "The format string can be either \"GDS2\", \"OASIS\", \"CIF\" or \"DXF\". Other formats may be available if\n" - "a suitable plugin is installed." - ) + - gsi::method ("format", &db::SaveLayoutOptions::format, - "@brief Gets the format name\n" - "\n" - "See \\format= for a description of that method.\n" - ) + - gsi::method ("add_layer", &db::SaveLayoutOptions::add_layer, gsi::arg ("layer_index"), gsi::arg ("properties"), - "@brief Add a layer to be saved \n" - "\n" - "\n" - "Adds the layer with the given index to the layer list that will be written.\n" - "If all layers have been selected previously, all layers will \n" - "be unselected first and only the new layer remains.\n" - "\n" - "The 'properties' argument can be used to assign different layer properties than the ones\n" - "present in the layout. Pass a default \\LayerInfo object to this argument to use the\n" - "properties from the layout object. Construct a valid \\LayerInfo object with explicit layer,\n" - "datatype and possibly a name to override the properties stored in the layout.\n" - ) + - gsi::method ("select_all_layers", &db::SaveLayoutOptions::select_all_layers, - "@brief Select all layers to be saved\n" - "\n" - "This method will clear all layers selected with \\add_layer so far and set the 'select all layers' flag.\n" - "This is the default.\n" - ) + - gsi::method ("deselect_all_layers", &db::SaveLayoutOptions::deselect_all_layers, - "@brief Unselect all layers: no layer will be saved\n" - "\n" - "This method will clear all layers selected with \\add_layer so far and clear the 'select all layers' flag.\n" - "Using this method is the only way to save a layout without any layers." - ) + - gsi::method ("select_cell", &db::SaveLayoutOptions::select_cell, gsi::arg ("cell_index"), - "@brief Selects a cell to be saved (plus hierarchy below)\n" - "\n" - "\n" - "This method is basically a convenience method that combines \\clear_cells and \\add_cell.\n" - "This method clears the 'select all cells' flag.\n" - "\n" - "This method has been added in version 0.22.\n" - ) + - gsi::method ("select_this_cell", &db::SaveLayoutOptions::select_this_cell, gsi::arg ("cell_index"), - "@brief Selects a cell to be saved\n" - "\n" - "\n" - "This method is basically a convenience method that combines \\clear_cells and \\add_this_cell.\n" - "This method clears the 'select all cells' flag.\n" - "\n" - "This method has been added in version 0.23.\n" - ) + - gsi::method ("clear_cells", &db::SaveLayoutOptions::clear_cells, - "@brief Clears all cells to be saved\n" - "\n" - "This method can be used to ensure that no cell is selected before \\add_cell is called to specify a cell.\n" - "This method clears the 'select all cells' flag.\n" - "\n" - "This method has been added in version 0.22.\n" - ) + - gsi::method ("add_this_cell", &db::SaveLayoutOptions::add_this_cell, gsi::arg ("cell_index"), - "@brief Adds a cell to be saved\n" - "\n" - "\n" - "The index of the cell must be a valid index in the context of the layout that will be saved.\n" - "This method clears the 'select all cells' flag.\n" - "Unlike \\add_cell, this method does not implicitly add all children of that cell.\n" - "\n" - "This method has been added in version 0.23.\n" - ) + - gsi::method ("add_cell", &db::SaveLayoutOptions::add_cell, gsi::arg ("cell_index"), - "@brief Add a cell (plus hierarchy) to be saved\n" - "\n" - "\n" - "The index of the cell must be a valid index in the context of the layout that will be saved.\n" - "This method clears the 'select all cells' flag.\n" - "\n" - "This method also implicitly adds the children of that cell. A method that does not add the " - "children in \\add_this_cell.\n" - ) + - gsi::method ("select_all_cells", &db::SaveLayoutOptions::select_all_cells, - "@brief Select all cells to save\n" - "\n" - "This method will clear all cells specified with \\add_cells so far and set the 'select all cells' flag.\n" - "This is the default.\n" - ) + - gsi::method ("write_context_info=", &db::SaveLayoutOptions::set_write_context_info, gsi::arg ("flag"), - "@brief Enables or disables context information\n" - "\n" - "If this flag is set to false, no context information for PCell or library cell instances is written. " - "Those cells will be converted to plain cells and KLayout will not be able to restore the identity of " - "those cells. Use this option to enforce compatibility with other tools that don't understand the " - "context information of KLayout.\n" - "\n" - "The default value is true (context information is stored). Not all formats support context information, hence " - "that flag has no effect for formats like CIF or DXF.\n" - "\n" - "This method was introduced in version 0.23.\n" - ) + - gsi::method ("write_context_info?", &db::SaveLayoutOptions::write_context_info, - "@brief Gets a flag indicating whether context information will be stored\n" - "\n" - "See \\write_context_info= for details about this flag.\n" - "\n" - "This method was introduced in version 0.23.\n" - ) + - gsi::method ("keep_instances=", &db::SaveLayoutOptions::set_keep_instances, gsi::arg ("flag"), - "@brief Enables or disables instances for dropped cells\n" - "\n" - "If this flag is set to true, instances for cells will be written, even if the cell is dropped. " - "That may happen, if cells are selected with \\select_this_cell or \\add_this_cell or \\no_empty_cells is used. " - "Even if cells called by such cells are not selected, instances will be written for that " - "cell if \"keep_instances\" is true. That feature is supported by the GDS format currently and " - "results in \"ghost cells\" which have instances but no cell definition.\n" - "\n" - "The default value is false (instances of dropped cells are not written).\n" - "\n" - "This method was introduced in version 0.23.\n" - ) + - gsi::method ("keep_instances?", &db::SaveLayoutOptions::keep_instances, - "@brief Gets a flag indicating whether instances will be kept even if the target cell is dropped\n" - "\n" - "See \\keep_instances= for details about this flag.\n" - "\n" - "This method was introduced in version 0.23.\n" - ) + - gsi::method ("dbu=", &db::SaveLayoutOptions::set_dbu, gsi::arg ("dbu"), - "@brief Sets the database unit to be used in the stream file\n" - "\n" - "By default, the database unit of the layout is used. This method allows one to explicitly use a different\n" - "database unit. A scale factor is introduced automatically which scales all layout objects accordingly so their physical dimensions remain the same. " - "When scaling to a larger database unit or one that is not an integer fraction of the original one, rounding errors may occur and the " - "layout may become slightly distorted." - ) + - gsi::method ("dbu", &db::SaveLayoutOptions::dbu, - "@brief Gets the explicit database unit if one is set\n" - "\n" - "See \\dbu= for a description of that attribute.\n" - ) + - gsi::method ("no_empty_cells=", &db::SaveLayoutOptions::set_dont_write_empty_cells, gsi::arg ("flag"), - "@brief Don't write empty cells if this flag is set\n" - "\n" - "By default, all cells are written (no_empty_cells is false).\n" - "This applies to empty cells which do not contain shapes for the specified layers " - "as well as cells which are empty because they reference empty cells only.\n" - ) + - gsi::method ("no_empty_cells?", &db::SaveLayoutOptions::dont_write_empty_cells, - "@brief Returns a flag indicating whether empty cells are not written.\n" - ) + - gsi::method ("libname=|#gds2_libname=", &db::SaveLayoutOptions::set_libname, gsi::arg ("libname"), - "@brief Sets the library name\n" - "\n" - "The library name is an attribute and specifies a formal name for a library, if the layout files is to be used as one.\n" - "Currently, this attribute is only supported by the GDS2 format. Hence the alias.\n" - "\n" - "By default or if the libname is an empty string, the current library name of the layout or 'LIB' is used.\n" - "\n" - "The 'libname' alias has been introduced in version 0.30.5. The original name \\gds2_libname= is still available." - ) + - gsi::method ("libname|#gds2_libname", &db::SaveLayoutOptions::libname, - "@brief Gets the library name\n" - "\n" - "See \\libname= for details.\n" - "The 'libname' alias has been introduced in version 0.30.5. The original name \\gds2_libname is still available." - ) + - gsi::method ("scale_factor=", &db::SaveLayoutOptions::set_scale_factor, gsi::arg ("scale_factor"), - "@brief Sets the scaling factor for the saving \n" - "\n" - "Using a scaling factor will scale all objects accordingly. " - "This scale factor adds to a potential scaling implied by using an explicit database unit.\n" - "\n" - "Be aware that rounding effects may occur if fractional scaling factors are used.\n" - "\n" - "By default, no scaling is applied." - ) + - gsi::method ("scale_factor", &db::SaveLayoutOptions::scale_factor, - "@brief Gets the scaling factor currently set\n" - ), - "@brief Options for saving layouts\n" - "\n" - "This class describes the various options for saving a layout to a stream file (GDS2, OASIS and others).\n" - "There are: layers to be saved, cell or cells to be saved, scale factor, format, database unit\n" - "and format specific options.\n" - "\n" - "Usually the default constructor provides a suitable object. Please note, that the format written is \"GDS2\" by default. Either explicitly set a " - "format using \\format= or derive the format from the file name using \\set_format_from_filename.\n" - "\n" - "The layers are specified by either selecting all layers or by defining layer by layer using the\n" - "\\add_layer method. \\select_all_layers will explicitly select all layers for saving, \\deselect_all_layers will explicitly clear the list of layers.\n" - "\n" - "Cells are selected in a similar fashion: by default, all cells are selected. Using \\add_cell, specific\n" - "cells can be selected for saving. All these cells plus their hierarchy will then be written to the stream file.\n" - "\n" -); - } diff --git a/src/db/db/gsiDeclDbSaveLayoutOptions.cc b/src/db/db/gsiDeclDbSaveLayoutOptions.cc new file mode 100644 index 000000000..c6c445db4 --- /dev/null +++ b/src/db/db/gsiDeclDbSaveLayoutOptions.cc @@ -0,0 +1,262 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2026 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 "gsiDecl.h" +#include "dbSaveLayoutOptions.h" + +namespace gsi +{ + +static db::SaveLayoutOptions *new_v () +{ + return new db::SaveLayoutOptions (); +} + +static std::string set_format_from_filename (db::SaveLayoutOptions *opt, const std::string &fn) +{ + auto ff = opt->set_format_from_filename (fn); + if (! ff.first) { + throw tl::Exception (tl::to_string (tr ("Cannot determine format from filename"))); + } + return ff.second; +} + +Class decl_SaveLayoutOptions ("db", "SaveLayoutOptions", + gsi::constructor ("new", &new_v, + "@brief Default constructor\n" + "\n" + "This will initialize the scale factor to 1.0, the database unit is set to\n" + "\"same as original\" and all layers are selected as well as all cells.\n" + "The default format is GDS2." + ) + + gsi::method_ext ("set_format_from_filename", &set_format_from_filename, gsi::arg ("filename"), + "@brief Select a format from the given file name\n" + "\n" + "This method will set the format according to the file's extension.\n" + "\n" + "This method has been introduced in version 0.22. " + "Beginning with version 0.23, this method always returns true, since the " + "only consumer for the return value, Layout#write, now ignores that " + "parameter and automatically determines the compression mode from the file name.\n" + "\n" + "Starting with version 0.30.8, this method allows specifying the desired format's extension in square brackets " + "after the file name (e.g. 'file.txt[def]'). This allows writing files with non-standard extensions. " + "The return value of this function now is the actual file name used without the square brackets ('file.txt' in the example case)." + ) + + gsi::method ("format=", &db::SaveLayoutOptions::set_format, gsi::arg ("format"), + "@brief Select a format\n" + "The format string can be either \"GDS2\", \"OASIS\", \"CIF\" or \"DXF\". Other formats may be available if\n" + "a suitable plugin is installed." + ) + + gsi::method ("format", &db::SaveLayoutOptions::format, + "@brief Gets the format name\n" + "\n" + "See \\format= for a description of that method.\n" + ) + + gsi::method ("add_layer", &db::SaveLayoutOptions::add_layer, gsi::arg ("layer_index"), gsi::arg ("properties"), + "@brief Add a layer to be saved \n" + "\n" + "\n" + "Adds the layer with the given index to the layer list that will be written.\n" + "If all layers have been selected previously, all layers will \n" + "be unselected first and only the new layer remains.\n" + "\n" + "The 'properties' argument can be used to assign different layer properties than the ones\n" + "present in the layout. Pass a default \\LayerInfo object to this argument to use the\n" + "properties from the layout object. Construct a valid \\LayerInfo object with explicit layer,\n" + "datatype and possibly a name to override the properties stored in the layout.\n" + ) + + gsi::method ("select_all_layers", &db::SaveLayoutOptions::select_all_layers, + "@brief Select all layers to be saved\n" + "\n" + "This method will clear all layers selected with \\add_layer so far and set the 'select all layers' flag.\n" + "This is the default.\n" + ) + + gsi::method ("deselect_all_layers", &db::SaveLayoutOptions::deselect_all_layers, + "@brief Unselect all layers: no layer will be saved\n" + "\n" + "This method will clear all layers selected with \\add_layer so far and clear the 'select all layers' flag.\n" + "Using this method is the only way to save a layout without any layers." + ) + + gsi::method ("select_cell", &db::SaveLayoutOptions::select_cell, gsi::arg ("cell_index"), + "@brief Selects a cell to be saved (plus hierarchy below)\n" + "\n" + "\n" + "This method is basically a convenience method that combines \\clear_cells and \\add_cell.\n" + "This method clears the 'select all cells' flag.\n" + "\n" + "This method has been added in version 0.22.\n" + ) + + gsi::method ("select_this_cell", &db::SaveLayoutOptions::select_this_cell, gsi::arg ("cell_index"), + "@brief Selects a cell to be saved\n" + "\n" + "\n" + "This method is basically a convenience method that combines \\clear_cells and \\add_this_cell.\n" + "This method clears the 'select all cells' flag.\n" + "\n" + "This method has been added in version 0.23.\n" + ) + + gsi::method ("clear_cells", &db::SaveLayoutOptions::clear_cells, + "@brief Clears all cells to be saved\n" + "\n" + "This method can be used to ensure that no cell is selected before \\add_cell is called to specify a cell.\n" + "This method clears the 'select all cells' flag.\n" + "\n" + "This method has been added in version 0.22.\n" + ) + + gsi::method ("add_this_cell", &db::SaveLayoutOptions::add_this_cell, gsi::arg ("cell_index"), + "@brief Adds a cell to be saved\n" + "\n" + "\n" + "The index of the cell must be a valid index in the context of the layout that will be saved.\n" + "This method clears the 'select all cells' flag.\n" + "Unlike \\add_cell, this method does not implicitly add all children of that cell.\n" + "\n" + "This method has been added in version 0.23.\n" + ) + + gsi::method ("add_cell", &db::SaveLayoutOptions::add_cell, gsi::arg ("cell_index"), + "@brief Add a cell (plus hierarchy) to be saved\n" + "\n" + "\n" + "The index of the cell must be a valid index in the context of the layout that will be saved.\n" + "This method clears the 'select all cells' flag.\n" + "\n" + "This method also implicitly adds the children of that cell. A method that does not add the " + "children in \\add_this_cell.\n" + ) + + gsi::method ("select_all_cells", &db::SaveLayoutOptions::select_all_cells, + "@brief Select all cells to save\n" + "\n" + "This method will clear all cells specified with \\add_cells so far and set the 'select all cells' flag.\n" + "This is the default.\n" + ) + + gsi::method ("write_context_info=", &db::SaveLayoutOptions::set_write_context_info, gsi::arg ("flag"), + "@brief Enables or disables context information\n" + "\n" + "If this flag is set to false, no context information for PCell or library cell instances is written. " + "Those cells will be converted to plain cells and KLayout will not be able to restore the identity of " + "those cells. Use this option to enforce compatibility with other tools that don't understand the " + "context information of KLayout.\n" + "\n" + "The default value is true (context information is stored). Not all formats support context information, hence " + "that flag has no effect for formats like CIF or DXF.\n" + "\n" + "This method was introduced in version 0.23.\n" + ) + + gsi::method ("write_context_info?", &db::SaveLayoutOptions::write_context_info, + "@brief Gets a flag indicating whether context information will be stored\n" + "\n" + "See \\write_context_info= for details about this flag.\n" + "\n" + "This method was introduced in version 0.23.\n" + ) + + gsi::method ("keep_instances=", &db::SaveLayoutOptions::set_keep_instances, gsi::arg ("flag"), + "@brief Enables or disables instances for dropped cells\n" + "\n" + "If this flag is set to true, instances for cells will be written, even if the cell is dropped. " + "That may happen, if cells are selected with \\select_this_cell or \\add_this_cell or \\no_empty_cells is used. " + "Even if cells called by such cells are not selected, instances will be written for that " + "cell if \"keep_instances\" is true. That feature is supported by the GDS format currently and " + "results in \"ghost cells\" which have instances but no cell definition.\n" + "\n" + "The default value is false (instances of dropped cells are not written).\n" + "\n" + "This method was introduced in version 0.23.\n" + ) + + gsi::method ("keep_instances?", &db::SaveLayoutOptions::keep_instances, + "@brief Gets a flag indicating whether instances will be kept even if the target cell is dropped\n" + "\n" + "See \\keep_instances= for details about this flag.\n" + "\n" + "This method was introduced in version 0.23.\n" + ) + + gsi::method ("dbu=", &db::SaveLayoutOptions::set_dbu, gsi::arg ("dbu"), + "@brief Sets the database unit to be used in the stream file\n" + "\n" + "By default, the database unit of the layout is used. This method allows one to explicitly use a different\n" + "database unit. A scale factor is introduced automatically which scales all layout objects accordingly so their physical dimensions remain the same. " + "When scaling to a larger database unit or one that is not an integer fraction of the original one, rounding errors may occur and the " + "layout may become slightly distorted." + ) + + gsi::method ("dbu", &db::SaveLayoutOptions::dbu, + "@brief Gets the explicit database unit if one is set\n" + "\n" + "See \\dbu= for a description of that attribute.\n" + ) + + gsi::method ("no_empty_cells=", &db::SaveLayoutOptions::set_dont_write_empty_cells, gsi::arg ("flag"), + "@brief Don't write empty cells if this flag is set\n" + "\n" + "By default, all cells are written (no_empty_cells is false).\n" + "This applies to empty cells which do not contain shapes for the specified layers " + "as well as cells which are empty because they reference empty cells only.\n" + ) + + gsi::method ("no_empty_cells?", &db::SaveLayoutOptions::dont_write_empty_cells, + "@brief Returns a flag indicating whether empty cells are not written.\n" + ) + + gsi::method ("libname=|#gds2_libname=", &db::SaveLayoutOptions::set_libname, gsi::arg ("libname"), + "@brief Sets the library name\n" + "\n" + "The library name is an attribute and specifies a formal name for a library, if the layout files is to be used as one.\n" + "Currently, this attribute is only supported by the GDS2 format. Hence the alias.\n" + "\n" + "By default or if the libname is an empty string, the current library name of the layout or 'LIB' is used.\n" + "\n" + "The 'libname' alias has been introduced in version 0.30.5. The original name \\gds2_libname= is still available." + ) + + gsi::method ("libname|#gds2_libname", &db::SaveLayoutOptions::libname, + "@brief Gets the library name\n" + "\n" + "See \\libname= for details.\n" + "The 'libname' alias has been introduced in version 0.30.5. The original name \\gds2_libname is still available." + ) + + gsi::method ("scale_factor=", &db::SaveLayoutOptions::set_scale_factor, gsi::arg ("scale_factor"), + "@brief Sets the scaling factor for the saving \n" + "\n" + "Using a scaling factor will scale all objects accordingly. " + "This scale factor adds to a potential scaling implied by using an explicit database unit.\n" + "\n" + "Be aware that rounding effects may occur if fractional scaling factors are used.\n" + "\n" + "By default, no scaling is applied." + ) + + gsi::method ("scale_factor", &db::SaveLayoutOptions::scale_factor, + "@brief Gets the scaling factor currently set\n" + ), + "@brief Options for saving layouts\n" + "\n" + "This class describes the various options for saving a layout to a stream file (GDS2, OASIS and others).\n" + "There are: layers to be saved, cell or cells to be saved, scale factor, format, database unit\n" + "and format specific options.\n" + "\n" + "Usually the default constructor provides a suitable object. Please note, that the format written is \"GDS2\" by default. Either explicitly set a " + "format using \\format= or derive the format from the file name using \\set_format_from_filename.\n" + "\n" + "The layers are specified by either selecting all layers or by defining layer by layer using the\n" + "\\add_layer method. \\select_all_layers will explicitly select all layers for saving, \\deselect_all_layers will explicitly clear the list of layers.\n" + "\n" + "Cells are selected in a similar fashion: by default, all cells are selected. Using \\add_cell, specific\n" + "cells can be selected for saving. All these cells plus their hierarchy will then be written to the stream file.\n" + "\n" +); + +} From cb9e25f1b35af4ce6454c585f19b54b6d9e0a4f4 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 21 Mar 2026 09:25:17 +0100 Subject: [PATCH 5/6] Issue #2300: added documentation about KLAYOUT_DEF_FORMAT and KLAYOUT_LEF_FORMAT environment variables. --- src/buddies/src/bd/strmclip.cc | 4 +- src/buddies/src/bd/strmxor.cc | 4 +- src/lay/lay/layApplication.cc | 107 ++++++++++++++++++++------------- 3 files changed, 72 insertions(+), 43 deletions(-) diff --git a/src/buddies/src/bd/strmclip.cc b/src/buddies/src/bd/strmclip.cc index 3af66f445..7f8d232da 100644 --- a/src/buddies/src/bd/strmclip.cc +++ b/src/buddies/src/bd/strmclip.cc @@ -176,7 +176,9 @@ BD_PUBLIC int strmclip (int argc, char *argv[]) << 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\"." + "\".oas\", \".gds.gz\", \".dxf\" or \".gds2\". You can also use any name, and specify the " + "desired suffix in square brackets after the file name. For example, 'file.clip[oas]' will " + "create an OASIS file called 'file.clip'." ) << 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." diff --git a/src/buddies/src/bd/strmxor.cc b/src/buddies/src/bd/strmxor.cc index b3437705a..1fb24be03 100644 --- a/src/buddies/src/bd/strmxor.cc +++ b/src/buddies/src/bd/strmxor.cc @@ -408,7 +408,9 @@ BD_PUBLIC int strmxor (int argc, char *argv[]) << tl::arg ("?output", &output, "The output file to which the XOR differences are written", "This argument is optional. If not given, the exit status alone will indicate whether the layouts " "are identical or not. The output is a layout file. The format of the file is derived " - "from the file name's suffix (.oas[.gz] for (gzipped) OASIS, .gds[.gz] for (gzipped) GDS2 etc.)." + "from the file name's suffix (.oas for OASIS, .gds[.gz] for (gzipped) GDS2 etc.). " + "You can also use any name, and specify the desired suffix in square brackets after the file name. " + "For example, 'file.xor[oas]' will create an OASIS file called 'file.xor'." ) << tl::arg ("-ta|--top-a=name", &top_a, "Specifies the top cell for the first layout", "Use this option to take a specific cell as the top cell from the first layout. All " diff --git a/src/lay/lay/layApplication.cc b/src/lay/lay/layApplication.cc index 8985f7041..63b6450cf 100644 --- a/src/lay/lay/layApplication.cc +++ b/src/lay/lay/layApplication.cc @@ -1060,47 +1060,72 @@ ApplicationBase::usage () { std::string r; r = std::string (lay::Version::exe_name ()) + " [] [] ..\n"; - r += tl::to_string (QObject::tr ("options")) + "\n"; - r += tl::to_string (QObject::tr (" -b Batch mode (same as -zz -nc -rx)")) + "\n"; - r += tl::to_string (QObject::tr (" -c Use this configuration file")) + "\n"; - r += tl::to_string (QObject::tr (" -nc Don't use a configuration file (implies -t)")) + "\n"; - r += tl::to_string (QObject::tr (" -d Set log level")) + "\n"; - r += tl::to_string (QObject::tr (" -e Editable mode (allow editing of files)")) + "\n"; - r += tl::to_string (QObject::tr (" -ne Readonly mode (editing of files is disabled)")) + "\n"; - r += tl::to_string (QObject::tr (" -gr Record GUI test file")) + "\n"; - r += tl::to_string (QObject::tr (" -gp Replay GUI test file")) + "\n"; - r += tl::to_string (QObject::tr (" -gb Replay GUI test file up to (including) line")) + "\n"; - r += tl::to_string (QObject::tr (" -gx Replay rate for GUI test file")) + "\n"; - r += tl::to_string (QObject::tr (" -gi Incremental logs for GUI test file")) + "\n"; - r += tl::to_string (QObject::tr (" -i Disable undo buffering (less memory requirements)")) + "\n"; - r += tl::to_string (QObject::tr (" -ni Enable undo buffering (default, overrides previous -i option)")) + "\n"; - r += tl::to_string (QObject::tr (" -j Add the given path to the macro project paths")) + "\n"; - r += tl::to_string (QObject::tr (" -k Write log to the given file plus stdout/stderr")) + "\n"; - r += tl::to_string (QObject::tr (" -l Use layer properties file")) + "\n"; - r += tl::to_string (QObject::tr (" -lx With -l: add other layers as well")) + "\n"; - r += tl::to_string (QObject::tr (" -lf With -l: use the lyp file as it is (no expansion to multiple layouts)")) + "\n"; - r += tl::to_string (QObject::tr (" -m Load RDB (report database) file (into previous layout view)")) + "\n"; - r += tl::to_string (QObject::tr (" -mn Load L2NDB (layout to netlist database) file (into previous layout view)")) + "\n"; - r += tl::to_string (QObject::tr (" -n Technology to use for next layout(s) on command line")) + "\n"; - r += tl::to_string (QObject::tr (" -nn Technology file (.lyt) to use for next layout(s) on command line")) + "\n"; - r += tl::to_string (QObject::tr (" -p Load the plugin (can be used multiple times)")) + "\n"; - r += tl::to_string (QObject::tr (" -r