1: %% 2: %% %CopyrightBegin% 3: %% 4: %% Copyright Ericsson AB 1997-2012. 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(erlc_SUITE). 20: 21: %% Tests the erlc command by compiling various types of files. 22: 23: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 24: init_per_group/2,end_per_group/2, compile_erl/1, 25: compile_yecc/1, compile_script/1, 26: compile_mib/1, good_citizen/1, deep_cwd/1, arg_overflow/1, 27: make_dep_options/1]). 28: 29: -include_lib("test_server/include/test_server.hrl"). 30: 31: suite() -> [{ct_hooks,[ts_install_cth]}]. 32: 33: all() -> 34: [compile_erl, compile_yecc, compile_script, compile_mib, 35: good_citizen, deep_cwd, arg_overflow, make_dep_options]. 36: 37: groups() -> 38: []. 39: 40: init_per_suite(Config) -> 41: Config. 42: 43: end_per_suite(_Config) -> 44: ok. 45: 46: init_per_group(_GroupName, Config) -> 47: Config. 48: 49: end_per_group(_GroupName, Config) -> 50: Config. 51: 52: %% Copy from erlc_SUITE_data/include/erl_test.hrl. 53: 54: -record(person, {name, shoe_size}). 55: 56: %% Tests that compiling Erlang source code works. 57: 58: compile_erl(Config) when is_list(Config) -> 59: ?line {SrcDir, OutDir, Cmd} = get_cmd(Config), 60: ?line FileName = filename:join(SrcDir, "erl_test_ok.erl"), 61: 62: %% By default, warnings are now turned on. 63: ?line run(Config, Cmd, FileName, "", 64: ["Warning: function foo/0 is unused\$", 65: "_OK_"]), 66: 67: %% Test that the compiled file is where it should be, 68: %% and that it is runnable. 69: 70: ?line {module, erl_test_ok} = code:load_abs(filename:join(OutDir, 71: "erl_test_ok")), 72: ?line 42 = erl_test_ok:shoe_size(#person{shoe_size=42}), 73: ?line code:purge(erl_test_ok), 74: 75: %% Try disabling warnings. 76: 77: ?line run(Config, Cmd, FileName, "-W0", ["_OK_"]), 78: 79: %% Try treating warnings as errors. 80: 81: ?line run(Config, Cmd, FileName, "-Werror", 82: ["compile: warnings being treated as errors\$", 83: "function foo/0 is unused\$", 84: "_ERROR_"]), 85: 86: %% Check a bad file. 87: 88: ?line BadFile = filename:join(SrcDir, "erl_test_bad.erl"), 89: ?line run(Config, Cmd, BadFile, "", ["function non_existing/1 undefined\$", 90: "_ERROR_"]), 91: 92: ok. 93: 94: %% Test that compiling yecc source code works. 95: 96: compile_yecc(Config) when is_list(Config) -> 97: ?line {SrcDir, _, OutDir} = get_dirs(Config), 98: ?line Cmd = erlc() ++ " -o" ++ OutDir ++ " ", 99: ?line FileName = filename:join(SrcDir, "yecc_test_ok.yrl"), 100: ?line run(Config, Cmd, FileName, "-W0", ["_OK_"]), 101: ?line true = exists(filename:join(OutDir, "yecc_test_ok.erl")), 102: 103: ?line BadFile = filename:join(SrcDir, "yecc_test_bad.yrl"), 104: ?line run(Config, Cmd, BadFile, "-W0", 105: ["rootsymbol form is not a nonterminal\$", 106: "undefined nonterminal: form\$", 107: "Nonterminals is missing\$", 108: "_ERROR_"]), 109: ?line exists(filename:join(OutDir, "yecc_test_ok.erl")), 110: 111: ok. 112: 113: %% Test that compiling start scripts works. 114: 115: compile_script(Config) when is_list(Config) -> 116: ?line {SrcDir, OutDir, Cmd} = get_cmd(Config), 117: ?line FileName = filename:join(SrcDir, "start_ok.script"), 118: ?line run(Config, Cmd, FileName, "", ["_OK_"]), 119: ?line true = exists(filename:join(OutDir, "start_ok.boot")), 120: 121: ?line BadFile = filename:join(SrcDir, "start_bad.script"), 122: ?line run(Config, Cmd, BadFile, "", ["syntax error before:", "_ERROR_"]), 123: ok. 124: 125: %% Test that compiling SNMP mibs works. 126: 127: compile_mib(Config) when is_list(Config) -> 128: ?line {SrcDir, OutDir, Cmd} = get_cmd(Config), 129: ?line FileName = filename:join(SrcDir, "GOOD-MIB.mib"), 130: ?line run(Config, Cmd, FileName, "", ["_OK_"]), 131: ?line Output = filename:join(OutDir, "GOOD-MIB.bin"), 132: ?line true = exists(Output), 133: 134: %% Try -W option. 135: 136: ?line ok = file:delete(Output), 137: ?line run(Config, Cmd, FileName, "-W", 138: ["_OK_"]), 139: ?line true = exists(Output), 140: 141: %% Try -W option and more verbose. 142: 143: ?line ok = file:delete(Output), 144: ?line case test_server:os_type() of 145: {unix,_} -> 146: ?line run(Config, Cmd, FileName, "-W +'{verbosity,info}'", 147: ["\\[GOOD-MIB[.]mib\\]\\[INF\\]: No accessfunction for 'sysDescr' => using default", 148: "_OK_"]), 149: ?line true = exists(Output), 150: ?line ok = file:delete(Output); 151: _ -> ok %Don't bother -- too much work. 152: end, 153: 154: %% Try a bad file. 155: 156: ?line BadFile = filename:join(SrcDir, "BAD-MIB.mib"), 157: ?line run(Config, Cmd, BadFile, "", 158: ["BAD-MIB.mib: 1: syntax error before: mibs\$", 159: "compilation_failed_ERROR_"]), 160: 161: %% Make sure that no -I option works. 162: 163: ?line NewCmd = erlc() ++ " -o" ++ OutDir ++ " ", 164: ?line run(Config, NewCmd, FileName, "", ["_OK_"]), 165: ?line true = exists(Output), 166: 167: ok. 168: 169: %% Checks that 'erlc' doesn't eat any input (important when called from a 170: %% shell script with redirected input). 171: good_citizen(Config) when is_list(Config) -> 172: case os:type() of 173: {unix, _} -> 174: ?line PrivDir = ?config(priv_dir, Config), 175: ?line Answer = filename:join(PrivDir, "answer"), 176: ?line Script = filename:join(PrivDir, "test_script"), 177: ?line Test = filename:join(PrivDir, "test.erl"), 178: ?line S = ["#! /bin/sh\n", "erlc ", Test, "\n", 179: "read reply\n", "echo $reply\n"], 180: ?line ok = file:write_file(Script, S), 181: ?line ok = file:write_file(Test, "-module(test).\n"), 182: ?line Cmd = "echo y | sh " ++ Script ++ " > " ++ Answer, 183: ?line os:cmd(Cmd), 184: ?line {ok, Answer0} = file:read_file(Answer), 185: ?line [$y|_] = binary_to_list(Answer0), 186: ok; 187: _ -> 188: {skip, "Unix specific"} 189: end. 190: 191: %% Make sure that compiling an Erlang module deep down in 192: %% in a directory with more than 255 characters works. 193: deep_cwd(Config) when is_list(Config) -> 194: case os:type() of 195: {unix, _} -> 196: PrivDir = ?config(priv_dir, Config), 197: deep_cwd_1(PrivDir); 198: _ -> 199: {skip, "Only a problem on Unix"} 200: end. 201: 202: deep_cwd_1(PrivDir) -> 203: ?line DeepDir0 = filename:join(PrivDir, lists:duplicate(128, $a)), 204: ?line DeepDir = filename:join(DeepDir0, lists:duplicate(128, $b)), 205: ?line ok = file:make_dir(DeepDir0), 206: ?line ok = file:make_dir(DeepDir), 207: ?line ok = file:set_cwd(DeepDir), 208: ?line ok = file:write_file("test.erl", "-module(test).\n\n"), 209: ?line io:format("~s\n", [os:cmd("erlc test.erl")]), 210: ?line true = filelib:is_file("test.beam"), 211: ok. 212: 213: %% Test that a large number of command line switches does not 214: %% overflow the argument buffer 215: arg_overflow(Config) when is_list(Config) -> 216: ?line {SrcDir, _OutDir, Cmd} = get_cmd(Config), 217: ?line FileName = filename:join(SrcDir, "erl_test_ok.erl"), 218: %% Each -D option will be expanded to three arguments when 219: %% invoking 'erl'. 220: ?line NumDOptions = num_d_options(), 221: ?line Args = lists:flatten([ ["-D", integer_to_list(N, 36), "=1 "] || 222: N <- lists:seq(1, NumDOptions) ]), 223: ?line run(Config, Cmd, FileName, Args, 224: ["Warning: function foo/0 is unused\$", 225: "_OK_"]), 226: ok. 227: 228: num_d_options() -> 229: case {os:type(),os:version()} of 230: {{win32,_},_} -> 231: %% The maximum size of a command line in the command 232: %% shell on Windows is 8191 characters. 233: %% Each -D option is expanded to "@dv NN 1", i.e. 234: %% 8 characters. (Numbers up to 1295 can be expressed 235: %% as two 36-base digits.) 236: 1000; 237: {{unix,linux},Version} when Version < {2,6,23} -> 238: %% On some older 64-bit versions of Linux, the maximum number 239: %% of arguments is 16383. 240: %% See: http://www.in-ulm.de/~mascheck/various/argmax/ 241: 5440; 242: {{unix,darwin},{Major,_,_}} when Major >= 11 -> 243: %% "getconf ARG_MAX" still reports 262144 (as in previous 244: %% version of MacOS X), but the useful space seem to have 245: %% shrunk significantly (or possibly the number of arguments). 246: %% 7673 247: 7500; 248: {_,_} -> 249: 12000 250: end. 251: 252: erlc() -> 253: case os:find_executable("erlc") of 254: false -> 255: test_server:fail("Can't find erlc"); 256: Erlc -> 257: "\"" ++ Erlc ++ "\"" 258: end. 259: 260: make_dep_options(Config) -> 261: {SrcDir,OutDir,Cmd} = get_cmd(Config), 262: FileName = filename:join(SrcDir, "erl_test_ok.erl"), 263: 264: 265: DepRE = ["/erl_test_ok[.]beam: \\\\$", 266: "/system_test/erlc_SUITE_data/src/erl_test_ok[.]erl \\\\$", 267: "/system_test/erlc_SUITE_data/include/erl_test[.]hrl$", 268: "_OK_"], 269: 270: DepRETarget = 271: ["^target: \\\\$", 272: "/system_test/erlc_SUITE_data/src/erl_test_ok[.]erl \\\\$", 273: "/system_test/erlc_SUITE_data/include/erl_test[.]hrl$", 274: "_OK_"], 275: 276: DepREMP = 277: ["/erl_test_ok[.]beam: \\\\$", 278: "/system_test/erlc_SUITE_data/src/erl_test_ok[.]erl \\\\$", 279: "/system_test/erlc_SUITE_data/include/erl_test[.]hrl$", 280: [], 281: "/system_test/erlc_SUITE_data/include/erl_test.hrl:$", 282: "_OK_"], 283: 284: DepREMissing = 285: ["/erl_test_missing_header[.]beam: \\\\$", 286: "/system_test/erlc_SUITE_data/src/erl_test_missing_header[.]erl \\\\$", 287: "/system_test/erlc_SUITE_data/include/erl_test[.]hrl \\\\$", 288: "missing.hrl$", 289: "_OK_"], 290: 291: %% Test plain -M 292: run(Config, Cmd, FileName, "-M", DepRE), 293: 294: %% Test -MF File 295: DepFile = filename:join(OutDir, "my.deps"), 296: run(Config, Cmd, FileName, "-MF "++DepFile, ["_OK_"]), 297: {ok,MFBin} = file:read_file(DepFile), 298: verify_result(binary_to_list(MFBin)++["_OK_"], DepRE), 299: 300: %% Test -MD 301: run(Config, Cmd, FileName, "-MD", ["_OK_"]), 302: MDFile = filename:join(OutDir, "erl_test_ok.Pbeam"), 303: {ok,MFBin} = file:read_file(MDFile), 304: 305: %% Test -M -MT Target 306: run(Config, Cmd, FileName, "-M -MT target", DepRETarget), 307: 308: %% Test -MF File -MT Target 309: TargetDepFile = filename:join(OutDir, "target.deps"), 310: run(Config, Cmd, FileName, "-MF "++TargetDepFile++" -MT target", 311: ["_OK_"]), 312: {ok,TargetBin} = file:read_file(TargetDepFile), 313: verify_result(binary_to_list(TargetBin)++["_OK_"], DepRETarget), 314: 315: %% Test -MD -MT Target 316: run(Config, Cmd, FileName, "-MD -MT target", ["_OK_"]), 317: TargetMDFile = filename:join(OutDir, "erl_test_ok.Pbeam"), 318: {ok,TargetBin} = file:read_file(TargetMDFile), 319: 320: %% Test -M -MQ Target. (Note: Passing a $ on the command line 321: %% portably for Unix and Windows is tricky, so we will just test 322: %% that MQ works at all.) 323: run(Config, Cmd, FileName, "-M -MQ target", DepRETarget), 324: 325: %% Test -M -MP 326: run(Config, Cmd, FileName, "-M -MP", DepREMP), 327: 328: %% Test -M -MG 329: MissingHeader = filename:join(SrcDir, "erl_test_missing_header.erl"), 330: run(Config, Cmd, MissingHeader, "-M -MG", DepREMissing), 331: ok. 332: 333: %% Runs a command. 334: 335: run(Config, Cmd0, Name, Options, Expect) -> 336: Cmd = Cmd0 ++ " " ++ Options ++ " " ++ Name, 337: io:format("~s", [Cmd]), 338: Result = run_command(Config, Cmd), 339: verify_result(Result, Expect). 340: 341: verify_result(Result, Expect) -> 342: Messages = split(Result, [], []), 343: io:format("Result: ~p", [Messages]), 344: io:format("Expected: ~p", [Expect]), 345: match_messages(Messages, Expect). 346: 347: split([$\n|Rest], Current, Lines) -> 348: split(Rest, [], [lists:reverse(Current)|Lines]); 349: split([$\r|Rest], Current, Lines) -> 350: split(Rest, Current, Lines); 351: split([Char|Rest], Current, Lines) -> 352: split(Rest, [Char|Current], Lines); 353: split([], [], Lines) -> 354: lists:reverse(Lines); 355: split([], Current, Lines) -> 356: split([], [], [lists:reverse(Current)|Lines]). 357: 358: match_messages([Msg|Rest1], [Regexp|Rest2]) -> 359: case re:run(Msg, Regexp, [{capture,none}]) of 360: match -> 361: ok; 362: nomatch -> 363: io:format("Not matching: ~s\n", [Msg]), 364: io:format("Regexp : ~s\n", [Regexp]), 365: test_server:fail(message_mismatch) 366: end, 367: match_messages(Rest1, Rest2); 368: match_messages([], [Expect|Rest]) -> 369: test_server:fail({too_few_messages, [Expect|Rest]}); 370: match_messages([Msg|Rest], []) -> 371: test_server:fail({too_many_messages, [Msg|Rest]}); 372: match_messages([], []) -> 373: ok. 374: 375: get_cmd(Cfg) -> 376: ?line {SrcDir, IncDir, OutDir} = get_dirs(Cfg), 377: ?line Cmd = erlc() ++ " -I" ++ IncDir ++ " -o" ++ OutDir ++ " ", 378: {SrcDir, OutDir, Cmd}. 379: 380: get_dirs(Cfg) -> 381: ?line DataDir = ?config(data_dir, Cfg), 382: ?line PrivDir = ?config(priv_dir, Cfg), 383: ?line SrcDir = filename:join(DataDir, "src"), 384: ?line IncDir = filename:join(DataDir, "include"), 385: {SrcDir, IncDir, PrivDir}. 386: 387: exists(Name) -> 388: filelib:is_file(Name). 389: 390: %% Runs the command using os:cmd/1. 391: %% 392: %% Returns the output from the command (as a list of characters with 393: %% embedded newlines). The very last line will indicate the 394: %% exit status of the command, where _OK_ means zero, and _ERROR_ 395: %% a non-zero exit status. 396: 397: run_command(Config, Cmd) -> 398: TmpDir = filename:join(?config(priv_dir, Config), "tmp"), 399: file:make_dir(TmpDir), 400: {RunFile, Run, Script} = run_command(TmpDir, os:type(), Cmd), 401: ok = file:write_file(filename:join(TmpDir, RunFile), Script), 402: os:cmd(Run). 403: 404: run_command(Dir, {win32, _}, Cmd) -> 405: BatchFile = filename:join(Dir, "run.bat"), 406: Run = re:replace(filename:rootname(BatchFile), "/", "\\", 407: [global,{return,list}]), 408: {BatchFile, 409: Run, 410: ["@echo off\r\n", 411: "set ERLC_EMULATOR=", atom_to_list(lib:progname()), "\r\n", 412: Cmd, "\r\n", 413: "if errorlevel 1 echo _ERROR_\r\n", 414: "if not errorlevel 1 echo _OK_\r\n"]}; 415: run_command(Dir, {unix, _}, Cmd) -> 416: Name = filename:join(Dir, "run"), 417: {Name, 418: "/bin/sh " ++ Name, 419: ["#!/bin/sh\n", 420: "ERLC_EMULATOR='", atom_to_list(lib:progname()), "'\n", 421: "export ERLC_EMULATOR\n", 422: Cmd, "\n", 423: "case $? in\n", 424: " 0) echo '_OK_';;\n", 425: " *) echo '_ERROR_';;\n", 426: "esac\n"]}; 427: run_command(_Dir, Other, _Cmd) -> 428: M = io_lib:format("Don't know how to test exit code for ~p", [Other]), 429: test_server:fail(lists:flatten(M)).