Skip to content

Commit 621db11

Browse files
committed
[GR-18163] Fix the absolute path of the main script after chdir (#2709)
PullRequest: truffleruby/3468
2 parents 95f5f97 + ac7a28e commit 621db11

File tree

10 files changed

+52
-10
lines changed

10 files changed

+52
-10
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ Compatibility:
3333
* Implement `rb_eval_cmd_kw` to support the `tk` gem (#2556, @aardvark179).
3434
* Fix `rb_class2name` to call `inspect` on anonymous classes like in CRuby (#2701, @aardvark179).
3535
* Implement `rb_ivar_foreach` to iterate over instance and class variables like in CRuby (#2701, @aardvark179).
36+
* Fix the absolute path of the main script after chdir (#2709, @eregon).
3637

3738
Performance:
3839

ci.jsonnet

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -543,8 +543,8 @@ local composition_environment = utils.add_inclusion_tracking(part_definitions, "
543543
"ruby-test-specs-darwin-aarch64-17": $.platform.darwin_aarch64 + $.jdk.v17 + $.env.jvm + gate_no_build + $.use.build + $.run.test_unit_tck + native_config + $.run.test_specs + { timelimit: "01:40:00" },
544544
"ruby-test-fast-linux-aarch64": $.platform.linux_aarch64 + $.jdk.v11 + $.env.jvm + gate + $.run.test_fast + native_config + { timelimit: "45:00" },
545545
"ruby-test-fast-linux": $.platform.linux + $.jdk.v11 + $.env.jvm + gate + $.run.test_fast + { timelimit: "45:00" }, # To catch missing slow tags
546-
"ruby-test-mri-linux": $.platform.linux + $.jdk.v11 + $.env.native + gate + $.run.test_mri + { timelimit: "01:10:00" },
547-
"ruby-test-mri-linux-aarch64": $.platform.linux_aarch64 + $.jdk.v11 + $.env.native + gate + $.run.test_mri + { timelimit: "01:10:00" },
546+
"ruby-test-mri-linux": $.platform.linux + $.jdk.v11 + $.env.native + gate + $.run.test_mri + { timelimit: "01:20:00" },
547+
"ruby-test-mri-linux-aarch64": $.platform.linux_aarch64 + $.jdk.v11 + $.env.native + gate + $.run.test_mri + { timelimit: "01:20:00" },
548548
"ruby-test-mri-darwin-amd64": $.platform.darwin_amd64 + $.jdk.v11 + $.env.native + gate + $.run.test_mri + { timelimit: "01:30:00" },
549549
"ruby-test-mri-darwin-aarch64": $.platform.darwin_aarch64 + $.jdk.v11 + $.env.native + gate + $.run.test_mri + { timelimit: "01:30:00" },
550550
"ruby-test-integration-linux": $.platform.linux + $.jdk.v11 + $.env.jvm + gate + $.run.test_integration,

spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@
1717
end
1818
end
1919

20+
it 'returns the correct absolute path when using a relative main script path and changing CWD' do
21+
script = fixture(__FILE__, 'subdir/absolute_path_main_chdir.rb')
22+
sibling = fixture(__FILE__, 'subdir/sibling.rb')
23+
subdir = File.dirname script
24+
Dir.chdir(fixture(__FILE__)) do
25+
ruby_exe('subdir/absolute_path_main_chdir.rb').should == "subdir/absolute_path_main_chdir.rb\n#{subdir}\n#{subdir}\n#{script}\n#{sibling}\n"
26+
end
27+
end
28+
2029
context "when used in eval with a given filename" do
2130
code = "caller_locations(0)[0].absolute_path"
2231

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
puts __FILE__
2+
puts __dir__
3+
Dir.chdir __dir__
4+
5+
# Check __dir__ is still correct after chdir
6+
puts __dir__
7+
8+
puts caller_locations(0)[0].absolute_path
9+
10+
# require_relative also needs to know the absolute path of the current file so we test it here too
11+
require_relative 'sibling'
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
puts __FILE__
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
slow:Thread::Backtrace::Location#absolute_path returns an absolute path when using a relative main script path
22
fails:Thread::Backtrace::Location#absolute_path canonicalization returns a canonical path without symlinks, even when __FILE__ is removed
3+
slow:Thread::Backtrace::Location#absolute_path returns the correct absolute path when using a relative main script path and changing CWD

src/main/java/org/truffleruby/core/kernel/KernelNodes.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import com.oracle.truffle.api.interop.InteropLibrary;
2323
import com.oracle.truffle.api.interop.UnsupportedMessageException;
2424
import com.oracle.truffle.api.object.PropertyGetter;
25+
import com.oracle.truffle.api.source.Source;
2526
import com.oracle.truffle.api.strings.AbstractTruffleString;
2627
import com.oracle.truffle.api.strings.TruffleString;
2728
import com.oracle.truffle.api.utilities.AssumedValue;
@@ -300,9 +301,9 @@ protected RubyString getCallerPath(Object feature,
300301
coreExceptions().loadError("cannot infer basepath", featureString, this));
301302
}
302303

303-
String sourcePath = getLanguage().getSourcePath(sourceSection.getSource());
304-
305-
sourcePath = getContext().getFeatureLoader().canonicalize(sourcePath);
304+
Source source = sourceSection.getSource();
305+
String sourcePath = getLanguage().getSourcePath(source);
306+
sourcePath = getContext().getFeatureLoader().canonicalize(sourcePath, source);
306307

307308
featurePath = getContext().getFeatureLoader().dirname(sourcePath) + "/" + featureString;
308309
}
@@ -394,7 +395,7 @@ protected RubyString canonicalPath(Object string,
394395
@Cached TruffleString.FromJavaStringNode fromJavaStringNode) {
395396
final String expandedPath = getContext()
396397
.getFeatureLoader()
397-
.canonicalize(RubyGuards.getJavaString(string));
398+
.canonicalize(RubyGuards.getJavaString(string), null);
398399
return createString(fromJavaStringNode, expandedPath, Encodings.UTF_8);
399400
}
400401

src/main/java/org/truffleruby/core/thread/ThreadBacktraceLocationNodes.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public static Object getAbsolutePath(SourceSection sourceSection, RubyBaseNode n
6666
return nil;
6767
} else if (source.getPath() != null) { // A normal file
6868
final String path = language.getSourcePath(source);
69-
final String canonicalPath = context.getFeatureLoader().canonicalize(path);
69+
final String canonicalPath = context.getFeatureLoader().canonicalize(path, source);
7070
var cachedRope = language.tstringCache.getTString(TStringUtils.utf8TString(canonicalPath),
7171
Encodings.UTF_8);
7272
return node.createString(cachedRope, Encodings.UTF_8);

src/main/java/org/truffleruby/language/loader/FeatureLoader.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@
6464

6565
public class FeatureLoader {
6666

67+
private static final int PATH_MAX = 1024; // jnr-posix hard codes this value
68+
private static final String[] EXTENSIONS = new String[]{ TruffleRuby.EXTENSION, RubyLanguage.CEXT_EXTENSION };
69+
6770
private final RubyContext context;
6871
private final RubyLanguage language;
6972

@@ -80,9 +83,9 @@ public class FeatureLoader {
8083

8184
private String cwd = null;
8285
private Object getcwd;
83-
private static final int PATH_MAX = 1024; // jnr-posix hard codes this value
8486

85-
private static final String[] EXTENSIONS = new String[]{ TruffleRuby.EXTENSION, RubyLanguage.CEXT_EXTENSION };
87+
private Source mainScriptSource;
88+
private String mainScriptAbsolutePath;
8689

8790
public FeatureLoader(RubyContext context, RubyLanguage language) {
8891
this.context = context;
@@ -95,6 +98,13 @@ public void initialize(NativeConfiguration nativeConfiguration, TruffleNFIPlatfo
9598
}
9699
}
97100

101+
public void setMainScript(Source source, String absolutePath) {
102+
assert mainScriptSource == null;
103+
assert mainScriptAbsolutePath == null;
104+
mainScriptSource = source;
105+
mainScriptAbsolutePath = absolutePath;
106+
}
107+
98108
public void addAutoload(RubyConstant autoloadConstant) {
99109
final String autoloadPath = autoloadConstant.getAutoloadConstant().getAutoloadPath();
100110
final String basename = basenameWithoutExtension(autoloadPath);
@@ -229,7 +239,13 @@ private String makeAbsolute(String path) {
229239
}
230240
}
231241

232-
public String canonicalize(String path) {
242+
public String canonicalize(String path, Source source) {
243+
// Special case for the main script which has a relative Source#getPath():
244+
// We need to resolve it correctly, even if the CWD changed since then.
245+
if (source != null && source.equals(mainScriptSource)) {
246+
return mainScriptAbsolutePath;
247+
}
248+
233249
// First, make the path absolute, by expanding relative to the context CWD
234250
// Otherwise, getCanonicalPath() uses user.dir as CWD which is incorrect.
235251
final String absolutePath = makeAbsolute(path);

src/main/java/org/truffleruby/language/loader/MainLoader.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ public RubySource loadFromFile(Env env, Node currentNode, String mainPath) throw
100100

101101
final Source mainSource = fileLoader.buildSource(file, mainPath, sourceTString, false, true);
102102

103+
context.getFeatureLoader().setMainScript(mainSource, file.getCanonicalFile().getPath());
104+
103105
return new RubySource(mainSource, mainPath, sourceTString);
104106
}
105107

0 commit comments

Comments
 (0)