First cookbook example for the docs: Hello World in C.
This commit is contained in:
parent
d664099c9d
commit
d663c9b67d
10 changed files with 216 additions and 2 deletions
|
|
@ -36,7 +36,7 @@ your home directory:
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
# Distro packages
|
# Pre-compiled packages
|
||||||
|
|
||||||
## MacOS
|
## MacOS
|
||||||
|
|
||||||
|
|
|
||||||
146
Documentation/cookbook/hello.md
Normal file
146
Documentation/cookbook/hello.md
Normal file
|
|
@ -0,0 +1,146 @@
|
||||||
|
### Hello!
|
||||||
|
|
||||||
|
Let's start with Hello World: famously, the simplest project that does
|
||||||
|
anything interesting. We'll write this one in C, but don't worry if
|
||||||
|
you're not a C programmer! We'll keep this simple.
|
||||||
|
|
||||||
|
To play with the code on your own machine, get the [redo
|
||||||
|
source code](https://github.com/apenwarr/redo) and look in the
|
||||||
|
`Documentation/cookbook/hello/` directory.
|
||||||
|
|
||||||
|
### Compiling the code
|
||||||
|
|
||||||
|
First, let's create a source file that we want to compile:
|
||||||
|
<pre><code lang='c' src='hello.c'></code></pre>
|
||||||
|
|
||||||
|
Now we need a .do file to tell redo how to build it:
|
||||||
|
<pre><code lang='sh' src='hello.do'></code></pre>
|
||||||
|
|
||||||
|
With those files in place, we can compile and run the program:
|
||||||
|
```shell
|
||||||
|
$ redo hello
|
||||||
|
redo hello
|
||||||
|
|
||||||
|
$ ./hello
|
||||||
|
Hello, world!
|
||||||
|
```
|
||||||
|
|
||||||
|
Use the `redo` command to forcibly re-run a specific rule (in this case, the
|
||||||
|
compile rule). Or, if you only want to recompile `hello` when its input
|
||||||
|
files (dependencies) have changed, use `redo-ifchange`.
|
||||||
|
```shell
|
||||||
|
$ redo hello
|
||||||
|
redo hello
|
||||||
|
|
||||||
|
# Rebuilds, whether we need it or not
|
||||||
|
$ redo hello
|
||||||
|
redo hello
|
||||||
|
|
||||||
|
# Does not rebuild because hello.c is unchanged
|
||||||
|
$ redo-ifchange hello
|
||||||
|
|
||||||
|
$ touch hello.c
|
||||||
|
|
||||||
|
# Notices the change to hello.c
|
||||||
|
$ redo-ifchange hello
|
||||||
|
redo hello
|
||||||
|
```
|
||||||
|
|
||||||
|
Usually we'll want to also provide an `all.do` file. `all` is the
|
||||||
|
default redo target when you don't specify one.
|
||||||
|
<pre><code lang='sh' src='all.do'></code></pre>
|
||||||
|
|
||||||
|
With that, now we can rebuild our project by just typing `redo`:
|
||||||
|
```shell
|
||||||
|
$ rm hello
|
||||||
|
|
||||||
|
# 'redo' runs all.do, which calls into hello.do.
|
||||||
|
$ redo
|
||||||
|
redo all
|
||||||
|
redo hello
|
||||||
|
|
||||||
|
# Notice that this forcibly re-runs the 'all'
|
||||||
|
# rule, but all.do calls redo-ifchange, not redo,
|
||||||
|
# so it doesn't forcibly recompile hello itself.
|
||||||
|
#
|
||||||
|
# You can use this trick to experiment with one
|
||||||
|
# step in your build process at a time, without
|
||||||
|
# having to play tricks to make that step re-run,
|
||||||
|
# like you might do with make (eg. by deleting or
|
||||||
|
# touching files).
|
||||||
|
$ redo
|
||||||
|
redo all
|
||||||
|
|
||||||
|
$ ./hello
|
||||||
|
Hello, world!
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Debugging your .do scripts
|
||||||
|
|
||||||
|
If you want to see exactly which commands are being run for each step,
|
||||||
|
you can use redo's `-x` and `-v` options, which work similarly to
|
||||||
|
`sh -x` and `sh -v`.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ rm hello
|
||||||
|
|
||||||
|
$ redo -x
|
||||||
|
redo all
|
||||||
|
* sh -ex all.do all all all.redo2.tmp
|
||||||
|
+ redo-ifchange hello
|
||||||
|
|
||||||
|
redo hello
|
||||||
|
* sh -ex hello.do hello hello hello.redo2.tmp
|
||||||
|
+ redo-ifchange hello.c
|
||||||
|
+ cc -o hello.redo2.tmp hello.c -Wall
|
||||||
|
redo hello (done)
|
||||||
|
|
||||||
|
redo all (done)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Running integration tests
|
||||||
|
|
||||||
|
What about tests? We can, of course, compile a C program that has some
|
||||||
|
unit tests. But since our program isn't very complicated, let's write
|
||||||
|
a shell "integration test" (also known as a "black box" test) to make
|
||||||
|
sure it works as expected, without depending on implementation details:
|
||||||
|
<pre><code lang='sh' src='test.do'></code></pre>
|
||||||
|
|
||||||
|
Even if we rewrote our hello world program in python, javascript, or
|
||||||
|
ruby, that integration test would still be useful.
|
||||||
|
|
||||||
|
|
||||||
|
### Housekeeping
|
||||||
|
|
||||||
|
Traditionally, it's considered polite to include a `clean` rule that
|
||||||
|
restores your project to pristine status, so people can rebuild from
|
||||||
|
scratch:
|
||||||
|
<pre><code lang='sh' src='clean.do'></code></pre>
|
||||||
|
|
||||||
|
Some people like to include a `.gitignore` file so that git won't pester
|
||||||
|
you about files that would be cleaned up by `clean.do` anyway. Let's add
|
||||||
|
one:
|
||||||
|
<pre><b align=center style="display: block">.gitignore</b><code>
|
||||||
|
hello
|
||||||
|
*~
|
||||||
|
.*~
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Congratulations! That's all it takes to make your first redo project.
|
||||||
|
|
||||||
|
Here's what it looks like when we're done:
|
||||||
|
```shell
|
||||||
|
$ ls
|
||||||
|
all.do clean.do hello.c hello.do test.do
|
||||||
|
```
|
||||||
|
|
||||||
|
Some people think this looks a little cluttered with .do files. But
|
||||||
|
notice one very useful feature: you can see, at a glance, exactly which
|
||||||
|
operations are possible in your project. You can redo all, clean,
|
||||||
|
hello, or test. Since most people downloading your project will just
|
||||||
|
want to build it, it's helpful to have the available actions so
|
||||||
|
prominently displayed. And if they have a problem with one of the
|
||||||
|
steps, it's very obvious which file contains the script that's causing
|
||||||
|
the problem.
|
||||||
3
Documentation/cookbook/hello/.gitignore
vendored
Normal file
3
Documentation/cookbook/hello/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
hello
|
||||||
|
*~
|
||||||
|
.*~
|
||||||
1
Documentation/cookbook/hello/all.do
Normal file
1
Documentation/cookbook/hello/all.do
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
redo-ifchange hello
|
||||||
1
Documentation/cookbook/hello/clean.do
Normal file
1
Documentation/cookbook/hello/clean.do
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
rm -f hello *~ .*~
|
||||||
6
Documentation/cookbook/hello/hello.c
Normal file
6
Documentation/cookbook/hello/hello.c
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
printf("Hello, world!\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
16
Documentation/cookbook/hello/hello.do
Normal file
16
Documentation/cookbook/hello/hello.do
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
# If hello.c changes, this script needs to be
|
||||||
|
# re-run.
|
||||||
|
redo-ifchange hello.c
|
||||||
|
|
||||||
|
# Compile hello.c into the 'hello' binary.
|
||||||
|
#
|
||||||
|
# $3 is the redo variable that represents the
|
||||||
|
# output filename. We want to build a file
|
||||||
|
# called "hello", but if we write that directly,
|
||||||
|
# then an interruption could result in a
|
||||||
|
# partially-written file. Instead, write it to
|
||||||
|
# $3, and redo will move our output into its
|
||||||
|
# final location, only if this script completes
|
||||||
|
# successfully.
|
||||||
|
#
|
||||||
|
cc -o $3 hello.c -Wall
|
||||||
12
Documentation/cookbook/hello/test.do
Normal file
12
Documentation/cookbook/hello/test.do
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
# Make sure everything has been built before we start
|
||||||
|
redo-ifchange all
|
||||||
|
|
||||||
|
# Ensure that the hello program, when run, says
|
||||||
|
# hello like we expect.
|
||||||
|
if ./hello | grep -i 'hello' >/dev/null; then
|
||||||
|
echo "success" >&2
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo "missing 'hello' message!" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
25
Documentation/fetchcode.js
Normal file
25
Documentation/fetchcode.js
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
async function fetchCode(e, lang, src) {
|
||||||
|
const resp = await fetch(src);
|
||||||
|
const t = await resp.text();
|
||||||
|
e.innerHTML = hljs.highlight(lang, t).value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchAndHighlightAll() {
|
||||||
|
const el = document.getElementsByTagName('code');
|
||||||
|
for (const e of el) {
|
||||||
|
const src = e.getAttribute('src');
|
||||||
|
if (!src) continue;
|
||||||
|
const lang = e.getAttribute('lang');
|
||||||
|
const title = document.createElement('b');
|
||||||
|
title.innerText = src;
|
||||||
|
title.style.textAlign = 'center';
|
||||||
|
title.style.display = 'block';
|
||||||
|
e.parentElement.insertBefore(title, e);
|
||||||
|
fetchCode(e, lang, src);
|
||||||
|
console.log('found', lang, src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(fetchAndHighlightAll, 0);
|
||||||
|
|
@ -1,12 +1,16 @@
|
||||||
site_name: the _redo_ build system
|
site_name: "redo: a recursive build system"
|
||||||
theme: readthedocs
|
theme: readthedocs
|
||||||
docs_dir: Documentation
|
docs_dir: Documentation
|
||||||
site_dir: website
|
site_dir: website
|
||||||
strict: true
|
strict: true
|
||||||
|
extra_javascript:
|
||||||
|
- fetchcode.js
|
||||||
|
|
||||||
pages:
|
pages:
|
||||||
- Introduction: index.md
|
- Introduction: index.md
|
||||||
- Getting Started: GettingStarted.md
|
- Getting Started: GettingStarted.md
|
||||||
|
- Cookbook:
|
||||||
|
- Hello World: cookbook/hello.md
|
||||||
- FAQ:
|
- FAQ:
|
||||||
- Basics: FAQBasics.md
|
- Basics: FAQBasics.md
|
||||||
- Semantics: FAQSemantics.md
|
- Semantics: FAQSemantics.md
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue