erlang/otp — pull request #402 by lrascao (2014-06-23 15:43:55+00:00)
Title: Fix bug windows directory symlinks

TL;DR: escript archive files won't work on Windows when located in directory symlinks between different drives

impact:
rebar for instance will not work in these circumstances

requirements:
- Windows machine, two different drives (eg. C: and J:)
- folder J:\otp_bug\otp_bug, containging an .escript file (generation of .escript described below)
- folder C:\tmp\otp_bug which is a folder symlink to J:\otp_bug (created with: cd c:\tmp, mklink /d otp_bug j:\otp_bug)

repro steps:
- snip repro scripts below, copy demo.erl, build_demo.erl and bug.erl to J:\otp_bug\otp_bug
- enter C:\tmp\otp_bug\otp_bug
- generate demo.escript (run ./build_demo.erl)
- run ./demo.script, it will fail with error undefined function demo:main
- change directory to J:\otp_bug\otp_bug, run the same exact ./demo.script, it will succeed
- run ./bug.erl for isolated problem cause (will print: path: C:/tmp/j:/otp_bug/otp_bug/demo.escript) with fix in place
correct output will be j:/otp_bug/otp_bug/demo.escript

problem description:
- problem is located in method real_path@erts/preloaded/src/erl_prim_loader.erl
- real_path assumes that when a symlink is encountered when exploring the path only the last element of the path array
can be replaced (with the symlink target)
- this is fine for unix, however in windows there is the possibility that the last n elements have to be replaced,
eg. C:\tmp\otp_bug (["gub_pto", "pmt", ":c"] should become ["gub_pto", ":j"])

repro scripts:
---------------- start demo.erl ---------------
%% Demo

-module(demo).
-mode(compile).

-export([main/1]).

main(_Args) ->
io:format("SMP support: ~p~n", [erlang:system_info(smp_support)]).
---------------- end demo.erl ---------------

---------------- start build_demo.erl ---------------
#!/usr/bin/env escript
%% This is an -*- erlang -*- file
%%!
main(_Args) ->
{ok, _, BeamCode} = compile:file("demo.erl", [binary, debug_info]),
{ok, SourceCode} = file:read_file("demo.erl"),
escript:create("demo.escript", [shebang, comment, {emu_args, ""}, {archive, [{"demo.erl", SourceCode}, {"demo.beam", BeamCode}], []}]),
escript:extract("demo.escript", []),
ok.
---------------- end build_demo.erl ---------------

---------------- start bug.erl ---------------
#!/usr/bin/env escript
%% This is an -*- erlang -*- file
%%!
main([]) ->
File = "./demo.escript",
{ok, ArchiveBin} = escript:parse_file(File),
{ok, FileInfo} = file:read_file_info(File),
{ok, Path} = erl_prim_loader:set_primary_archive(File, ArchiveBin, FileInfo, fun escript:parse_file/1),
io:format("path: ~s~n", [Path]).
---------------- end bug.erl ---------------