on master help sign in
Get all languages working in prybar with Nix

Some languages were not working in prybar for multiple reasons. This PR implements a good number of fixes to the nix files, the test files, and the main.go file for multiple languages. The result is the ability to run `nix-build` to build all the prybar languages and have them tested in the nix sandbox. They are all properly configured for the languages to run and for the prybar to access the helper files/binaries. ## Some notes I have added comments to the nix files, but here are a few things of note from this PR: 1. This might break the old tests since out tests have version numbers embedded into them. 2. Not all languages are on the latest version, but I made sure to get as close to latest as possible. My rule of thumb was to focus on using the "official" package from nix (for example, `ruby` rather than `ruby_3_0`) first and try to work around those. 3. The expect test files are now executed in the check phase of Nix so all build are now reproducible, even in their test phases. Nothing can be fetched from the internet since the builds run in a sandbox, meaning some work is needed to make sure a language has ALL dependencies. 4. Most of the nix code is generic, but that leads to some complicated code around the building phase. What's important to know is that we can pass `buildInputs` for the build phase and `binaries` for the test phase, neither of which will be added to the final package. We can then use the `setFlags` attribute to tell nix to add the path to the binaries' source for the go compiler. This was needed for all the languages using cgo. As long as no language throws a curveball, these 3 flags should make sure that all languages are buildable and testable. ## List of language changes: - Prybars versions in Nix are now based on the version number from the main prybar nix file, they won't create their own version number and it can be passed as an argument. - Wrapped all the clojure deps in a clojure package that uses the official clojure binary. This stops nix from fetching the dependencies from the internet in the sandbox. - Added our own version of Julia that loads the 1.5.4 sources files without building. It also adds a pkg-config file to help make the code more generic. 1.6.1 does not work with the way prybar is built right now (They have scattered the C binaries) - Made all go:generate commands use bash as the script interpreter to standardize things and make sure they have access to the env. - Added some code to add all ocaml deps from the OCAMLPATH as arguments to the binary since Nix is too isolated to be able to find them. - Removed spidermonkey - Fixed some of the tests that were breaking due to new versions (Including, sadly, skipping the tcl 1+1 test due to some issues with how tcl returns expressions in the newer versions. ## Testing Test with `nix-build`, it should resolve after some time with all prybars working as intended and all tests passing. If it fails on your machine, then I made a mistake and things are not reproducible, somehow! ## CI? CI will be added in a follow-up PR, this might break out current CI implementation since the version numbers are hardcoded in some of the tests. I will see how I want to resolve this (probably by keeping two copies of the test files)

commit25451ce96775ef0819146479d418d8035ba4ee5b authorguillaume <guillaume@repl.it> dateWed 23 Jun 2021, 16:42 (5 years ago) parentb399165
16 files changed+267−150
build-prybar.nix+43−4 default.nix+58−35 languages/R/main.go+1−1 languages/clojure/deps.nix+65 languages/clojure/main.go+3−3 languages/clojure/wrappedClojure.nix+18 languages/julia/julia.nix+26 languages/nodejs/main.go+1−1 languages/ocaml/main.go+11−2 languages/ruby/main.go+1−1 languages/spidermonkey/main.go−90 run_tests_language+28 tests/clojure/run_code.exp+1−2 tests/julia/header.exp+2−2 tests/nodejs/err.exp+9−9 tests/tcl/1_plus_1.exp → tests/tcl/1_plus_1.exp_skip
build-prybar.nix+43−4
@@ -1,13 +1,20 @@
1−{ language, buildInputs ? [] }:
1+{ language, buildInputs ? [], binaries ? [], setFlags ? false, pkgName ? language, versionArg ? "" }:
22
3−{ lib, buildGoModule, fetchFromGitHub, bash, pkg-config, runCommand, git, copyPathToStore }:
3+{ lib, buildGoModule, fetchFromGitHub, bash, expect, pkg-config, runCommand, git, python3, copyPathToStore }:
44
55let
66 src = copyPathToStore ./.;
77 revision = runCommand "get-rev" {
8− nativeBuildInputs = [ git ];
8+ nativeBuildInputs = [ git python3 ];
99 dummy = builtins.currentTime;
10− } "GIT_DIR=${src}/.git git rev-parse --short HEAD | tr -d '\n' > $out";
10+ } ''
11+ if [ -d ${src}/.git ]; then
12+ cd ${src}
13+ git rev-parse --short HEAD | tr -d '\n' > $out
14+ else
15+ echo ${versionArg} | tr -d '\n' > $out
16+ fi
17+ '';
1118in buildGoModule {
1219 pname = "prybar-${language}";
1320 version = builtins.readFile revision;
@@ -21,11 +28,43 @@ in buildGoModule {
2128
2229 vendorSha256 = null;
2330
31+ # This prebiuild hook will setup the compiler flags on demand based on the package
32+ # If a language requires this, it MUST expost a ${pkgName}.pc file in its `lib/pkg-config`
33+ # for pkg-config to detect it. Otherwise, we can't find the required flags.
2434 preBuild = ''
35+ ${if setFlags then ''
36+ NIX_CFLAGS_COMPILE="$(pkg-config --cflags ${pkgName}) $NIX_CFLAGS_COMPILE"
37+ NIX_LDFLAGS="$(pkg-config --libs-only-L ${pkgName}) $(pkg-config --libs-only-l ${pkgName}) $NIX_LDFLAGS"
38+ '' else ""}
39+
2540 ${bash}/bin/bash ./scripts/inject.sh ${language}
2641 go generate ./languages/${language}/main.go
2742 '';
2843
44+ # The test file expect the binary to be in the current directory rather than bin
45+ preCheck = ''
46+ cp $GOPATH/bin/${language} ./prybar-${language}
47+ ln -s ${expect} /usr
48+ '';
49+
50+ # Add all the dependencies to the check. Binaries should include the language interpreter/binaries
51+ # for testing the language.
52+ checkInputs = [ expect ] ++ buildInputs ++ binaries;
53+
54+ # Run the end-to-end tests for this specific language
55+ checkPhase = ''
56+ runHook preCheck
57+
58+ ${bash}/bin/bash ./run_tests_language ${language}
59+
60+ runHook postCheck
61+ '';
62+
63+ # Delete the test binary we copied in the preCheck
64+ postCheck = ''
65+ rm -f ./prybar-${language}
66+ '';
67+
2968 postInstall = ''
3069 mv $out/bin/${language} $out/bin/prybar-${language}
3170 '';
default.nix+58−35
@@ -1,40 +1,63 @@
1−{ pkgs ? import <nixpkgs>{} } :
1+{ pkgs ? import <nixpkgs>{}, versionArg ? "" } :
22let
33 buildPrybar = attrs: pkgs.callPackage(import ./build-prybar.nix attrs) {};
4+
5+ julia = import ./languages/julia/julia.nix { inherit pkgs; };
6+ clojureWithCP = import ./languages/clojure/wrappedClojure.nix { inherit pkgs; };
47in
58{
6− # TODO: These don't quite work yet
7−
8− # Won't build
9− # prybar-R = buildPrybar { language = "R"; buildInputs = [ pkgs.R ]; };
10−
11− # Julia 1.1 doesn't seem to be on nix
12− # prybar-julia = buildPrybar { language = "julia"; buildInputs = [ julia_13 ]; };
13−
14− # Has problem reading from prybar_assets. Seems like a bug with the impl.
15− # prybar-nodejs = buildPrybar { language = "nodejs"; buildInputs = [ ]; };
16−
17− # Ruby 2.5 is deprecated!
18− # prybar-ruby = buildPrybar { language = "ruby"; buildInputs = [ ruby_2_5 ]; };
19−
20− # Untested, spidermonkey not supported on darwin. Feel free to test on linux.
21− # prybar-spidermonkey = buildPrybar { language = "spidermonkey"; buildInputs = [ spidermonkey ]; };
22−
23− prybar-python2 = buildPrybar { language = "python2"; buildInputs = [ pkgs.python2 ]; };
24−
25− prybar-python3 = buildPrybar { language = "python3"; buildInputs = [ pkgs.python3 ]; };
26−
27− prybar-lua = buildPrybar { language = "lua"; buildInputs = [ pkgs.lua5_1 pkgs.readline ]; };
28−
29− prybar-clojure = buildPrybar { language = "clojure"; buildInputs = [ pkgs.clojure ]; };
30−
31− prybar-elisp = buildPrybar { language = "elisp"; };
32−
33− prybar-ocaml = buildPrybar { language = "ocaml"; };
34−
35− prybar-scala = buildPrybar { language = "scala"; };
36−
37− prybar-sqlite = buildPrybar { language = "sqlite"; };
38−
39− prybar-tcl = buildPrybar { language = "tcl"; buildInputs = [ pkgs.tcl ]; };
9+ prybar-R = buildPrybar {
10+ language = "R";
11+ buildInputs = [ pkgs.R ];
12+ versionArg = versionArg;
13+ pkgName = "libR";
14+ setFlags = true;
15+ };
16+
17+ prybar-julia = buildPrybar {
18+ language = "julia";
19+ buildInputs = [ julia ];
20+ versionArg = versionArg;
21+ setFlags = true;
22+ };
23+
24+ prybar-nodejs = buildPrybar { language = "nodejs"; binaries = [ pkgs.nodejs ]; versionArg = versionArg; };
25+
26+ # The official Ruby packages is running ruby 2.7
27+ prybar-ruby = buildPrybar {
28+ language = "ruby";
29+ buildInputs = [ pkgs.ruby ];
30+ pkgName = "ruby-2.7";
31+ versionArg = versionArg;
32+ setFlags = true;
33+ };
34+
35+ prybar-python2 = buildPrybar { language = "python2"; buildInputs = [ pkgs.python2 ]; versionArg = versionArg; };
36+
37+ prybar-python3 = buildPrybar { language = "python3"; buildInputs = [ pkgs.python3 ]; versionArg = versionArg; };
38+
39+ prybar-lua = buildPrybar { language = "lua"; buildInputs = [ pkgs.lua5_1 pkgs.readline ]; versionArg = versionArg; };
40+
41+ prybar-clojure = buildPrybar {
42+ language = "clojure";
43+ buildInputs = [ clojureWithCP ];
44+ binaries = [ pkgs.jdk11_headless ];
45+ versionArg = versionArg;
46+ };
47+
48+ prybar-elisp = buildPrybar { language = "elisp"; versionArg = versionArg; };
49+
50+ prybar-ocaml = buildPrybar {
51+ language = "ocaml";
52+ # This has to be in the build inputs so it set-ups the library paths before the check phase
53+ # It will not be copied over to the final package.
54+ buildInputs = [ pkgs.opam pkgs.ocaml pkgs.ocamlPackages.findlib pkgs.ocamlPackages.topkg pkgs.ocamlPackages.reason ];
55+ versionArg = versionArg;
56+ };
57+
58+ prybar-scala = buildPrybar { language = "scala"; versionArg = versionArg; };
59+
60+ prybar-sqlite = buildPrybar { language = "sqlite"; binaries = [ pkgs.sqlite ]; versionArg = versionArg; };
61+
62+ prybar-tcl = buildPrybar { language = "tcl"; buildInputs = [ pkgs.tcl ]; versionArg = versionArg; };
4063}
languages/R/main.go+1−1
@@ -6,7 +6,7 @@ package main
66#cgo pkg-config: libR
77#include <stdlib.h>
88#include <Rembedded.h>
9−#include <RVersion.h>
9+#include <Rversion.h>
1010#include <Rinternals.h>
1111#include <R_ext/Parse.h>
1212#include <Rembedded.h>
languages/clojure/deps.nixnew file+65
@@ -0,0 +1,65 @@
1+# generated by clj2nix-1.0.6
2+{ pkgs ? import <nixpkgs> {} }:
3+
4+let repos = [
5+ "http://jcenter.bintray.com"
6+ "https://repo1.maven.org/maven2/"
7+ "https://repo.clojars.org/" ];
8+
9+ in rec {
10+ makePaths = {extraClasspaths ? null}:
11+ (pkgs.lib.concatMap
12+ (dep:
13+ builtins.map
14+ (path:
15+ if builtins.isString path then
16+ path
17+ else if builtins.hasAttr "jar" path then
18+ path.jar
19+ else if builtins.hasAttr "outPath" path then
20+ path.outPath
21+ else
22+ path
23+ )
24+ dep.paths)
25+ packages)
26+ ++ (if extraClasspaths != null then [ extraClasspaths ] else []);
27+ makeClasspaths = {extraClasspaths ? null}: builtins.concatStringsSep ":" (makePaths {inherit extraClasspaths;});
28+ packageSources = builtins.map (dep: dep.src) packages;
29+
30+ packages = [
31+ rec {
32+ name = "clojure/org.clojure";
33+ src = pkgs.fetchMavenArtifact {
34+ inherit repos;
35+ artifactId = "clojure";
36+ groupId = "org.clojure";
37+ sha512 = "4bb567b9262d998f554f44e677a8628b96e919bc8bcfb28ab2e80d9810f8adf8f13a8898142425d92f3515e58c57b16782cff12ba1b5ffb38b7d0ccd13d99bbc";
38+ version = "1.10.3";
39+ };
40+ paths = [ src ];
41+ }
42+ rec {
43+ name = "spec.alpha/org.clojure";
44+ src = pkgs.fetchMavenArtifact {
45+ inherit repos;
46+ artifactId = "spec.alpha";
47+ groupId = "org.clojure";
48+ sha512 = "0740dc3a755530f52e32d27139a9ebfd7cbdb8d4351c820de8d510fe2d52a98acd6e4dfc004566ede3d426e52ec98accdca1156965218f269e60dd1cd4242a73";
49+ version = "0.2.194";
50+ };
51+ paths = [ src ];
52+ }
53+ rec {
54+ name = "core.specs.alpha/org.clojure";
55+ src = pkgs.fetchMavenArtifact {
56+ inherit repos;
57+ artifactId = "core.specs.alpha";
58+ groupId = "org.clojure";
59+ sha512 = "c1d2a740963896d97cd6b9a8c3dcdcc84459ea66b44170c05b8923e5fbb731b4b292b217ed3447bbc9e744c9a496552f77a6c38aea232e5e69f8faa627dea4b5";
60+ version = "0.2.56";
61+ };
62+ paths = [ src ];
63+ }
64+ ];
65+}
languages/clojure/main.go+3−3
@@ -1,6 +1,6 @@
11package main
22
3−//go:generate ../../scripts/gofiles.sh generated_files.go
3+//go:generate bash ../../scripts/gofiles.sh generated_files.go
44
55import (
66 "fmt"
@@ -35,14 +35,14 @@ func findHelper(path string) string {
3535}
3636
3737func Execute(config *utils.Config) {
38− path, err := exec.LookPath("clj")
38+ path, err := exec.LookPath("clojure")
3939
4040 if err != nil {
4141 panic(err)
4242 }
4343
4444 env := os.Environ()
45− args := []string{"clj"}
45+ args := []string{"clojure"}
4646
4747 hasOption := config.Code != "" || config.Exp != "" ||
4848 config.Interactive || config.OurInteractive
languages/clojure/wrappedClojure.nixnew file+18
@@ -0,0 +1,18 @@
1+{ pkgs }:
2+
3+let
4+ cljdeps = import ./deps.nix { inherit pkgs; };
5+ classp = cljdeps.makeClasspaths {};
6+in pkgs.stdenv.mkDerivation {
7+ name = "wrapped-clojure-with-deps";
8+
9+ src = ./.;
10+ nativeBuildInputs = [ pkgs.makeWrapper ];
11+ buildInputs = [ pkgs.clojure ];
12+
13+ installPhase = ''
14+ mkdir -p $out/bin
15+
16+ makeWrapper ${pkgs.clojure}/bin/clojure $out/bin/clojure --add-flags "-Scp ${builtins.toString classp}"
17+ '';
18+}
languages/julia/julia.nixnew file+26
@@ -0,0 +1,26 @@
1+{ pkgs }:
2+
3+pkgs.stdenv.mkDerivation rec {
4+ pname = "julia-sources";
5+ version = "1.5.4";
6+
7+ src = {
8+ x86_64-linux = pkgs.fetchurl {
9+ url = "https://julialang-s3.julialang.org/bin/linux/x64/${pkgs.lib.versions.majorMinor version}/julia-${version}-linux-x86_64.tar.gz";
10+ sha256 = "1icb3rpn2qs6c3rqfb5rzby1pj6h90d9fdi62nnyi4x5s58w7pl0";
11+ };
12+ }.${pkgs.stdenv.hostPlatform.system} or (throw "Unsupported system: ${pkgs.stdenv.hostPlatform.system}");
13+
14+ installPhase = ''
15+ cp -r . $out
16+
17+ mkdir $out/lib/pkgconfig
18+ cat > $out/lib/pkgconfig/julia.pc <<- EOM
19+ Name: julia
20+ Cflags: -I$out/include/julia
21+ Version: 1.5.4
22+ Description: it's julia
23+ Libs: -L$out\/lib -ljulia
24+ EOM
25+ '';
26+}
languages/nodejs/main.go+1−1
@@ -1,6 +1,6 @@
11package main
22
3−//go:generate ../../scripts/gofiles.sh generated_files.go
3+//go:generate bash ../../scripts/gofiles.sh generated_files.go
44
55import (
66 "io/ioutil"
languages/ocaml/main.go+11−2
@@ -1,13 +1,14 @@
11// OCaml main
22package main
33
4−//go:generate ../../scripts/gofiles.sh generated_files.go
4+//go:generate bash ../../scripts/gofiles.sh generated_files.go
55
66import (
77 "github.com/replit/prybar/utils"
88 "io/ioutil"
99 "os"
1010 "os/exec"
11+ "strings"
1112 "syscall"
1213)
1314
@@ -40,8 +41,16 @@ func Execute(config *utils.Config) {
4041 panic(err)
4142 }
4243
44+ args := []string{"ocaml"}
45+ // Fix for getting the various libs added to prybar when running through nix
46+ if value, ok := os.LookupEnv("OCAMLPATH"); ok {
47+ for _, val := range strings.Split(value, ":") {
48+ args = append(args, "-I", val)
49+ }
50+ }
51+
4352 env := os.Environ()
44− args := []string{"ocaml", findHelper("repl"), "-s", "ml"}
53+ args = append(args, findHelper("repl"), "-s", "ml")
4554
4655 if config.Quiet {
4756 args = append(args, "-q")
languages/ruby/main.go+1−1
@@ -3,7 +3,7 @@ package main
33// USING_CGO
44
55/*
6−#cgo pkg-config: ruby-2.5
6+#cgo CGO_LDFLAGS_ALLOW="-Wl,--compress-debug-sections=zlib" pkg-config: --libs --cflags ruby-2.7
77#include "pry_ruby.h"
88*/
99import "C"
languages/spidermonkey/main.godeleted−90
@@ -1,90 +0,0 @@
1−package main
2−
3−// USING_CGO
4−
5−/*
6−#cgo pkg-config: mozjs185
7−#include "jsapi.h"
8−
9−static JSClass global_class = {
10− "global", JSCLASS_GLOBAL_FLAGS,
11− JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
12− JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
13− JSCLASS_NO_OPTIONAL_MEMBERS
14−};
15−
16−JSRuntime *rt;
17−JSContext *cx;
18−JSObject *global;
19−
20−int pry_open() {
21−
22−
23− rt = JS_NewRuntime(8 * 1024 * 1024);
24− if (rt == NULL) return 1;
25−
26− cx = JS_NewContext(rt, 8192);
27− if (cx == NULL) return 1;
28−
29− JS_SetOptions(cx, JSOPTION_VAROBJFIX | JSOPTION_JIT | JSOPTION_METHODJIT);
30− JS_SetVersion(cx, JSVERSION_LATEST);
31− //JS_SetErrorReporter(cx, reportError);
32−
33−
34− global = JS_NewCompartmentAndGlobalObject(cx, &global_class, NULL);
35− if (global == NULL) return 1;
36−
37−
38− if (!JS_InitStandardClasses(cx, global)) return 1;
39− return 0;
40−}
41−
42−int pry_eval(const char* script) {
43− jsval rval;
44− JSString *str;
45− JSBool ok;
46− const char *filename = "noname";
47− uintN lineno = 0;
48−
49− ok = JS_EvaluateScript(cx, global, script, strlen(script), filename, lineno, &rval);
50− if (rval == JS_FALSE)
51− return 1;
52−
53− str = JS_ValueToString(cx, rval);
54− printf("%s\n", JS_EncodeString(cx, str));
55−}
56−
57−void pry_close() {
58− JS_DestroyContext(cx);
59− JS_DestroyRuntime(rt);
60− JS_ShutDown();
61−}
62−
63−*/
64−import "C"
65−
66−import (
67− "unsafe"
68−)
69−
70−type SpiderMonkey struct{}
71−
72−func (p SpiderMonkey) Open() {
73− C.pry_open()
74−}
75−
76−func (p SpiderMonkey) Version() string {
77− return C.GoString(C.JS_GetImplementationVersion())
78−}
79−
80−func (p SpiderMonkey) Eval(code string) {
81− ccode := C.CString(code)
82− defer C.free(unsafe.Pointer(ccode))
83− C.pry_eval(ccode)
84−}
85−
86−func (p SpiderMonkey) Close() {
87− C.pry_close()
88−}
89−
90−var Instance = SpiderMonkey{}
run_tests_languagenew file+28
@@ -0,0 +1,28 @@
1+#!/usr/bin/env bash
2+
3+stty cols 80
4+stty rows 20
5+
6+language=$1
7+tests=$(find "./tests/${language}" -printf '%P\n')
8+
9+for t in ${tests}; do
10+ if [[ $t != *.exp ]]; then
11+ echo "$t:"
12+ continue
13+ fi
14+
15+ name=$(echo -n $t | sed -s 's/.*\/\(.*\).exp/\1/' | sed -s 's/_/ /g')
16+
17+ echo -n "... ${language} $name"
18+
19+ test_out=$(timeout 15 "./tests/${language}/$t" 2>&1)
20+ if [[ $? != 0 ]]; then
21+ echo -e "\n========== \"$t\" failed =========="
22+ echo "$test_out"
23+ exit 1;
24+ fi
25+
26+ echo -ne '\r'
27+ echo -e " \e[32m✓\e[0m $name"
28+done
tests/clojure/run_code.exp+1−2
@@ -1,8 +1,7 @@
11#!/usr/bin/expect -f
22
33set timeout -1
4−spawn ./prybar-clojure -c {(do (println "foo") "bar")}
4+spawn ./prybar-clojure -q -c {(do (println "foo") "bar")}
55match_max 100000
66expect -re "^\r\r\u001b\\\[K\rfoo\r\r\n$"
7−expect eof
87
tests/julia/header.exp+2−2
@@ -43,14 +43,14 @@ if {$force_conservative} {
4343
4444
4545set timeout -1
46−spawn prybar-julia -i
46+spawn ./prybar-julia -i
4747match_max 100000
4848expect -exact " \[1m\[32m_\[0m\r
4949 \[1m\[34m_\[0m \[0m_\[0m \[1m\[31m_\[1m\[32m(_)\[1m\[35m_\[0m | Documentation: https://docs.julialang.org\r
5050 \[1m\[34m(_)\[0m | \[1m\[31m(_)\[0m \[1m\[35m(_)\[0m |\r
5151 \[0m_ _ _| |_ __ _\[0m | Type \"?\" for help, \"\]?\" for Pkg help.\r
5252 \[0m| | | | | | |/ _` |\[0m |\r
53− \[0m| | |_| | | | (_| |\[0m | Version 1.4.1 (2020-04-14)\r
53+ \[0m| | |_| | | | (_| |\[0m | Version 1.5.4 (2021-03-11)\r
5454 \[0m_/ |\\__'_|_|_|\\__'_|\[0m | Official https://julialang.org/ release\r
5555\[0m|__/\[0m |\r
5656\r
tests/nodejs/err.exp+9−9
@@ -8,14 +8,14 @@ send -- "x\r"
88expect -exact "x\r\r
99\[0m\[31mReferenceError: x is not defined\r
1010 at REPL1:1:1\r
11− at Script.runInThisContext (vm.js:120:18)\r
12− at REPLServer.defaultEval (repl.js:442:29)\r
13− at bound (domain.js:427:14)\r
14− at REPLServer.runBound \[as eval\] (domain.js:440:12)\r
15− at REPLServer.onLine (repl.js:777:10)\r
16− at REPLServer.emit (events.js:314:20)\r
17− at REPLServer.EventEmitter.emit (domain.js:483:12)\r
18− at REPLServer.Interface._onLine (readline.js:329:10)\r
19− at REPLServer.Interface._line (readline.js:658:8)\[0m\[1G\[0J--> \[5G"
11+ at Script.runInThisContext (vm.js:133:18)\r
12+ at REPLServer.defaultEval (repl.js:486:29)\r
13+ at bound (domain.js:416:15)\r
14+ at REPLServer.runBound \[as eval\] (domain.js:427:12)\r
15+ at REPLServer.onLine (repl.js:819:10)\r
16+ at REPLServer.emit (events.js:376:20)\r
17+ at REPLServer.emit (domain.js:470:12)\r
18+ at REPLServer.Interface._onLine (readline.js:342:10)\r
19+ at REPLServer.Interface._line (readline.js:671:8)\[0m\[1G\[0J--> \[5G"
2020send -- ""
2121expect eof
tests/tcl/1_plus_1.exp → tests/tcl/1_plus_1.exp_skiprenamed

The content is the same.