├── .gitignore
├── .travis.yml
├── Main.hs
├── README.md
├── Util.hs
├── examples
├── basic-aql
├── basic-fabricate.py
├── basic-fbuild.py
├── basic-gup
│ ├── all.gup
│ └── output.gup
├── basic-make
├── basic-ninja.ninja
├── basic-scons
├── basic-shake.hs
├── basic-tup
├── digest-aql
├── digest-fbuild.py
├── digest-gup
│ ├── all.gup
│ ├── input-digest.gup
│ └── output.gup
├── digest-scons
├── digest-shake.hs
├── include-aql
├── include-fabricate.py
├── include-fbuild.py
├── include-gup
│ ├── all.gup
│ └── main.o.gup
├── include-make
├── include-ninja.ninja
├── include-scons
├── include-shake.hs
├── include-tup
├── intermediate-fbuild.py
├── intermediate-make
├── intermediate-shake.hs
├── monad1-aql
├── monad1-fabricate.py
├── monad1-fbuild.py
├── monad1-gup
│ ├── all.gup
│ └── output.gup
├── monad1-make
├── monad1-ninja.ninja
├── monad1-scons
├── monad1-shake.hs
├── monad1-tup
├── monad2-aql
├── monad2-fabricate.py
├── monad2-fbuild.py
├── monad2-gup
│ ├── all.gup
│ ├── list.gup
│ └── output.gup
├── monad2-make
├── monad2-ninja.ninja
├── monad2-scons
├── monad2-shake.hs
├── monad2-tup
├── monad3-aql
├── monad3-fabricate.py
├── monad3-fbuild.py
├── monad3-gup
│ ├── all.gup
│ ├── gen.gup
│ ├── list.gup
│ └── output.gup
├── monad3-make
├── monad3-scons
├── monad3-shake.hs
├── multiple-aql
├── multiple-fabricate.py
├── multiple-fbuild.py
├── multiple-gup
│ ├── all.gup
│ ├── output1.gup
│ ├── output2.gup
│ ├── source.gup
│ ├── source1.gup
│ └── source2.gup
├── multiple-ninja.ninja
├── multiple-scons
├── multiple-shake.hs
├── multiple-tup
├── nofileout-aql
├── nofileout-fbuild.py
├── nofileout-gup
│ ├── all.gup
│ └── input-digest.gup
├── nofileout-tup
├── noleftover-shake.hs.broken
├── noleftover-tup
├── parallel-aql
├── parallel-fbuild.py
├── parallel-gup
│ ├── all.gup
│ ├── output1.gup
│ └── output2.gup
├── parallel-make
├── parallel-ninja.ninja
├── parallel-scons
├── parallel-shake.hs
├── parallel-tup
├── pool-ninja.ninja
├── pool-shake.hs
├── secondary-make
├── secondary-shake.hs
├── spaces-aql
├── spaces-fabricate.py
├── spaces-fbuild.py
├── spaces-gup
│ ├── all.gup
│ └── output file.gup
├── spaces-make
├── spaces-ninja.ninja
├── spaces-scons
├── spaces-shake.hs
├── spaces-tup.broken
├── spaces-tuplua.lua
├── system1-aql
├── system1-fbuild.py
├── system1-gup
│ ├── all.gup
│ ├── output.gup
│ └── source.gup
├── system1-scons
├── system1-shake.hs
├── system2-aql
├── system2-gup
│ ├── all.gup
│ ├── data.gup
│ └── output.gup
├── system2-scons
├── system2-shake.hs
├── system2-tup
├── unchanged-aql
├── unchanged-fabricate.py
├── unchanged-fbuild.py
├── unchanged-gup
│ ├── all.gup
│ ├── output.gup
│ └── source.gup
├── unchanged-ninja.ninja
├── unchanged-scons
├── unchanged-shake.hs
├── unchanged-tup
├── wildcard-aql
├── wildcard-fabricate.py
├── wildcard-fbuild.py
├── wildcard-gup
│ ├── Gupfile
│ └── copier
├── wildcard-make
├── wildcard-scons
├── wildcard-shake.hs
└── wildcard-tup
├── travis.sh
└── util
├── basic-run
├── digest-run
├── include-1.h
├── include-2.h
├── include-main.c
├── intermediate-run
├── monad2-run
├── monad3-gen
├── monad3-run
├── multiple-gen
├── multiple-run
├── nofileout-run
├── noleftover-run
├── parallel-run
├── pool-run
├── secondary-run
├── system1-gen
├── system1-run
├── system2-run
├── unchanged-gen
└── unchanged-run
/.gitignore:
--------------------------------------------------------------------------------
1 | temp
2 | .log
3 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: haskell
2 | install: echo No cabal install necessary
3 | script: sh travis.sh
4 |
--------------------------------------------------------------------------------
/Main.hs:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env runhaskell
2 |
3 | -- | A test script to check those build systems claiming to implement a test
4 | -- do in fact do so.
5 | module Main(main) where
6 |
7 | import Util
8 |
9 |
10 | main :: IO ()
11 | main = do
12 | test "basic" basic
13 | test "parallel" parallel
14 | test "include" include
15 | test "wildcard" wildcard
16 | test "spaces" spaces
17 | test "monad1" monad1
18 | test "monad2" monad2
19 | test "monad3" monad3
20 | test "unchanged" unchanged
21 | test "multiple" multiple
22 | test "system1" system1
23 | test "system2" system2
24 | test "pool" pool
25 | test "digest" digest
26 | test "nofileout" nofileout
27 | test "noleftover" noleftover
28 | test "secondary" secondary
29 | test "intermediate" intermediate
30 |
31 |
32 | basic :: ([Opt] -> IO ()) -> IO ()
33 | basic run = do
34 | writeFile "input" "xyz"
35 | run [Contents "output" "xyz"]
36 | run [NoChange]
37 | writeFile "input" "abc"
38 | run [Contents "output" "abc"]
39 | run [NoChange]
40 |
41 |
42 | parallel :: ([Opt] -> IO ()) -> IO ()
43 | parallel run = do
44 | writeFile "input1" "xyz"
45 | writeFile "input2" "abc"
46 | run [Parallel 2, Log "start start end end"]
47 | run [NoChange]
48 |
49 |
50 | include :: ([Opt] -> IO ()) -> IO ()
51 | include run = do
52 | run [Change "main.o"]
53 | run [NoChange]
54 | appendFile "include-2.h" "\n/* comment */"
55 | run [Change "main.o"]
56 | run [NoChange]
57 |
58 |
59 | wildcard :: ([Opt] -> IO ()) -> IO ()
60 | wildcard run = do
61 | x <- randomRIO (1::Int,1000000)
62 | let name = "name" ++ show x
63 | writeFile (name ++ ".in") "abc"
64 | run [Target $ name ++ ".out", Contents (name ++ ".out") "abc"]
65 | run [Target $ name ++ ".out", NoChange]
66 | writeFile (name ++ ".in") "xyz"
67 | run [Target $ name ++ ".out", Contents (name ++ ".out") "xyz"]
68 | run [Target $ name ++ ".out", NoChange]
69 |
70 |
71 | spaces :: ([Opt] -> IO ()) -> IO ()
72 | spaces run = do
73 | writeFile "input file" "abc"
74 | run [Target "output file", Contents "output file" "abc"]
75 | run [Target "output file", NoChange]
76 | writeFile "input file" "xyz"
77 | run [Target "output file", Contents "output file" "xyz"]
78 | run [Target "output file", NoChange]
79 |
80 |
81 | monad1 :: ([Opt] -> IO ()) -> IO ()
82 | monad1 run = do
83 | writeBinary "list" "input1\ninput2\n"
84 | writeFile "input1" "test"
85 | writeFile "input2" "again"
86 | run [Target "output", Contents "output" "testagain"]
87 | run [Target "output", NoChange]
88 | writeFile "input1" "more"
89 | run [Target "output", Contents "output" "moreagain"]
90 | run [Target "output", NoChange]
91 | writeBinary "list" "input1\n"
92 | run [Target "output", Contents "output" "more"]
93 | run [Target "output", NoChange]
94 | writeFile "input2" "x"
95 | run [Target "output", NoChange]
96 |
97 |
98 | monad2 :: ([Opt] -> IO ()) -> IO ()
99 | monad2 run = do
100 | writeBinary "source" "output1\noutput2\n"
101 | writeFile "input1" "test"
102 | writeFile "input2" "again"
103 | run [Target "output", Contents "output" "testagain", Log "run"]
104 | run [Target "output", NoChange, Log "run"]
105 | writeFile "input1" "more"
106 | run [Target "output", Contents "output" "moreagain"]
107 | run [Target "output", NoChange]
108 | writeBinary "source" "output1\n"
109 | run [Target "output", Contents "output" "more", Log "run run"]
110 | run [Target "output", NoChange]
111 | writeFile "input2" "x"
112 | run [Target "output", NoChange, Log "run run"]
113 |
114 |
115 | monad3 :: ([Opt] -> IO ()) -> IO ()
116 | monad3 run = do
117 | writeBinary "source" "output1\noutput2\n"
118 | writeFile "input1" "test"
119 | writeFile "input2" "again"
120 | run [Target "output", Contents "output" "testagain", Log "run"]
121 | run [Target "output", NoChange, Log "run", Missing "gen"]
122 | writeBinary "source" "gen\noutput2\n"
123 | run [Target "output", Contents "output" "Generated\nagain"]
124 | run [Target "output", NoChange]
125 |
126 |
127 | unchanged :: ([Opt] -> IO ()) -> IO ()
128 | unchanged run = do
129 | writeFile "input" "foo is in here"
130 | run [Target "output", Contents "source" "foo is out here", Contents "output" "foo xs out here", Log "run"]
131 | run [Target "output", NoChange]
132 | writeFile "input" "bar is in here"
133 | run [Target "output", Contents "source" "bar is out here", Contents "output" "bar xs out here", Log "run run"]
134 | run [Target "output", NoChange]
135 | writeFile "input" "bar is out here"
136 | run [Target "output", Contents "source" "bar is out here", Contents "output" "bar xs out here", Log "run run"]
137 | run [Target "output", NoChange]
138 |
139 |
140 | multiple :: ([Opt] -> IO ()) -> IO ()
141 | multiple run = do
142 | writeFile "input" "abbc"
143 | run [Target "output1", Target "output2", Contents "output1" "AbbC", Contents "output2" "aBBC", Log "run run"]
144 | run [Target "output1", Target "output2", NoChange]
145 | writeFile "input" "aBBc"
146 | run [Target "output1", Target "output2", Contents "output1" "ABBC", Contents "output2" "aBBC", Log "run run run"]
147 | run [Target "output1", NoChange]
148 | writeFile "input" "ab"
149 | run [Target "output1", Contents "output1" "Ab", Contents "output2" "aBBC"]
150 | run [Target "output2", Contents "output1" "Ab", Contents "output2" "aB"]
151 | run [Target "output1", Target "output2", NoChange]
152 |
153 |
154 | system1 :: ([Opt] -> IO ()) -> IO ()
155 | system1 run = do
156 | writeFile "system1-data" "foo"
157 | writeFile "source" "none"
158 | run [Target "output", Contents "output" "foo", Log "gen run"]
159 | run [Target "output", Contents "output" "foo", Log "gen run gen"]
160 | writeFile "system1-data" "bar"
161 | run [Target "output", Contents "output" "bar", Log "gen run gen gen run"]
162 |
163 |
164 | system2 :: ([Opt] -> IO ()) -> IO ()
165 | system2 run = do
166 | let varName = "SYSTEM2_DATA"
167 | run [Contents "output" "", Log "run"]
168 | run [NoChange]
169 | run [Contents "output" "foo", Log "run run", Env varName "foo"]
170 | run [NoChange, Env varName "foo"]
171 | run [Contents "output" "bar", Log "run run run", Env varName "bar"]
172 | run [NoChange, Env varName "bar"]
173 | run [Contents "output" "", Log "run run run run"]
174 | run [NoChange]
175 |
176 |
177 | pool :: ([Opt] -> IO ()) -> IO ()
178 | pool run = do
179 | writeFile "input1" "xyz"
180 | writeFile "input2" "abc"
181 | writeFile "input3" "def"
182 | run [Parallel 8, Log "start start end start end end"]
183 | run [NoChange]
184 |
185 |
186 | digest :: ([Opt] -> IO ()) -> IO ()
187 | digest run = do
188 | writeFile "input" "xyz"
189 | run [Contents "output" "xyz"]
190 | run [NoChange]
191 | writeFile "input" "abc"
192 | run [Contents "output" "abc"]
193 | run [NoChange]
194 | writeFile "input" "abc"
195 | run [NoChange]
196 |
197 |
198 | nofileout :: ([Opt] -> IO ()) -> IO ()
199 | nofileout run = do
200 | writeFile "input" "xyz"
201 | run [Log "xyz"]
202 | run [NoChange]
203 | writeFile "input" "abc"
204 | run [Log "xyzabc"]
205 | run [NoChange]
206 |
207 |
208 | noleftover :: ([Opt] -> IO ()) -> IO ()
209 | noleftover run = do
210 | writeFile "foo.in" "foo"
211 | writeFile "bar.in" "bar"
212 | run [Contents "foo.out" "foo", Contents "bar.out" "bar"]
213 | run [NoChange]
214 | removeFile "bar.in"
215 | writeFile "baz.in" "baz"
216 | run [Contents "foo.out" "foo", Contents "baz.out" "baz", Missing "bar.out"]
217 | run [NoChange]
218 |
219 |
220 | secondary :: ([Opt] -> IO ()) -> IO ()
221 | secondary run = do
222 | writeFile "input" "xyz"
223 | run [Contents "output" "xyz * *", Contents "secondary" "xyz *", Log "run run"]
224 | run [NoChange]
225 | removeFile "secondary"
226 | run [Contents "output" "xyz * *", Missing "secondary", Log "run run"]
227 | run [NoChange]
228 | writeFile "input" "abc"
229 | run [Contents "output" "abc * *", Contents "secondary" "abc *", Log "run run run run"]
230 | run [NoChange]
231 |
232 |
233 | intermediate :: ([Opt] -> IO ()) -> IO ()
234 | intermediate run = do
235 | writeFile "input" "xyz"
236 | run [Contents "output" "xyz * *", Missing "intermediate", Log "run run"]
237 | run [NoChange]
238 | writeFile "input" "abc"
239 | run [Contents "output" "abc * *", Missing "intermediate", Log "run run run run"]
240 | run [NoChange]
241 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Build System Shootout [](https://travis-ci.org/ndmitchell/build-shootout)
2 |
3 | This project attempts to clarify the relative power of various build systems. Compared to the [Computer Language Shootout](http://benchmarksgame.alioth.debian.org/), this Shootout attempts to answer whether a build system is capable of expressing a particular dependency structure, but does not measure performance. The following build systems have at least one entry:
4 |
5 | * [Make](http://www.gnu.org/software/make/) (GNU version), cross-platform.
6 | * [Ninja](http://martine.github.io/ninja/), cross-platform.
7 | * [Shake](https://github.com/ndmitchell/shake#readme), cross-platform.
8 | * [tup](http://gittup.org/tup/), cross-platform, requiring FUSE on Linux. Does not work with [Travis](https://travis-ci.org/) and cannot be compiled on Windows.
9 | * [fabricate](https://code.google.com/p/fabricate/), works on Linux, some Windows support on some machines, requires at least admin configuration on Vista and above. Works partially with [Travis](https://travis-ci.org/).
10 | * [SCons](http://www.scons.org/), cross-platform.
11 | * [Aqualid](https://github.com/aqualid/), cross-platform.
12 | * [Fbuild](https://github.com/felix-lang/fbuild), cross-platform.
13 | * [Gup](https://github.com/timbertson/gup), cross-platform.
14 |
15 | All build scripts are in the [examples directory](https://github.com/ndmitchell/build-shootout/tree/master/examples), as testname-buildsystem. You can run all the examples with `runhaskell Main` (after installing the [Haskell Platform](http://www.haskell.org/platform/), and any build systems you want to run). Use the argument `make` to only run Make examples, or `basic` to only run the basic test.
16 |
17 | Below are a list of tests, a description of the test, and how each build system fares on it. The tests have pseudo-code for the equivalent untracked straight-line shell script.
18 |
19 | To pass a test the build system must:
20 |
21 | * Follow the specification, including the test case. _Warning:_ this project is young, the specifications and tests are still evolving slightly.
22 | * Must not rebuild things in subsequent runs, all files must end up clean.
23 | * Must not require explicit assume dirty/assume clean flags to be specified.
24 | * Must not explicitly check for the existence of a file (you can always write a build system in a line of shell for any of these problems, the idea is to use the build system).
25 | * All build-shootout shell scripts must be treated as black boxes. any file listed before `--` is treated as an input, any file after is an output. Programs like `gcc`, `cat`, `xargs` and `cp` are the standard Posix utilities.
26 |
27 | Performance is deliberately not measured as all actions are specified via shell scripts to make the results as similar as possible - even if some of the build systems would not normally use that approach.
28 |
29 | #### Contributions
30 |
31 | I welcome contributions, including:
32 |
33 | * Examples in different build systems
34 | * New implementations for existing build systems
35 | * New test cases (provided they show something interesting)
36 | * Corrections of my egregious errors
37 |
38 | ## Test cases
39 |
40 | ### basic: Basic dependency
41 |
42 | Given an input file, create an output file which is a copy of the input file. If the input file changes, or the output file is not present, the output file should be created.
43 |
44 | basic-run input -- output
45 |
46 | * **fabricate: success**
47 | * **Make: success**
48 | * **Ninja: success**
49 | * **SCons: success**
50 | * **Aqualid: success**
51 | * **Fbuild: success**
52 | * **Shake: success**
53 | * **tup: success**
54 | * **Gup: success**
55 |
56 | ### parallel: Parallelism
57 |
58 | Given two targets, build them in parallel.
59 |
60 | parallel-run input1 -- output1; parallel-run input2 -- output2
61 |
62 | * fabricate: unimplemented, not tried
63 | * **Make: success**
64 | * **Ninja: success**
65 | * **SCons: success**
66 | * **Aqualid: success**
67 | * **Fbuild: success**
68 | * **Shake: success**
69 | * **tup: success**
70 | * **Gup: success**
71 |
72 | ### include: C #include files
73 |
74 | Given a C file, compile it, automatically figuring out any transitively included dependencies. If any of those dependencies change in future, it should rebuild.
75 |
76 | gcc main.o -c include-main.c -o main.o
77 |
78 | * **fabricate: success**, but fails on Travis
79 | * **Make: success**
80 | * **Ninja: success**
81 | * **SCons: success**
82 | * **Aqualid: success**
83 | * **Fbuild: success**
84 | * **Shake: success**
85 | * **tup: success**
86 | * **Gup: success**
87 |
88 | ### wildcard: Build a file specified by an extension wildcard
89 |
90 | Given a command line argument of `123.in`, copy `123.in` to `123.out`. Should work for any file with a `.in` suffix.
91 |
92 | cp $1 $1.out
93 |
94 | * **fabricate: success**
95 | * **Make: success**
96 | * Ninja: failure, requires all rules to be listed in full
97 | * **SCons: success**
98 | * **Aqualid: success**
99 | * **Fbuild: success**
100 | * **Shake: success**
101 | * **tup: success**
102 | * **Gup: success**
103 |
104 | ### spaces: Build a file containing spaces
105 |
106 | Work with files including spaces.
107 |
108 | cp "input file" "output file"
109 |
110 | * fabricate: partial, generally works but requires custom code to get command line support for space-including targets
111 | * **Make: success**
112 | * **Ninja: success**
113 | * **SCons: success**
114 | * **Aqualid: success**
115 | * **Fbuild: success**
116 | * **Shake: success**
117 | * **tup: success**, seems to require Lua
118 | * **Gup: success**
119 |
120 | ### monad1: Monadic patterns
121 |
122 | The monad series of tests are designed to probe the difference between applicative build systems and monadic ones, also showing which features allow applicative build systems to "fake" some monadic actions. The first requires depending on a list of files itself stored in a file.
123 |
124 | cat list | xargs cat > output
125 |
126 | * **fabricate: success**, but fails on Travis
127 | * **Make: success**
128 | * **Ninja: success**
129 | * **SCons: success**
130 | * **Aqualid: success**
131 | * **Fbuild: success**
132 | * **Shake: success**
133 | * **tup: success**
134 | * **Gup: success**
135 |
136 | ### monad2: More monadic patterns
137 |
138 | The second test is like the first, but the `list` file itself is generated.
139 |
140 | monad2-run source -- list
141 | cat list | xargs cat > output
142 |
143 | * **fabricate: success**, but fails on Travis
144 | * **Make: success**
145 | * **Ninja: success**
146 | * **SCons: success**
147 | * **Aqualid: success**
148 | * **Fbuild: success**
149 | * **Shake: success**
150 | * **tup: success**
151 | * **Gup: success**
152 |
153 |
154 | ### monad3: More monadic patterns
155 |
156 | The third test requires generating `list`, then generating the files `list` refers to.
157 |
158 | monad3-run source -- list
159 | monad3-gen -- gen # only if gen is in list
160 | cat list | xargs cat > output
161 |
162 | * **fabricate: success**, but fails on Travis
163 | * **Make: success, requires automatic restarting**
164 | * Ninja: unsure, no one has been able to implement it yet
165 | * **SCons: success**
166 | * **Aqualid: success**
167 | * **Fbuild: success**
168 | * **Shake: success**
169 | * tup: unsure, no one has been able to implement it yet
170 | * **Gup: success**
171 |
172 |
173 | ### unchanged: Handle files which do not change
174 |
175 | In some cases `input` will change, but `source` will not change in response. It is important that in these cases `output` is not regenerated.
176 |
177 | unchanged-gen input -- source
178 | unchanged-run source -- output
179 |
180 | * **fabricate: success**, but fails on Travis
181 | * Make: failure, does not seem to work
182 | * **Ninja: success**, requires `restat` to be added
183 | * **SCons: success**
184 | * **Aqualid: success**
185 | * **Fbuild: success**
186 | * **Shake: success**
187 | * **tup: success**, requires `^o^` to be added
188 | * **Gup: success**, requires explicitly calling `gup --contents`
189 |
190 |
191 | ### multiple: Rules with multiple outputs
192 |
193 | In some cases one output will change, but not the other.
194 |
195 | multiple-gen input -- source1 source2
196 | multiple-run source1 -- output1
197 | multiple-run source2 -- output2
198 |
199 | I believe this test can be written on top of `unchanged`, by encoding the dependencies appropriately.
200 |
201 | * **fabricate: success**, but fails on Travis
202 | * Make: failure, does not seem to work
203 | * **Ninja: success**, requires `restat` to be added
204 | * **SCons: success**
205 | * **Aqualid: success**
206 | * **Fbuild: success**
207 | * **Shake: success**
208 | * **tup: success**, requires `^o^` to be added
209 | * **Gup: success**, though hackish.
210 |
211 |
212 | ### system1: Dependency on system information
213 |
214 | Introduce a dependency on a piece of system information that must be recomputed every run. In this scenario `system1-gen` might be equivalent to `gcc --version` and `system1-run` might be `gcc -c`. You must always test the `gcc` version, but only want to rebuild if it changes.
215 |
216 | system1-gen -- source # must always be run
217 | system1-run source -- output # must not run if source does not change
218 |
219 | I believe that given a small amount of shell scripting glue (to run `system1-gen`) this test can be written on top of `unchanged`.
220 |
221 | * fabricate: unsure
222 | * Make: unsure
223 | * Ninja: unsure
224 | * **SCons: success**
225 | * **Aqualid: success**
226 | * **Fbuild: success**
227 | * **Shake: success**
228 | * tup: unsure
229 | * **Gup: success**
230 |
231 |
232 | ### system2: Dependency on system environment variable
233 |
234 | Rerun if and only if `output` does not exist or system environment variable
235 | `SYSTEM2_DATA` was changed.
236 |
237 | system2-run -- output
238 |
239 | * fabricate: unsure
240 | * Make: unsure
241 | * Ninja: unsure
242 | * SCons: unsure
243 | * **Aqualid: success**
244 | * **Shake: success**
245 | * Fbuild: failure
246 | * **tup: success**
247 | * **Gup: success**
248 |
249 |
250 | ### pool: Limit the parallelism in a specific stage
251 |
252 | Run with a parallelism of 8, but limit a specific stage to no more than 2 concurrent runs.
253 |
254 | pool-run input1 -- output1
255 | pool-run input2 -- output2
256 | pool-run input3 -- output3
257 |
258 | * fabricate: unsure
259 | * Make: failure, doesn't seem to work
260 | * **Ninja: success**
261 | * SCons: failure, doesn't support pools
262 | * Aqualid: failure, doesn't support pools
263 | * Fbuild: failure, doesn't support pools
264 | * **Shake: success**
265 | * tup: unsure, nothing I can see
266 | * Gup: failure, doesn't support pools
267 |
268 |
269 | ### digest: Don't rebuild when a file is modified to the same value
270 |
271 | The `input` file will be changed, but sometimes to the same value.
272 |
273 | digest-run input -- output
274 |
275 | * fabricate: unsure
276 | * Make: failure, doesn't support digests
277 | * Ninja: failure, doesn't support digests
278 | * **SCons: success**
279 | * **Aqualid: success**
280 | * **Fbuild: success**
281 | * **Shake: success**, requires setting `Digest` change mode.
282 | * tup: unsure
283 | * **Gup: success**, requires explicitly calling `gup --contents`
284 |
285 |
286 | ### nofileout: Don't produce an output file
287 |
288 | Rerun if and only if `input` file was changed.
289 |
290 | nofileout-run input --
291 |
292 | * fabricate: unsure
293 | * Make: unsure
294 | * Ninja: unsure
295 | * SCons: unsure
296 | * **Aqualid: success**
297 | * **Fbuild: success**
298 | * Shake: failure, doesn't support rules that are only run if the dependencies change but don't produce an output file
299 | * **tup: success**
300 | * **Gup: success**, though a stamp file still needs to be created.
301 |
302 |
303 | ### noleftover: Remove files left over from a previous build
304 |
305 | # initialize
306 | echo "foo" > foo.in
307 | echo "bar" > bar.in
308 | # build
309 | noleftover-run foo.in -- foo.out
310 | noleftover-run bar.in -- bar.out
311 | # modify
312 | rm bar.in
313 | echo "baz" > baz.in
314 | # rebuld
315 | rm bar.out
316 | noleftover-run baz.in -- baz.out
317 |
318 | * fabricate: unsure
319 | * Make: unsure
320 | * Ninja: unsure
321 | * SCons: unsure
322 | * Aqualid: failure
323 | * Fbuild: failure
324 | * Shake: failure, doesn't seem to be supported
325 | * **tup: success**
326 | * Gup: failure
327 |
328 |
329 | ### secondary: Secondary target
330 |
331 | Building an `output` file from an `input` file requires an auxiliary result `secondary` file.
332 | Removing `secondary` file doesn't cause rebuilding `output` file as long as `input` file wasn't changed.
333 | Changing `input` file causes a rebuild of both files, `secondary` and `output`.
334 |
335 | Within the scope of this test `change` means modification of both, contents and timestamp.
336 |
337 | * fabricate: unsure
338 | * **Make: success**
339 | * Ninja: unsure
340 | * SCons: unsure
341 | * Aqualid: failure
342 | * Fbuild: failure
343 | * **Shake: success**
344 | * tup: unsure
345 | * Gup: failure
346 |
347 |
348 | ### intermediate: Intermediate target
349 |
350 | Building an `output` file from an `input` file requires an auxiliary result `intermediate` file which is automatically removed at the end.
351 | An `intermediate` file isn't created in subsequent builds as long as `input` file wasn't changed.
352 | Changing `input` file causes building `intermediate` file, rebuilding `output` file, and removing `intermediate` file, eventually.
353 |
354 | Within the scope of this test `change` means modification of both, contents and timestamp.
355 |
356 | * fabricate: unsure
357 | * **Make: success**
358 | * Ninja: unsure
359 | * SCons: unsure
360 | * Aqualid: failure
361 | * **Fbuild: success**
362 | * **Shake: success**
363 | * tup: unsure
364 | * Gup: failure
365 |
366 |
367 | ## Build System Power
368 |
369 | The intention of this project is to figure out what dependency features each build system offers, what power they give, and which features can be expressed in terms of others. This section is speculative and evolving.
370 |
371 | ### Pre dependencies (applicative) [all but Fabricate]
372 |
373 | A pre dependency is one where you can introduce a dependency at the start, for example Make's `output: input`. Each output is allowed to express multiple dependencies, but they are all evaluated in isolation from each other.
374 |
375 | ### Post dependencies [Ninja, Fbuild, Shake, tup, Gup]
376 |
377 | A post dependency is one where you introduce a dependency at the end, for example Ninja's `depfile`. These dependencies do not alter this build run, but will add dependencies for the next run.
378 |
379 | ### Mid dependencies (monadic) [Shake, Fbuild, Gup, subsumes pre and post dependencies]
380 |
381 | A monadic dependency lets you introduce a new dependency while running an action after consulting previous dependencies, for example Shake's `need`.
382 |
383 | ### Auto post dependencies [tup, subsumes post dependencies]
384 |
385 | An auto post dependency is one computed from what you actually used, rather than explicitly stated dependencies.
386 |
387 | ### Auto cached commands [fabricate, Fbuild]
388 |
389 | A cached command is where the inputs/outputs for a command are tracked, and the command is treated as a pure function and skipped if its inputs didn't change. This feature is more useful in build systems that go forward (from inputs to outputs) rather than the standard build systems that go from outputs to inputs.
390 |
391 | ### Regenerate [Make]
392 |
393 | Make lets you regenerate the Makefile and then continue again. How that works is anyones guess.
394 |
--------------------------------------------------------------------------------
/Util.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE ScopedTypeVariables #-}
2 | {-# OPTIONS_GHC -fwarn-unused-binds -fwarn-unused-imports #-}
3 |
4 | -- | A test script to check those build systems claiming to implement a test
5 | -- do in fact do so.
6 | module Util(
7 | test, Opt(..),
8 | randomRIO,
9 | writeBinary, removeFile
10 | ) where
11 |
12 | import Control.Concurrent
13 | import Control.Exception as E
14 | import Control.Monad
15 | import Data.Char
16 | import Data.List
17 | import System.Directory
18 | import System.Environment
19 | import System.Exit
20 | import System.FilePath
21 | import System.IO
22 | import System.Info
23 | import System.Process
24 | import System.Random
25 | import System.Posix.Files
26 |
27 |
28 | copyDir :: FilePath -> FilePath -> IO ()
29 | copyDir src dst = do
30 | contents <- getDirectoryContents src
31 | let files = [ file | file <- contents
32 | , file /= "."
33 | , file /= ".." ]
34 | createDirectory dst
35 | forM_ files $ copy src dst
36 |
37 |
38 | copy :: FilePath -> FilePath -> FilePath -> IO ()
39 | copy src dst file = do
40 | directoryExists <- doesDirectoryExist $ src > file
41 | if directoryExists
42 | then do
43 | copyDir (src > file) (dst > file)
44 | else
45 | copyFile (src > file) (dst > file)
46 |
47 |
48 | data Opt
49 | = NoChange
50 | | Contents FilePath String
51 | | Missing FilePath
52 | | Change FilePath
53 | | Parallel Int
54 | | Target FilePath
55 | | Log String
56 | | Env String String
57 | deriving Show
58 |
59 |
60 | data Tool = Tup | TupLua | Ninja | Shake | Make | Fabricate | SCons | Aql | Fbuild | Gup
61 | deriving (Show,Eq,Enum,Bounded)
62 |
63 |
64 | writeBinary :: FilePath -> String -> IO ()
65 | writeBinary file out = withBinaryFile file WriteMode $ \h -> hPutStr h out
66 |
67 | filterArgs :: IO ([String], String -> Bool, Tool -> Bool)
68 | filterArgs = do
69 | xs <- getArgs
70 | (args, xs) <- return $ partition ("-" `isPrefixOf`) xs
71 | let ts = [lcase $ show t| t :: Tool <- [minBound..maxBound]]
72 | let (tool,norm) = partition (`elem` ts) $ map lcase xs
73 | return (map (dropWhile (== '-')) args
74 | ,(\x -> null norm || lcase x `elem` norm)
75 | ,(\x -> null tool || lcase (show x) `elem` tool))
76 |
77 |
78 | lcase :: String -> String
79 | lcase = map toLower
80 |
81 |
82 | test :: String -> (([Opt] -> IO ()) -> IO ()) -> IO ()
83 | test name f = do
84 | (options, filtName, filtTool) <- filterArgs
85 | hSetBuffering stdout NoBuffering
86 | ts <- findTools name
87 | forM_ ts $ \t -> when (filtName name && filtTool t) $ do
88 | putStr $ "## " ++ name ++ " " ++ lcase (show t) ++ " ... "
89 | killDir "temp"
90 | createDir "temp"
91 | forM_ ["examples","util"] $ \dir -> do
92 | files <- getDirectoryContents dir
93 | forM_ files $ \file ->
94 | when ((lcase name ++ "-") `isPrefixOf` file) $
95 | copy dir "temp" file
96 | writeFile ".log" ""
97 | withCurrentDirectory "temp" $
98 | (if "continue" `elem` options then continue else id) $
99 | f $ run name t
100 | killDir "temp" -- deliberately don't clean up on failure
101 | removeFile ".log"
102 | putStrLn $ "Success"
103 |
104 |
105 | continue :: IO () -> IO ()
106 | continue act = E.catch act $ \(e :: SomeException) -> print e >> putStrLn ""
107 |
108 |
109 |
110 | killDir :: FilePath -> IO ()
111 | killDir x = retryIO (fmap not $ doesDirectoryExist x) $ removeDirectoryRecursive x
112 |
113 | createDir :: FilePath -> IO ()
114 | createDir x = retryIO (doesDirectoryExist x) $ createDirectoryIfMissing True x
115 |
116 | retryIO :: IO Bool -> IO () -> IO ()
117 | retryIO test act = f 20
118 | where
119 | pause = threadDelay 100000
120 | f 0 = act >> pause
121 | f i = do b <- test
122 | unless b $ do
123 | E.catch act $ \(_ :: SomeException) -> return ()
124 | pause
125 | f (i-1)
126 |
127 |
128 | findTools :: String -> IO [Tool]
129 | findTools name = do
130 | xs <- getDirectoryContents "examples"
131 | let ts = [v | x <- xs, takeExtension x /= ".broken", Just v <- [stripPrefix (name ++ "-") $ dropExtensions x]]
132 | return [x | x <- [minBound..maxBound], map toLower (show x) `elem` ts]
133 |
134 |
135 | run :: String -> Tool -> [Opt] -> IO ()
136 | run name tool opts = do
137 | verbose <- fmap ("--verbose" `elem`) getArgs
138 | when verbose $ putStrLn $ "\n" ++ name ++ " " ++ show tool ++ " " ++ show opts
139 | xs <- mapM (opt tool) opts
140 | threadDelay 1000000
141 | let p = last $ 1 : [i | Parallel i <- opts]
142 | let target = unwords ["\"" ++ x ++ "\"" | Target x <- opts]
143 | let system_ cmd = do
144 | env <- getEnvironment
145 | (_,_,_,p) <- createProcess (shell cmd){env = Just $ env ++ [(a,b) | Env a b <- opts]}
146 | r <- waitForProcess p
147 | when (r /= ExitSuccess) $ error $ "System command failed: " ++ cmd
148 | case tool of
149 | Shake -> system_ $ "runhaskell -Werror -fwarn-unused-binds -fwarn-unused-imports " ++ name ++ "-shake.hs --quiet -j" ++ show p ++ " " ++ target
150 | Make -> system_ $ "make --file=" ++ name ++ "-make --quiet -j" ++ show p ++ " " ++ target
151 | Aql -> system_ $ "aql -f " ++ name ++ "-aql -s -j" ++ show p ++ " " ++ target
152 | SCons -> system_ $ "scons -f " ++ name ++ "-scons -s -j" ++ show p ++ " " ++ target
153 | Ninja -> system_ $ "ninja -f " ++ name ++ "-ninja.ninja -j" ++ show p ++ " " ++ target ++ " > " ++ devNull
154 | Tup -> do
155 | b <- doesDirectoryExist ".tup"
156 | unless b $ system_ $ "tup init > " ++ devNull
157 | writeFile ".tup/options" "[updater]\nwarnings = 0"
158 | copyFile (name ++ "-tup") "Tupfile"
159 | system_ $ "tup -j" ++ show p ++ " " ++ target ++ " > " ++ devNull
160 | removeFile "Tupfile"
161 | TupLua -> do
162 | b <- doesDirectoryExist ".tup"
163 | unless b $ system_ $ "tup init > " ++ devNull
164 | writeFile ".tup/options" "[updater]\nwarnings = 0"
165 | copyFile (name ++ "-tuplua.lua") "Tupfile.lua"
166 | system_ $ "tup -j" ++ show p ++ " " ++ target ++ " > " ++ devNull
167 | removeFile "Tupfile.lua"
168 | Fabricate -> do
169 | system_ $ "python " ++ name ++ "-fabricate.py" ++ (if verbose then "" else " --quiet") ++ " -j" ++ show p ++ " " ++ target
170 | when verbose $ putStrLn =<< readFile ".deps"
171 | Fbuild -> do
172 | copyFile (name ++ "-fbuild.py") "fbuildroot.py"
173 | system_ $ "fbuild -j" ++ show p ++ " " ++ target ++ " > " ++ devNull
174 | removeFile "fbuildroot.py"
175 | Gup -> do
176 | createSymbolicLink (name ++ "-gup") "gup"
177 | system_ $ "gup -u -j" ++ show p ++ " " ++ target ++ " &> " ++ devNull
178 | removeFile "gup"
179 | sequence_ xs
180 |
181 | windows :: Bool
182 | windows = os == "mingw32"
183 |
184 | devNull :: String
185 | devNull = if windows then "nul" else "/dev/null"
186 |
187 |
188 | opt :: Tool -> Opt -> IO (IO ())
189 | opt _ (Missing file) = return $ do
190 | b <- doesFileExist file
191 | when b $ error $ "Fail should be missing: " ++ file
192 | opt _ (Contents file x) = return $ do
193 | src <- readFile file
194 | when (src /= x) $ error $
195 | "File is wrong: " ++ file ++ "\n" ++
196 | "Expected: " ++ show x ++ "\n" ++
197 | "Got : " ++ show src
198 | opt _ (Log x) = return $ do
199 | src <- readFile "../.log"
200 | when (lines src /= words x) $ error $
201 | "File is wrong: ../.log\n" ++
202 | "Expected: " ++ show x ++ "\n" ++
203 | "Got : " ++ show (unwords $ words src)
204 | opt _ NoChange = do
205 | dir <- getDirectoryContents "."
206 | times <- mapM modTime dir
207 | return $ do
208 | dir2 <- getDirectoryContents "."
209 | same "List of files changed" dir dir2
210 | times2 <- mapM modTime dir2
211 | same "File was modified" (zip dir times) (zip dir2 times2)
212 | opt _ (Change file) = do
213 | a <- modTime file
214 | return $ do
215 | b <- modTime file
216 | when (a == b) $ error "File did not change"
217 | opt _ _ = return $ return ()
218 |
219 |
220 | same :: (Show a, Eq a) => String -> [a] -> [a] -> IO ()
221 | same msg a b | null add && null del = return ()
222 | | otherwise = error $ msg ++ "\nOld values: " ++ show del ++ "\nNew values: " ++ show add
223 | where
224 | add = b \\ a
225 | del = a \\ b
226 |
227 |
228 | modTime :: FilePath -> IO (Maybe String)
229 | modTime file | "." `isPrefixOf` file = return Nothing
230 | | otherwise = do
231 | b <- doesFileExist file
232 | if b then fmap (Just . show) $ getModificationTime file else return Nothing
233 |
--------------------------------------------------------------------------------
/examples/basic-aql:
--------------------------------------------------------------------------------
1 |
2 | tools.Command( 'sh', './basic-run', File('./input'), '--', target = './output', cwd = '.' )
3 |
--------------------------------------------------------------------------------
/examples/basic-fabricate.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | from fabricate import *
3 |
4 | def build():
5 | run('sh','basic-run','input','--','output')
6 |
7 | main()
8 |
--------------------------------------------------------------------------------
/examples/basic-fbuild.py:
--------------------------------------------------------------------------------
1 | import fbuild.db
2 |
3 | @fbuild.db.caches
4 | def run(ctx, src: fbuild.db.SRC, dst: fbuild.db.DST):
5 | ctx.execute(['sh', 'basic-run', src, '--', dst], 'basic-run')
6 |
7 | def build(ctx):
8 | run(ctx, 'input', 'output')
9 |
--------------------------------------------------------------------------------
/examples/basic-gup/all.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 | gup -u output
3 |
--------------------------------------------------------------------------------
/examples/basic-gup/output.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | gup -u input
4 | sh basic-run input -- "$1"
5 |
--------------------------------------------------------------------------------
/examples/basic-make:
--------------------------------------------------------------------------------
1 |
2 | output: input
3 | sh basic-run input -- output
4 |
--------------------------------------------------------------------------------
/examples/basic-ninja.ninja:
--------------------------------------------------------------------------------
1 |
2 | rule run
3 | command = sh basic-run $in -- $out
4 |
5 | build output: run input
6 |
--------------------------------------------------------------------------------
/examples/basic-scons:
--------------------------------------------------------------------------------
1 | import os
2 | env = Environment(ENV = os.environ)
3 | env.AppendENVPath('PATH','.')
4 | env.Command('output','input','sh basic-run $SOURCE -- $TARGET')
5 |
--------------------------------------------------------------------------------
/examples/basic-shake.hs:
--------------------------------------------------------------------------------
1 | import Development.Shake
2 |
3 | main = shakeArgs shakeOptions $ do
4 | want ["output"]
5 | "output" %> \_ -> do
6 | need ["input"]
7 | cmd "sh basic-run input -- output"
8 |
--------------------------------------------------------------------------------
/examples/basic-tup:
--------------------------------------------------------------------------------
1 |
2 | : input |> sh basic-run input -- output |> output
3 |
--------------------------------------------------------------------------------
/examples/digest-aql:
--------------------------------------------------------------------------------
1 |
2 | out = tools.Command( 'sh', './digest-run', File('input'), '--', target = './output', cwd = '.' )
3 |
--------------------------------------------------------------------------------
/examples/digest-fbuild.py:
--------------------------------------------------------------------------------
1 | import fbuild.db
2 |
3 | @fbuild.db.caches
4 | def run(ctx, src: fbuild.db.SRC, dst: fbuild.db.DST):
5 | ctx.execute(['sh', 'digest-run', src, '--', dst], 'digest-run')
6 |
7 | def build(ctx):
8 | run(ctx, 'input', 'output')
9 |
--------------------------------------------------------------------------------
/examples/digest-gup/all.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | gup -u output
4 |
--------------------------------------------------------------------------------
/examples/digest-gup/input-digest.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | gup --always
4 | gup --contents input
5 |
--------------------------------------------------------------------------------
/examples/digest-gup/output.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | gup -u input-digest
4 | sh digest-run input -- output
5 |
--------------------------------------------------------------------------------
/examples/digest-scons:
--------------------------------------------------------------------------------
1 | import os
2 | env = Environment(ENV = os.environ)
3 | env.AppendENVPath('PATH','.')
4 | env.Command('output','input','sh digest-run $SOURCE -- $TARGET')
5 |
--------------------------------------------------------------------------------
/examples/digest-shake.hs:
--------------------------------------------------------------------------------
1 | import Development.Shake
2 |
3 | main = shakeArgs shakeOptions{shakeChange=ChangeDigest} $ do
4 | want ["output"]
5 | "output" %> \_ -> do
6 | need ["input"]
7 | cmd "sh digest-run input -- output"
8 |
--------------------------------------------------------------------------------
/examples/include-aql:
--------------------------------------------------------------------------------
1 | tools.c.Compile( 'include-main.c', target = './main' )
2 |
--------------------------------------------------------------------------------
/examples/include-fabricate.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | from fabricate import *
3 |
4 | def build():
5 | run('gcc','-c','include-main.c','-o','main.o')
6 |
7 | main()
8 |
--------------------------------------------------------------------------------
/examples/include-fbuild.py:
--------------------------------------------------------------------------------
1 | from fbuild.builders.c import guess_static
2 | import fbuild.db, os
3 |
4 | @fbuild.db.caches
5 | def copy(ctx, src: fbuild.db.SRC, dst: fbuild.db.DST):
6 | src.copy(dst)
7 |
8 | def build(ctx):
9 | c = guess_static(ctx)
10 | c.compile('include-main.c', 'main.o')
11 | # Fbuild puts object files in ctx.buildroot,
12 | # which is good...except that Main.hs wants them in the current directory.
13 | # This symlinks it as a workaround
14 | try: os.symlink(ctx.buildroot / 'main.o', 'main.o')
15 | except: pass
16 |
--------------------------------------------------------------------------------
/examples/include-gup/all.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 | gup -u main.o
--------------------------------------------------------------------------------
/examples/include-gup/main.o.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | gup -u include-main.c
4 | gcc -MM -MF main.d include-main.c
5 | read _ DEPS < main.d
6 | gup -u $DEPS
7 | gcc -c include-main.c -o "$1"
8 |
--------------------------------------------------------------------------------
/examples/include-make:
--------------------------------------------------------------------------------
1 |
2 |
3 | main.o: include-main.c
4 | gcc -MMD -MT main.o -MF main.d -c include-main.c -o main.o
5 |
6 | -include main.d
7 |
--------------------------------------------------------------------------------
/examples/include-ninja.ninja:
--------------------------------------------------------------------------------
1 |
2 | rule gcc
3 | deps = gcc
4 | depfile = $out.d
5 | command = gcc -MMD -MT $out -MF $out.d -c $in -o $out
6 |
7 | build main.o: gcc include-main.c
8 |
--------------------------------------------------------------------------------
/examples/include-scons:
--------------------------------------------------------------------------------
1 | Object('main.o','include-main.c')
2 |
--------------------------------------------------------------------------------
/examples/include-shake.hs:
--------------------------------------------------------------------------------
1 | import Development.Shake
2 | import Development.Shake.Util
3 |
4 | main = shakeArgs shakeOptions $ do
5 | want ["main.o"]
6 | "main.o" %> \out -> do
7 | () <- cmd "gcc -MMD -MT main.o -MF main.d -c include-main.c -o main.o"
8 | needMakefileDependencies "main.d"
9 |
--------------------------------------------------------------------------------
/examples/include-tup:
--------------------------------------------------------------------------------
1 |
2 | : |> gcc -c include-main.c -o main.o |> main.o
3 |
4 |
--------------------------------------------------------------------------------
/examples/intermediate-fbuild.py:
--------------------------------------------------------------------------------
1 | from fbuild.path import Path
2 | import fbuild.db
3 |
4 | @fbuild.db.caches
5 | def run(ctx, src: fbuild.db.SRC, dst: fbuild.db.DST):
6 | i = Path('intermediate')
7 | ctx.execute(['sh', 'intermediate-run', src, '--', i], 'intermediate-run')
8 | ctx.execute(['sh', 'intermediate-run', i, '--', dst], 'intermediate-run')
9 | i.remove()
10 |
11 | def build(ctx):
12 | run(ctx, 'input', 'output')
13 |
--------------------------------------------------------------------------------
/examples/intermediate-make:
--------------------------------------------------------------------------------
1 | output: intermediate
2 | sh intermediate-run intermediate -- output
3 |
4 | intermediate: input
5 | sh intermediate-run input -- intermediate
6 |
7 | .INTERMEDIATE: intermediate
8 |
--------------------------------------------------------------------------------
/examples/intermediate-shake.hs:
--------------------------------------------------------------------------------
1 |
2 | import Development.Shake
3 |
4 | main = shakeArgs shakeOptions $ do
5 | want ["output"]
6 | "output" %> \out -> do
7 | need ["input"]
8 | orderOnly ["intermediate"]
9 | cmd "sh intermediate-run intermediate -- output"
10 | "intermediate" %> \out -> do
11 | need ["input"]
12 | removeFilesAfter "." ["intermediate"]
13 | cmd "sh intermediate-run input -- intermediate"
14 |
--------------------------------------------------------------------------------
/examples/monad1-aql:
--------------------------------------------------------------------------------
1 | import os.path
2 |
3 | from aql import readTextFile
4 |
5 | def CatFiles( builder, node, target ):
6 | src_file = node.getSources()[0]
7 |
8 | cmd = "cat '{src_file}' | xargs cat > '{target}'".format( src_file = src_file, target = target )
9 |
10 | builder.execCmd( cmd, cwd = '.')
11 |
12 | files = readTextFile( src_file )
13 | files = [ file.strip() for file in files.splitlines() ]
14 |
15 | node.addTargets( target, implicit_deps = files )
16 |
17 | content = tools.ExecuteMethod( File('list'), method = CatFiles, args = ('output',) )
18 | Alias( 'output', content )
19 |
--------------------------------------------------------------------------------
/examples/monad1-fabricate.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | from fabricate import *
3 |
4 | def output():
5 | run('sh','-c','cat list | xargs cat > output')
6 |
7 | main()
8 |
--------------------------------------------------------------------------------
/examples/monad1-fbuild.py:
--------------------------------------------------------------------------------
1 | from fbuild.target import register
2 | import fbuild.db
3 |
4 | @fbuild.db.caches
5 | def run(ctx, srcs: fbuild.db.SRCS, dst: fbuild.db.DST):
6 | ctx.execute('cat {} > {}'.format(' '.join(srcs[1:]), dst), 'cat', shell=True)
7 |
8 | @register()
9 | def output(ctx):
10 | with open('list') as f:
11 | lines = f.read().splitlines()
12 | run(ctx, ['list']+lines, 'output')
13 |
--------------------------------------------------------------------------------
/examples/monad1-gup/all.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | gup -u output
--------------------------------------------------------------------------------
/examples/monad1-gup/output.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | gup -u list
4 | cat list | xargs gup -u
5 | cat list | xargs cat > "$1"
--------------------------------------------------------------------------------
/examples/monad1-make:
--------------------------------------------------------------------------------
1 |
2 | output : list $(shell cat list)
3 | cat $(filter-out $<,$^) > $@
4 |
--------------------------------------------------------------------------------
/examples/monad1-ninja.ninja:
--------------------------------------------------------------------------------
1 |
2 |
3 | rule run
4 | depfile = $out.d
5 | command = sh -c "(cat $in | xargs cat > $out) && (cat $in | xargs echo $out: > $depfile)"
6 |
7 | build output: run list
8 |
--------------------------------------------------------------------------------
/examples/monad1-scons:
--------------------------------------------------------------------------------
1 | import os
2 | import SCons.Util
3 |
4 | def ConsFileDeps(env, target, source, *args, **kw):
5 | if not SCons.SCons.Util.is_List(target):
6 | target = [target]
7 | if not SCons.SCons.Util.is_List(source):
8 | source = [source]
9 | env.Command(target[0],source[0],'cat $SOURCE | xargs cat > $TARGET')
10 | with open(source[0],"r") as f:
11 | for l in f.readlines():
12 | env.Depends(target[0], l.rstrip('\n'))
13 |
14 | env = Environment(ENV = os.environ)
15 | env.AddMethod(ConsFileDeps, "ConsFileDeps")
16 | env.ConsFileDeps('output','list')
17 |
--------------------------------------------------------------------------------
/examples/monad1-shake.hs:
--------------------------------------------------------------------------------
1 | import Development.Shake
2 |
3 | main = shakeArgs shakeOptions $ do
4 | "output" %> \out -> do
5 | src <- readFileLines "list"
6 | need src
7 | cmd Shell "cat" src ">" [out]
8 |
--------------------------------------------------------------------------------
/examples/monad1-tup:
--------------------------------------------------------------------------------
1 | : |> cat list | xargs cat > %o |> output
2 |
3 |
--------------------------------------------------------------------------------
/examples/monad2-aql:
--------------------------------------------------------------------------------
1 | import os.path
2 |
3 | from aql import readTextFile
4 |
5 | def CatFiles( builder, node, target ):
6 | src_file = node.getSources()[0]
7 |
8 | cmd = "cat '{src_file}' | xargs cat > '{target}'".format( src_file = src_file, target = target )
9 |
10 | builder.execCmd( cmd, cwd = '.')
11 |
12 | files = readTextFile( src_file )
13 | files = [ file.strip() for file in files.splitlines() ]
14 |
15 | node.addTargets( target, implicit_deps = files )
16 |
17 | gen_list = tools.Command( 'sh','./monad2-run', File('source'), '--', target = './list', cwd = '.' )
18 |
19 | content = tools.Method( gen_list, method = CatFiles, args = ('output',) )
20 | Alias( 'output', content )
21 |
--------------------------------------------------------------------------------
/examples/monad2-fabricate.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | from fabricate import *
3 |
4 | def list():
5 | run('sh','monad2-run','source','--','list')
6 |
7 | def output():
8 | list()
9 | run('sh','-c','cat list | xargs cat > output')
10 |
11 | main()
12 |
--------------------------------------------------------------------------------
/examples/monad2-fbuild.py:
--------------------------------------------------------------------------------
1 | from fbuild.target import register
2 | import fbuild.db
3 |
4 | @fbuild.db.caches
5 | def make_list(ctx, src: fbuild.db.SRC, dst: fbuild.db.DST):
6 | ctx.execute(['sh', 'monad2-run', src, '--', dst], 'monad2-run')
7 |
8 | @fbuild.db.caches
9 | def run(ctx, srcs: fbuild.db.SRCS, dst: fbuild.db.DST):
10 | ctx.execute('cat {} > {}'.format(' '.join(srcs[1:]), dst), 'cat', shell=True)
11 |
12 | @register()
13 | def output(ctx):
14 | make_list(ctx, 'source', 'list')
15 | with open('list') as f:
16 | lines = f.read().splitlines()
17 | run(ctx, ['list']+lines, 'output')
18 |
--------------------------------------------------------------------------------
/examples/monad2-gup/all.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | gup -u output
--------------------------------------------------------------------------------
/examples/monad2-gup/list.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | gup -u monad2-run source
4 | sh monad2-run source -- "$1"
--------------------------------------------------------------------------------
/examples/monad2-gup/output.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | gup -u list
4 | cat list | xargs gup -u
5 | cat list | xargs cat > "$1"
--------------------------------------------------------------------------------
/examples/monad2-make:
--------------------------------------------------------------------------------
1 |
2 | output: list
3 | cat list | xargs cat > output
4 | cat list | xargs echo output: > output.d
5 |
6 | list: source
7 | sh monad2-run source -- list
8 |
9 | -include output.d
10 |
--------------------------------------------------------------------------------
/examples/monad2-ninja.ninja:
--------------------------------------------------------------------------------
1 |
2 |
3 | rule run
4 | depfile = $out.d
5 | command = sh -c "(cat $in | xargs cat > $out) && (cat $in | xargs echo $out: > $depfile)"
6 |
7 | build output: run list
8 |
9 |
10 | rule run2
11 | command = sh monad2-run $in -- $out
12 |
13 | build list: run2 source
14 |
--------------------------------------------------------------------------------
/examples/monad2-scons:
--------------------------------------------------------------------------------
1 | import os
2 | import SCons.Builder
3 |
4 | def scansrcs(target, source, env):
5 | try:
6 | f=open(str(source[0]), 'r')
7 | except:
8 | print "scansrcs: Can't open source list file '%s'" % str(source[0])
9 | raise
10 | for line in f:
11 | src = line.strip()
12 | try:
13 | env['SCANSRCS_FUNC'](env, src, env['SCANSRCS_TARGET'])
14 | except:
15 | print "SCANSRCS func raised exception:"
16 | raise
17 | f.close()
18 |
19 | def add_target(env, source, tgt):
20 | env.Depends(tgt, source)
21 |
22 | env = Environment(ENV = os.environ)
23 | env.AppendENVPath('PATH','.')
24 | env.Append(BUILDERS = {'ScanSrcs' : SCons.Builder.Builder(action=scansrcs)})
25 | list = env.Command('list','source','sh monad2-run $SOURCE -- $TARGET')
26 | output = env.Command('output',list,'cat $SOURCE | xargs cat > $TARGET')
27 | dummy = env.ScanSrcs('#dummy-target', list, SCANSRCS_FUNC=add_target, SCANSRCS_TARGET=output)
28 | env.Depends(output, dummy)
29 |
--------------------------------------------------------------------------------
/examples/monad2-shake.hs:
--------------------------------------------------------------------------------
1 | import Development.Shake
2 |
3 | main = shakeArgs shakeOptions $ do
4 | "output" %> \out -> do
5 | src <- readFileLines "list"
6 | need src
7 | cmd Shell "cat" src ">" [out]
8 |
9 | "list" %> \out -> do
10 | need ["source"]
11 | cmd Shell "sh monad2-run source -- list"
12 |
--------------------------------------------------------------------------------
/examples/monad2-tup:
--------------------------------------------------------------------------------
1 | : source |> sh monad2-run %f -- %o |> list
2 | : list |> cat %f | xargs cat > %o |> output
3 |
4 |
--------------------------------------------------------------------------------
/examples/monad3-aql:
--------------------------------------------------------------------------------
1 | import os.path
2 |
3 | from aql import readTextFile
4 |
5 |
6 | def GenFile( builder, node ):
7 | src_file = node.getSources()[0]
8 |
9 | files = readTextFile( src_file )
10 | files = [ line.strip() for line in files.splitlines() ]
11 |
12 | for file in files:
13 | if os.path.basename(file) == 'gen':
14 | builder.execCmd( ['sh','./monad3-gen', '--', 'gen'], cwd = '.')
15 | break
16 |
17 | node.setNoTargets()
18 |
19 | def CatFiles( builder, node, target ):
20 | src_file = node.getSources()[0]
21 |
22 | cmd = "cat '{src_file}' | xargs cat > '{target}'".format( src_file = src_file, target = target )
23 |
24 | builder.execCmd( cmd, cwd = '.')
25 |
26 | files = readTextFile( src_file )
27 | files = [ file.strip() for file in files.splitlines() ]
28 |
29 | node.addTargets( target, implicit_deps = files )
30 |
31 |
32 | gen_list = tools.Command( 'sh','./monad3-run', File('source'), '--', target = './list', cwd = '.' )
33 |
34 | gen = tools.Method( gen_list, method = GenFile )
35 |
36 | content = tools.Method( gen_list, method = CatFiles, args = ('output',) )
37 | Depends( content, gen )
38 |
39 | Alias( 'output', content )
40 |
--------------------------------------------------------------------------------
/examples/monad3-fabricate.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | from fabricate import *
3 |
4 | def input1():
5 | pass # source file
6 |
7 | def gen():
8 | run('sh','monad3-gen','--','gen')
9 |
10 | def lst():
11 | run('sh','monad3-run','source','--','list')
12 |
13 | def output():
14 | lst()
15 | array = []
16 | with open('list', 'r') as f:
17 | for line in f:
18 | line = line.replace('\n', '')
19 | array.append(line)
20 | if line == "gen": gen()
21 | run('sh','-c','cat ' + ' '.join(array) + ' > output')
22 |
23 | main()
24 |
--------------------------------------------------------------------------------
/examples/monad3-fbuild.py:
--------------------------------------------------------------------------------
1 | from fbuild.target import register
2 | import fbuild.db
3 |
4 | @fbuild.db.caches
5 | def make_list(ctx, src: fbuild.db.SRC, dst: fbuild.db.DST):
6 | ctx.execute(['sh', 'monad3-run', src, '--', dst], 'monad3-run')
7 |
8 | @fbuild.db.caches
9 | def gen(ctx, dst: fbuild.db.DST):
10 | ctx.execute(['sh', 'monad3-gen', '--', dst], 'monad3-gen')
11 |
12 | @fbuild.db.caches
13 | def run(ctx, srcs: fbuild.db.SRCS, dst: fbuild.db.DST):
14 | ctx.execute('cat {} > {}'.format(' '.join(srcs[1:]), dst), 'cat', shell=True)
15 |
16 | genmap = {'gen': gen}
17 |
18 | @register()
19 | def output(ctx):
20 | make_list(ctx, 'source', 'list')
21 | with open('list') as f:
22 | lines = f.read().splitlines()
23 | for dep in lines:
24 | genmap.get(dep, lambda *_: None)(ctx, dep)
25 | run(ctx, ['list']+lines, 'output')
26 |
--------------------------------------------------------------------------------
/examples/monad3-gup/all.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | gup -u output
--------------------------------------------------------------------------------
/examples/monad3-gup/gen.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | gup -u monad3-gen
4 | sh monad3-gen -- "$1"
--------------------------------------------------------------------------------
/examples/monad3-gup/list.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | gup -u monad3-run source
4 | sh monad3-run source -- "$1"
5 |
--------------------------------------------------------------------------------
/examples/monad3-gup/output.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | gup -u list
4 | cat list | xargs gup -u
5 | cat list | xargs cat > "$1"
--------------------------------------------------------------------------------
/examples/monad3-make:
--------------------------------------------------------------------------------
1 | output: list
2 | cat `cat list` > output
3 |
4 | list.d: list
5 | echo output: `cat list` > list.d
6 |
7 | list: source
8 | sh monad3-run source -- list
9 |
10 | gen:
11 | sh monad3-gen -- gen
12 |
13 | -include list.d
14 |
--------------------------------------------------------------------------------
/examples/monad3-scons:
--------------------------------------------------------------------------------
1 | import os
2 | import SCons.Builder
3 |
4 | def scansrcs(target, source, env):
5 | try:
6 | f=open(str(source[0]), 'r')
7 | except:
8 | print "scansrcs: Can't open source list file '%s'" % str(source[0])
9 | raise
10 | for line in f:
11 | src = line.strip()
12 | try:
13 | if src != "gen":
14 | env['SCANSRCS_FUNC'](env, src, env['SCANSRCS_TARGET'])
15 | else:
16 | env['GEN_FUNC'](env, source, env['SCANSRCS_TARGET'])
17 | except:
18 | raise
19 | f.close()
20 |
21 | def add_target(env, source, tgt):
22 | env.Depends(tgt, source)
23 |
24 | def run_gencmd(env, source, tgt):
25 | gen = env.Command('gen', source, 'sh monad3-gen -- $TARGET')
26 | env.Depends(tgt, gen)
27 |
28 | env = Environment(ENV = os.environ)
29 | env.AppendENVPath('PATH','.')
30 | env.Append(BUILDERS = {'ScanSrcs' : SCons.Builder.Builder(action=scansrcs)})
31 | list = env.Command('list','source','sh monad3-run $SOURCE -- $TARGET')
32 | output = env.Command('output',list,'cat $SOURCE | xargs cat > $TARGET')
33 | dummy = env.ScanSrcs('#dummy-target', list,
34 | SCANSRCS_FUNC=add_target, SCANSRCS_TARGET=output,
35 | GEN_FUNC=run_gencmd)
36 | env.Depends(output, dummy)
37 |
--------------------------------------------------------------------------------
/examples/monad3-shake.hs:
--------------------------------------------------------------------------------
1 | import Development.Shake
2 |
3 | main = shakeArgs shakeOptions $ do
4 | "output" %> \out -> do
5 | src <- readFileLines "list"
6 | need src
7 | cmd Shell "cat" src ">" [out]
8 |
9 | "list" %> \out -> do
10 | need ["source"]
11 | cmd Shell "sh monad3-run source -- list"
12 |
13 | "gen" %> \out -> do
14 | cmd Shell "sh monad3-gen -- gen"
15 |
--------------------------------------------------------------------------------
/examples/multiple-aql:
--------------------------------------------------------------------------------
1 |
2 |
3 | sources = tools.Command( 'sh', './multiple-gen', File('input'), '--', target = ['./source1', './source2'], cwd = '.' )
4 |
5 | out1 = tools.Command( 'sh', './multiple-run', sources[0], '--', target = './output1', cwd = '.' )
6 | out2 = tools.Command( 'sh', './multiple-run', sources[1], '--', target = './output2', cwd = '.' )
7 |
8 | Alias( 'output1', out1 )
9 | Alias( 'output2', out2 )
10 |
--------------------------------------------------------------------------------
/examples/multiple-fabricate.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | from fabricate import *
3 |
4 | def sources():
5 | run('sh','multiple-gen','input','--','source1','source2')
6 |
7 | def output1():
8 | sources()
9 | run('sh','multiple-run','source1','--','output1')
10 |
11 | def output2():
12 | sources()
13 | run('sh','multiple-run','source2','--','output2')
14 |
15 | main()
16 |
--------------------------------------------------------------------------------
/examples/multiple-fbuild.py:
--------------------------------------------------------------------------------
1 | from fbuild.target import register
2 | import fbuild.db
3 |
4 | @fbuild.db.caches
5 | def gen(ctx, src: fbuild.db.SRC, dsts: fbuild.db.DSTS):
6 | ctx.execute(['sh', 'multiple-gen', src, '--'] + dsts, 'multiple-gen')
7 |
8 | @fbuild.db.caches
9 | def run(ctx, src: fbuild.db.SRC, dst: fbuild.db.DST):
10 | ctx.execute(['sh', 'multiple-run', src, '--', dst], 'multiple-run')
11 |
12 | @register()
13 | def output1(ctx):
14 | gen(ctx, 'input', ['source1', 'source2'])
15 | run(ctx, 'source1', 'output1')
16 |
17 | @register()
18 | def output2(ctx):
19 | gen(ctx, 'input', ['source1', 'source2'])
20 | run(ctx, 'source2', 'output2')
21 |
--------------------------------------------------------------------------------
/examples/multiple-gup/all.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | gup -u output1 output2
--------------------------------------------------------------------------------
/examples/multiple-gup/output1.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | gup -u source1
4 | sh multiple-run source1 -- "$1"
5 |
--------------------------------------------------------------------------------
/examples/multiple-gup/output2.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | gup -u source2
4 | sh multiple-run source2 -- "$1"
--------------------------------------------------------------------------------
/examples/multiple-gup/source.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | gup -u input
4 | sh multiple-gen input -- source1 source2
5 | touch "$1"
6 |
--------------------------------------------------------------------------------
/examples/multiple-gup/source1.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | gup -u source
4 | touch "$2"
5 | gup --contents "$2"
6 |
--------------------------------------------------------------------------------
/examples/multiple-gup/source2.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | gup -u source
4 | touch "$2"
5 | gup --contents "$2"
6 |
--------------------------------------------------------------------------------
/examples/multiple-ninja.ninja:
--------------------------------------------------------------------------------
1 |
2 | rule run
3 | command = sh multiple-run $in -- $out
4 |
5 | build output1: run source1
6 | build output2: run source2
7 |
8 | rule gen
9 | command = sh multiple-gen $in -- $out
10 | restat = 1
11 |
12 | build source1 source2: gen input
13 |
--------------------------------------------------------------------------------
/examples/multiple-scons:
--------------------------------------------------------------------------------
1 | import os
2 | env = Environment(ENV = os.environ)
3 | env.AppendENVPath('PATH','.')
4 | env.Command(['source1','source2'], 'input', 'sh multiple-gen $SOURCE -- $TARGETS')
5 | env.Command('output1', 'source1', 'sh multiple-run $SOURCE -- $TARGET')
6 | env.Command('output2', 'source2', 'sh multiple-run $SOURCE -- $TARGET')
7 |
--------------------------------------------------------------------------------
/examples/multiple-shake.hs:
--------------------------------------------------------------------------------
1 | import Development.Shake
2 |
3 | main = shakeArgs shakeOptions $ do
4 | let run inp out = out %> \_ -> do
5 | need [inp]
6 | cmd "sh multiple-run" [inp] "--" [out]
7 | run "source1" "output1"
8 | run "source2" "output2"
9 | ["source1","source2"] |%> \out -> do
10 | need ["input"]
11 | cmd "sh multiple-gen input -- source1 source2"
12 |
--------------------------------------------------------------------------------
/examples/multiple-tup:
--------------------------------------------------------------------------------
1 | : input |> ^o^ sh multiple-gen input -- %o |> source1 source2
2 | : source1 |> sh multiple-run source1 -- %o |> output1
3 | : source2 |> sh multiple-run source2 -- %o |> output2
4 |
--------------------------------------------------------------------------------
/examples/nofileout-aql:
--------------------------------------------------------------------------------
1 |
2 | tools.Command( 'sh', './nofileout-run', File('input'), '--', cwd = '.' )
3 |
--------------------------------------------------------------------------------
/examples/nofileout-fbuild.py:
--------------------------------------------------------------------------------
1 | import fbuild.db
2 |
3 | @fbuild.db.caches
4 | def run(ctx, src: fbuild.db.SRC):
5 | ctx.execute(['sh', 'nofileout-run', src], 'nofileout-run')
6 |
7 | def build(ctx):
8 | run(ctx, 'input')
9 |
--------------------------------------------------------------------------------
/examples/nofileout-gup/all.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | gup -u input-digest
4 | sh nofileout-run input --
5 | touch "$1"
6 |
--------------------------------------------------------------------------------
/examples/nofileout-gup/input-digest.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | gup --always
4 | gup --contents input
5 |
--------------------------------------------------------------------------------
/examples/nofileout-tup:
--------------------------------------------------------------------------------
1 | : input |> sh nofileout-run %f |>
2 |
--------------------------------------------------------------------------------
/examples/noleftover-shake.hs.broken:
--------------------------------------------------------------------------------
1 | import Development.Shake
2 | import Development.Shake.FilePath
3 |
4 | main = shakeArgs shakeOptions $ do
5 |
6 | action $ do
7 | is <- getDirectoryFiles "." ["*.in"]
8 | let os = [i -<.> "out" | i <- is]
9 | need os
10 |
11 | "*.out" *> \o -> do
12 | let i = o -<.> "in"
13 | cmd "sh noleftover-run" [i] "--" [o]
14 |
--------------------------------------------------------------------------------
/examples/noleftover-tup:
--------------------------------------------------------------------------------
1 | : foreach *.in |> sh noleftover-run %f -- %o |> %B.out
2 |
--------------------------------------------------------------------------------
/examples/parallel-aql:
--------------------------------------------------------------------------------
1 |
2 | tools.Command( 'sh', './parallel-run', File('./input1'), '--', target = './output1', cwd = '.' )
3 | tools.Command( 'sh', './parallel-run', File('./input2'), '--', target = './output2', cwd = '.' )
4 |
--------------------------------------------------------------------------------
/examples/parallel-fbuild.py:
--------------------------------------------------------------------------------
1 | from functools import partial
2 | import fbuild.db
3 |
4 | @fbuild.db.caches
5 | def run(ctx, src: fbuild.db.SRC) -> fbuild.db.DST:
6 | dst = src.replace('in', 'out')
7 | ctx.execute(['sh', 'parallel-run', src, '--', dst], 'parallel-run')
8 | return dst
9 |
10 | def build(ctx):
11 | ctx.scheduler.map(partial(run, ctx), ['input1', 'input2'])
12 |
--------------------------------------------------------------------------------
/examples/parallel-gup/all.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | gup -u output1 output2
--------------------------------------------------------------------------------
/examples/parallel-gup/output1.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | gup -u input1
4 | sh parallel-run input1 -- "$1"
5 |
--------------------------------------------------------------------------------
/examples/parallel-gup/output2.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | gup -u input2
4 | sh parallel-run input2 -- "$1"
5 |
--------------------------------------------------------------------------------
/examples/parallel-make:
--------------------------------------------------------------------------------
1 |
2 | .PHONY: all
3 | all: output1 output2
4 |
5 | output1: input1
6 | sh parallel-run input1 -- output1
7 |
8 | output2: input2
9 | sh parallel-run input2 -- output2
10 |
--------------------------------------------------------------------------------
/examples/parallel-ninja.ninja:
--------------------------------------------------------------------------------
1 |
2 | rule run
3 | command = sh parallel-run $in -- $out
4 |
5 | build output1: run input1
6 | build output2: run input2
7 |
--------------------------------------------------------------------------------
/examples/parallel-scons:
--------------------------------------------------------------------------------
1 | import os
2 | env = Environment(ENV = os.environ)
3 | env.AppendENVPath('PATH','.')
4 | env.Command('output1','input1','sh parallel-run $SOURCE -- $TARGET')
5 | env.Command('output2','input2','sh parallel-run $SOURCE -- $TARGET')
6 |
--------------------------------------------------------------------------------
/examples/parallel-shake.hs:
--------------------------------------------------------------------------------
1 | import Development.Shake
2 |
3 | main = shakeArgs shakeOptions $ do
4 | want ["output1","output2"]
5 | let run inp out = out %> \_ -> do
6 | need [inp]
7 | cmd "sh parallel-run" [inp] "--" [out]
8 | run "input1" "output1"
9 | run "input2" "output2"
10 |
--------------------------------------------------------------------------------
/examples/parallel-tup:
--------------------------------------------------------------------------------
1 | : input1 |> sh parallel-run %f -- %o |> output1
2 | : input2 |> sh parallel-run %f -- %o |> output2
3 |
4 |
--------------------------------------------------------------------------------
/examples/pool-ninja.ninja:
--------------------------------------------------------------------------------
1 |
2 | pool limit
3 | depth = 2
4 |
5 | rule run
6 | command = sh pool-run $in -- $out
7 | pool = limit
8 |
9 | build output1: run input1
10 | build output2: run input2
11 | build output3: run input3
12 |
--------------------------------------------------------------------------------
/examples/pool-shake.hs:
--------------------------------------------------------------------------------
1 | import Development.Shake
2 |
3 | main = shakeArgs shakeOptions $ do
4 | want ["output1","output2","output3"]
5 | pool <- newResource "pool" 2
6 | let run inp out = out %> \_ -> do
7 | need [inp]
8 | withResource pool 1 $ cmd "sh pool-run" [inp] "--" [out]
9 | run "input1" "output1"
10 | run "input2" "output2"
11 | run "input3" "output3"
12 |
--------------------------------------------------------------------------------
/examples/secondary-make:
--------------------------------------------------------------------------------
1 | output: secondary
2 | sh secondary-run secondary -- output
3 |
4 | secondary: input
5 | sh secondary-run input -- secondary
6 |
7 | .SECONDARY: secondary
8 |
--------------------------------------------------------------------------------
/examples/secondary-shake.hs:
--------------------------------------------------------------------------------
1 |
2 | import Development.Shake
3 |
4 | main = shakeArgs shakeOptions $ do
5 | want ["output"]
6 | "output" %> \out -> do
7 | need ["input"]
8 | orderOnly ["secondary"]
9 | cmd "sh secondary-run secondary -- output"
10 | "secondary" %> \out -> do
11 | need ["input"]
12 | cmd "sh secondary-run input -- secondary"
13 |
--------------------------------------------------------------------------------
/examples/spaces-aql:
--------------------------------------------------------------------------------
1 |
2 | target = GetBuildTargets()[0]
3 |
4 | node = tools.Command( 'cp', File('./input file'), target = './' + target, cwd = '.' )
5 | Alias( target, node )
6 |
--------------------------------------------------------------------------------
/examples/spaces-fabricate.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | from fabricate import *
3 | import sys
4 |
5 | def output_file():
6 | run('cp','input file','output file')
7 |
8 |
9 | # Hacky, perhaps a bug that can be fixed by fabricate?
10 | for i, x in enumerate(sys.argv):
11 | if x == 'output file':
12 | sys.argv[i] = 'output_file'
13 |
14 | main()
15 |
--------------------------------------------------------------------------------
/examples/spaces-fbuild.py:
--------------------------------------------------------------------------------
1 | from fbuild.target import register
2 | import fbuild.db
3 |
4 | @fbuild.db.caches
5 | def run(ctx, src: fbuild.db.SRC, dst: fbuild.db.DST):
6 | ctx.execute(['cp', src, dst], 'cp')
7 |
8 | @register(name='output file')
9 | def build(ctx):
10 | run(ctx, 'input file', 'output file')
11 |
--------------------------------------------------------------------------------
/examples/spaces-gup/all.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | gup -u 'output file'
--------------------------------------------------------------------------------
/examples/spaces-gup/output file.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | gup -u 'input file'
4 | cp 'input file' "$1"
--------------------------------------------------------------------------------
/examples/spaces-make:
--------------------------------------------------------------------------------
1 |
2 | output\ file: input\ file
3 | cp input\ file output\ file
4 |
--------------------------------------------------------------------------------
/examples/spaces-ninja.ninja:
--------------------------------------------------------------------------------
1 |
2 | rule cp
3 | command = cp $in $out
4 |
5 | build output$ file: cp input$ file
6 |
--------------------------------------------------------------------------------
/examples/spaces-scons:
--------------------------------------------------------------------------------
1 | import os
2 | env = Environment(ENV = os.environ)
3 | env.Command('output file','input file','cp $SOURCE $TARGET')
4 |
--------------------------------------------------------------------------------
/examples/spaces-shake.hs:
--------------------------------------------------------------------------------
1 | import Development.Shake
2 |
3 | main = shakeArgs shakeOptions $ do
4 | "output file" %> \out -> do
5 | copyFile' "input file" out
6 |
--------------------------------------------------------------------------------
/examples/spaces-tup.broken:
--------------------------------------------------------------------------------
1 | : |> cp input\ file output\ file |> output\ file
2 |
3 |
--------------------------------------------------------------------------------
/examples/spaces-tuplua.lua:
--------------------------------------------------------------------------------
1 | tup.rule({"input file"}, "cp '%f' '%o'", {"output file"})
2 |
--------------------------------------------------------------------------------
/examples/system1-aql:
--------------------------------------------------------------------------------
1 |
2 | source = tools.Command( 'sh', './system1-gen', '--', target = './source', cwd = '.' )
3 | AlwaysBuild( source )
4 |
5 | out = tools.Command( 'sh', './system1-run', source, '--', target = './output', cwd = '.' )
6 | Alias( 'output', out )
7 |
--------------------------------------------------------------------------------
/examples/system1-fbuild.py:
--------------------------------------------------------------------------------
1 | from fbuild.target import register
2 | import fbuild.db
3 |
4 | def gen(ctx, dst) -> fbuild.db.DST:
5 | ctx.execute(['sh', 'system1-gen', '--', dst], 'system1-gen')
6 | return dst
7 |
8 | @fbuild.db.caches
9 | def run(ctx, src: fbuild.db.SRC, dst: fbuild.db.DST):
10 | ctx.execute(['sh', 'system1-run', src, '--', dst], 'system1-run')
11 |
12 | @register()
13 | def output(ctx):
14 | run(ctx, gen(ctx, 'source'), 'output')
15 |
--------------------------------------------------------------------------------
/examples/system1-gup/all.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 | gup -u output
3 |
--------------------------------------------------------------------------------
/examples/system1-gup/output.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 | gup -u source
3 | sh system1-run source -- "$1"
4 |
--------------------------------------------------------------------------------
/examples/system1-gup/source.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 | gup --always
3 | sh system1-gen -- "$1"
4 | gup --contents "$1"
5 |
--------------------------------------------------------------------------------
/examples/system1-scons:
--------------------------------------------------------------------------------
1 | import os
2 | env = Environment(ENV = os.environ)
3 | env.AppendENVPath('PATH', '.')
4 | s = env.Command('source', [], 'sh system1-gen -- $TARGET')
5 | env.Precious(s)
6 | env.AlwaysBuild(s)
7 | env.Command('output', s, 'sh system1-run $SOURCE -- $TARGET')
8 |
--------------------------------------------------------------------------------
/examples/system1-shake.hs:
--------------------------------------------------------------------------------
1 | import Development.Shake
2 |
3 | main = shakeArgs shakeOptions $ do
4 | "output" %> \out -> do
5 | need ["source"]
6 | cmd "sh system1-run source -- " [out]
7 | "source" %> \out -> do
8 | alwaysRerun
9 | cmd "sh system1-gen -- " [out]
10 |
--------------------------------------------------------------------------------
/examples/system2-aql:
--------------------------------------------------------------------------------
1 |
2 | out = tools.Command( 'sh', './system2-run', '--', target = './output', cwd = '.' )
3 |
4 | data = Entity( options.env.get().get( 'SYSTEM2_DATA', '' ) )
5 |
6 | Depends( out, data )
7 |
8 | Alias( 'output', out )
9 |
--------------------------------------------------------------------------------
/examples/system2-gup/all.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 | gup -u output
3 |
--------------------------------------------------------------------------------
/examples/system2-gup/data.gup:
--------------------------------------------------------------------------------
1 | #!bash -e
2 |
3 | gup --always
4 | gup --contents <<<"$SYSTEM2_DATA"
5 |
--------------------------------------------------------------------------------
/examples/system2-gup/output.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 | gup -u data
3 | sh system2-run -- "$1"
4 |
--------------------------------------------------------------------------------
/examples/system2-scons:
--------------------------------------------------------------------------------
1 | import os
2 | import SCons.Node.Python
3 | env = Environment(ENV = os.environ)
4 | env.AppendENVPath('PATH', '.')
5 | r = env.Command('output', [], 'sh system2-run -- $TARGET')
6 | n = SCons.Node.Python.Value(os.environ.get('SYSTEM2_DATA',''))
7 | env.Depends(r, n)
8 |
--------------------------------------------------------------------------------
/examples/system2-shake.hs:
--------------------------------------------------------------------------------
1 | import Development.Shake
2 |
3 | main = shakeArgs shakeOptions $ do
4 | want ["output"]
5 | "output" %> \out -> do
6 | getEnv "SYSTEM2_DATA" -- just for the dependency
7 | cmd "sh system2-run" "--" [out]
8 |
--------------------------------------------------------------------------------
/examples/system2-tup:
--------------------------------------------------------------------------------
1 | export SYSTEM2_DATA
2 | : |> sh system2-run -- %o |> output
3 |
--------------------------------------------------------------------------------
/examples/unchanged-aql:
--------------------------------------------------------------------------------
1 |
2 | src = tools.Command( 'sh', './unchanged-gen', File('input'), '--', target = './source', cwd = '.' )
3 | out = tools.Command( 'sh', './unchanged-run', src, '--', target = './output', cwd = '.' )
4 |
5 | Alias( 'output', out )
6 |
--------------------------------------------------------------------------------
/examples/unchanged-fabricate.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | from fabricate import *
3 |
4 | def source():
5 | run('sh','unchanged-gen','input','--','source')
6 |
7 | def output():
8 | source()
9 | run('sh','unchanged-run','source','--','output')
10 |
11 | main()
12 |
--------------------------------------------------------------------------------
/examples/unchanged-fbuild.py:
--------------------------------------------------------------------------------
1 | from fbuild.target import register
2 | import fbuild.db
3 |
4 | @fbuild.db.caches
5 | def gen(ctx, src: fbuild.db.SRC, dst: fbuild.db.DST):
6 | ctx.execute(['sh', 'unchanged-gen', src, '--', dst], 'unchanged-gen')
7 |
8 | @fbuild.db.caches
9 | def run(ctx, src: fbuild.db.SRC, dst: fbuild.db.DST):
10 | ctx.execute(['sh', 'unchanged-run', src, '--', dst], 'unchanged-run')
11 |
12 | @register()
13 | def output(ctx):
14 | gen(ctx, 'input', 'source')
15 | run(ctx, 'source', 'output')
16 |
--------------------------------------------------------------------------------
/examples/unchanged-gup/all.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | gup -u output
4 |
--------------------------------------------------------------------------------
/examples/unchanged-gup/output.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | gup -u unchanged-run source
4 | sh unchanged-run source -- "$1"
5 |
--------------------------------------------------------------------------------
/examples/unchanged-gup/source.gup:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | gup -u unchanged-gen input
4 | sh unchanged-gen input -- "$1"
5 | gup --contents "$1"
--------------------------------------------------------------------------------
/examples/unchanged-ninja.ninja:
--------------------------------------------------------------------------------
1 |
2 | rule run
3 | command = sh unchanged-run $in -- $out
4 |
5 | build output: run source
6 |
7 | rule gen
8 | command = sh unchanged-gen $in -- $out
9 | restat = 1
10 |
11 | build source: gen input
12 |
--------------------------------------------------------------------------------
/examples/unchanged-scons:
--------------------------------------------------------------------------------
1 | import os
2 | env = Environment(ENV = os.environ)
3 | env.AppendENVPath('PATH','.')
4 | env.Command('output', 'source', 'sh unchanged-run $SOURCE -- $TARGET')
5 | env.Command('source', 'input', 'sh unchanged-gen $SOURCE -- $TARGET')
6 |
--------------------------------------------------------------------------------
/examples/unchanged-shake.hs:
--------------------------------------------------------------------------------
1 | import Development.Shake
2 |
3 | main = shakeArgs shakeOptions $ do
4 | "output" %> \out -> do
5 | need ["source"]
6 | cmd "sh unchanged-run source -- output"
7 | "source" %> \out -> do
8 | need ["input"]
9 | cmd "sh unchanged-gen input -- source"
10 |
--------------------------------------------------------------------------------
/examples/unchanged-tup:
--------------------------------------------------------------------------------
1 | : |> ^o^ sh unchanged-gen input -- %o |> source
2 | : source |> sh unchanged-run source -- %o |> output
3 |
4 |
--------------------------------------------------------------------------------
/examples/wildcard-aql:
--------------------------------------------------------------------------------
1 |
2 | import os.path
3 |
4 | target = GetBuildTargets()[0]
5 | source = os.path.splitext(target)[0] + '.in'
6 |
7 | out = tools.Command( 'cp', File(source), target = './' + target, cwd = '.' )
8 |
9 | Alias( target, out )
10 |
11 |
--------------------------------------------------------------------------------
/examples/wildcard-fabricate.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | from fabricate import *
3 | import sys, os, glob
4 |
5 | def get_cp(inf, out):
6 | @staticmethod
7 | def _f(): run('cp', inf, out)
8 | return _f
9 |
10 | for f in glob.glob('*.in'):
11 | base = os.path.splitext(f)[0]
12 | globals()[base] = type(base, (object,), {'out': get_cp(f, base+'.out')})
13 |
14 | main()
15 |
--------------------------------------------------------------------------------
/examples/wildcard-fbuild.py:
--------------------------------------------------------------------------------
1 | from fbuild.target import register
2 | from fbuild.path import Path
3 | import fbuild.db
4 |
5 | @fbuild.db.caches
6 | def cp(ctx, src: fbuild.db.SRC, dst: fbuild.db.DST):
7 | ctx.execute(['cp', src, dst], 'cp')
8 |
9 | for path in Path.glob('*.in'):
10 | out = path.replaceext('.out')
11 | @register(name=str(out))
12 | def func(ctx):
13 | cp(ctx, path, out)
14 |
--------------------------------------------------------------------------------
/examples/wildcard-gup/Gupfile:
--------------------------------------------------------------------------------
1 | copier:
2 | *.out
--------------------------------------------------------------------------------
/examples/wildcard-gup/copier:
--------------------------------------------------------------------------------
1 | #!bash -eu
2 |
3 | SRC=${2%.out}.in
4 | gup -u "$SRC"
5 | cp "$SRC" "$1"
--------------------------------------------------------------------------------
/examples/wildcard-make:
--------------------------------------------------------------------------------
1 |
2 | %.out: %.in
3 | cp $< $@
4 |
--------------------------------------------------------------------------------
/examples/wildcard-scons:
--------------------------------------------------------------------------------
1 | import os
2 | import SCons.Builder
3 |
4 | cpbuild_ = SCons.Builder.Builder(action="cp $SOURCE $TARGET",
5 | single_src=True, src_suffix=".in", suffix=".out")
6 |
7 | env = Environment(ENV=os.environ, BUILDERS={'CpBuild' : cpbuild_})
8 | env.CpBuild([os.path.splitext(t)[0] for t in BUILD_TARGETS])
9 |
10 |
--------------------------------------------------------------------------------
/examples/wildcard-shake.hs:
--------------------------------------------------------------------------------
1 | import Development.Shake
2 | import Development.Shake.FilePath
3 |
4 | main = shakeArgs shakeOptions $ do
5 | "*.out" %> \out -> do
6 | copyFile' (out -<.> "in") out
7 |
--------------------------------------------------------------------------------
/examples/wildcard-tup:
--------------------------------------------------------------------------------
1 | : foreach *.in |> cp %g.in %g.out |> %g.out
2 |
--------------------------------------------------------------------------------
/travis.sh:
--------------------------------------------------------------------------------
1 | set -ex
2 | sudo apt-get update
3 |
4 | echo Install Shake
5 | git clone https://github.com/ndmitchell/shake
6 | (cd shake && cabal install)
7 |
8 | echo Install Ninja
9 | git clone https://github.com/martine/ninja
10 | (cd ninja && ./bootstrap.py)
11 | export PATH=$PATH:`pwd`/ninja
12 |
13 | echo Install tup
14 | sudo apt-get install libfuse-dev
15 | modprobe fuse
16 | git clone https://github.com/gittup/tup
17 | (cd tup && ./bootstrap.sh)
18 | export PATH=$PATH:`pwd`/tup
19 |
20 | echo Install fabricate
21 | wget https://raw.githubusercontent.com/SimonAlfie/fabricate/master/fabricate.py
22 | export PYTHONPATH=$PWD:$PYTHONPATH
23 |
24 | echo Install Aqualid
25 | wget https://github.com/aqualid/aqualid/releases/download/v0.53-beta/Aqualid-0.53.tar.bz2
26 | tar -xjf Aqualid-0.53.tar.bz2
27 | export AQLPATH=$PWD/Aqualid
28 | export PATH=$PATH:$AQLPATH
29 | export PYTHONPATH=$PYTHONPATH:$AQLPATH
30 | (cd Aqualid-0.53 && python setup.py install --install-lib $AQLPATH --install-headers $AQLPATH --install-scripts $AQLPATH --install-data $AQLPATH)
31 |
32 | echo Install fbuild
33 | sudo apt-get install python3 python3-setuptools
34 | git clone https://github.com/felix-lang/fbuild.git
35 | (cd fbuild && sudo python3 setup.py install)
36 |
37 | echo Install gup
38 | # see http://installion.co.uk/ubuntu/vivid/universe/0/0install-core/install/index.html
39 | sudo cat /etc/apt/sources.list
40 | sudo apt-get install 0install-core --no-install-recommends
41 | 0install add gup http://gfxmonk.net/dist/0install/gup.xml
42 |
43 | export PATH=/home/travis/.ghc-multi/7.6.3/bin:$PATH
44 | runhaskell Main shake make ninja fabricate aql fbuild tup
45 | # gup
46 | # Temporarily disabled gup due to: https://travis-ci.org/ndmitchell/build-shootout/builds/586561022#L1172
47 |
--------------------------------------------------------------------------------
/util/basic-run:
--------------------------------------------------------------------------------
1 | cp $1 $3
2 |
--------------------------------------------------------------------------------
/util/digest-run:
--------------------------------------------------------------------------------
1 | cp $1 $3
2 |
--------------------------------------------------------------------------------
/util/include-1.h:
--------------------------------------------------------------------------------
1 |
2 | #include "include-2.h"
3 |
--------------------------------------------------------------------------------
/util/include-2.h:
--------------------------------------------------------------------------------
1 |
2 | /* Empty */
3 |
--------------------------------------------------------------------------------
/util/include-main.c:
--------------------------------------------------------------------------------
1 |
2 | #include "include-1.h"
3 |
4 | int main;
5 |
--------------------------------------------------------------------------------
/util/intermediate-run:
--------------------------------------------------------------------------------
1 | echo run >> ../.log
2 | cp $1 $3
3 | echo -n " *" >> $3
4 |
--------------------------------------------------------------------------------
/util/monad2-run:
--------------------------------------------------------------------------------
1 | echo run >> ../.log
2 | sed 's/out/in/g' $1 > $3
3 |
--------------------------------------------------------------------------------
/util/monad3-gen:
--------------------------------------------------------------------------------
1 | echo Generated > $2
2 |
--------------------------------------------------------------------------------
/util/monad3-run:
--------------------------------------------------------------------------------
1 | echo run >> ../.log
2 | sed 's/out/in/g' $1 > $3
3 |
--------------------------------------------------------------------------------
/util/multiple-gen:
--------------------------------------------------------------------------------
1 | if [ -f $3 ]; then
2 | OLD=`cat $3`
3 | else
4 | OLD=_none_
5 | fi
6 | NEW=`sed 's/a/A/g' $1`
7 | if [ "$OLD" != "$NEW" ] ; then
8 | sed 's/a/A/g' $1 > $3
9 | fi
10 |
11 | if [ -f $4 ]; then
12 | OLD=`cat $4`
13 | else
14 | OLD=_none_
15 | fi
16 | NEW=`sed 's/b/B/g' $1`
17 | if [ "$OLD" != "$NEW" ] ; then
18 | sed 's/b/B/g' $1 > $4
19 | fi
20 |
--------------------------------------------------------------------------------
/util/multiple-run:
--------------------------------------------------------------------------------
1 | echo run >> ../.log
2 | sed 's/c/C/g' $1 > $3
3 |
--------------------------------------------------------------------------------
/util/nofileout-run:
--------------------------------------------------------------------------------
1 | cat $1 >> ../.log
2 |
--------------------------------------------------------------------------------
/util/noleftover-run:
--------------------------------------------------------------------------------
1 | cp $1 $3
2 |
--------------------------------------------------------------------------------
/util/parallel-run:
--------------------------------------------------------------------------------
1 | echo start >> ../.log
2 | sleep 1s
3 | cp $1 $3
4 | echo end >> ../.log
5 |
--------------------------------------------------------------------------------
/util/pool-run:
--------------------------------------------------------------------------------
1 | echo start >> ../.log
2 | if [ "$3" = "output1" ] ; then sleep 1s ; fi
3 | if [ "$3" = "output2" ] ; then sleep 2s ; fi
4 | if [ "$3" = "output3" ] ; then sleep 3s ; fi
5 | cp $1 $3
6 | echo end >> ../.log
7 |
--------------------------------------------------------------------------------
/util/secondary-run:
--------------------------------------------------------------------------------
1 | echo run >> ../.log
2 | cp $1 $3
3 | echo -n " *" >> $3
4 |
--------------------------------------------------------------------------------
/util/system1-gen:
--------------------------------------------------------------------------------
1 | echo gen >> ../.log
2 | A=`cat $2`
3 | B=`cat system1-data`
4 | if [ "$A" != "$B" ] ; then
5 | cp system1-data $2
6 | fi
7 |
--------------------------------------------------------------------------------
/util/system1-run:
--------------------------------------------------------------------------------
1 | echo run >> ../.log
2 | cp $1 $3
3 |
--------------------------------------------------------------------------------
/util/system2-run:
--------------------------------------------------------------------------------
1 | echo run >> ../.log
2 | echo -n "$SYSTEM2_DATA" > $2
3 |
--------------------------------------------------------------------------------
/util/unchanged-gen:
--------------------------------------------------------------------------------
1 | if [ -f $3 ]; then
2 | OLD=`cat $3`
3 | else
4 | OLD=_none_
5 | fi
6 | NEW=`sed 's/in/out/g' $1`
7 | if [ "$OLD" != "$NEW" ] ; then
8 | sed 's/in/out/g' $1 > $3
9 | fi
10 |
--------------------------------------------------------------------------------
/util/unchanged-run:
--------------------------------------------------------------------------------
1 | echo run >> ../.log
2 | sed 's/i/x/g' $1 > $3
3 |
--------------------------------------------------------------------------------