Skip to content

Commit 9fd7346

Browse files
authored
Merge pull request #10057 from hzeller/feature-20260405-breakout-rd
Break out Tcl readline set-up into separate file.
2 parents 29d97c4 + a982a18 commit 9fd7346

5 files changed

Lines changed: 157 additions & 105 deletions

File tree

BUILD.bazel

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,20 @@ OPENROAD_DEFINES = [
160160
"ABC_NAMESPACE=abc",
161161
]
162162

163+
cc_library(
164+
name = "tcl_readline_setup",
165+
srcs = ["src/tcl_readline_setup.cc"],
166+
hdrs = ["src/tcl_readline_setup.h"],
167+
deps = [
168+
"@tcl_lang//:tcl",
169+
# tclreadline: provides ENABLE_READLINE define + links libtclreadline.
170+
# On systems without tclreadline these are empty stub targets (no-op).
171+
# See: https://github.com/The-OpenROAD-Project/OpenROAD/issues/7115
172+
"@tclreadline",
173+
"@tclreadline//:tclreadline_defs",
174+
],
175+
)
176+
163177
cc_binary(
164178
name = "openroad",
165179
srcs = [
@@ -177,19 +191,15 @@ cc_binary(
177191
":openroad_version",
178192
":opt_notification",
179193
":ord",
194+
":tcl_readline_setup",
180195
"//bazel:tcl_library_init",
181-
"@rules_cc//cc/runfiles", # sets BAZEL_CURRENT_REPOSITORY
182196
"//src/cut",
183197
"//src/gui",
184198
"//src/sta:opensta_lib",
185199
"//src/utl",
186200
"@boost.stacktrace",
201+
"@rules_cc//cc/runfiles", # sets BAZEL_CURRENT_REPOSITORY
187202
"@tcl_lang//:tcl",
188-
# tclreadline: provides ENABLE_READLINE define + links libtclreadline.
189-
# On systems without tclreadline these are empty stub targets (no-op).
190-
# See: https://github.com/The-OpenROAD-Project/OpenROAD/issues/7115
191-
"@tclreadline",
192-
"@tclreadline//:tclreadline_defs",
193203
],
194204
)
195205

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ set(OPENROAD_SOURCE
3232
Design.cc
3333
Timing.cc
3434
Tech.cc
35+
tcl_readline_setup.cc
3536
OpenRoad.cc
3637
Main.cc
3738
)

src/Main.cc

Lines changed: 5 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,6 @@
1818

1919
#include "boost/stacktrace/stacktrace.hpp"
2020
#include "tcl.h"
21-
#ifdef ENABLE_READLINE
22-
// If you get an error on this include be sure you have
23-
// the package tcl-tclreadline-devel installed
24-
#include "tclreadline.h"
25-
#endif
2621
#ifdef ENABLE_PYTHON3
2722
#define PY_SSIZE_T_CLEAN
2823
#include "Python.h"
@@ -47,6 +42,8 @@
4742
#include "bazel/tcl_library_init.h"
4843
#endif
4944

45+
#include "tcl_readline_setup.h"
46+
5047
using sta::findCmdLineFlag;
5148
using sta::findCmdLineKey;
5249
using sta::sourceTclFile;
@@ -317,90 +314,7 @@ int main(int argc, char* argv[])
317314
}
318315

319316
#ifdef ENABLE_READLINE
320-
// A stopgap fallback from the hardcoded TCLRL_LIBRARY path for OpenROAD,
321-
// not essential for OpenSTA
322-
static std::string findPathToTclreadlineInit(Tcl_Interp* interp)
323-
{
324-
// TL;DR it is possible to run the OpenROAD binary from within the
325-
// official Docker image on a different distribution than the
326-
// distribution within the Docker image.
327-
//
328-
// In this case we have to look up
329-
// the location of the tclreadline scripts instead of using the hardcoded
330-
// path.
331-
//
332-
// It is helpful to use the official Docker image as CI infrastructure and
333-
// also because it is a good way to have as similar an environment as possible
334-
// during testing and deployment.
335-
//
336-
// See
337-
// https://github.com/The-OpenROAD-Project/bazel-orfs/blob/main/docker.BUILD.bazel
338-
// for the details on how this is done.
339-
//
340-
// Running Docker within a bazel isolated environment introduces lots of
341-
// problems and is not really done.
342-
const char* tcl_script = R"(
343-
namespace eval temp {
344-
# Check standard Bazel runfiles relative path
345-
set runfiles_path [file join [pwd] "external/tclreadline/tclreadlineInit.tcl"]
346-
if {[file exists $runfiles_path]} {
347-
return $runfiles_path
348-
}
349-
350-
# Check Bazel runfiles in adjacent directories for other run strategies
351-
set runfiles_execroot_path [file join [pwd] "../tclreadline/tclreadlineInit.tcl"]
352-
if {[file exists $runfiles_execroot_path]} {
353-
return $runfiles_execroot_path
354-
}
355-
356-
foreach dir $::auto_path {
357-
set folder [file join $dir]
358-
set path [file join $folder "tclreadline)" TCLRL_VERSION_STR
359-
R"(" "tclreadlineInit.tcl"]
360-
if {[file exists $path]} {
361-
return $path
362-
}
363-
}
364-
error "tclreadlineInit.tcl not found in any of the directories in auto_path"
365-
}
366-
)";
367-
368-
if (Tcl_Eval(interp, tcl_script) == TCL_ERROR) {
369-
std::cerr << "Tcl_Eval failed: " << Tcl_GetStringResult(interp) << '\n';
370-
return "";
371-
}
372-
373-
return Tcl_GetStringResult(interp);
374-
}
375-
376-
static bool TryReadlineStaticInit(Tcl_Interp* interp)
377-
{
378-
Tcl_StaticPackage(
379-
interp, "tclreadline", Tclreadline_Init, Tclreadline_SafeInit);
380-
381-
return Tcl_EvalFile(interp, TCLRL_LIBRARY "/tclreadlineInit.tcl") == TCL_OK;
382-
}
383-
384-
static bool TryTclBazelInit(Tcl_Interp* interp)
385-
{
386-
// Here, ::tclreadline::library is already set up by SetupTclEnvironment
387-
constexpr char kInitializeReadlineLib[] = R"(
388-
set ::tclreadline::setup_path [file join $::tclreadline::library "tclreadlineSetup.tcl"]
389-
set ::tclreadline::completer_path [file join $::tclreadline::library "tclreadlineCompleter.tcl"]
390-
if {[info commands history] == ""} { proc history {args} {} };
391-
source [file join $::tclreadline::library "tclreadlineInit.tcl"]
392-
)";
393-
394-
return Tcl_Eval(interp, kInitializeReadlineLib) == TCL_OK;
395-
}
396-
397-
static bool TrySearchPathManuallyInit(Tcl_Interp* interp)
398-
{
399-
const std::string path = findPathToTclreadlineInit(interp);
400-
return !path.empty() && Tcl_EvalFile(interp, path.c_str()) == TCL_OK;
401-
}
402-
403-
static int tclReadlineInit(Tcl_Interp* interp)
317+
static int tclOrdReadlineInit(Tcl_Interp* interp)
404318
{
405319
std::array<const char*, 2> readline_cmds
406320
= {"ord::setup_tclreadline", "::tclreadline::Loop"};
@@ -452,19 +366,11 @@ static int tclAppInit(int& argc,
452366
}
453367
#endif
454368

455-
#ifdef ENABLE_READLINE
456369
if (!exit_after_cmd_file) {
457-
if (Tclreadline_Init(interp) == TCL_ERROR) {
458-
return TCL_ERROR;
459-
}
460-
461-
if (!TryReadlineStaticInit(interp) && //
462-
!TryTclBazelInit(interp) && //
463-
!TrySearchPathManuallyInit(interp)) {
370+
if (ord::SetupTclReadlineLibrary(interp) == TCL_ERROR) {
464371
printf("Failed to load tclreadline\n");
465372
}
466373
}
467-
#endif
468374

469375
ord::initOpenRoad(
470376
interp, log_filename, metrics_filename, exit_after_cmd_file);
@@ -545,7 +451,7 @@ static int tclAppInit(int& argc,
545451
}
546452
#ifdef ENABLE_READLINE
547453
if (!gui::Gui::enabled() && !exit_after_cmd_file) {
548-
return tclReadlineInit(interp);
454+
return tclOrdReadlineInit(interp);
549455
}
550456
#endif
551457
return TCL_OK;

src/tcl_readline_setup.cc

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
// Copyright (c) 2026, The OpenROAD Authors
3+
4+
#include "tcl_readline_setup.h"
5+
6+
#include "tcl.h"
7+
8+
#ifdef ENABLE_READLINE
9+
#include <iostream>
10+
#include <string>
11+
12+
// If you get an error on this include be sure you have
13+
// the package tcl-tclreadline-devel installed
14+
#include "tclreadline.h"
15+
#endif
16+
17+
// Various way to initialize tcl readline. This might be made easier once we
18+
// have one build system.
19+
20+
namespace ord {
21+
22+
#ifdef ENABLE_READLINE
23+
// A stopgap fallback from the hardcoded TCLRL_LIBRARY path for OpenROAD,
24+
// not essential for OpenSTA
25+
// TODO: this might need to not be needed anymore after we have an all batteries
26+
// included //zipfs:/ set-up.
27+
static std::string findPathToTclreadlineInit(Tcl_Interp* interp)
28+
{
29+
// TL;DR it is possible to run the OpenROAD binary from within the
30+
// official Docker image on a different distribution than the
31+
// distribution within the Docker image.
32+
//
33+
// In this case we have to look up
34+
// the location of the tclreadline scripts instead of using the hardcoded
35+
// path.
36+
//
37+
// It is helpful to use the official Docker image as CI infrastructure and
38+
// also because it is a good way to have as similar an environment as possible
39+
// during testing and deployment.
40+
//
41+
// See
42+
// https://github.com/The-OpenROAD-Project/bazel-orfs/blob/main/docker.BUILD.bazel
43+
// for the details on how this is done.
44+
//
45+
// Running Docker within a bazel isolated environment introduces lots of
46+
// problems and is not really done.
47+
const char* tcl_script = R"(
48+
namespace eval temp {
49+
# Check standard Bazel runfiles relative path
50+
set runfiles_path [file join [pwd] "external/tclreadline/tclreadlineInit.tcl"]
51+
if {[file exists $runfiles_path]} {
52+
return $runfiles_path
53+
}
54+
55+
# Check Bazel runfiles in adjacent directories for other run strategies
56+
set runfiles_execroot_path [file join [pwd] "../tclreadline/tclreadlineInit.tcl"]
57+
if {[file exists $runfiles_execroot_path]} {
58+
return $runfiles_execroot_path
59+
}
60+
61+
foreach dir $::auto_path {
62+
set folder [file join $dir]
63+
set path [file join $folder "tclreadline)" TCLRL_VERSION_STR
64+
R"(" "tclreadlineInit.tcl"]
65+
if {[file exists $path]} {
66+
return $path
67+
}
68+
}
69+
error "tclreadlineInit.tcl not found in any of the directories in auto_path"
70+
}
71+
)";
72+
73+
if (Tcl_Eval(interp, tcl_script) == TCL_ERROR) {
74+
std::cerr << "Tcl_Eval failed: " << Tcl_GetStringResult(interp) << '\n';
75+
return "";
76+
}
77+
78+
return Tcl_GetStringResult(interp);
79+
}
80+
81+
static bool TryReadlineStaticInit(Tcl_Interp* interp)
82+
{
83+
Tcl_StaticPackage(
84+
interp, "tclreadline", Tclreadline_Init, Tclreadline_SafeInit);
85+
86+
return Tcl_EvalFile(interp, TCLRL_LIBRARY "/tclreadlineInit.tcl") == TCL_OK;
87+
}
88+
89+
static bool TryTclBazelInit(Tcl_Interp* interp)
90+
{
91+
// Here, ::tclreadline::library is already set up by SetupTclEnvironment()
92+
constexpr char kInitializeReadlineLib[] = R"(
93+
set ::tclreadline::setup_path [file join $::tclreadline::library "tclreadlineSetup.tcl"]
94+
set ::tclreadline::completer_path [file join $::tclreadline::library "tclreadlineCompleter.tcl"]
95+
if {[info commands history] == ""} { proc history {args} {} };
96+
source [file join $::tclreadline::library "tclreadlineInit.tcl"]
97+
)";
98+
99+
return Tcl_Eval(interp, kInitializeReadlineLib) == TCL_OK;
100+
}
101+
102+
static bool TrySearchPathManuallyInit(Tcl_Interp* interp)
103+
{
104+
const std::string path = findPathToTclreadlineInit(interp);
105+
return !path.empty() && Tcl_EvalFile(interp, path.c_str()) == TCL_OK;
106+
}
107+
#endif
108+
109+
int SetupTclReadlineLibrary(Tcl_Interp* interp)
110+
{
111+
#ifdef ENABLE_READLINE
112+
if (Tclreadline_Init(interp) == TCL_ERROR) {
113+
return TCL_ERROR;
114+
}
115+
116+
if (!TryReadlineStaticInit(interp) && //
117+
!TryTclBazelInit(interp) && //
118+
!TrySearchPathManuallyInit(interp)) {
119+
return TCL_ERROR;
120+
}
121+
#endif
122+
return TCL_OK;
123+
}
124+
} // namespace ord

src/tcl_readline_setup.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
// Copyright (c) 2026, The OpenROAD Authors
3+
4+
#pragma once
5+
6+
#include "tcl.h"
7+
8+
namespace ord {
9+
// Initialiize tcl readline, applying various ways to initialize.
10+
int SetupTclReadlineLibrary(Tcl_Interp* interp);
11+
} // namespace ord

0 commit comments

Comments
 (0)