diff -Naur otp_src_R13B/erts/doc/src/erlc.xml otp_src_R13B-makedep/erts/doc/src/erlc.xml --- otp_src_R13B/erts/doc/src/erlc.xml 2009-03-12 13:15:18.000000000 +0100 +++ otp_src_R13B-makedep/erts/doc/src/erlc.xml 2009-06-08 11:21:54.410808851 +0200 @@ -137,6 +137,50 @@ for compiling native code, which needs to be compiled with the same run-time system that it should be run on.

+ -M + +

Produces a Makefile rule to track headers dependencies. The + rule is sent to stdout. No object file is produced. +

+
+ -MF Makefile + +

Like the option above, except that the + Makefile is written to Makefile. No object + file is produced. +

+
+ -MD + +

Same as .Pbeam]]>. +

+
+ -MT Target + +

In conjunction with or + , change the name of the rule emitted + to Target. +

+
+ -MQ Target + +

Like the option above, except that + characters special to make(1) or quoted. +

+
+ -MP + +

In conjunction with or + , add a phony target for each dependency. +

+
+ -MG + +

In conjunction with or + , consider missing headers as generated + files and add them to the dependencies. +

+
--

Signals that no more options will follow. diff -Naur otp_src_R13B/erts/etc/common/erlc.c otp_src_R13B-makedep/erts/etc/common/erlc.c --- otp_src_R13B/erts/etc/common/erlc.c 2009-03-12 13:16:53.000000000 +0100 +++ otp_src_R13B-makedep/erts/etc/common/erlc.c 2009-06-08 11:21:54.417804766 +0200 @@ -256,6 +256,66 @@ case 'I': PUSH2("@i", process_opt(&argc, &argv, 0)); break; + case 'M': + { + char *buf, *key, *val; + size_t buf_len, key_len, val_len; + + if (argv[1][2] == '\0') { /* -M */ + buf = emalloc(4); + buf[0] = '\''; + buf[1] = argv[1][1]; + buf[2] = '\''; + buf[3] = '\0'; + + PUSH2("@option", buf); + } else { + switch(argv[1][2]) { + case 'P': /* -MP */ + case 'D': /* -MD */ + case 'G': /* -MG */ + buf = emalloc(5); + buf[0] = '\''; + buf[1] = argv[1][1]; + buf[2] = argv[1][2]; + buf[3] = '\''; + buf[4] = '\0'; + + PUSH2("@option", buf); + break; + case 'T': /* -MT */ + case 'Q': /* -MQ */ + case 'F': /* -MF */ + switch (argv[1][2]) { + case 'T': + key = "'MT'"; + break; + case 'Q': + key = "'MQ'"; + break; + case 'F': + key = "'MF'"; + break; + default: + key = "'M?'"; + break; + } + key_len = strlen(key); + val = process_opt(&argc, &argv, 1); + val_len = strlen(val); + + buf_len = 1 + key_len + 2 + val_len + 2 + 1; + buf = emalloc(buf_len); + snprintf(buf, buf_len, "{%s,\"%s\"}", key, val); + + PUSH2("@option", buf); + break; + default: + goto error; + } + } + } + break; case 'o': PUSH2("@outdir", process_opt(&argc, &argv, 0)); break; @@ -555,6 +615,15 @@ {"-hybrid", "compile using hybrid-heap emulator"}, {"-help", "shows this help text"}, {"-I path", "where to search for include files"}, + {"-M", "generate a rule for make(1) describing the dependencies"}, + {"-MF file", "write the dependencies to `file'"}, + {"-MT target", "change the target of the rule emitted by dependency " + "generation"}, + {"-MQ target", "same as -MT but quote characters special to make(1)"}, + {"-MG", "consider missing headers as generated files and add them to " + "the dependencies"}, + {"-MP", "add a phony target for each dependency"}, + {"-MD", "same as -M -MT file (with default `file')"}, {"-o name", "name output directory or file"}, {"-pa path", "add path to the front of Erlang's code path"}, {"-pz path", "add path to the end of Erlang's code path"}, diff -Naur otp_src_R13B/lib/compiler/doc/src/compile.xml otp_src_R13B-makedep/lib/compiler/doc/src/compile.xml --- otp_src_R13B/lib/compiler/doc/src/compile.xml 2009-04-16 11:23:44.000000000 +0200 +++ otp_src_R13B-makedep/lib/compiler/doc/src/compile.xml 2009-06-08 11:21:54.471265980 +0200 @@ -164,6 +164,55 @@ for details.

+ 'M' + +

Produces a Makefile rule to track headers dependencies. The + rule is sent to stdout. No object file is produced. +

+
+ + {'MF',Makefile} + +

Like the 'M' option above, except that the Makefile + is written to Makefile. No object file is produced. +

+
+ + 'MD' + +

Same as ['M', {'MF', .Pbeam]]>}]. +

+
+ + {'MT',Target} + +

In conjunction with 'M' or 'MF', change the + name of the rule emitted to Target. +

+
+ + {'MQ',Target} + +

Like the {'MT',Target} option above, except that + characters special to make(1) or quoted. +

+
+ + 'MP' + +

In conjunction with 'M' or 'MF', add a phony + target for each dependency. +

+
+ + 'MG' + +

In conjunction with 'M' or 'MF', consider + missing headers as generated files and add them to the + dependencies. +

+
+ 'P'

Produces a listing of the parsed code after preprocessing diff -Naur otp_src_R13B/lib/compiler/src/compile.erl otp_src_R13B-makedep/lib/compiler/src/compile.erl --- otp_src_R13B/lib/compiler/src/compile.erl 2009-04-16 11:23:39.000000000 +0200 +++ otp_src_R13B-makedep/lib/compiler/src/compile.erl 2009-06-08 11:21:54.543231742 +0200 @@ -150,6 +150,12 @@ expand_opt(no_float_opt, Os) -> %%Turn off the entire type optimization pass. [no_topt|Os]; +expand_opt('MD', Os) -> + ['M', {'MF', default} | Os]; +expand_opt({'MQ', T}, Os) -> + Fun = fun($$) -> "$$"; (C) -> C end, + T1 = lists:flatten(lists:map(Fun, T)), + [{'MT', T1} | Os]; expand_opt(O, Os) -> [O|Os]. %% format_error(ErrorDescriptor) -> string() @@ -404,6 +410,8 @@ %% file will be Ext. (Ext should not contain %% a period.) No more passes will be run. %% +%% done End compilation at this point. +%% %% {done,Ext} End compilation at this point. Produce a listing %% as with {listing,Ext}, unless 'binary' is %% specified, in which case the current @@ -437,6 +445,8 @@ [{listing,fun (St) -> src_listing(Ext, St) end}]; select_passes([{listing,Ext}|_], _Opts) -> [{listing,fun (St) -> listing(Ext, St) end}]; +select_passes([done|_], _Opts) -> + []; select_passes([{done,Ext}|_], Opts) -> select_passes([{unless,binary,{listing,Ext}}], Opts); select_passes([{iff,Flag,Pass}|Ps], Opts) -> @@ -519,6 +529,10 @@ standard_passes() -> [?pass(transform_module), + + {iff,'M',?pass(makedep)}, + {iff,'M',done}, + {iff,'dpp',{listing,"pp"}}, ?pass(lint_module), {iff,'P',{src_listing,"P"}}, @@ -862,6 +876,120 @@ errors=St#compile.errors ++ Es}} end. +makedep(#compile{options = Opts} = St) -> + Ifile = St#compile.ifile, + Ofile = St#compile.ofile, + % Get the target of the Makefile rule. + Target = case proplists:get_value('MT', Opts) of + undefined -> + % The target is derived from the output filename: eventually + % remove the current working directory to obtain a relative + % path. + Cwd = proplists:get_value(cwd, Opts), + case lists:prefix(Cwd, Ofile) of + true -> lists:nthtail(length(Cwd) + 1, Ofile); + false -> Ofile + end; + T -> + % The caller specified one with "-MT". + T + end, + Target1 = Target ++ ":", + % List the dependencies (includes) for this target. + {Main_Target, Phony} = makedep_add_headers(Ifile, St#compile.code, + [], length(Target1), Target1, "", Opts), + % Prepare the content of the Makefile. For instance: + % hello.erl: hello.hrl common.hrl + % + % Or if phony targets are enabled: + % hello.erl: hello.hrl common.hrl + % + % hello.hrl: + % + % common.hrl: + Makefile = case lists:member('MP', Opts) of + true -> Main_Target ++ Phony; + false -> Main_Target + end, + % Write this Makefile to the selected output. + case proplists:get_value('MF', Opts) of + undefined -> + % Output to stdout. + io:format("~s~n", [Makefile]); + O -> + % Output to a regular file. + Output = case O of + default -> filename:basename(Ofile, ".beam") ++ ".Pbeam"; + _ -> O + end, + case file:open(Output, write) of + {ok, Io_Dev} -> + io:fwrite(Io_Dev, "~s~n", [Makefile]), + file:close(Io_Dev); + {error, Reason} -> + io:format("Couldn't open makefile `~s': ~p~n", + [Output, Reason]) + end + end, + {ok, St}. + +makedep_add_headers(Ifile, [{attribute, _, file, {File, _}} | Rest], + Included, Line_Len, Main_Target, Phony, Opts) -> + {Included1, Line_Len1, Main_Target1, Phony1} = makedep_add_header( + Ifile, Included, Line_Len, Main_Target, Phony, File), + makedep_add_headers(Ifile, Rest, Included1, Line_Len1, + Main_Target1, Phony1, Opts); +makedep_add_headers(Ifile, [{error, {_, epp, {include, file, File}}} | Rest], + Included, Line_Len, Main_Target, Phony, Opts) -> + % The header doesn't exist, do we add it? + case lists:member('MG', Opts) of + true -> + {Included1, Line_Len1, Main_Target1, Phony1} = makedep_add_header( + Ifile, Included, Line_Len, Main_Target, Phony, File), + makedep_add_headers(Ifile, Rest, Included1, Line_Len1, + Main_Target1, Phony1, Opts); + false -> + makedep_add_headers(Ifile, Rest, Included, Line_Len, + Main_Target, Phony, Opts) + end; +makedep_add_headers(Ifile, [_ | Rest], Included, Line_Len, + Main_Target, Phony, Opts) -> + makedep_add_headers(Ifile, Rest, Included, + Line_Len, Main_Target, Phony, Opts); +makedep_add_headers(_Ifile, [], _Included, _Line_Len, + Main_Target, Phony, _Opts) -> + {Main_Target, Phony}. + +makedep_add_header(Ifile, Included, Line_Len, Main_Target, Phony, File) -> + case lists:member(File, Included) of + true -> + % This file was already listed in the dependencies, skip it. + {Included, Line_Len, Main_Target, Phony}; + false -> + Included1 = [File | Included], + % Remove "./" in front of the dependency filename. + File1 = case lists:prefix("./", File) of + true -> lists:nthtail(2, File); + false -> File + end, + % Prepare the phony target name. + Phony1 = case File of + Ifile -> Phony; + _ -> Phony ++ "\n\n" ++ File1 ++ ":" + end, + % Add the file to the dependencies. + if + Line_Len + 1 + length(File1) > 76 -> + Line_Len1 = 2 + length(File1), + Main_Target1 = Main_Target ++ " \\\n " ++ File1, + {Included1, Line_Len1, Main_Target1, Phony1}; + true -> + Line_Len1 = Line_Len + 1 + length(File1), + Main_Target1 = Main_Target ++ " " ++ File1, + {Included1, Line_Len1, Main_Target1, Phony1} + end + end. + %% expand_module(State) -> State' %% Do the common preprocessing of the input forms.