mirror of
https://github.com/roc-lang/roc.git
synced 2025-12-23 08:48:03 +00:00
Merge pull request #8461 from roc-lang/fix-relative-paths
Fix relative paths to platforms
This commit is contained in:
commit
719b878da4
3 changed files with 100 additions and 12 deletions
|
|
@ -1353,22 +1353,37 @@ pub fn setupSharedMemoryWithModuleEnv(allocs: *Allocators, roc_file_path: []cons
|
|||
defer builtin_modules.deinit();
|
||||
|
||||
// Try to find and compile platform modules
|
||||
const app_dir = std.fs.path.dirname(roc_file_path) orelse ".";
|
||||
const platform_dir = try std.fs.path.join(allocs.gpa, &[_][]const u8{ app_dir, "platform" });
|
||||
defer allocs.gpa.free(platform_dir);
|
||||
// Extract the actual platform path from the app header to support paths like "../platform/main.roc"
|
||||
const app_dir = std.fs.path.dirname(roc_file_path) orelse return error.InvalidAppPath;
|
||||
|
||||
const platform_main_path = try std.fs.path.join(allocs.gpa, &[_][]const u8{ platform_dir, "main.roc" });
|
||||
defer allocs.gpa.free(platform_main_path);
|
||||
const platform_spec = try extractPlatformSpecFromApp(allocs, roc_file_path);
|
||||
|
||||
// Resolve relative paths (starting with ./ or ../) relative to app directory
|
||||
// Non-relative paths (like package URLs) are not local platform files
|
||||
const platform_main_path: ?[]const u8 = if (std.mem.startsWith(u8, platform_spec, "./") or std.mem.startsWith(u8, platform_spec, "../"))
|
||||
try std.fs.path.join(allocs.gpa, &[_][]const u8{ app_dir, platform_spec })
|
||||
else
|
||||
null;
|
||||
defer if (platform_main_path) |p| allocs.gpa.free(p);
|
||||
|
||||
// Get the platform directory from the resolved path
|
||||
const platform_dir: ?[]const u8 = if (platform_main_path) |p|
|
||||
std.fs.path.dirname(p) orelse return error.InvalidPlatformPath
|
||||
else
|
||||
null;
|
||||
|
||||
// Extract exposed modules from the platform header (if platform exists)
|
||||
var exposed_modules = std.ArrayList([]const u8).empty;
|
||||
defer exposed_modules.deinit(allocs.gpa);
|
||||
|
||||
var has_platform = true;
|
||||
extractExposedModulesFromPlatform(allocs, platform_main_path, &exposed_modules) catch {
|
||||
// No platform found - that's fine, just continue with no platform modules
|
||||
has_platform = false;
|
||||
};
|
||||
var has_platform = false;
|
||||
if (platform_main_path) |pmp| {
|
||||
has_platform = true;
|
||||
extractExposedModulesFromPlatform(allocs, pmp, &exposed_modules) catch {
|
||||
// Platform file not found or couldn't be parsed - continue without platform modules
|
||||
has_platform = false;
|
||||
};
|
||||
}
|
||||
|
||||
// IMPORTANT: Create header FIRST before any module compilation.
|
||||
// The interpreter_shim expects the Header to be at FIRST_ALLOC_OFFSET (504).
|
||||
|
|
@ -1402,10 +1417,13 @@ pub fn setupSharedMemoryWithModuleEnv(allocs: *Allocators, roc_file_path: []cons
|
|||
defer allocs.gpa.free(platform_env_ptrs);
|
||||
|
||||
for (exposed_modules.items, 0..) |module_name, i| {
|
||||
// platform_dir is guaranteed to be non-null if exposed_modules is non-empty
|
||||
// because we only populate exposed_modules when platform_main_path is non-null
|
||||
const plat_dir = platform_dir orelse unreachable;
|
||||
const module_filename = try std.fmt.allocPrint(allocs.gpa, "{s}.roc", .{module_name});
|
||||
defer allocs.gpa.free(module_filename);
|
||||
|
||||
const module_path = try std.fs.path.join(allocs.gpa, &[_][]const u8{ platform_dir, module_filename });
|
||||
const module_path = try std.fs.path.join(allocs.gpa, &[_][]const u8{ plat_dir, module_filename });
|
||||
defer allocs.gpa.free(module_path);
|
||||
|
||||
const module_env_ptr = try compileModuleToSharedMemory(
|
||||
|
|
@ -1459,9 +1477,10 @@ pub fn setupSharedMemoryWithModuleEnv(allocs: *Allocators, roc_file_path: []cons
|
|||
if (has_platform) {
|
||||
// Cast []*ModuleEnv to []const *ModuleEnv for the function parameter
|
||||
const const_platform_env_ptrs: []const *ModuleEnv = platform_env_ptrs;
|
||||
// platform_main_path is guaranteed non-null when has_platform is true
|
||||
platform_main_env = compileModuleToSharedMemory(
|
||||
allocs,
|
||||
platform_main_path,
|
||||
platform_main_path.?,
|
||||
"main.roc",
|
||||
shm_allocator,
|
||||
&builtin_modules,
|
||||
|
|
|
|||
|
|
@ -119,6 +119,60 @@ test "fx platform effectful functions" {
|
|||
try testing.expect(std.mem.indexOf(u8, run_result.stderr, "Line 3 to stdout") == null);
|
||||
}
|
||||
|
||||
test "fx platform with dotdot starting path" {
|
||||
const allocator = testing.allocator;
|
||||
|
||||
try ensureRocBinary(allocator);
|
||||
|
||||
// Run the app from a subdirectory that uses ../ at the START of its platform path
|
||||
// This tests that relative paths starting with .. are handled correctly
|
||||
// Bug: paths starting with ../ fail with TypeMismatch, while ./path/../ works
|
||||
const run_result = try std.process.Child.run(.{
|
||||
.allocator = allocator,
|
||||
.argv = &[_][]const u8{
|
||||
"./zig-out/bin/roc",
|
||||
"test/fx/subdir/app.roc",
|
||||
},
|
||||
});
|
||||
defer allocator.free(run_result.stdout);
|
||||
defer allocator.free(run_result.stderr);
|
||||
|
||||
switch (run_result.term) {
|
||||
.Exited => |code| {
|
||||
if (code != 0) {
|
||||
std.debug.print("Run failed with exit code {}\n", .{code});
|
||||
std.debug.print("STDOUT: {s}\n", .{run_result.stdout});
|
||||
std.debug.print("STDERR: {s}\n", .{run_result.stderr});
|
||||
return error.RunFailed;
|
||||
}
|
||||
},
|
||||
else => {
|
||||
std.debug.print("Run terminated abnormally: {}\n", .{run_result.term});
|
||||
std.debug.print("STDOUT: {s}\n", .{run_result.stdout});
|
||||
std.debug.print("STDERR: {s}\n", .{run_result.stderr});
|
||||
return error.RunFailed;
|
||||
},
|
||||
}
|
||||
|
||||
// Verify stdout contains expected messages
|
||||
try testing.expect(std.mem.indexOf(u8, run_result.stdout, "Hello from stdout!") != null);
|
||||
try testing.expect(std.mem.indexOf(u8, run_result.stdout, "Line 1 to stdout") != null);
|
||||
try testing.expect(std.mem.indexOf(u8, run_result.stdout, "Line 3 to stdout") != null);
|
||||
|
||||
// Verify stderr contains expected messages
|
||||
try testing.expect(std.mem.indexOf(u8, run_result.stderr, "Error from stderr!") != null);
|
||||
try testing.expect(std.mem.indexOf(u8, run_result.stderr, "Line 2 to stderr") != null);
|
||||
|
||||
// Verify stderr messages are NOT in stdout
|
||||
try testing.expect(std.mem.indexOf(u8, run_result.stdout, "Error from stderr!") == null);
|
||||
try testing.expect(std.mem.indexOf(u8, run_result.stdout, "Line 2 to stderr") == null);
|
||||
|
||||
// Verify stdout messages are NOT in stderr
|
||||
try testing.expect(std.mem.indexOf(u8, run_result.stderr, "Hello from stdout!") == null);
|
||||
try testing.expect(std.mem.indexOf(u8, run_result.stderr, "Line 1 to stdout") == null);
|
||||
try testing.expect(std.mem.indexOf(u8, run_result.stderr, "Line 3 to stdout") == null);
|
||||
}
|
||||
|
||||
test "fx platform stdin to stdout" {
|
||||
const allocator = testing.allocator;
|
||||
|
||||
|
|
|
|||
15
test/fx/subdir/app.roc
Normal file
15
test/fx/subdir/app.roc
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
app [main!] { pf: platform "../platform/main.roc" }
|
||||
|
||||
import pf.Stdout
|
||||
import pf.Stderr
|
||||
|
||||
str : Str -> Str
|
||||
str = |s| s
|
||||
|
||||
main! = || {
|
||||
Stdout.line!(str("Hello from stdout!"))
|
||||
Stdout.line!(str("Line 1 to stdout"))
|
||||
Stderr.line!(str("Line 2 to stderr"))
|
||||
Stdout.line!(str("Line 3 to stdout"))
|
||||
Stderr.line!(str("Error from stderr!"))
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue