Producing dependencies Makefile for Erlang using erlc(1)

View: New views
5 Messages — Rating Filter:   Alert me  

Producing dependencies Makefile for Erlang using erlc(1)

by Jean-Sébastien Pédron-4 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hello,

When using erlc(1) and standard Makefiles to compile Erlang source
files, there's currently no way to track header dependencies. Thus, if a
header is modified, a module that depends on it will be recompiled only
if the dependency is explicitly indicated in the Makefile. This becomes
problematic when the header is external (for instance, a header from
another application, such as xmerl.hrl).

Attached is a patch that adds support for producing dependencies
Makefiles to erlc(1) and compiler(3). It was modeled after GCC.

For example, let's take the following module:
    -module(mod1).
    -include("header1.hrl").
    ...

The command "erlc -M mod1.erl" will output:
    mod1.beam: mod1.erl header1.hrl

The patch adds the following options to erlc(1) and compiler(3):

    -M          generate a rule describing dependencies; output on
                stdout.
    -MF File    rule(s) is(are) written to `File'.
    -MT Target  change the name of the rule emitted.
    -MQ Target  same as -MT but quote special characters for make(1).
    -MG         consider missing headers as generated files and add the
                to the dependencies
    -MP         add a phony target for each dependency.
    -MD         same as -M -MT file.Pbeam

They're the same as GCC. The following options are not supported:

    -MM         ignore system headers
    -MMD        same as -MD but ignore system headers

I choose to keep the same names as GCC because I'm working on Erlang
support in Automake and it wants to use these options. Regarding
compiler(3), options could have a more Erlang-fashion name.

The patch, against R12B-4, includes the documentation updates. But I
don't know how to make it, so it's untested.

Thanks,

PS: I already sent an older patch to erlang-questions@ more than a year
ago. The attached patch obsoletes it.

- --
Jean-Sébastien Pédron
http://www.dumbbell.fr/

PGP Key: http://www.dumbbell.fr/pgp/pubkey.asc

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (FreeBSD)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iEYEARECAAYFAkkIfWAACgkQa+xGJsFYOlP9swCdGsB6Ydsa6yTz3dLijCeyFAGh
NzgAoMAsrNe2oEan+1ItXO+IuRVuSNwU
=GOml
-----END PGP SIGNATURE-----

diff -Naur otp_src_R12B-4/erts/doc/src/erlc.xml otp_src_R12B-4-makedep/erts/doc/src/erlc.xml
--- otp_src_R12B-4/erts/doc/src/erlc.xml 2008-02-05 14:37:24.000000000 +0100
+++ otp_src_R12B-4-makedep/erts/doc/src/erlc.xml 2008-10-29 15:50:58.000000000 +0100
@@ -135,6 +135,50 @@
           for compiling native code, which needs to be compiled with the same
           run-time system that it should be run on.</p>
       </item>
+      <tag>-M</tag>
+      <item>
+        <p>Produces a Makefile rule to track headers dependencies. The
+          rule is sent to stdout. No object file is produced.
+        </p>
+      </item>
+      <tag>-MF <em>Makefile</em></tag>
+      <item>
+        <p>Like the <c><![CDATA[M]]></c> option above, except that the
+          Makefile is written to <em>Makefile</em>. No object
+          file is produced.
+        </p>
+      </item>
+      <tag>-MD</tag>
+      <item>
+        <p>Same as <c><![CDATA[-M -MF <File>.Pbeam]]></c>.
+        </p>
+      </item>
+      <tag>-MT <em>Target</em></tag>
+      <item>
+        <p>In conjunction with <c><![CDATA[-M]]></c> or
+          <c><![CDATA[-MF]]></c>, change the name of the rule emitted
+          to <em>Target</em>.
+        </p>
+      </item>
+      <tag>-MQ <em>Target</em></tag>
+      <item>
+        <p>Like the <c><![CDATA[-MT]]></c> option above, except that
+          characters special to make(1) or quoted.
+        </p>
+      </item>
+      <tag>-MP</tag>
+      <item>
+        <p>In conjunction with <c><![CDATA[-M]]></c> or
+          <c><![CDATA[-MF]]></c>, add a phony target for each dependency.
+        </p>
+      </item>
+      <tag>-MG</tag>
+      <item>
+        <p>In conjunction with <c><![CDATA[-M]]></c> or
+          <c><![CDATA[-MF]]></c>, consider missing headers as generated
+          files and add them to the dependencies.
+        </p>
+      </item>
       <tag>--</tag>
       <item>
         <p>Signals that no more options will follow.
diff -Naur otp_src_R12B-4/erts/etc/common/erlc.c otp_src_R12B-4-makedep/erts/etc/common/erlc.c
--- otp_src_R12B-4/erts/etc/common/erlc.c 2008-02-05 14:37:24.000000000 +0100
+++ otp_src_R12B-4-makedep/erts/etc/common/erlc.c 2008-10-29 11:53:03.000000000 +0100
@@ -255,6 +255,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 <target> */
+ case 'Q': /* -MQ <target> */
+ case 'F': /* -MF <file>   */
+    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;
@@ -557,6 +617,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_R12B-4/lib/compiler/doc/src/compile.xml otp_src_R12B-4-makedep/lib/compiler/doc/src/compile.xml
--- otp_src_R12B-4/lib/compiler/doc/src/compile.xml 2008-10-29 15:29:03.000000000 +0100
+++ otp_src_R12B-4-makedep/lib/compiler/doc/src/compile.xml 2008-10-29 15:50:26.000000000 +0100
@@ -164,6 +164,55 @@
               for details.</p>
           </item>
 
+          <tag><c>'M'</c></tag>
+          <item>
+            <p>Produces a Makefile rule to track headers dependencies. The
+              rule is sent to stdout. No object file is produced.
+            </p>
+          </item>
+
+          <tag><c>{'MF',Makefile}</c></tag>
+          <item>
+            <p>Like the <c>'M'</c> option above, except that the Makefile
+              is written to <c>Makefile</c>. No object file is produced.
+            </p>
+          </item>
+
+          <tag><c>'MD'</c></tag>
+          <item>
+            <p>Same as <c>['M', {'MF', <![CDATA[<File>.Pbeam]]>}]</c>.
+            </p>
+          </item>
+
+          <tag><c>{'MT',Target}</c></tag>
+          <item>
+            <p>In conjunction with <c>'M'</c> or <c>'MF'</c>, change the
+              name of the rule emitted to <c>Target</c>.
+            </p>
+          </item>
+
+          <tag><c>{'MQ',Target}</c></tag>
+          <item>
+            <p>Like the <c>{'MT',Target}</c> option above, except that
+              characters special to make(1) or quoted.
+            </p>
+          </item>
+
+          <tag><c>'MP'</c></tag>
+          <item>
+            <p>In conjunction with <c>'M'</c> or <c>'MF'</c>, add a phony
+              target for each dependency.
+            </p>
+          </item>
+
+          <tag><c>'MG'</c></tag>
+          <item>
+            <p>In conjunction with <c>'M'</c> or <c>'MF'</c>, consider
+              missing headers as generated files and add them to the
+              dependencies.
+            </p>
+          </item>
+
           <tag><c>'P'</c></tag>
           <item>
             <p>Produces a listing of the parsed code after preprocessing
diff -Naur otp_src_R12B-4/lib/compiler/src/compile.erl otp_src_R12B-4-makedep/lib/compiler/src/compile.erl
--- otp_src_R12B-4/lib/compiler/src/compile.erl 2008-04-07 15:57:56.000000000 +0200
+++ otp_src_R12B-4-makedep/lib/compiler/src/compile.erl 2008-10-29 15:18:37.000000000 +0100
@@ -151,6 +151,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()
@@ -398,6 +404,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
@@ -431,6 +439,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) ->
@@ -513,6 +523,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"}},
@@ -852,6 +866,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.
 

_______________________________________________
erlang-patches mailing list
erlang-patches@...
http://www.erlang.org/mailman/listinfo/erlang-patches

Re: Producing dependencies Makefile for Erlang using erlc(1)

by Oscar Hellström-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi,

How does this deal with header files included with -include_lib(). For our build purposes we use a small shellscript to generate dependencies, which also takes -include_lib directives into consideration.

I must say that I really welcome this though :) Now we just support for pkg-config to be able to compile drivers without pain as well. Ofc, we have a erl-config script for this instead :/

----- "Jean-Sébastien Pédron" <jean-sebastien.pedron@...> wrote:

> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> Hello,
>
> When using erlc(1) and standard Makefiles to compile Erlang source
> files, there's currently no way to track header dependencies. Thus, if
> a
> header is modified, a module that depends on it will be recompiled
> only
> if the dependency is explicitly indicated in the Makefile. This
> becomes
> problematic when the header is external (for instance, a header from
> another application, such as xmerl.hrl).
>
> Attached is a patch that adds support for producing dependencies
> Makefiles to erlc(1) and compiler(3). It was modeled after GCC.
>
> For example, let's take the following module:
>     -module(mod1).
>     -include("header1.hrl").
>     ...
>
> The command "erlc -M mod1.erl" will output:
>     mod1.beam: mod1.erl header1.hrl
>
> The patch adds the following options to erlc(1) and compiler(3):
>
>     -M          generate a rule describing dependencies; output on
>                 stdout.
>     -MF File    rule(s) is(are) written to `File'.
>     -MT Target  change the name of the rule emitted.
>     -MQ Target  same as -MT but quote special characters for make(1).
>     -MG         consider missing headers as generated files and add
> the
>                 to the dependencies
>     -MP         add a phony target for each dependency.
>     -MD         same as -M -MT file.Pbeam
>
> They're the same as GCC. The following options are not supported:
>
>     -MM         ignore system headers
>     -MMD        same as -MD but ignore system headers
>
> I choose to keep the same names as GCC because I'm working on Erlang
> support in Automake and it wants to use these options. Regarding
> compiler(3), options could have a more Erlang-fashion name.
>
> The patch, against R12B-4, includes the documentation updates. But I
> don't know how to make it, so it's untested.
>
> Thanks,
>
> PS: I already sent an older patch to erlang-questions@ more than a
> year
> ago. The attached patch obsoletes it.
>
> - --
> Jean-Sébastien Pédron
> http://www.dumbbell.fr/
>
> PGP Key: http://www.dumbbell.fr/pgp/pubkey.asc
>
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.9 (FreeBSD)
> Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
>
> iEYEARECAAYFAkkIfWAACgkQa+xGJsFYOlP9swCdGsB6Ydsa6yTz3dLijCeyFAGh
> NzgAoMAsrNe2oEan+1ItXO+IuRVuSNwU
> =GOml
> -----END PGP SIGNATURE-----
>
> diff -Naur otp_src_R12B-4/erts/doc/src/erlc.xml
> otp_src_R12B-4-makedep/erts/doc/src/erlc.xml
> --- otp_src_R12B-4/erts/doc/src/erlc.xml 2008-02-05 14:37:24.000000000
> +0100
> +++ otp_src_R12B-4-makedep/erts/doc/src/erlc.xml 2008-10-29
> 15:50:58.000000000 +0100
> @@ -135,6 +135,50 @@
>            for compiling native code, which needs to be compiled with
> the same
>            run-time system that it should be run on.</p>
>        </item>
> +      <tag>-M</tag>
> +      <item>
> +        <p>Produces a Makefile rule to track headers dependencies.
> The
> +          rule is sent to stdout. No object file is produced.
> +        </p>
> +      </item>
> +      <tag>-MF <em>Makefile</em></tag>
> +      <item>
> +        <p>Like the <c><![CDATA[M]]></c> option above, except that
> the
> +          Makefile is written to <em>Makefile</em>. No object
> +          file is produced.
> +        </p>
> +      </item>
> +      <tag>-MD</tag>
> +      <item>
> +        <p>Same as <c><![CDATA[-M -MF <File>.Pbeam]]></c>.
> +        </p>
> +      </item>
> +      <tag>-MT <em>Target</em></tag>
> +      <item>
> +        <p>In conjunction with <c><![CDATA[-M]]></c> or
> +          <c><![CDATA[-MF]]></c>, change the name of the rule emitted
> +          to <em>Target</em>.
> +        </p>
> +      </item>
> +      <tag>-MQ <em>Target</em></tag>
> +      <item>
> +        <p>Like the <c><![CDATA[-MT]]></c> option above, except that
> +          characters special to make(1) or quoted.
> +        </p>
> +      </item>
> +      <tag>-MP</tag>
> +      <item>
> +        <p>In conjunction with <c><![CDATA[-M]]></c> or
> +          <c><![CDATA[-MF]]></c>, add a phony target for each
> dependency.
> +        </p>
> +      </item>
> +      <tag>-MG</tag>
> +      <item>
> +        <p>In conjunction with <c><![CDATA[-M]]></c> or
> +          <c><![CDATA[-MF]]></c>, consider missing headers as
> generated
> +          files and add them to the dependencies.
> +        </p>
> +      </item>
>        <tag>--</tag>
>        <item>
>          <p>Signals that no more options will follow.
> diff -Naur otp_src_R12B-4/erts/etc/common/erlc.c
> otp_src_R12B-4-makedep/erts/etc/common/erlc.c
> --- otp_src_R12B-4/erts/etc/common/erlc.c 2008-02-05
> 14:37:24.000000000 +0100
> +++ otp_src_R12B-4-makedep/erts/etc/common/erlc.c 2008-10-29
> 11:53:03.000000000 +0100
> @@ -255,6 +255,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 <target> */
> + case 'Q': /* -MQ <target> */
> + case 'F': /* -MF <file>   */
> +    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;
> @@ -557,6 +617,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_R12B-4/lib/compiler/doc/src/compile.xml
> otp_src_R12B-4-makedep/lib/compiler/doc/src/compile.xml
> --- otp_src_R12B-4/lib/compiler/doc/src/compile.xml 2008-10-29
> 15:29:03.000000000 +0100
> +++ otp_src_R12B-4-makedep/lib/compiler/doc/src/compile.xml 2008-10-29
> 15:50:26.000000000 +0100
> @@ -164,6 +164,55 @@
>                for details.</p>
>            </item>
>
> +          <tag><c>'M'</c></tag>
> +          <item>
> +            <p>Produces a Makefile rule to track headers
> dependencies. The
> +              rule is sent to stdout. No object file is produced.
> +            </p>
> +          </item>
> +
> +          <tag><c>{'MF',Makefile}</c></tag>
> +          <item>
> +            <p>Like the <c>'M'</c> option above, except that the
> Makefile
> +              is written to <c>Makefile</c>. No object file is
> produced.
> +            </p>
> +          </item>
> +
> +          <tag><c>'MD'</c></tag>
> +          <item>
> +            <p>Same as <c>['M', {'MF',
> <![CDATA[<File>.Pbeam]]>}]</c>.
> +            </p>
> +          </item>
> +
> +          <tag><c>{'MT',Target}</c></tag>
> +          <item>
> +            <p>In conjunction with <c>'M'</c> or <c>'MF'</c>, change
> the
> +              name of the rule emitted to <c>Target</c>.
> +            </p>
> +          </item>
> +
> +          <tag><c>{'MQ',Target}</c></tag>
> +          <item>
> +            <p>Like the <c>{'MT',Target}</c> option above, except
> that
> +              characters special to make(1) or quoted.
> +            </p>
> +          </item>
> +
> +          <tag><c>'MP'</c></tag>
> +          <item>
> +            <p>In conjunction with <c>'M'</c> or <c>'MF'</c>, add a
> phony
> +              target for each dependency.
> +            </p>
> +          </item>
> +
> +          <tag><c>'MG'</c></tag>
> +          <item>
> +            <p>In conjunction with <c>'M'</c> or <c>'MF'</c>,
> consider
> +              missing headers as generated files and add them to the
> +              dependencies.
> +            </p>
> +          </item>
> +
>            <tag><c>'P'</c></tag>
>            <item>
>              <p>Produces a listing of the parsed code after
> preprocessing
> diff -Naur otp_src_R12B-4/lib/compiler/src/compile.erl
> otp_src_R12B-4-makedep/lib/compiler/src/compile.erl
> --- otp_src_R12B-4/lib/compiler/src/compile.erl 2008-04-07
> 15:57:56.000000000 +0200
> +++ otp_src_R12B-4-makedep/lib/compiler/src/compile.erl 2008-10-29
> 15:18:37.000000000 +0100
> @@ -151,6 +151,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()
> @@ -398,6 +404,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
> @@ -431,6 +439,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) ->
> @@ -513,6 +523,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"}},
> @@ -852,6 +866,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.
>
>
> _______________________________________________
> erlang-patches mailing list
> erlang-patches@...
> http://www.erlang.org/mailman/listinfo/erlang-patches

Best regards
--
Oscar Hellström, oscar@...
Phone:  +44 (0)798 45 44 773
Mobile: +44 (0)207 65 50 337
Web:    http://www.erlang-consulting.com

_______________________________________________
erlang-patches mailing list
erlang-patches@...
http://www.erlang.org/mailman/listinfo/erlang-patches

Re: Producing dependencies Makefile for Erlang using erlc(1)

by Jean-Sébastien Pédron-4 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 29.10.2008 16:30, Oscar Hellström wrote:
> How does this deal with header files included with -include_lib().
> For our build purposes we use a small shellscript to generate
> dependencies, which also takes -include_lib directives into
> consideration.

"-include_lib" directives are expanded before the new options are
treateed so it's supported.

If we take the following module:
    -module(mod3).
    -include_lib("xmerl/include/xmerl.hrl").
    ...

The command "erlc -M mod3.erl" will output:
    mod3.beam: mod3.erl \
      /usr/local/lib/erlang/lib/xmerl-1.1.10/include/xmerl.hrl

> I must say that I really welcome this though :)

Thanks!

> Now we just support for pkg-config to be able to compile drivers
> without pain as well.

Yes, it would be great to have this :)

- --
Jean-Sébastien Pédron
http://www.dumbbell.fr/

PGP Key: http://www.dumbbell.fr/pgp/pubkey.asc
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (FreeBSD)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iEYEARECAAYFAkkIhmwACgkQa+xGJsFYOlPvcQCfXK5XPNowRVxFocpjDeC1z3H0
Ae8AoKxxboK9brW2cohGJ9AxtZLySF4y
=aaBI
-----END PGP SIGNATURE-----
_______________________________________________
erlang-patches mailing list
erlang-patches@...
http://www.erlang.org/mailman/listinfo/erlang-patches

Re: Producing dependencies Makefile for Erlang using erlc(1)

by Paul Fisher-3 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Did anything ever happen with this patch?  I would love to see such a
thing added to erlc, since for better or worse, we do use .hrl files for
record and guard definitions pretty heavily.


--
paul

Jean-Sébastien Pédron wrote:

> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> Hello,
>
> When using erlc(1) and standard Makefiles to compile Erlang source
> files, there's currently no way to track header dependencies. Thus, if a
> header is modified, a module that depends on it will be recompiled only
> if the dependency is explicitly indicated in the Makefile. This becomes
> problematic when the header is external (for instance, a header from
> another application, such as xmerl.hrl).
>
> Attached is a patch that adds support for producing dependencies
> Makefiles to erlc(1) and compiler(3). It was modeled after GCC.
>
> For example, let's take the following module:
>     -module(mod1).
>     -include("header1.hrl").
>     ...
>
> The command "erlc -M mod1.erl" will output:
>     mod1.beam: mod1.erl header1.hrl
>
> The patch adds the following options to erlc(1) and compiler(3):
>
>     -M          generate a rule describing dependencies; output on
>                 stdout.
>     -MF File    rule(s) is(are) written to `File'.
>     -MT Target  change the name of the rule emitted.
>     -MQ Target  same as -MT but quote special characters for make(1).
>     -MG         consider missing headers as generated files and add the
>                 to the dependencies
>     -MP         add a phony target for each dependency.
>     -MD         same as -M -MT file.Pbeam
>
> They're the same as GCC. The following options are not supported:
>
>     -MM         ignore system headers
>     -MMD        same as -MD but ignore system headers
>
> I choose to keep the same names as GCC because I'm working on Erlang
> support in Automake and it wants to use these options. Regarding
> compiler(3), options could have a more Erlang-fashion name.
>
> The patch, against R12B-4, includes the documentation updates. But I
> don't know how to make it, so it's untested.
>
> Thanks,
>
> PS: I already sent an older patch to erlang-questions@ more than a year
> ago. The attached patch obsoletes it.
>
> - --
> Jean-Sébastien Pédron
> http://www.dumbbell.fr/
>
> PGP Key: http://www.dumbbell.fr/pgp/pubkey.asc
>
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.9 (FreeBSD)
> Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
>
> iEYEARECAAYFAkkIfWAACgkQa+xGJsFYOlP9swCdGsB6Ydsa6yTz3dLijCeyFAGh
> NzgAoMAsrNe2oEan+1ItXO+IuRVuSNwU
> =GOml
> -----END PGP SIGNATURE-----
>

_______________________________________________
erlang-patches mailing list
erlang-patches@...
http://www.erlang.org/mailman/listinfo/erlang-patches

Re: Producing dependencies Makefile for Erlang using erlc(1)

by David Sveningsson-3 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Paul Fisher wrote:
> Did anything ever happen with this patch?  I would love to see such a
> thing added to erlc, since for better or worse, we do use .hrl files for
> record and guard definitions pretty heavily.
>
>
> --
> paul

I would also like to see this functionality added. Currently I use
grep+sed magic to create dependency rules.

> Jean-Sébastien Pédron wrote:
> Hello,
>
> When using erlc(1) and standard Makefiles to compile Erlang source
> files, there's currently no way to track header dependencies. Thus, if a
> header is modified, a module that depends on it will be recompiled only
> if the dependency is explicitly indicated in the Makefile. This becomes
> problematic when the header is external (for instance, a header from
> another application, such as xmerl.hrl).
>
> Attached is a patch that adds support for producing dependencies
> Makefiles to erlc(1) and compiler(3). It was modeled after GCC.
>
> For example, let's take the following module:
>     -module(mod1).
>     -include("header1.hrl").
>     ...
>
> The command "erlc -M mod1.erl" will output:
>     mod1.beam: mod1.erl header1.hrl
>
> The patch adds the following options to erlc(1) and compiler(3):
>
>     -M          generate a rule describing dependencies; output on
>                 stdout.
>     -MF File    rule(s) is(are) written to `File'.
>     -MT Target  change the name of the rule emitted.
>     -MQ Target  same as -MT but quote special characters for make(1).
>     -MG         consider missing headers as generated files and add the
>                 to the dependencies
>     -MP         add a phony target for each dependency.
>     -MD         same as -M -MT file.Pbeam
>
> They're the same as GCC. The following options are not supported:
>
>     -MM         ignore system headers
>     -MMD        same as -MD but ignore system headers
>
> I choose to keep the same names as GCC because I'm working on Erlang
> support in Automake and it wants to use these options. Regarding
> compiler(3), options could have a more Erlang-fashion name.
>
> The patch, against R12B-4, includes the documentation updates. But I
> don't know how to make it, so it's untested.
>
> Thanks,
>
> PS: I already sent an older patch to erlang-questions@ more than a year
> ago. The attached patch obsoletes it.
>
>>

> _______________________________________________
> erlang-patches mailing list
> erlang-patches@...
> http://www.erlang.org/mailman/listinfo/erlang-patches

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.11 (Darwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iEYEARECAAYFAkn1e4QACgkQ6pa1H/H5pqW7HACgqkP7+KjU/jyA9aWpWuiXcRzp
VR4Anjc5ykYROfiJOpJ+NS82OkUrO0eg
=C8ul
-----END PGP SIGNATURE-----
_______________________________________________
erlang-patches mailing list
erlang-patches@...
http://www.erlang.org/mailman/listinfo/erlang-patches