1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
|
<p>Here we will show how to write a Tupfile to handle a fairly common use-case: an automatically generated header file.</p>
<p>Handling auto-generated header files in tup is actually fairly easy, though at first glance it may appear to be tricky. They are not completely automatic, however. You do still need to specify the generated header as an input to any compilation rules. Let's get a C program from the <a href="ex_a_first_tupfile.html">First Tupfile example</a> to start with. Later we will generate a new header file from a separate shell script and see how that affects our Tupfile.</p>
<pre>
<span class="prompt">$</span> tup init tup_test3
<span class="prompt">$</span> cd tup_test3
</pre>
<span class="fileheader">Tupfile</span>
<pre class="code">: foreach *.c |> gcc -Wall -c %f -o %o |> %B.o
: *.o |> gcc %f -o %o |> hello
</pre>
<span class="fileheader">hello.c</span>
<pre class="code">#include <stdio.h>
#include "square.h"
int main(void)
{
printf("Hi, everybody!\n");
printf("Five squared is: %i\n", square(5));
return 0;
}
</pre>
<span class="fileheader">square.c</span>
<pre class="code">#include "square.h"
int square(int x)
{
return x * x;
}
</pre>
<span class="fileheader">square.h</span>
<pre class="code">int square(int x);
</pre>
<p>Now we can run our code and get the original output:</p>
<pre>
<span class="prompt">$</span> tup
[ tup ] Scanning filesystem...0.005s
[ tup ] No tup.config changes.
[ tup ] Parsing Tupfiles...
[ 1/1 ] .
[ tup ] No files to delete.
[ tup ] Executing Commands...
[ 1/3 ] gcc -Wall -c square.c -o square.o
[ 2/3 ] gcc -Wall -c hello.c -o hello.o
[ 3/3 ] gcc hello.o square.o -o hello
[ tup ] Updated.
<span class="prompt">$</span> ./hello
Hi, everybody!
Five squared is: 25
</pre>
<p>Suppose we want to add a new interface, only for reasons beyond our control, we have to generate the header file from a shell script rather than write it by hand. This is not a problem - we just have to tell tup how to create the header file.</p>
<span class="fileheader">gen_triangle.sh</span>
<pre class="code">#! /bin/sh
echo '#define SIDES_OF_A_TRIANGLE 3'
</pre>
<span class="modfileheader">Tupfile</span>
<pre class="code"><span class="added">: |> sh gen_triangle.sh > %o |> triangle.h</span>
: foreach *.c |> gcc -Wall -c %f -o %o |> %B.o
: *.o |> gcc %f -o %o |> hello
</pre>
<pre>
<span class="prompt">$</span> tup
[ tup ] Scanning filesystem...0.009s
[ tup ] No tup.config changes.
[ tup ] Parsing Tupfiles...
[ 1/1 ] .
[ tup ] No files to delete.
[ tup ] Executing Commands...
[ 1/1 ] sh gen_triangle.sh > triangle.h
[ tup ] Updated.
<span class="prompt">$</span> cat triangle.h
#define SIDES_OF_A_TRIANGLE 3
</pre>
<p>It looks like our new header file was generated properly. Let's try to include it in our <span class="filename">hello.c</span> file:</p>
<span class="modfileheader">hello.c</span>
<pre class="code">#include <stdio.h>
#include "square.h"
<span class="added">#include "triangle.h"</span>
int main(void)
{
printf("Hi, everybody!\n");
printf("Five squared is: %i\n", square(5));
<span class="added"> printf("A triangle has %i sides\n", SIDES_OF_A_TRIANGLE);</span>
return 0;
}
</pre>
<pre>
<span class="prompt">$</span> tup
[ tup ] Scanning filesystem...0.007s
[ tup ] No tup.config changes.
[ tup ] No Tupfiles to parse.
[ tup ] No files to delete.
[ tup ] Executing Commands...
[ 1/2 ] gcc -Wall -c hello.c -o hello.o
*** tup errors ***
tup error: Missing input dependency - a file was read from, and was not
specified as an input link for the command. This is an issue because the file
was created from another command, and without the input link the commands may
execute out of order. You should add this file as an input, since it is
possible this could randomly break in the future.
- [17] triangle.h
*** Command ID=8 ran successfully, but tup failed to save the dependencies.
</pre>
<p>Oops, looks like we stumbled onto the missing input dependency error message again. Since <span class="filename">triangle.h</span> is an output of another command, we have to tell tup about it in the input section of the compilation rule. Right now the input section of our compilation rule (highlighted below) has 'foreach *.c':</p>
<pre class="code">: <font style="background-color: #ffbbff">foreach *.c</font> |> gcc -Wall -c %f -o %o |> %B.o
</pre>
<p>Note that we can't simply list <span class="filename">triangle.h</span> as another input here, because the foreach keyword will try to iterate over it and generate a rule to compile the header file directly. Instead, we want to make use of the order-only inputs section, which is separated from the rest of the inputs by a single '|' (pipe) character. The difference between regular inputs and order-only inputs is that regular inputs are used in foreach and flags like <span class="keyword">%f</span>, while order-only inputs are not. All inputs also add dependency links from the file pointing to the commands.</p>
<span class="modfileheader">Tupfile</span>
<pre class="code">: |> sh gen_triangle.sh > %o |> triangle.h
: foreach *.c<span class="added"> | triangle.h</span> |> gcc -Wall -c %f -o %o |> %B.o
: *.o |> gcc %f -o %o |> hello
</pre>
<p>Now we can finish the update and take a look at the dependency graph:</p>
<pre>
<span class="prompt">$</span> tup
[ tup ] Scanning filesystem...0.008s
[ tup ] No tup.config changes.
[ tup ] Parsing Tupfiles...
[ 1/1 ] .
[ tup ] No files to delete.
[ tup ] Executing Commands...
[ 1/2 ] gcc -Wall -c hello.c -o hello.o
[ 2/2 ] gcc hello.o square.o -o hello
[ tup ] Updated.
<span class="prompt">$</span> tup graph . | dot -Tpng > ~/ex_autogen_1.png
</pre>
<div class="img"><img src="ex_autogen_1.png"></div>
<div style="clear:both"></div>
<p>Even though we specified <span class="filename">triangle.h</span> as an input dependency for both compilation rules, you can see that changing the <span class="filename">gen_triangle.sh</span> script only re-compiles the one file that actually includes the header. In contrast, changing <span class="filename">square.h</span> re-compiles both files, since they both include that header. The compilation commands are highlighted below:</p>
<pre>
<span class="prompt">$</span> touch gen_triangle.sh
<span class="prompt">$</span> tup
[ tup ] Scanning filesystem...0.006s
[ tup ] No tup.config changes.
[ tup ] No Tupfiles to parse.
[ tup ] No files to delete.
[ tup ] Executing Commands...
[ 1/3 ] sh gen_triangle.sh > triangle.h
<font style="background-color: #ffbbff">[ 2/3 ] gcc -Wall -c hello.c -o hello.o</font>
[ 3/3 ] gcc hello.o square.o -o hello
[ tup ] Updated.
<span class="prompt">$</span> touch square.h
<span class="prompt">$</span> tup
[ tup ] Scanning filesystem...0.006s
[ tup ] No tup.config changes.
[ tup ] No Tupfiles to parse.
[ tup ] No files to delete.
[ tup ] Executing Commands...
<font style="background-color: #ffbbff">[ 1/3 ] gcc -Wall -c square.c -o square.o
[ 2/3 ] gcc -Wall -c hello.c -o hello.o</font>
[ 3/3 ] gcc hello.o square.o -o hello
[ tup ] Updated.
</pre>
<p>Although it is difficult to show in this example, the presence of the dotted-line dependency from <span class="filename">triangle.h</span> to the <span class="cmd">gcc -Wall -c square.c -o square.o</span> command is very important. If we were to, in a single patch perhaps, change the <span class="filename">gen_triangle.sh</span> script, <b>and</b> change <span class="filename">square.c</span> to include the generated header, the dependency ensures that the header is generated before the C file is compiled. Until the C file actually includes the header, however, the dependency link is somewhat dormant - changing the header will not cause the C file to be re-compiled.</p>
<p>The result is that tup only re-compiles the minimum number of files necessary, even if inputs in the Tupfile are over-specified. Additionally, inputs in the Tupfile always guarantee the order that the commands are executed in, even if the commands do not actually read from the input files.</p>
|