1: %% 2: %% %CopyrightBegin% 3: %% 4: %% Copyright Ericsson AB 2007-2013. All Rights Reserved. 5: %% 6: %% The contents of this file are subject to the Erlang Public License, 7: %% Version 1.1, (the "License"); you may not use this file except in 8: %% compliance with the License. You should have received a copy of the 9: %% Erlang Public License along with this software. If not, it can be 10: %% retrieved online at http://www.erlang.org/. 11: %% 12: %% Software distributed under the License is distributed on an "AS IS" 13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 14: %% the License for the specific language governing rights and limitations 15: %% under the License. 16: %% 17: %% %CopyrightEnd% 18: 19: -module(escript_SUITE). 20: -export([ 21: all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 22: init_per_group/2,end_per_group/2, 23: init_per_testcase/2, 24: end_per_testcase/2, 25: basic/1, 26: errors/1, 27: strange_name/1, 28: emulator_flags/1, 29: emulator_flags_no_shebang/1, 30: module_script/1, 31: beam_script/1, 32: archive_script/1, 33: archive_script_file_access/1, 34: epp/1, 35: create_and_extract/1, 36: foldl/1, 37: overflow/1, 38: verify_sections/3, 39: unicode/1 40: ]). 41: 42: -include_lib("test_server/include/test_server.hrl"). 43: -include_lib("kernel/include/file.hrl"). 44: 45: suite() -> [{ct_hooks,[ts_install_cth]}]. 46: 47: all() -> 48: [basic, errors, strange_name, emulator_flags, 49: emulator_flags_no_shebang, 50: module_script, beam_script, archive_script, epp, 51: create_and_extract, foldl, overflow, 52: archive_script_file_access, unicode]. 53: 54: groups() -> 55: []. 56: 57: init_per_suite(Config) -> 58: Config. 59: 60: end_per_suite(_Config) -> 61: ok. 62: 63: init_per_group(_GroupName, Config) -> 64: Config. 65: 66: end_per_group(_GroupName, Config) -> 67: Config. 68: 69: init_per_testcase(_Case, Config) -> 70: ?line Dog = ?t:timetrap(?t:minutes(5)), 71: [{watchdog,Dog}|Config]. 72: 73: end_per_testcase(_Case, Config) -> 74: Dog = ?config(watchdog, Config), 75: test_server:timetrap_cancel(Dog), 76: ok. 77: 78: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 79: 80: basic(Config) when is_list(Config) -> 81: Data = ?config(data_dir, Config), 82: Dir = filename:absname(Data), %Get rid of trailing slash. 83: ?line run(Dir, "factorial 5", 84: <<"factorial 5 = 120\nExitCode:0">>), 85: ?line run(Dir, "factorial_compile 10", 86: <<"factorial 10 = 3628800\nExitCode:0">>), 87: ?line run(Dir, "factorial_compile_main 7", 88: <<"factorial 7 = 5040\nExitCode:0">>), 89: ?line run(Dir, "factorial_warning 20", 90: [data_dir,<<"factorial_warning:12: Warning: function bar/0 is unused\n" 91: "factorial 20 = 2432902008176640000\nExitCode:0">>]), 92: ?line run_with_opts(Dir, "-s", "factorial_warning", 93: [data_dir,<<"factorial_warning:12: Warning: function bar/0 is unused\nExitCode:0">>]), 94: ?line run_with_opts(Dir, "-s -i", "factorial_warning", 95: [data_dir,<<"factorial_warning:12: Warning: function bar/0 is unused\nExitCode:0">>]), 96: ?line run_with_opts(Dir, "-c -s", "factorial_warning", 97: [data_dir,<<"factorial_warning:12: Warning: function bar/0 is unused\nExitCode:0">>]), 98: ?line run(Dir, "filesize "++filename:join(?config(data_dir, Config),"filesize"), 99: [data_dir,<<"filesize:11: Warning: function id/1 is unused\n324\nExitCode:0">>]), 100: ?line run(Dir, "test_script_name", 101: [data_dir,<<"test_script_name\nExitCode:0">>]), 102: ?line run(Dir, "tail_rec 1000", 103: [<<"ok\nExitCode:0">>]), 104: 105: %% We expect the trap_exit flag for the process to be false, 106: %% since that is the default state for newly spawned processes. 107: ?line run(Dir, "trap_exit", 108: <<"false\nExitCode:0">>), 109: ok. 110: 111: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 112: 113: errors(Config) when is_list(Config) -> 114: Data = ?config(data_dir, Config), 115: Dir = filename:absname(Data), %Get rid of trailing slash. 116: ?line run(Dir, "compile_error", 117: [data_dir,<<"compile_error:5: syntax error before: '*'\n">>, 118: data_dir,<<"compile_error:8: syntax error before: blarf\n">>, 119: <<"escript: There were compilation errors.\nExitCode:127">>]), 120: ?line run(Dir, "lint_error", 121: [data_dir,<<"lint_error:6: function main/1 already defined\n">>, 122: data_dir,"lint_error:8: variable 'ExitCode' is unbound\n", 123: <<"escript: There were compilation errors.\nExitCode:127">>]), 124: ?line run_with_opts(Dir, "-s", "lint_error", 125: [data_dir,<<"lint_error:6: function main/1 already defined\n">>, 126: data_dir,"lint_error:8: variable 'ExitCode' is unbound\n", 127: <<"escript: There were compilation errors.\nExitCode:127">>]), 128: ok. 129: 130: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 131: 132: strange_name(Config) when is_list(Config) -> 133: Data = ?config(data_dir, Config), 134: Dir = filename:absname(Data), %Get rid of trailing slash. 135: ?line run(Dir, "strange.name -arg1 arg2 arg3", 136: [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" 137: "ExitCode:0">>]), 138: ok. 139: 140: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 141: 142: emulator_flags(Config) when is_list(Config) -> 143: Data = ?config(data_dir, Config), 144: Dir = filename:absname(Data), %Get rid of trailing slash. 145: ?line run(Dir, "emulator_flags -arg1 arg2 arg3", 146: [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" 147: "nostick:[{nostick,[]}]\n" 148: "mnesia:[{mnesia,[\"dir\",\"a/directory\"]},{mnesia,[\"debug\",\"verbose\"]}]\n" 149: "ERL_FLAGS=false\n" 150: "unknown:[]\n" 151: "ExitCode:0">>]), 152: ok. 153: 154: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 155: 156: emulator_flags_no_shebang(Config) when is_list(Config) -> 157: Data = ?config(data_dir, Config), 158: Dir = filename:absname(Data), %Get rid of trailing slash. 159: %% Need run_with_opts, to always use "escript" explicitly 160: ?line run_with_opts(Dir, "", "emulator_flags_no_shebang -arg1 arg2 arg3", 161: [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" 162: "nostick:[{nostick,[]}]\n" 163: "mnesia:[{mnesia,[\"dir\",\"a/directory\"]},{mnesia,[\"debug\",\"verbose\"]}]\n" 164: "ERL_FLAGS=false\n" 165: "unknown:[]\n" 166: "ExitCode:0">>]), 167: ok. 168: 169: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 170: %% Pick the source code from the emulator_flags script 171: %% Generate a new escript with a module header 172: 173: module_script(Config) when is_list(Config) -> 174: %% Read orig file 175: Data = ?config(data_dir, Config), 176: OrigFile = filename:join([Data,"emulator_flags"]), 177: {ok, OrigBin} = file:read_file(OrigFile), 178: ?line [Shebang, Mode, Flags | Source] = string:tokens(binary_to_list(OrigBin), "\n"), 179: ?line {ok, OrigFI} = file:read_file_info(OrigFile), 180: 181: %% Write source file 182: Priv = ?config(priv_dir, Config), 183: Dir = filename:absname(Priv), % Get rid of trailing slash. 184: Base = "module_script", 185: ErlFile = filename:join([Priv, Base ++ ".erl"]), 186: ErlCode = ["\n-module(", Base, ").\n", 187: "-export([main/1]).\n\n", 188: string:join(Source, "\n"), 189: "\n"], 190: ?line ok = file:write_file(ErlFile, ErlCode), 191: 192: %%%%%%% 193: %% Create and run scripts without emulator flags 194: 195: %% With shebang 196: NoArgsBase = Base ++ "_no_args_with_shebang", 197: NoArgsFile = filename:join([Priv, NoArgsBase]), 198: ?line ok = file:write_file(NoArgsFile, 199: [Shebang, "\n", 200: ErlCode]), 201: ?line ok = file:write_file_info(NoArgsFile, OrigFI), 202: 203: ?line run(Dir, NoArgsBase ++ " -arg1 arg2 arg3", 204: [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" 205: "nostick:[]\n" 206: "mnesia:[]\n" 207: "ERL_FLAGS=false\n" 208: "unknown:[]\n" 209: "ExitCode:0">>]), 210: 211: ?line run_with_opts(Dir, "", NoArgsBase ++ " -arg1 arg2 arg3", 212: [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" 213: "nostick:[]\n" 214: "mnesia:[]\n" 215: "ERL_FLAGS=false\n" 216: "unknown:[]\n" 217: "ExitCode:0">>]), 218: 219: %% Without shebang 220: NoArgsBase2 = Base ++ "_no_args_without_shebang", 221: NoArgsFile2 = filename:join([Priv, NoArgsBase2]), 222: ?line ok = file:write_file(NoArgsFile2, 223: ["Something else than shebang!!!", "\n", 224: ErlCode]), 225: ?line ok = file:write_file_info(NoArgsFile2, OrigFI), 226: 227: ?line run_with_opts(Dir, "", NoArgsBase2 ++ " -arg1 arg2 arg3", 228: [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" 229: "nostick:[]\n" 230: "mnesia:[]\n" 231: "ERL_FLAGS=false\n" 232: "unknown:[]\n" 233: "ExitCode:0">>]), 234: 235: %% Plain module without header 236: NoArgsBase3 = Base ++ "_no_args_without_header", 237: NoArgsFile3 = filename:join([Priv, NoArgsBase3]), 238: ?line ok = file:write_file(NoArgsFile3, [ErlCode]), 239: ?line ok = file:write_file_info(NoArgsFile3, OrigFI), 240: 241: ?line run_with_opts(Dir, "", NoArgsBase3 ++ " -arg1 arg2 arg3", 242: [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" 243: "nostick:[]\n" 244: "mnesia:[]\n" 245: "ERL_FLAGS=false\n" 246: "unknown:[]\n" 247: "ExitCode:0">>]), 248: 249: %%%%%%% 250: %% Create and run scripts with emulator flags 251: 252: %% With shebang 253: ArgsBase = Base ++ "_args_with_shebang", 254: ArgsFile = filename:join([Priv, ArgsBase]), 255: ?line ok = file:write_file(ArgsFile, 256: [Shebang, "\n", 257: Mode, "\n", 258: Flags, "\n", 259: ErlCode]), 260: ?line ok = file:write_file_info(ArgsFile, OrigFI), 261: 262: ?line run(Dir, ArgsBase ++ " -arg1 arg2 arg3", 263: [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" 264: "nostick:[{nostick,[]}]\n" 265: "mnesia:[{mnesia,[\"dir\",\"a/directory\"]},{mnesia,[\"debug\",\"verbose\"]}]\n" 266: "ERL_FLAGS=false\n" 267: "unknown:[]\n" 268: "ExitCode:0">>]), 269: 270: ok. 271: 272: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 273: %% Pick the source code from the emulator_flags script and compile it. 274: %% Generate a new escript containing the beam code and the escript header 275: beam_script(Config) when is_list(Config) -> 276: %% Read orig file 277: Data = ?config(data_dir, Config), 278: OrigFile = filename:join([Data,"emulator_flags"]), 279: {ok, OrigBin} = file:read_file(OrigFile), 280: ?line [Shebang, Mode, Flags | Source] = string:tokens(binary_to_list(OrigBin), "\n"), 281: ?line {ok, OrigFI} = file:read_file_info(OrigFile), 282: 283: %% Write source file 284: Priv = ?config(priv_dir, Config), 285: Dir = filename:absname(Priv), % Get rid of trailing slash. 286: Base = "beam_script", 287: ErlFile = filename:join([Priv, Base ++ ".erl"]), 288: ?line ok = file:write_file(ErlFile, 289: ["\n-module(", Base, ").\n", 290: "-export([main/1]).\n\n", 291: string:join(Source, "\n"), 292: "\n"]), 293: 294: %% Compile the code 295: ?line {ok, _Mod, BeamCode} = compile:file(ErlFile, [binary]), 296: 297: %%%%%%% 298: %% Create and run scripts without emulator flags 299: 300: %% With shebang 301: NoArgsBase = Base ++ "_no_args_with_shebang", 302: NoArgsFile = filename:join([Priv, NoArgsBase]), 303: ?line ok = file:write_file(NoArgsFile, 304: [Shebang, "\n", 305: BeamCode]), 306: ?line ok = file:write_file_info(NoArgsFile, OrigFI), 307: 308: ?line run(Dir, NoArgsBase ++ " -arg1 arg2 arg3", 309: [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" 310: "nostick:[]\n" 311: "mnesia:[]\n" 312: "ERL_FLAGS=false\n" 313: "unknown:[]\n" 314: "ExitCode:0">>]), 315: 316: ?line run_with_opts(Dir, "", NoArgsBase ++ " -arg1 arg2 arg3", 317: [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" 318: "nostick:[]\n" 319: "mnesia:[]\n" 320: "ERL_FLAGS=false\n" 321: "unknown:[]\n" 322: "ExitCode:0">>]), 323: 324: %% Without shebang 325: NoArgsBase2 = Base ++ "_no_args_without_shebang", 326: NoArgsFile2 = filename:join([Priv, NoArgsBase2]), 327: ?line ok = file:write_file(NoArgsFile2, 328: ["Something else than shebang!!!", "\n", 329: BeamCode]), 330: ?line ok = file:write_file_info(NoArgsFile2, OrigFI), 331: 332: ?line run_with_opts(Dir, "", NoArgsBase2 ++ " -arg1 arg2 arg3", 333: [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" 334: "nostick:[]\n" 335: "mnesia:[]\n" 336: "ERL_FLAGS=false\n" 337: "unknown:[]\n" 338: "ExitCode:0">>]), 339: 340: %% Plain beam file without header 341: NoArgsBase3 = Base ++ "_no_args_without_header", 342: NoArgsFile3 = filename:join([Priv, NoArgsBase3]), 343: ?line ok = file:write_file(NoArgsFile3, [BeamCode]), 344: ?line ok = file:write_file_info(NoArgsFile3, OrigFI), 345: 346: ?line run_with_opts(Dir, "", NoArgsBase3 ++ " -arg1 arg2 arg3", 347: [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" 348: "nostick:[]\n" 349: "mnesia:[]\n" 350: "ERL_FLAGS=false\n" 351: "unknown:[]\n" 352: "ExitCode:0">>]), 353: 354: %%%%%%% 355: %% Create and run scripts with emulator flags 356: 357: %% With shebang 358: ArgsBase = Base ++ "_args", 359: ArgsFile = filename:join([Priv, ArgsBase]), 360: ?line ok = file:write_file(ArgsFile, 361: [Shebang, "\n", 362: Mode, "\n", 363: Flags, "\n", 364: BeamCode]), 365: ?line ok = file:write_file_info(ArgsFile, OrigFI), 366: 367: ?line run(Dir, ArgsBase ++ " -arg1 arg2 arg3", 368: [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" 369: "nostick:[{nostick,[]}]\n" 370: "mnesia:[{mnesia,[\"dir\",\"a/directory\"]},{mnesia,[\"debug\",\"verbose\"]}]\n" 371: "ERL_FLAGS=false\n" 372: "unknown:[]\n" 373: "ExitCode:0">>]), 374: ok. 375: 376: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 377: %% Create an archive file containing two entire applications plus two 378: %% alternate main modules. Generate a new escript containing the archive 379: %% (with .app and .beam files and) and the escript header. 380: 381: archive_script(Config) when is_list(Config) -> 382: %% Copy the orig files to priv_dir 383: DataDir = ?config(data_dir, Config), 384: PrivDir = ?config(priv_dir, Config), 385: Archive = filename:join([PrivDir, "archive_script.zip"]), 386: ?line {ok, _} = zip:create(Archive, ["archive_script"], 387: [{compress, []}, {cwd, DataDir}]), 388: ?line {ok, _} = zip:extract(Archive, [{cwd, PrivDir}]), 389: TopDir = filename:join([PrivDir, "archive_script"]), 390: 391: %% Compile the code 392: ?line ok = compile_app(TopDir, "archive_script_dict"), 393: ?line ok = compile_app(TopDir, "archive_script_dummy"), 394: ?line {ok, MainFiles} = file:list_dir(TopDir), 395: ?line ok = compile_files(MainFiles, TopDir, TopDir), 396: 397: %% Create the archive 398: {ok, TopFiles} = file:list_dir(TopDir), 399: ?line {ok, {_, ArchiveBin}} = zip:create(Archive, TopFiles, 400: [memory, {compress, []}, {cwd, TopDir}]), 401: 402: %% Read the source script 403: OrigFile = filename:join([DataDir, "emulator_flags"]), 404: {ok, OrigBin} = file:read_file(OrigFile), 405: ?line [Shebang, Mode, _Flags | _Source] = 406: string:tokens(binary_to_list(OrigBin), "\n"), 407: Flags = "%%! -archive_script_dict foo bar" 408: " -archive_script_dict foo" 409: " -archive_script_dummy bar", 410: ?line {ok, OrigFI} = file:read_file_info(OrigFile), 411: 412: %%%%%%% 413: %% Create and run scripts without emulator flags 414: MainBase = "archive_script_main", 415: MainScript = filename:join([PrivDir, MainBase]), 416: 417: %% With shebang 418: ?line ok = file:write_file(MainScript, 419: [Shebang, "\n", 420: Flags, "\n", 421: ArchiveBin]), 422: ?line ok = file:write_file_info(MainScript, OrigFI), 423: 424: ?line run(PrivDir, MainBase ++ " -arg1 arg2 arg3", 425: [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" 426: "dict:[{archive_script_dict,[\"foo\",\"bar\"]},{archive_script_dict,[\"foo\"]}]\n" 427: "dummy:[{archive_script_dummy,[\"bar\"]}]\n" 428: "priv:{ok,<<\"Some private data...\\n\">>}\n" 429: "ExitCode:0">>]), 430: 431: ?line run_with_opts(PrivDir, "", MainBase ++ " -arg1 arg2 arg3", 432: [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" 433: "dict:[{archive_script_dict,[\"foo\",\"bar\"]},{archive_script_dict,[\"foo\"]}]\n" 434: "dummy:[{archive_script_dummy,[\"bar\"]}]\n" 435: "priv:{ok,<<\"Some private data...\\n\">>}\n" 436: "ExitCode:0">>]), 437: 438: ?line ok = file:rename(MainScript, MainScript ++ "_with_shebang"), 439: 440: %% Without shebang (no flags) 441: ?line ok = file:write_file(MainScript, 442: ["Something else than shebang!!!", "\n", 443: ArchiveBin]), 444: ?line ok = file:write_file_info(MainScript, OrigFI), 445: 446: ?line run_with_opts(PrivDir, "", MainBase ++ " -arg1 arg2 arg3", 447: [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" 448: "dict:[]\n" 449: "dummy:[]\n" 450: "priv:{ok,<<\"Some private data...\\n\">>}\n" 451: "ExitCode:0">>]), 452: ?line ok = file:rename(MainScript, MainScript ++ "_without_shebang"), 453: 454: %% Plain archive without header (no flags) 455: 456: ?line ok = file:write_file(MainScript, [ArchiveBin]), 457: ?line ok = file:write_file_info(MainScript, OrigFI), 458: 459: ?line run_with_opts(PrivDir, "", MainBase ++ " -arg1 arg2 arg3", 460: [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" 461: "dict:[]\n" 462: "dummy:[]\n" 463: "priv:{ok,<<\"Some private data...\\n\">>}\n" 464: "ExitCode:0">>]), 465: ?line ok = file:rename(MainScript, MainScript ++ "_without_header"), 466: 467: %%%%%%% 468: %% Create and run scripts with emulator flags 469: AltBase = "archive_script_alternate_main", 470: AltScript = filename:join([PrivDir, AltBase]), 471: ?line ok = file:write_file(AltScript, 472: [Shebang, "\n", 473: Mode, "\n", 474: Flags, " -escript main archive_script_main2\n", 475: ArchiveBin]), 476: ?line ok = file:write_file_info(AltScript, OrigFI), 477: 478: ?line run(PrivDir, AltBase ++ " -arg1 arg2 arg3", 479: [<<"main2:[\"-arg1\",\"arg2\",\"arg3\"]\n" 480: "dict:[{archive_script_dict,[\"foo\",\"bar\"]},{archive_script_dict,[\"foo\"]}]\n" 481: "dummy:[{archive_script_dummy,[\"bar\"]}]\n" 482: "priv:{ok,<<\"Some private data...\\n\">>}\n" 483: "ExitCode:0">>]), 484: 485: ok. 486: 487: %% Test the correction of OTP-10071 488: %% The errors identified are 489: %% 490: %% a) If primary archive was named "xxx", then a file in the same 491: %% directory named "xxxyyy" would be interpreted as a file named yyy 492: %% inside the archive. 493: %% 494: %% b) erl_prim_loader did not correctly create and normalize absolute 495: %% paths for primary archive and files inside it, so unless given 496: %% with exact same path files inside the archive would not be 497: %% found. E.g. if escript was started as ./xxx then "xxx/file" 498: %% would not be found since erl_prim_loader would try to match 499: %% /full/path/to/xxx with /full/path/to/./xxx. Same problem with 500: %% ../. Also, the use of symlinks in the path to the archive would 501: %% cause problems. 502: %% 503: %% c) Depending on how the primary archive was built, 504: %% erl_prim_loader:list_dir/1 would sometimes return an empty string 505: %% inside the file list. This was a virtual element representing the 506: %% top directory of the archive. This shall not occur. 507: %% 508: archive_script_file_access(Config) when is_list(Config) -> 509: %% Copy the orig files to priv_dir 510: DataDir = ?config(data_dir, Config), 511: PrivDir = ?config(priv_dir, Config), 512: 513: MainMod = "archive_script_file_access", 514: MainSrc = MainMod ++ ".erl", 515: MainBeam = MainMod ++ ".beam", 516: 517: Archive = filename:join([PrivDir, "archive_script_file_access.zip"]), 518: ?line {ok, _} = zip:create(Archive, ["archive_script_file_access"], 519: [{compress, []}, {cwd, DataDir}]), 520: ?line {ok, _} = zip:extract(Archive, [{cwd, PrivDir}]), 521: TopDir = filename:join([PrivDir, "archive_script_file_access"]), 522: 523: %% Compile the code 524: ?line ok = compile_files([MainSrc], TopDir, TopDir), 525: 526: %% First, create a file structure which will be included in the archive: 527: %% 528: %% dir1/ 529: %% dir1/subdir1/ 530: %% dir1/subdir1/file1 531: %% 532: {ok, OldDir} = file:get_cwd(), 533: ok = file:set_cwd(TopDir), 534: DummyDir = "dir1", 535: DummySubDir = filename:join(DummyDir, "subdir1"), 536: RelDummyFile = filename:join(DummySubDir, "file1"), 537: DummyFile = filename:join(TopDir,RelDummyFile), 538: ok = filelib:ensure_dir(DummyFile), 539: ok = file:write_file(DummyFile, ["foo\nbar\nbaz"]), 540: 541: %% 1. Create zip archive by adding the dummy file and the beam 542: %% file as binaries to zip. 543: %% 544: %% This used to provoke the following issues when the script was run as 545: %% "./<script_name>": 546: %% a. erl_prim_loader:read_file_info/1 returning 'error' 547: %% b. erl_prim_loader:list_dir/1 returning {ok, ["dir1", [], "file1"]} 548: %% leading to an infinite loop in reltool_target:spec_dir/1 549: Files1 = 550: lists:map(fun(Filename) -> 551: {ok, Bin} = file:read_file(Filename), 552: {Filename,Bin} 553: end, 554: [RelDummyFile,MainBeam]), 555: {ok, {"mem", Bin1}} = zip:create("mem", Files1, [memory]), 556: 557: %% Create the escript 558: ScriptName1 = "archive_script_file_access1", 559: Script1 = filename:join([PrivDir, ScriptName1]), 560: Flags = "-escript main " ++ MainMod, 561: ok = escript:create(Script1,[shebang,{emu_args,Flags},{archive,Bin1}]), 562: ok = file:change_mode(Script1,8#00744), 563: 564: %% If supported, create a symlink to the script. This is used to 565: %% test error b) described above this test case. 566: SymlinkName1 = "symlink_to_"++ScriptName1, 567: Symlink1 = filename:join([PrivDir, SymlinkName1]), 568: file:make_symlink(ScriptName1,Symlink1), % will fail if not supported 569: 570: %% Also add a dummy file in the same directory with the same name 571: %% as the script except is also has an extension. This used to 572: %% test error a) described above this test case. 573: ok = file:write_file(Script1 ++ ".extension", 574: <<"same name as script, but with extension">>), 575: 576: %% Change to script's directory and run it as "./<script_name>" 577: ok = file:set_cwd(PrivDir), 578: run(PrivDir, "./" ++ ScriptName1 ++ " " ++ ScriptName1, 579: [<<"ExitCode:0">>]), 580: ok = file:set_cwd(TopDir), 581: 582: 583: %% 2. Create zip archive by letting zip read the files from the file system 584: %% 585: %% The difference compared to the archive_script_file_access1 is 586: %% that this will have a file element for each directory in the 587: %% archive - while archive_script_file_access1 will only have a 588: %% file element per regular file. 589: Files2 = [DummyDir,MainBeam], 590: {ok, {"mem", Bin2}} = zip:create("mem", Files2, [memory]), 591: 592: %% Create the escript 593: ScriptName2 = "archive_script_file_access2", 594: Script2 = filename:join([PrivDir, ScriptName2]), 595: ok = escript:create(Script2,[shebang,{emu_args,Flags},{archive,Bin2}]), 596: ok = file:change_mode(Script2,8#00744), 597: 598: %% Also add a dummy file in the same directory with the same name 599: %% as the script except is also has an extension. This used to 600: %% test error a) described above this test case. 601: ok = file:write_file(Script2 ++ ".extension", 602: <<"same name as script, but with extension">>), 603: 604: %% If supported, create a symlink to the script. This is used to 605: %% test error b) described above this test case. 606: SymlinkName2 = "symlink_to_"++ScriptName2, 607: Symlink2 = filename:join([PrivDir, SymlinkName2]), 608: file:make_symlink(ScriptName2,Symlink2), % will fail if not supported 609: 610: %% Change to script's directory and run it as "./<script_name>" 611: ok = file:set_cwd(PrivDir), 612: run(PrivDir, "./" ++ ScriptName2 ++ " " ++ ScriptName2, 613: [<<"ExitCode:0">>]), 614: 615: %% 3. If symlinks are supported, run one of the scripts via a symlink. 616: %% 617: %% This is in order to test error b) described above this test case. 618: case element(1,os:type()) =:= win32 orelse file:read_link(Symlink2) of 619: {ok,_} -> 620: run(PrivDir, "./" ++ SymlinkName2 ++ " " ++ ScriptName2, 621: [<<"ExitCode:0">>]); 622: _ -> % not supported 623: ok 624: end, 625: ok = file:set_cwd(OldDir). 626: 627: 628: compile_app(TopDir, AppName) -> 629: AppDir = filename:join([TopDir, AppName]), 630: SrcDir = filename:join([AppDir, "src"]), 631: OutDir = filename:join([AppDir, "ebin"]), 632: ?line {ok, Files} = file:list_dir(SrcDir), 633: compile_files(Files, SrcDir, OutDir). 634: 635: compile_files([File | Files], SrcDir, OutDir) -> 636: case filename:extension(File) of 637: ".erl" -> 638: AbsFile = filename:join([SrcDir, File]), 639: case compile:file(AbsFile, [{outdir, OutDir},report_errors]) of 640: {ok, _Mod} -> 641: compile_files(Files, SrcDir, OutDir); 642: Error -> 643: {compilation_error, AbsFile, OutDir, Error} 644: end; 645: _ -> 646: compile_files(Files, SrcDir, OutDir) 647: end; 648: compile_files([], _, _) -> 649: ok. 650: 651: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 652: 653: epp(Config) when is_list(Config) -> 654: Data = ?config(data_dir, Config), 655: Dir = filename:absname(Data), %Get rid of trailing slash. 656: ?line run(Dir, "factorial_epp 5", 657: <<"factorial 5 = 120\nExitCode:0">>), 658: ok. 659: 660: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 661: 662: create_and_extract(Config) when is_list(Config) -> 663: {NewFile, FileInfo, 664: EmuArg, Source, 665: _ErlBase, ErlCode, 666: _BeamBase, BeamCode, 667: ArchiveBin} = 668: prepare_creation("create_and_extract", Config), 669: 670: Bodies = 671: [[{source, ErlCode}], 672: [{beam, BeamCode}], 673: [{archive, ArchiveBin}]], 674: 675: %% Verify all combinations of scripts with shebangs 676: [verify_sections(NewFile, FileInfo, S ++ C ++ E ++ B) || 677: S <- [[{shebang, default}], 678: [{shebang, "/usr/bin/env escript"}]], 679: C <- [[], 680: [{comment, undefined}], 681: [{comment, default}], 682: [{comment, "This is a nonsense comment"}]], 683: E <- [[], 684: [{emu_args, undefined}], 685: [{emu_args, EmuArg}]], 686: B <- [[{source, Source}] | Bodies]], 687: 688: %% Verify all combinations of scripts without shebangs 689: [verify_sections(NewFile, FileInfo, S ++ C ++ E ++ B) || 690: S <- [[], [{shebang, undefined}]], 691: C <- [[], [{comment, undefined}]], 692: E <- [[], [{emu_args, undefined}]], 693: B <- Bodies], 694: 695: %% Verify the compile_source option 696: file:delete(NewFile), 697: ?line ok = escript:create(NewFile, [{source, Source}]), 698: ?line {ok, [_, _, _, {source, Source}]} = escript:extract(NewFile, []), 699: ?line {ok, [_, _, _, {source, BeamCode2}]} = 700: escript:extract(NewFile, [compile_source]), 701: verify_sections(NewFile, FileInfo, 702: [{shebang, default}, 703: {comment, default}, 704: {beam, BeamCode2}]), 705: 706: file:delete(NewFile), 707: ok. 708: 709: prepare_creation(Base, Config) -> 710: %% Read the source 711: PrivDir = ?config(priv_dir, Config), 712: DataDir = ?config(data_dir, Config), 713: OrigFile = filename:join([DataDir,"emulator_flags"]), 714: ?line {ok, FileInfo} = file:read_file_info(OrigFile), 715: NewFile = filename:join([PrivDir, Base]), 716: ?line {ok, [{shebang, default}, 717: {comment, _}, 718: {emu_args, EmuArg}, 719: {source, Source}]} = 720: escript:extract(OrigFile, []), 721: 722: %% Compile the code 723: ErlFile = NewFile ++ ".erl", 724: ErlCode = list_to_binary(["\n-module(", Base, ").\n", 725: "-export([main/1]).\n\n", 726: Source, "\n\n"]), 727: ?line ok = file:write_file(ErlFile, ErlCode), 728: 729: %% Compile the code 730: ?line {ok, _Mod, BeamCode} = 731: compile:file(ErlFile, [binary, debug_info]), 732: 733: %% Create an archive 734: ?line {ok, {_, ArchiveBin}} = 735: zip:create("dummy_archive_name", 736: [{Base ++ ".erl", ErlCode}, 737: {Base ++ ".beam", BeamCode}], 738: [{compress, []}, memory]), 739: {NewFile, FileInfo, 740: EmuArg, Source, 741: Base ++ ".erl", ErlCode, 742: Base ++ ".beam", BeamCode, 743: ArchiveBin}. 744: 745: verify_sections(File, FileInfo, Sections) -> 746: io:format("~p:verify_sections(\n\t~p,\n\t~p,\n\t~p).\n", 747: [?MODULE, File, FileInfo, Sections]), 748: 749: %% Create 750: file:delete(File), 751: ?line ok = escript:create(File, Sections), 752: ?line ok = file:write_file_info(File, FileInfo), 753: 754: %% Run 755: Dir = filename:absname(filename:dirname(File)), 756: Base = filename:basename(File), 757: 758: HasArg = fun(Tag) -> 759: case lists:keysearch(Tag, 1, Sections) of 760: false -> false; 761: {value, {_, undefined}} -> false; 762: {value, _} -> true 763: end 764: end, 765: ExpectedMain = <<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n">>, 766: ExpectedOutput = 767: case HasArg(emu_args) of 768: true -> 769: <<"nostick:[{nostick,[]}]\n" 770: "mnesia:[{mnesia,[\"dir\",\"a/directory\"]},{mnesia,[\"debug\",\"verbose\"]}]\n" 771: "ERL_FLAGS=false\n" 772: "unknown:[]\n" 773: "ExitCode:0">>; 774: false -> 775: <<"nostick:[]\nmnesia:[]\nERL_FLAGS=false\nunknown:[]\nExitCode:0">> 776: end, 777: 778: InputArgs = Base ++ " -arg1 arg2 arg3", 779: Expected = <<ExpectedMain/binary, ExpectedOutput/binary>>, 780: case HasArg(shebang) of 781: true -> 782: ?line run(Dir, InputArgs, [Expected]); 783: false -> 784: ?line run_with_opts(Dir, [], InputArgs, [Expected]) 785: end, 786: 787: %% Verify 788: ?line {ok, Bin} = escript:create(binary, Sections), 789: ?line {ok, Read} = file:read_file(File), 790: ?line Bin = Read, % Assert 791: 792: Normalized = normalize_sections(Sections), 793: ?line {ok, Extracted} = escript:extract(File, []), 794: io:format("Normalized; ~p\n", [Normalized]), 795: io:format("Extracted ; ~p\n", [Extracted]), 796: ?line Normalized = Extracted, % Assert 797: ok. 798: 799: normalize_sections(Sections) -> 800: AtomToTuple = 801: fun(Val) -> 802: if 803: is_atom(Val) -> {Val, default}; 804: true -> Val 805: end 806: end, 807: case lists:map(AtomToTuple, [{K, V} || {K, V} <- Sections, V =/= undefined]) of 808: [{shebang, Shebang} | Rest] -> 809: [{shebang, Shebang} | 810: case Rest of 811: [{comment, Comment} | Rest2] -> 812: [{comment, Comment} | 813: case Rest2 of 814: [{emu_args, EmuArgs}, Body] -> 815: [{emu_args, EmuArgs}, Body]; 816: [Body] -> 817: [{emu_args, undefined}, Body] 818: end 819: ]; 820: [{emu_args, EmuArgs}, Body] -> 821: [{comment, undefined}, {emu_args, EmuArgs}, Body]; 822: [Body] -> 823: [{comment, undefined}, {emu_args, undefined}, Body] 824: end 825: ]; 826: [Body] -> 827: [{shebang, undefined}, {comment, undefined}, {emu_args, undefined}, Body] 828: end. 829: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 830: 831: 832: 833: foldl(Config) when is_list(Config) -> 834: {NewFile, _FileInfo, 835: _EmuArg, _Source, 836: ErlBase, ErlCode, 837: BeamBase, _BeamCode, 838: ArchiveBin} = 839: prepare_creation("foldl", Config), 840: 841: Collect = fun(Name, GetInfo, GetBin, Acc) -> 842: [{Name, GetInfo(), GetBin()} | Acc] 843: end, 844: 845: %% Get line numbers and the file attribute right 846: SourceFile = NewFile ++ ".erl", 847: <<_:1/binary, ErlCode2/binary>> = ErlCode, 848: ?line ok = file:write_file(SourceFile, ErlCode2), 849: ?line {ok, _Mod, BeamCode} = 850: compile:file(SourceFile, [binary, debug_info]), 851: 852: %% Verify source script 853: ?line ok = escript:create(SourceFile, [{source, ErlCode}]), 854: ?line {ok, [{".", _, BeamCode2}]} 855: = escript_foldl(Collect, [], SourceFile), 856: 857: ?line {ok, Abstr} = beam_lib:chunks(BeamCode, [abstract_code]), 858: ?line {ok, Abstr2} = beam_lib:chunks(BeamCode2, [abstract_code]), 859: %% io:format("abstr1=~p\n", [Abstr]), 860: %% io:format("abstr2=~p\n", [Abstr2]), 861: ?line Abstr = Abstr2, % Assert 862: 863: %% Verify beam script 864: ?line ok = escript:create(NewFile, [{beam, BeamCode}]), 865: ?line {ok, [{".", _, BeamCode}]} 866: = escript_foldl(Collect, [], NewFile), 867: 868: %% Verify archive scripts 869: ?line ok = escript:create(NewFile, [{archive, ArchiveBin}]), 870: ?line {ok, [{BeamBase, #file_info{}, _}, 871: {ErlBase, #file_info{}, _}]} 872: = escript_foldl(Collect, [], NewFile), 873: 874: ArchiveFiles = [{ErlBase, ErlCode}, {BeamBase, BeamCode}], 875: ?line ok = escript:create(NewFile, [{archive, ArchiveFiles, []}]), 876: ?line {ok, [{BeamBase, _, _}, 877: {ErlBase, _, _}]} 878: = escript_foldl(Collect, [], NewFile), 879: 880: ok. 881: 882: escript_foldl(Fun, Acc, File) -> 883: code:ensure_loaded(zip), 884: case erlang:function_exported(zip, foldl, 3) of 885: true -> 886: emulate_escript_foldl(Fun, Acc, File); 887: false -> 888: escript:foldl(Fun, Acc, File) 889: end. 890: 891: emulate_escript_foldl(Fun, Acc, File) -> 892: case escript:extract(File, [compile_source]) of 893: {ok, [_Shebang, _Comment, _EmuArgs, Body]} -> 894: case Body of 895: {source, BeamCode} -> 896: GetInfo = fun() -> file:read_file_info(File) end, 897: GetBin = fun() -> BeamCode end, 898: {ok, Fun(".", GetInfo, GetBin, Acc)}; 899: {beam, BeamCode} -> 900: GetInfo = fun() -> file:read_file_info(File) end, 901: GetBin = fun() -> BeamCode end, 902: {ok, Fun(".", GetInfo, GetBin, Acc)}; 903: {archive, ArchiveBin} -> 904: zip:foldl(Fun, Acc, {File, ArchiveBin}) 905: end; 906: {error, Reason} -> 907: {error, Reason} 908: end. 909: 910: unicode(Config) when is_list(Config) -> 911: Data = ?config(data_dir, Config), 912: Dir = filename:absname(Data), %Get rid of trailing slash. 913: run(Dir, "unicode1", 914: [<<"escript: exception error: an error occurred when evaluating" 915: " an arithmetic expression\n in operator '/'/2\n " 916: "called as <<224,170,170>> / <<224,170,170>>\nExitCode:127">>]), 917: run(Dir, "unicode2", 918: [<<"escript: exception error: an error occurred when evaluating" 919: " an arithmetic expression\n in operator '/'/2\n " 920: "called as <<\"\xaa\">> / <<\"\xaa\">>\nExitCode:127">>]), 921: run(Dir, "unicode3", [<<"ExitCode:0">>]), 922: run(Dir, "unicode4", [<<"ExitCode:0">>]), 923: run(Dir, "unicode5", [<<"ExitCode:0">>]), 924: run(Dir, "unicode6", [<<"ExitCode:0">>]), 925: ok. 926: 927: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 928: 929: overflow(Config) when is_list(Config) -> 930: Data = ?config(data_dir, Config), 931: Dir = filename:absname(Data), %Get rid of trailing slash. 932: ?line run(Dir, "arg_overflow", 933: [<<"ExitCode:0">>]), 934: ?line run(Dir, "linebuf_overflow", 935: [<<"ExitCode:0">>]), 936: ok. 937: 938: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 939: 940: run(Dir, Cmd0, Expected0) -> 941: Expected = iolist_to_binary(expected_output(Expected0, Dir)), 942: Cmd = case os:type() of 943: {win32,_} -> "escript " ++ filename:nativename(Dir) ++ "\\" ++ Cmd0; 944: _ -> Cmd0 945: end, 946: do_run(Dir, Cmd, Expected). 947: 948: run_with_opts(Dir, Opts, Cmd0, Expected) -> 949: Cmd = case os:type() of 950: {win32,_} -> "escript " ++ Opts ++ " " ++ filename:nativename(Dir) ++ "\\" ++ Cmd0; 951: _ -> "escript " ++ Opts ++ " " ++ Dir ++ "/" ++ Cmd0 952: end, 953: do_run(Dir, Cmd, Expected). 954: 955: do_run(Dir, Cmd, Expected0) -> 956: io:format("Run: ~p\n", [Cmd]), 957: Expected = iolist_to_binary(expected_output(Expected0, Dir)), 958: 959: Env = [{"PATH",Dir++":"++os:getenv("PATH")}], 960: Port = open_port({spawn,Cmd}, [exit_status,eof,in,{env,Env}]), 961: Res = get_data(Port, []), 962: receive 963: {Port,{exit_status,ExitCode}} -> 964: case iolist_to_binary([Res,"ExitCode:"++integer_to_list(ExitCode)]) of 965: Expected -> 966: ok; 967: Actual -> 968: io:format("Expected: ~p\n", [Expected]), 969: io:format("Actual: ~p\n", [Actual]), 970: ?t:fail() 971: end 972: end. 973: 974: get_data(Port, SoFar) -> 975: receive 976: {Port,{data,Bytes}} -> 977: get_data(Port, [SoFar|Bytes]); 978: {Port,eof} -> 979: erlang:port_close(Port), 980: SoFar 981: end. 982: 983: expected_output([data_dir|T], Data) -> 984: Slash = case os:type() of 985: {win32,_} -> "\\"; 986: _ -> "/" 987: end, 988: [filename:nativename(Data)++Slash|expected_output(T, Data)]; 989: expected_output([H|T], Data) -> 990: [H|expected_output(T, Data)]; 991: expected_output([], _) -> 992: []; 993: expected_output(Bin, _) when is_binary(Bin) -> 994: Bin. 995: