├── .gitignore ├── .luacheckrc ├── LICENSE ├── README.md ├── assets ├── logo.png ├── logo.svg └── result.png ├── bin └── fourmi ├── doc ├── classes │ ├── fourmi.plan.html │ ├── fourmi.task.html │ ├── plan.html │ └── task.html ├── index.html ├── ldoc.css └── modules │ ├── fourmi.builtins.html │ ├── fourmi.html │ └── task.html ├── examples ├── buildlua.fourmi.plan.lua └── mingzip.fourmi.plan.lua ├── fourmi-dev-1.rockspec └── fourmi ├── builtins.lua ├── init.lua ├── log.lua ├── plan.lua ├── task.lua └── utils.lua /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Lua sources 2 | luac.out 3 | 4 | # luarocks build files 5 | *.src.rock 6 | *.zip 7 | *.tar.gz 8 | 9 | # Object files 10 | *.o 11 | *.os 12 | *.ko 13 | *.obj 14 | *.elf 15 | 16 | # Precompiled Headers 17 | *.gch 18 | *.pch 19 | 20 | # Libraries 21 | *.lib 22 | *.a 23 | *.la 24 | *.lo 25 | *.def 26 | *.exp 27 | 28 | # Shared objects (inc. Windows DLLs) 29 | *.dll 30 | *.so 31 | *.so.* 32 | *.dylib 33 | 34 | # Executables 35 | *.exe 36 | *.out 37 | *.app 38 | *.i*86 39 | *.x86_64 40 | *.hex 41 | 42 | -------------------------------------------------------------------------------- /.luacheckrc: -------------------------------------------------------------------------------- 1 | self = false --Ignore unused self warnings 2 | 3 | ignore = { 4 | "212" -- Unused argument 5 | } 6 | 7 | globals = { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Benoit Giannangeli 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | fourmi 3 |

4 | 5 | 6 | # fourmi 7 | 🐜 A small taskrunner that harnesses the power of Lua's metamethods to easily express flow of tasks. 8 | 9 | **Note:** Fourmi is in active development. 10 | 11 | ## Installation 12 | 13 | Requirements: 14 | - Lua 5.3 15 | - luarocks >= 3.0 (_Note: `hererocks -rlatest` will install 2.4, you need to specify it with `-r3.0`_) 16 | 17 | ```bash 18 | luarocks install --server=http://luarocks.org/dev fourmi 19 | ``` 20 | 21 | ## Usage 22 | 23 | Write a [fourmi.plan.lua](#plan) file in your project directory and then: 24 | 25 | ```bash 26 | # If `plan` is not provided, will run plan named `all` 27 | fourmi [--file ] [-h] [] ... 28 | ``` 29 | 30 | ## Task 31 | 32 | Task are relatively small jobs that you can combine using operators. 33 | 34 | ```lua 35 | local mytask = task "mytask" 36 | :description "A short description of what it does" 37 | :file "output.o" 38 | :requires { "file1.x", "file2.x" } 39 | :property("propertykey", propertyvalue) 40 | :perform(function(self, ...) 41 | -- Do something with ... 42 | 43 | return output1, ..., ouputn 44 | end) 45 | ``` 46 | 47 | ### Operators 48 | 49 | - **`task1 .. task2`**: returns a new task that does `task1` then `task2` 50 | - **`task1 & task2`**: returns a new task that does `task2` has output, pipes it to `task2` 51 | - **`task1 | task2`**: returns a new task that does `task2` only if `task1` returns falsy value 52 | - **`task1 >> task2`**: returns a new task that pipes `task1` into `task2` 53 | - **`task1 * task2`**: returns a new task that does `task2` for all output of `task1` 54 | - **`task1 ^ (condition)`**: returns a new task that does `task1` if `condition` (expression or function to be evaluated) is true 55 | 56 | Here's an commented excerpt of [`fourmi.plan.lua`](https://github.com/giann/fourmi/blob/master/example-fourmi.plan.lua): 57 | 58 | ```lua 59 | -- Define variables 60 | -- Those can be overwritten with cli arguments of the form `key=value` 61 | var { 62 | min = "luamin", 63 | src = "./fourmi", 64 | dest = "~/tmp-code", 65 | } 66 | 67 | -- ... 68 | 69 | return { 70 | -- Default plan 71 | plan "all" 72 | -- Small description of what the plan does 73 | :description "Minify and gzip lua files" 74 | -- Define its task 75 | :task( 76 | -- List files in `./fourmi` ending with `.lua` 77 | ls("@{src}", "%.lua$") 78 | -- For each of them: if the gzipped file does not exist or is older than the original, 79 | -- minify then gzip then move to `~/tmp-code` 80 | * (outdated "@{dest}/#{original}.gz" 81 | & minify >> gzip >> mv "@{dest}") 82 | ), 83 | 84 | -- Clean plan 85 | plan "clean" 86 | :description "Cleanup" 87 | :task( 88 | -- Remove all files from `~/tmp-code` 89 | empty "@{dest}" 90 | ) 91 | } 92 | ``` 93 | 94 | If you don't want to use operators, you can use their aliases: 95 | - **`..`**: after 96 | - **`&`**: success 97 | - **`|`**: failure 98 | - **`>>`**: into 99 | - **`*`**: each 100 | - **`^`**: meet 101 | 102 | ### `__` function 103 | 104 | `__(string, [context])` interpolates special special sequences in a string: 105 | - **`~`**: will be replaced with `$HOME` 106 | - **`${ENV}`**: will be replaced by the `ENV` environment variable 107 | - **`#{variable}`**: fourmi will look for `variable` in `context` or `_G` if none provided 108 | - **`@{fourmi_variable}`**: will be replaced by the `fourmi_variable` defined with `var(name, value)` 109 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/giann/fourmi/c630a33fa05b3ef24a14a1b503ec439b5916374e/assets/logo.png -------------------------------------------------------------------------------- /assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /assets/result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/giann/fourmi/c630a33fa05b3ef24a14a1b503ec439b5916374e/assets/result.png -------------------------------------------------------------------------------- /bin/fourmi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local log = require "fourmi.log" 4 | local builtins = require "fourmi.builtins" 5 | local __ = builtins.__ 6 | local var = builtins.var 7 | local argparse = require "argparse" 8 | local lfs = require "lfs" 9 | 10 | print( 11 | "🐜 Fourmi 0.0.1 (C) 2019 Benoit Giannangeli" 12 | ) 13 | 14 | --- Empty ~/.fourmi/tmp 15 | local function cleanup() 16 | for file in lfs.dir(os.getenv "HOME" .. "/.fourmi/tmp") do 17 | if file ~= "." and file ~= ".." then 18 | os.remove(os.getenv "HOME" .. "/.fourmi/tmp/" .. file) 19 | end 20 | end 21 | end 22 | 23 | -- Make sure ~/.fourmi is there 24 | if not lfs.attributes(os.getenv "HOME" .. "/.fourmi") then 25 | lfs.mkdir(os.getenv "HOME" .. "/.fourmi") 26 | end 27 | 28 | -- Make sure ~/.fourmi/tmp is there 29 | if not lfs.attributes(os.getenv "HOME" .. "/.fourmi/tmp") then 30 | lfs.mkdir(os.getenv "HOME" .. "/.fourmi/tmp") 31 | end 32 | 33 | -- Cleanup in case previous fourmi failed to do so 34 | cleanup() 35 | 36 | -- Initialize global table to hold fourmi's information 37 | _G.__fourmi = { 38 | -- Variables 39 | vars = { 40 | tmp = os.getenv "HOME" .. "/.fourmi/tmp" 41 | }, 42 | 43 | -- Dependencies 44 | deps = { 45 | -- file = task 46 | } 47 | } 48 | 49 | -- Build parser 50 | local parser = argparse() 51 | :name "fourmi" 52 | :description "🐜 A small taskrunner written in Lua" 53 | 54 | -- Plan argument 55 | parser:argument "plan" 56 | :description "Plan to execute" 57 | :args "?" 58 | 59 | parser:argument "arguments" 60 | :description "Plan's arguments" 61 | :args "*" 62 | 63 | parser:option "--file -f" 64 | :description "Specify plan file (if it's not fourmi.plan.lua)" 65 | :args(1) 66 | 67 | -- Parse arguments 68 | local arguments = parser:parse() 69 | 70 | local function checkinvalid(condition, message) 71 | if condition then 72 | log.err(message) 73 | 74 | os.exit(1) 75 | end 76 | end 77 | 78 | -- Search for fourmi.plan.lua in current directory 79 | local fourmi_filename = arguments.file or "fourmi.plan.lua" 80 | local fourmi_lua = lfs.attributes(fourmi_filename) 81 | 82 | checkinvalid( 83 | not fourmi_lua or fourmi_lua.mode ~= "file", 84 | "Could not find `fourmi.plan.lua` in current directory." 85 | ) 86 | 87 | -- Load it 88 | local fourmi_fn, err = loadfile(fourmi_filename) 89 | 90 | checkinvalid(not fourmi_fn, "Could not find parse `" .. fourmi_filename .. "`: " .. tostring(err)) 91 | 92 | -- Run it 93 | local ok, plans = xpcall(fourmi_fn, debug.traceback) 94 | 95 | checkinvalid(not ok, "An error occured while running `" .. fourmi_filename .. "`:\n" .. tostring(plan)) 96 | 97 | if type(plans) ~= "table" then 98 | checkinvalid("`fourmi.plan.lua` should return a table") 99 | end 100 | 101 | -- Run a plan 102 | local function runPlan(plan) 103 | xpcall(plan, function(err) 104 | log.err( 105 | "Plan `" .. plan.__name .. "` failed with error:\n" .. err 106 | ) 107 | end, arguments) 108 | end 109 | 110 | -- Run first target that matches a key in plan 111 | local plan 112 | local defaultPlan 113 | 114 | for _, target in ipairs(plans) do 115 | if target.__name == "all" then 116 | defaultPlan = target 117 | end 118 | 119 | if arguments.plan == target.__name then 120 | plan = target 121 | break 122 | end 123 | end 124 | 125 | checkinvalid(not plan and not defaultPlan, "No matching plan") 126 | 127 | -- If no plan provided, arguments.plan should be part of arguments.arguments 128 | if not plan then 129 | table.insert(arguments.arguments, 1, arguments.plan) 130 | end 131 | 132 | -- Overwrite vars with cli arguments 133 | for _, arg in ipairs(arguments.arguments) do 134 | local key, value = arg:match "^([^=]*)=(.*)" 135 | 136 | log.warn(__"Overwritten variable `#{key}` with `#{value}`", { 137 | key = key, 138 | value = value 139 | }) 140 | 141 | var(key, value) 142 | end 143 | 144 | runPlan(plan or defaultPlan) 145 | 146 | cleanup() 147 | -------------------------------------------------------------------------------- /doc/classes/fourmi.plan.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Reference 7 | 8 | 9 | 10 | 11 |
12 | 13 |
14 | 15 |
16 |
17 |
18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 52 | 53 |
54 | 55 |

Class fourmi.plan

56 |

Plan module

57 |

58 |

Info:

59 |
    60 |
  • Copyright: Benoit Giannangeli 2019
  • 61 |
  • License: MIT
  • 62 |
  • Author: Benoit Giannangeli
  • 63 |
64 | 65 | 66 |

Metamethods

67 | 68 | 69 | 70 | 71 | 72 |
fourmi.plan:__call (self, arguments)Run the plan
73 |

Methods

74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 |
fourmi.plan:task (self, task)Set plan's task
fourmi.plan:description (self, description)Set plan's description
fourmi.plan:name (self, name)Set plan's name
88 | 89 |
90 |
91 | 92 | 93 |

Metamethods

94 | 95 |
96 |
97 | 98 | fourmi.plan:__call (self, arguments) 99 |
100 |
101 | Run the plan 102 | 103 | 104 |

Parameters:

105 |
    106 |
  • self 107 | plan 108 | 109 |
  • 110 |
  • arguments 111 | table 112 | List of arguments 113 |
  • 114 |
115 | 116 | 117 | 118 | 119 | 120 |
121 |
122 |

Methods

123 | 124 |
125 |
126 | 127 | fourmi.plan:task (self, task) 128 |
129 |
130 | Set plan's task 131 | 132 | 133 |

Parameters:

134 |
    135 |
  • self 136 | plan 137 | 138 |
  • 139 |
  • task 140 | task 141 | 142 |
  • 143 |
144 | 145 | 146 | 147 | 148 | 149 |
150 |
151 | 152 | fourmi.plan:description (self, description) 153 |
154 |
155 | Set plan's description 156 | 157 | 158 |

Parameters:

159 |
    160 |
  • self 161 | plan 162 | 163 |
  • 164 |
  • description 165 | string 166 | 167 |
  • 168 |
169 | 170 | 171 | 172 | 173 | 174 |
175 |
176 | 177 | fourmi.plan:name (self, name) 178 |
179 |
180 | Set plan's name 181 | 182 | 183 |

Parameters:

184 |
    185 |
  • self 186 | plan 187 | 188 |
  • 189 |
  • name 190 | string 191 | 192 |
  • 193 |
194 | 195 | 196 | 197 | 198 | 199 |
200 |
201 | 202 | 203 |
204 |
205 |
206 | generated by LDoc 1.4.6 207 | Last updated 2019-03-11 08:32:32 208 |
209 |
210 | 211 | 212 | -------------------------------------------------------------------------------- /doc/classes/fourmi.task.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Reference 7 | 8 | 9 | 10 | 11 |
12 | 13 |
14 | 15 |
16 |
17 |
18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 52 | 53 |
54 | 55 |

Class fourmi.task

56 |

Task module

57 |

58 |

Info:

59 |
    60 |
  • Copyright: Benoit Giannangeli 2019
  • 61 |
  • License: MIT
  • 62 |
  • Author: Benoit Giannangeli
  • 63 |
64 | 65 | 66 |

Metamethods

67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 |
fourmi.task:__div (task1, task2)Runs task1 in parallel of task2
fourmi.task:__concat (task1, task2)Runs task1 then task2
fourmi.task:__band (task1, task2)Runs task2 with task1 ouput if any and first output is truthy value
fourmi.task:__bor (task1, task2)Runs task2 only if task1 returns falsy value
fourmi.task:__shr (task1, task2)Pipes task1's output to task2 input
fourmi.task:__shl (task2, task1)Pipes task2's output to task1 input
fourmi.task:__mul (task1, task2)Runs task2 for each output of task1, task2 is silenced
fourmi.task:__pow (task1, condition)Runs task1 if a condition is met
fourmi.task:__call (self, ...)Set options
105 |

Methods

106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 |
fourmi.task:description (self, description)Set task's description
fourmi.task:name (self, name)Set task's name
fourmi.task:perform (self, fn)Set task's action
fourmi.task:property (self, name, value)Set a property
124 | 125 |
126 |
127 | 128 | 129 |

Metamethods

130 | 131 |
132 |
133 | 134 | fourmi.task:__div (task1, task2) 135 |
136 |
137 | Runs task1 in parallel of task2 138 | 139 | 140 |

Parameters:

141 |
    142 |
  • task1 143 | task 144 | 145 |
  • 146 |
  • task2 147 | task 148 | 149 |
  • 150 |
151 | 152 |

Returns:

153 |
    154 | 155 | task 156 | 157 |
158 | 159 | 160 | 161 | 162 |
163 |
164 | 165 | fourmi.task:__concat (task1, task2) 166 |
167 |
168 | Runs task1 then task2 169 | 170 | 171 |

Parameters:

172 |
    173 |
  • task1 174 | task 175 | 176 |
  • 177 |
  • task2 178 | task 179 | 180 |
  • 181 |
182 | 183 |

Returns:

184 |
    185 | 186 | task 187 | 188 |
189 | 190 | 191 | 192 | 193 |
194 |
195 | 196 | fourmi.task:__band (task1, task2) 197 |
198 |
199 | Runs task2 with task1 ouput if any and first output is truthy value 200 | 201 | 202 |

Parameters:

203 |
    204 |
  • task1 205 | task 206 | 207 |
  • 208 |
  • task2 209 | task 210 | 211 |
  • 212 |
213 | 214 |

Returns:

215 |
    216 | 217 | task 218 | 219 |
220 | 221 | 222 | 223 | 224 |
225 |
226 | 227 | fourmi.task:__bor (task1, task2) 228 |
229 |
230 | Runs task2 only if task1 returns falsy value 231 | 232 | 233 |

Parameters:

234 |
    235 |
  • task1 236 | task 237 | 238 |
  • 239 |
  • task2 240 | task 241 | 242 |
  • 243 |
244 | 245 |

Returns:

246 |
    247 | 248 | task 249 | 250 |
251 | 252 | 253 | 254 | 255 |
256 |
257 | 258 | fourmi.task:__shr (task1, task2) 259 |
260 |
261 | Pipes task1's output to task2 input 262 | 263 | 264 |

Parameters:

265 |
    266 |
  • task1 267 | task 268 | 269 |
  • 270 |
  • task2 271 | task 272 | 273 |
  • 274 |
275 | 276 |

Returns:

277 |
    278 | 279 | task 280 | 281 |
282 | 283 | 284 | 285 | 286 |
287 |
288 | 289 | fourmi.task:__shl (task2, task1) 290 |
291 |
292 | Pipes task2's output to task1 input 293 | 294 | 295 |

Parameters:

296 |
    297 |
  • task2 298 | task 299 | 300 |
  • 301 |
  • task1 302 | task 303 | 304 |
  • 305 |
306 | 307 |

Returns:

308 |
    309 | 310 | task 311 | 312 |
313 | 314 | 315 | 316 | 317 |
318 |
319 | 320 | fourmi.task:__mul (task1, task2) 321 |
322 |
323 | Runs task2 for each output of task1, task2 is silenced 324 | 325 | 326 |

Parameters:

327 |
    328 |
  • task1 329 | task 330 | 331 |
  • 332 |
  • task2 333 | task 334 | 335 |
  • 336 |
337 | 338 |

Returns:

339 |
    340 | 341 | task 342 | 343 |
344 | 345 | 346 | 347 | 348 |
349 |
350 | 351 | fourmi.task:__pow (task1, condition) 352 |
353 |
354 | Runs task1 if a condition is met 355 | 356 | 357 |

Parameters:

358 |
    359 |
  • task1 360 | task 361 | 362 |
  • 363 |
  • condition 364 | boolean or function 365 | If a function, will be run when resulting task is invoked 366 |
  • 367 |
368 | 369 |

Returns:

370 |
    371 | 372 | task 373 | 374 |
375 | 376 | 377 | 378 | 379 |
380 |
381 | 382 | fourmi.task:__call (self, ...) 383 |
384 |
385 | Set options 386 | 387 | 388 |

Parameters:

389 |
    390 |
  • self 391 | task 392 | 393 |
  • 394 |
  • ... 395 | Task input 396 |
  • 397 |
398 | 399 | 400 | 401 | 402 | 403 |
404 |
405 |

Methods

406 | 407 |
408 |
409 | 410 | fourmi.task:description (self, description) 411 |
412 |
413 | Set task's description 414 | 415 | 416 |

Parameters:

417 |
    418 |
  • self 419 | task 420 | 421 |
  • 422 |
  • description 423 | string 424 | 425 |
  • 426 |
427 | 428 | 429 | 430 | 431 | 432 |
433 |
434 | 435 | fourmi.task:name (self, name) 436 |
437 |
438 | Set task's name 439 | 440 | 441 |

Parameters:

442 |
    443 |
  • self 444 | task 445 | 446 |
  • 447 |
  • name 448 | string 449 | 450 |
  • 451 |
452 | 453 | 454 | 455 | 456 | 457 |
458 |
459 | 460 | fourmi.task:perform (self, fn) 461 |
462 |
463 | Set task's action 464 | 465 | 466 |

Parameters:

467 |
    468 |
  • self 469 | task 470 | 471 |
  • 472 |
  • fn 473 | string 474 | function 475 |
  • 476 |
477 | 478 | 479 | 480 | 481 | 482 |
483 |
484 | 485 | fourmi.task:property (self, name, value) 486 |
487 |
488 | Set a property 489 | 490 | 491 |

Parameters:

492 |
    493 |
  • self 494 | task 495 | 496 |
  • 497 |
  • name 498 | string 499 | Option's name 500 |
  • 501 |
  • value 502 | Option's value 503 |
  • 504 |
505 | 506 | 507 | 508 | 509 | 510 |
511 |
512 | 513 | 514 |
515 |
516 |
517 | generated by LDoc 1.4.6 518 | Last updated 2019-03-11 08:32:32 519 |
520 |
521 | 522 | 523 | -------------------------------------------------------------------------------- /doc/classes/plan.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Reference 7 | 8 | 9 | 10 | 11 |
12 | 13 |
14 | 15 |
16 |
17 |
18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 52 | 53 |
54 | 55 |

Class plan

56 |

Plan module

57 |

58 |

Info:

59 |
    60 |
  • Copyright: Benoit Giannangeli 2019
  • 61 |
  • License: MIT
  • 62 |
  • Author: Benoit Giannangeli
  • 63 |
64 | 65 | 66 |

Metamethods

67 | 68 | 69 | 70 | 71 | 72 |
plan:__call (self, arguments)Run the plan
73 |

Methods

74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 |
plan:task (self, task)Set plan's task
plan:description (self, description)Set plan's description
plan:name (self, name)Set plan's name
88 | 89 |
90 |
91 | 92 | 93 |

Metamethods

94 | 95 |
96 |
97 | 98 | plan:__call (self, arguments) 99 |
100 |
101 | Run the plan 102 | 103 | 104 |

Parameters:

105 |
    106 |
  • self 107 | plan 108 | 109 |
  • 110 |
  • arguments 111 | table 112 | List of arguments 113 |
  • 114 |
115 | 116 | 117 | 118 | 119 | 120 |
121 |
122 |

Methods

123 | 124 |
125 |
126 | 127 | plan:task (self, task) 128 |
129 |
130 | Set plan's task 131 | 132 | 133 |

Parameters:

134 |
    135 |
  • self 136 | plan 137 | 138 |
  • 139 |
  • task 140 | task 141 | 142 |
  • 143 |
144 | 145 | 146 | 147 | 148 | 149 |
150 |
151 | 152 | plan:description (self, description) 153 |
154 |
155 | Set plan's description 156 | 157 | 158 |

Parameters:

159 |
    160 |
  • self 161 | plan 162 | 163 |
  • 164 |
  • description 165 | string 166 | 167 |
  • 168 |
169 | 170 | 171 | 172 | 173 | 174 |
175 |
176 | 177 | plan:name (self, name) 178 |
179 |
180 | Set plan's name 181 | 182 | 183 |

Parameters:

184 |
    185 |
  • self 186 | plan 187 | 188 |
  • 189 |
  • name 190 | string 191 | 192 |
  • 193 |
194 | 195 | 196 | 197 | 198 | 199 |
200 |
201 | 202 | 203 |
204 |
205 |
206 | generated by LDoc 1.4.6 207 | Last updated 2019-03-08 11:17:34 208 |
209 |
210 | 211 | 212 | -------------------------------------------------------------------------------- /doc/classes/task.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Reference 7 | 8 | 9 | 10 | 11 |
12 | 13 |
14 | 15 |
16 |
17 |
18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 52 | 53 |
54 | 55 |

Class task

56 |

Task module

57 |

58 |

Info:

59 |
    60 |
  • Copyright: Benoit Giannangeli 2019
  • 61 |
  • License: MIT
  • 62 |
  • Author: Benoit Giannangeli
  • 63 |
64 | 65 | 66 |

Metamethods

67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 |
task:__div (task1, task2)Runs task1 in parallel of task2
task:__concat (task1, task2)Runs task1 then task2
task:__band (task1, task2)Runs task2 if task1 returns truthy value
task:__bor (task1, task2)Runs task2 only if task1 returns falsy value
task:__shr (task1, task2)Pipes task1's output to task2 input
task:__shl (task2, task1)Pipes task2's output to task1 input
task:__bxor (task1, task2)Runs task2 with task1 output if any, otherwise doesn't run task2
task:__mul (task1, task2)Runs task2 for each output of task1
task:__pow (task1, condition)Runs task1 if a condition is met
task:__call (self, ...)Run the task
109 |

Methods

110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 |
task:description (self, description)Set task's description
task:name (self, name)Set task's name
task:perform (self, fn)Set task's action
task:option (self, name, value)Set a task's option
128 | 129 |
130 |
131 | 132 | 133 |

Metamethods

134 | 135 |
136 |
137 | 138 | task:__div (task1, task2) 139 |
140 |
141 | Runs task1 in parallel of task2 142 | 143 | 144 |

Parameters:

145 |
    146 |
  • task1 147 | task 148 | 149 |
  • 150 |
  • task2 151 | task 152 | 153 |
  • 154 |
155 | 156 |

Returns:

157 |
    158 | 159 | task 160 | 161 |
162 | 163 | 164 | 165 | 166 |
167 |
168 | 169 | task:__concat (task1, task2) 170 |
171 |
172 | Runs task1 then task2 173 | 174 | 175 |

Parameters:

176 |
    177 |
  • task1 178 | task 179 | 180 |
  • 181 |
  • task2 182 | task 183 | 184 |
  • 185 |
186 | 187 |

Returns:

188 |
    189 | 190 | task 191 | 192 |
193 | 194 | 195 | 196 | 197 |
198 |
199 | 200 | task:__band (task1, task2) 201 |
202 |
203 | Runs task2 if task1 returns truthy value 204 | 205 | 206 |

Parameters:

207 |
    208 |
  • task1 209 | task 210 | 211 |
  • 212 |
  • task2 213 | task 214 | 215 |
  • 216 |
217 | 218 |

Returns:

219 |
    220 | 221 | task 222 | 223 |
224 | 225 | 226 | 227 | 228 |
229 |
230 | 231 | task:__bor (task1, task2) 232 |
233 |
234 | Runs task2 only if task1 returns falsy value 235 | 236 | 237 |

Parameters:

238 |
    239 |
  • task1 240 | task 241 | 242 |
  • 243 |
  • task2 244 | task 245 | 246 |
  • 247 |
248 | 249 |

Returns:

250 |
    251 | 252 | task 253 | 254 |
255 | 256 | 257 | 258 | 259 |
260 |
261 | 262 | task:__shr (task1, task2) 263 |
264 |
265 | Pipes task1's output to task2 input 266 | 267 | 268 |

Parameters:

269 |
    270 |
  • task1 271 | task 272 | 273 |
  • 274 |
  • task2 275 | task 276 | 277 |
  • 278 |
279 | 280 |

Returns:

281 |
    282 | 283 | task 284 | 285 |
286 | 287 | 288 | 289 | 290 |
291 |
292 | 293 | task:__shl (task2, task1) 294 |
295 |
296 | Pipes task2's output to task1 input 297 | 298 | 299 |

Parameters:

300 |
    301 |
  • task2 302 | task 303 | 304 |
  • 305 |
  • task1 306 | task 307 | 308 |
  • 309 |
310 | 311 |

Returns:

312 |
    313 | 314 | task 315 | 316 |
317 | 318 | 319 | 320 | 321 |
322 |
323 | 324 | task:__bxor (task1, task2) 325 |
326 |
327 | Runs task2 with task1 output if any, otherwise doesn't run task2 328 | 329 | 330 |

Parameters:

331 |
    332 |
  • task1 333 | task 334 | 335 |
  • 336 |
  • task2 337 | task 338 | 339 |
  • 340 |
341 | 342 |

Returns:

343 |
    344 | 345 | task 346 | 347 |
348 | 349 | 350 | 351 | 352 |
353 |
354 | 355 | task:__mul (task1, task2) 356 |
357 |
358 | Runs task2 for each output of task1 359 | 360 | 361 |

Parameters:

362 |
    363 |
  • task1 364 | task 365 | 366 |
  • 367 |
  • task2 368 | task 369 | 370 |
  • 371 |
372 | 373 |

Returns:

374 |
    375 | 376 | task 377 | 378 |
379 | 380 | 381 | 382 | 383 |
384 |
385 | 386 | task:__pow (task1, condition) 387 |
388 |
389 | Runs task1 if a condition is met 390 | 391 | 392 |

Parameters:

393 |
    394 |
  • task1 395 | task 396 | 397 |
  • 398 |
  • condition 399 | boolean or function 400 | If a function, will be run when resulting task is invoked 401 |
  • 402 |
403 | 404 |

Returns:

405 |
    406 | 407 | task 408 | 409 |
410 | 411 | 412 | 413 | 414 |
415 |
416 | 417 | task:__call (self, ...) 418 |
419 |
420 | Run the task 421 | 422 | 423 |

Parameters:

424 |
    425 |
  • self 426 | task 427 | 428 |
  • 429 |
  • ... 430 | Task input 431 |
  • 432 |
433 | 434 | 435 | 436 | 437 | 438 |
439 |
440 |

Methods

441 | 442 |
443 |
444 | 445 | task:description (self, description) 446 |
447 |
448 | Set task's description 449 | 450 | 451 |

Parameters:

452 |
    453 |
  • self 454 | task 455 | 456 |
  • 457 |
  • description 458 | string 459 | 460 |
  • 461 |
462 | 463 | 464 | 465 | 466 | 467 |
468 |
469 | 470 | task:name (self, name) 471 |
472 |
473 | Set task's name 474 | 475 | 476 |

Parameters:

477 |
    478 |
  • self 479 | task 480 | 481 |
  • 482 |
  • name 483 | string 484 | 485 |
  • 486 |
487 | 488 | 489 | 490 | 491 | 492 |
493 |
494 | 495 | task:perform (self, fn) 496 |
497 |
498 | Set task's action 499 | 500 | 501 |

Parameters:

502 |
    503 |
  • self 504 | task 505 | 506 |
  • 507 |
  • fn 508 | string 509 | function 510 |
  • 511 |
512 | 513 | 514 | 515 | 516 | 517 |
518 |
519 | 520 | task:option (self, name, value) 521 |
522 |
523 | Set a task's option 524 | 525 | 526 |

Parameters:

527 |
    528 |
  • self 529 | task 530 | 531 |
  • 532 |
  • name 533 | string 534 | Option's name 535 |
  • 536 |
  • value 537 | Option's value 538 |
  • 539 |
540 | 541 | 542 | 543 | 544 | 545 |
546 |
547 | 548 | 549 |
550 |
551 |
552 | generated by LDoc 1.4.6 553 | Last updated 2019-03-08 11:17:34 554 |
555 |
556 | 557 | 558 | -------------------------------------------------------------------------------- /doc/index.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Reference 7 | 8 | 9 | 10 | 11 |
12 | 13 |
14 | 15 |
16 |
17 |
18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 44 | 45 |
46 | 47 | 48 | 49 |

Modules

50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 |
fourmi.builtinsCommon helpers and basic tasks
fourmiMain fourmi module
60 |

Classes

61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 |
fourmi.planPlan module
fourmi.taskTask module
71 | 72 |
73 |
74 |
75 | generated by LDoc 1.4.6 76 | Last updated 2019-03-11 08:32:32 77 |
78 |
79 | 80 | 81 | -------------------------------------------------------------------------------- /doc/ldoc.css: -------------------------------------------------------------------------------- 1 | /* BEGIN RESET 2 | 3 | Copyright (c) 2010, Yahoo! Inc. All rights reserved. 4 | Code licensed under the BSD License: 5 | http://developer.yahoo.com/yui/license.html 6 | version: 2.8.2r1 7 | */ 8 | html { 9 | color: #000; 10 | background: #FFF; 11 | } 12 | body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td { 13 | margin: 0; 14 | padding: 0; 15 | } 16 | table { 17 | border-collapse: collapse; 18 | border-spacing: 0; 19 | } 20 | fieldset,img { 21 | border: 0; 22 | } 23 | address,caption,cite,code,dfn,em,strong,th,var,optgroup { 24 | font-style: inherit; 25 | font-weight: inherit; 26 | } 27 | del,ins { 28 | text-decoration: none; 29 | } 30 | li { 31 | margin-left: 20px; 32 | } 33 | caption,th { 34 | text-align: left; 35 | } 36 | h1,h2,h3,h4,h5,h6 { 37 | font-size: 100%; 38 | font-weight: bold; 39 | } 40 | q:before,q:after { 41 | content: ''; 42 | } 43 | abbr,acronym { 44 | border: 0; 45 | font-variant: normal; 46 | } 47 | sup { 48 | vertical-align: baseline; 49 | } 50 | sub { 51 | vertical-align: baseline; 52 | } 53 | legend { 54 | color: #000; 55 | } 56 | input,button,textarea,select,optgroup,option { 57 | font-family: inherit; 58 | font-size: inherit; 59 | font-style: inherit; 60 | font-weight: inherit; 61 | } 62 | input,button,textarea,select {*font-size:100%; 63 | } 64 | /* END RESET */ 65 | 66 | body { 67 | margin-left: 1em; 68 | margin-right: 1em; 69 | font-family: arial, helvetica, geneva, sans-serif; 70 | background-color: #ffffff; margin: 0px; 71 | } 72 | 73 | code, tt { font-family: monospace; font-size: 1.1em; } 74 | span.parameter { font-family:monospace; } 75 | span.parameter:after { content:":"; } 76 | span.types:before { content:"("; } 77 | span.types:after { content:")"; } 78 | .type { font-weight: bold; font-style:italic } 79 | 80 | body, p, td, th { font-size: .95em; line-height: 1.2em;} 81 | 82 | p, ul { margin: 10px 0 0 0px;} 83 | 84 | strong { font-weight: bold;} 85 | 86 | em { font-style: italic;} 87 | 88 | h1 { 89 | font-size: 1.5em; 90 | margin: 20px 0 20px 0; 91 | } 92 | h2, h3, h4 { margin: 15px 0 10px 0; } 93 | h2 { font-size: 1.25em; } 94 | h3 { font-size: 1.15em; } 95 | h4 { font-size: 1.06em; } 96 | 97 | a:link { font-weight: bold; color: #004080; text-decoration: none; } 98 | a:visited { font-weight: bold; color: #006699; text-decoration: none; } 99 | a:link:hover { text-decoration: underline; } 100 | 101 | hr { 102 | color:#cccccc; 103 | background: #00007f; 104 | height: 1px; 105 | } 106 | 107 | blockquote { margin-left: 3em; } 108 | 109 | ul { list-style-type: disc; } 110 | 111 | p.name { 112 | font-family: "Andale Mono", monospace; 113 | padding-top: 1em; 114 | } 115 | 116 | pre { 117 | background-color: rgb(245, 245, 245); 118 | border: 1px solid #C0C0C0; /* silver */ 119 | padding: 10px; 120 | margin: 10px 0 10px 0; 121 | overflow: auto; 122 | font-family: "Andale Mono", monospace; 123 | } 124 | 125 | pre.example { 126 | font-size: .85em; 127 | } 128 | 129 | table.index { border: 1px #00007f; } 130 | table.index td { text-align: left; vertical-align: top; } 131 | 132 | #container { 133 | margin-left: 1em; 134 | margin-right: 1em; 135 | background-color: #f0f0f0; 136 | } 137 | 138 | #product { 139 | text-align: center; 140 | border-bottom: 1px solid #cccccc; 141 | background-color: #ffffff; 142 | } 143 | 144 | #product big { 145 | font-size: 2em; 146 | } 147 | 148 | #main { 149 | background-color: #f0f0f0; 150 | border-left: 2px solid #cccccc; 151 | } 152 | 153 | #navigation { 154 | float: left; 155 | width: 14em; 156 | vertical-align: top; 157 | background-color: #f0f0f0; 158 | overflow: visible; 159 | } 160 | 161 | #navigation h2 { 162 | background-color:#e7e7e7; 163 | font-size:1.1em; 164 | color:#000000; 165 | text-align: left; 166 | padding:0.2em; 167 | border-top:1px solid #dddddd; 168 | border-bottom:1px solid #dddddd; 169 | } 170 | 171 | #navigation ul 172 | { 173 | font-size:1em; 174 | list-style-type: none; 175 | margin: 1px 1px 10px 1px; 176 | } 177 | 178 | #navigation li { 179 | text-indent: -1em; 180 | display: block; 181 | margin: 3px 0px 0px 22px; 182 | } 183 | 184 | #navigation li li a { 185 | margin: 0px 3px 0px -1em; 186 | } 187 | 188 | #content { 189 | margin-left: 14em; 190 | padding: 1em; 191 | width: 700px; 192 | border-left: 2px solid #cccccc; 193 | border-right: 2px solid #cccccc; 194 | background-color: #ffffff; 195 | } 196 | 197 | #about { 198 | clear: both; 199 | padding: 5px; 200 | border-top: 2px solid #cccccc; 201 | background-color: #ffffff; 202 | } 203 | 204 | @media print { 205 | body { 206 | font: 12pt "Times New Roman", "TimeNR", Times, serif; 207 | } 208 | a { font-weight: bold; color: #004080; text-decoration: underline; } 209 | 210 | #main { 211 | background-color: #ffffff; 212 | border-left: 0px; 213 | } 214 | 215 | #container { 216 | margin-left: 2%; 217 | margin-right: 2%; 218 | background-color: #ffffff; 219 | } 220 | 221 | #content { 222 | padding: 1em; 223 | background-color: #ffffff; 224 | } 225 | 226 | #navigation { 227 | display: none; 228 | } 229 | pre.example { 230 | font-family: "Andale Mono", monospace; 231 | font-size: 10pt; 232 | page-break-inside: avoid; 233 | } 234 | } 235 | 236 | table.module_list { 237 | border-width: 1px; 238 | border-style: solid; 239 | border-color: #cccccc; 240 | border-collapse: collapse; 241 | } 242 | table.module_list td { 243 | border-width: 1px; 244 | padding: 3px; 245 | border-style: solid; 246 | border-color: #cccccc; 247 | } 248 | table.module_list td.name { background-color: #f0f0f0; min-width: 200px; } 249 | table.module_list td.summary { width: 100%; } 250 | 251 | 252 | table.function_list { 253 | border-width: 1px; 254 | border-style: solid; 255 | border-color: #cccccc; 256 | border-collapse: collapse; 257 | } 258 | table.function_list td { 259 | border-width: 1px; 260 | padding: 3px; 261 | border-style: solid; 262 | border-color: #cccccc; 263 | } 264 | table.function_list td.name { background-color: #f0f0f0; min-width: 200px; } 265 | table.function_list td.summary { width: 100%; } 266 | 267 | ul.nowrap { 268 | overflow:auto; 269 | white-space:nowrap; 270 | } 271 | 272 | dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;} 273 | dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;} 274 | dl.table h3, dl.function h3 {font-size: .95em;} 275 | 276 | /* stop sublists from having initial vertical space */ 277 | ul ul { margin-top: 0px; } 278 | ol ul { margin-top: 0px; } 279 | ol ol { margin-top: 0px; } 280 | ul ol { margin-top: 0px; } 281 | 282 | /* make the target distinct; helps when we're navigating to a function */ 283 | a:target + * { 284 | background-color: #FF9; 285 | } 286 | 287 | 288 | /* styles for prettification of source */ 289 | pre .comment { color: #558817; } 290 | pre .constant { color: #a8660d; } 291 | pre .escape { color: #844631; } 292 | pre .keyword { color: #aa5050; font-weight: bold; } 293 | pre .library { color: #0e7c6b; } 294 | pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; } 295 | pre .string { color: #8080ff; } 296 | pre .number { color: #f8660d; } 297 | pre .operator { color: #2239a8; font-weight: bold; } 298 | pre .preprocessor, pre .prepro { color: #a33243; } 299 | pre .global { color: #800080; } 300 | pre .user-keyword { color: #800080; } 301 | pre .prompt { color: #558817; } 302 | pre .url { color: #272fc2; text-decoration: underline; } 303 | 304 | -------------------------------------------------------------------------------- /doc/modules/fourmi.builtins.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Reference 7 | 8 | 9 | 10 | 11 |
12 | 13 |
14 | 15 |
16 |
17 |
18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 53 | 54 |
55 | 56 |

Module fourmi.builtins

57 |

Common helpers and basic tasks

58 |

59 |

Info:

60 |
    61 |
  • Copyright: Benoit Giannangeli 2019
  • 62 |
  • License: MIT
  • 63 |
  • Author: Benoit Giannangeli
  • 64 |
65 | 66 | 67 |

Functions

68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 |
sh (program, ...)Runs a command captures stderr and returns it as error message
outdated (original, target)Returns true if a file is outdated
__ (str[, context])String interpolation helper 80 | - `${VARIABLE}` -> `os.getenv "VARIABLE"` 81 | - `#{variable}` -> `variable` in `context` or caller locals or `_G` 82 | - `~` -> `os.getenv "HOME"` 83 | - `???` -> `_G.__fourmi_vars[variable]`
var (key, value)Set a fourmi variable
builtins.task.sh (command, ...)Create a task that runs a single shell command
94 |

Tables

95 | 96 | 97 | 98 | 99 | 100 |
taskBuiltin tasks
101 |

Fields

102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 |
builtins.task.lsA task that list files
builtins.task.emptyEmpty files of a directory
builtins.task.outdatedFilter out up-to-date files
116 | 117 |
118 |
119 | 120 | 121 |

Functions

122 | 123 |
124 |
125 | 126 | sh (program, ...) 127 |
128 |
129 | Runs a command captures stderr and returns it as error message 130 | 131 | 132 |

Parameters:

133 |
    134 |
  • program 135 | string 136 | command to run 137 |
  • 138 |
  • ... 139 | string 140 | program arguments If an arguments contains spaces it'll be quoted 141 |
  • 142 |
143 | 144 |

Returns:

145 |
    146 | 147 | boolean 148 | true if command succeded 149 |
150 |

Or

151 |
    152 | 153 | string 154 | message in case of failure 155 |
156 | 157 | 158 | 159 | 160 |
161 |
162 | 163 | outdated (original, target) 164 |
165 |
166 | Returns true if a file is outdated 167 | 168 | 169 |

Parameters:

170 |
    171 |
  • original 172 | string 173 | If alone checks that it exists 174 |
  • 175 |
  • target 176 | string 177 | Outdated if not present or older than original 178 |
  • 179 |
180 | 181 | 182 | 183 | 184 | 185 |
186 |
187 | 188 | __ (str[, context]) 189 |
190 |
191 | String interpolation helper 192 | - `${VARIABLE}` -> `os.getenv "VARIABLE"` 193 | - `#{variable}` -> `variable` in `context` or caller locals or `_G` 194 | - `~` -> `os.getenv "HOME"` 195 | - `???` -> `_G.__fourmi_vars[variable]` 196 | 197 | 198 |

Parameters:

199 |
    200 |
  • str 201 | string 202 | String to interpolate 203 |
  • 204 |
  • context 205 | table 206 | Table in which to search variables to interpolates 207 | (optional) 208 |
  • 209 |
210 | 211 |

Returns:

212 |
    213 | 214 | string 215 | 216 |
217 | 218 | 219 | 220 | 221 |
222 |
223 | 224 | var (key, value) 225 |
226 |
227 | Set a fourmi variable 228 | 229 | 230 |

Parameters:

231 |
    232 |
  • key 233 | string or table 234 | or table of (key, value) 235 |
  • 236 |
  • value 237 | string, number or boolean 238 | 239 |
  • 240 |
241 | 242 | 243 | 244 | 245 | 246 |
247 |
248 | 249 | builtins.task.sh (command, ...) 250 |
251 |
252 | Create a task that runs a single shell command 253 | 254 | 255 |

Parameters:

256 |
    257 |
  • command 258 | string 259 | 260 |
  • 261 |
  • ... 262 | string 263 | 264 |
  • 265 |
266 | 267 |

Returns:

268 |
    269 | 270 | task 271 | 272 |
273 | 274 | 275 | 276 | 277 |
278 |
279 |

Tables

280 | 281 |
282 |
283 | 284 | task 285 |
286 |
287 | Builtin tasks 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 |
296 |
297 |

Fields

298 | 299 |
300 |
301 | 302 | builtins.task.ls 303 |
304 |
305 | A task that list files 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 |
314 |
315 | 316 | builtins.task.empty 317 |
318 |
319 | Empty files of a directory 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 |
328 |
329 | 330 | builtins.task.outdated 331 |
332 |
333 | Filter out up-to-date files 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 |
342 |
343 | 344 | 345 |
346 |
347 |
348 | generated by LDoc 1.4.6 349 | Last updated 2019-03-11 08:32:32 350 |
351 |
352 | 353 | 354 | -------------------------------------------------------------------------------- /doc/modules/fourmi.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Reference 7 | 8 | 9 | 10 | 11 |
12 | 13 |
14 | 15 |
16 |
17 |
18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 47 | 48 |
49 | 50 |

Module fourmi

51 |

Main fourmi module

52 |

53 |

Info:

54 |
    55 |
  • Copyright: Benoit Giannangeli 2019
  • 56 |
  • License: MIT
  • 57 |
  • Author: Benoit Giannangeli
  • 58 |
59 | 60 | 61 | 62 |
63 |
64 | 65 | 66 | 67 | 68 |
69 |
70 |
71 | generated by LDoc 1.4.6 72 | Last updated 2019-03-11 08:32:32 73 |
74 |
75 | 76 | 77 | -------------------------------------------------------------------------------- /doc/modules/task.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Reference 7 | 8 | 9 | 10 | 11 |
12 | 13 |
14 | 15 |
16 |
17 |
18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 46 | 47 |
48 | 49 |

Module task

50 |

Task module

51 |

52 |

Info:

53 |
    54 |
  • Copyright: Benoit Giannangeli 2019
  • 55 |
  • License: MIT
  • 56 |
  • Author: Benoit Giannangeli
  • 57 |
58 | 59 | 60 |

Functions

61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 |
__div (task1, task2)t1 / t2: t1 in parallel of t2
__concat (task1, task2)t1 ..
__band (task1, task2)t1 & t2: t2 if t1 returns truthy value
__bor (task1, task2)t1 | t2: t2 only if t1 returns falsy value
__shr (task1, task2)t1 >> t2: t1 output to t2 input
__shl (task2, task1)t2 << t1: same
__bxor (task1, task2)t1 ~ t2: if t1 has output, give it to t2
__mul (task1, task2)t1 * t2: do t2 for all output of t1
__pow (task1, condition)t1 ^ (condition): do t1 if condition (expression or function to be evaluated) is met
__call (self, ...)Run the task
103 | 104 |
105 |
106 | 107 | 108 |

Functions

109 | 110 |
111 |
112 | 113 | __div (task1, task2) 114 |
115 |
116 | t1 / t2: t1 in parallel of t2 117 | 118 | 119 |

Parameters:

120 |
    121 |
  • task1 122 | 123 |
  • 124 |
  • task2 125 | 126 |
  • 127 |
128 | 129 | 130 | 131 | 132 | 133 |
134 |
135 | 136 | __concat (task1, task2) 137 |
138 |
139 | t1 .. t2: t1 then t2 140 | 141 | 142 |

Parameters:

143 |
    144 |
  • task1 145 | 146 |
  • 147 |
  • task2 148 | 149 |
  • 150 |
151 | 152 | 153 | 154 | 155 | 156 |
157 |
158 | 159 | __band (task1, task2) 160 |
161 |
162 | t1 & t2: t2 if t1 returns truthy value 163 | 164 | 165 |

Parameters:

166 |
    167 |
  • task1 168 | 169 |
  • 170 |
  • task2 171 | 172 |
  • 173 |
174 | 175 | 176 | 177 | 178 | 179 |
180 |
181 | 182 | __bor (task1, task2) 183 |
184 |
185 | t1 | t2: t2 only if t1 returns falsy value 186 | 187 | 188 |

Parameters:

189 |
    190 |
  • task1 191 | 192 |
  • 193 |
  • task2 194 | 195 |
  • 196 |
197 | 198 | 199 | 200 | 201 | 202 |
203 |
204 | 205 | __shr (task1, task2) 206 |
207 |
208 | t1 >> t2: t1 output to t2 input 209 | 210 | 211 |

Parameters:

212 |
    213 |
  • task1 214 | 215 |
  • 216 |
  • task2 217 | 218 |
  • 219 |
220 | 221 | 222 | 223 | 224 | 225 |
226 |
227 | 228 | __shl (task2, task1) 229 |
230 |
231 | t2 << t1: same 232 | 233 | 234 |

Parameters:

235 |
    236 |
  • task2 237 | 238 |
  • 239 |
  • task1 240 | 241 |
  • 242 |
243 | 244 | 245 | 246 | 247 | 248 |
249 |
250 | 251 | __bxor (task1, task2) 252 |
253 |
254 | t1 ~ t2: if t1 has output, give it to t2 255 | 256 | 257 |

Parameters:

258 |
    259 |
  • task1 260 | 261 |
  • 262 |
  • task2 263 | 264 |
  • 265 |
266 | 267 | 268 | 269 | 270 | 271 |
272 |
273 | 274 | __mul (task1, task2) 275 |
276 |
277 | t1 * t2: do t2 for all output of t1 278 | 279 | 280 |

Parameters:

281 |
    282 |
  • task1 283 | 284 |
  • 285 |
  • task2 286 | 287 |
  • 288 |
289 | 290 | 291 | 292 | 293 | 294 |
295 |
296 | 297 | __pow (task1, condition) 298 |
299 |
300 | t1 ^ (condition): do t1 if condition (expression or function to be evaluated) is met 301 | 302 | 303 |

Parameters:

304 |
    305 |
  • task1 306 | 307 |
  • 308 |
  • condition 309 | 310 |
  • 311 |
312 | 313 | 314 | 315 | 316 | 317 |
318 |
319 | 320 | __call (self, ...) 321 |
322 |
323 | Run the task 324 | 325 | 326 |

Parameters:

327 |
    328 |
  • self 329 | 330 |
  • 331 |
  • ... 332 | 333 |
  • 334 |
335 | 336 | 337 | 338 | 339 | 340 |
341 |
342 | 343 | 344 |
345 |
346 |
347 | generated by LDoc 1.4.6 348 | Last updated 2019-03-08 07:45:45 349 |
350 |
351 | 352 | 353 | -------------------------------------------------------------------------------- /examples/buildlua.fourmi.plan.lua: -------------------------------------------------------------------------------- 1 | -- luarocks install sirocco 2 | -- fourmi -f example/buildlua.fourmi.plan.lua 3 | 4 | local builtins = require "fourmi.builtins" 5 | local plan = require "fourmi.plan" 6 | local task = require "fourmi.task" 7 | local log = require "fourmi.log" 8 | local sh = builtins.sh 9 | local var = builtins.var 10 | local chdir = builtins.chdir 11 | local getvar = builtins.getvar 12 | local __ = builtins.__ 13 | local outdated = builtins.task.outdated 14 | local sht = builtins.task.sh 15 | 16 | var { -- User overridable settings 17 | PLAT = "none", 18 | SYSCFLAGS = {}, 19 | SYSLDFLAGS = {}, 20 | SYSLIBS = {}, 21 | MYCFLAGS = {}, 22 | MYLDFLAGS = {}, 23 | MYLIBS = {}, 24 | MYOBJS = {}, 25 | } 26 | 27 | var { 28 | PLATS = { 29 | { 30 | value = "aix", 31 | label = "aix", 32 | }, 33 | { 34 | value = "bsd", 35 | label = "bsd", 36 | }, 37 | { 38 | value = "c89", 39 | label = "c89", 40 | }, 41 | { 42 | value = "freebsd", 43 | label = "freebsd", 44 | }, 45 | { 46 | value = "generic", 47 | label = "generic", 48 | }, 49 | { 50 | value = "linux", 51 | label = "linux", 52 | }, 53 | { 54 | value = "macosx", 55 | label = "macosx", 56 | }, 57 | { 58 | value = "mingw", 59 | label = "mingw", 60 | }, 61 | { 62 | value = "posix", 63 | label = "posix", 64 | }, 65 | { 66 | value = "solaris", 67 | label = "solaris", 68 | }, 69 | }, 70 | 71 | CC = "gcc -std=gnu99", 72 | CFLAGS = "-O2 -Wall -Wextra -DLUA_COMPAT_5_2 @{SYSCFLAGS} @{MYCFLAGS}", 73 | LDFLAGS = "@{SYSLDFLAGS} @{MYLDFLAGS}", 74 | LIBS = "-lm @{SYSLIBS} @{MYLIBS}", 75 | 76 | AR = "ar rcu", 77 | RANLIB = "ranlib", 78 | } 79 | 80 | var { 81 | LUA_A = "liblua.a", 82 | LUA_T = "lua", 83 | LUA_O = "lua.o", 84 | LUAC_T = "luac", 85 | LUAC_O = "luac.o", 86 | } 87 | 88 | var { 89 | ALL_T = { 90 | getvar "LUA_A", 91 | getvar "LUA_T", 92 | getvar "LUAC_T", 93 | }, 94 | CORE_O = { 95 | "lapi.o", 96 | "lcode.o", 97 | "lctype.o", 98 | "ldebug.o", 99 | "ldo.o", 100 | "ldump.o", 101 | "lfunc.o", 102 | "lgc.o", 103 | "llex.o", 104 | "lmem.o", 105 | "lobject.o", 106 | "lopcodes.o", 107 | "lparser.o", 108 | "lstate.o", 109 | "lstring.o", 110 | "ltable.o", 111 | "ltm.o", 112 | "lundump.o", 113 | "lvm.o", 114 | "lzio.o", 115 | }, 116 | AUX_O = "lauxlib.o", 117 | LIB_O = { 118 | "lauxlib.o", 119 | "lbaselib.o", 120 | "lbitlib.o", 121 | "lcorolib.o", 122 | "ldblib.o", 123 | "liolib.o", 124 | "lmathlib.o", 125 | "loslib.o", 126 | "lstrlib.o", 127 | "ltablib.o", 128 | "lutf8lib.o", 129 | "loadlib.o", 130 | "linit.o", 131 | }, 132 | } 133 | 134 | var { 135 | BASE_O = { 136 | getvar "CORE_O", 137 | getvar "LIB_O", 138 | getvar "MYOBJS", 139 | } 140 | } 141 | 142 | var { 143 | ALL_O = { 144 | getvar "BASE_O", 145 | getvar "LUA_O", 146 | getvar "LUAC_O", 147 | }, 148 | } 149 | 150 | task "LUA_A" 151 | :file "@{LUA_A}" 152 | :requires { 153 | getvar "BASE_O" 154 | } 155 | :perform(function(self) 156 | if not sh("@{AR}", "@{LUA_A}", "@{BASE_O}") 157 | or not sh("@{RANLIB}", "@{LUA_A}") then 158 | error(__"Could not build @{LUA_A}") 159 | end 160 | 161 | return __"@{LUA_A}" 162 | end) 163 | 164 | task "LUA_T" 165 | :file "@{LUA_T}" 166 | :requires { 167 | getvar "LUA_O", 168 | getvar "LUA_A" 169 | } 170 | :perform(function(self) 171 | local ok, msg = 172 | sh( 173 | "@{CC}", "-o", "@{LUA_T}", "@{LDFLAGS}", 174 | "@{LUA_O}", "@{LUA_A}", "@{LIBS}" 175 | ) 176 | 177 | if not ok then 178 | error(msg) 179 | end 180 | 181 | return __"@{LUA_T}" 182 | end) 183 | 184 | task "LUAC_T" 185 | :file "@{LUAC_T}" 186 | :requires { 187 | getvar "LUAC_O", 188 | getvar "LUA_A" 189 | } 190 | :perform(function(self) 191 | local ok, msg = 192 | sh( 193 | "@{CC}", "-o", "@{LUAC_T}", 194 | "@{LDFLAGS}", "@{LUAC_O}", "@{LUA_A}", "@{LIBS}" 195 | ) 196 | 197 | if not ok then 198 | error(msg) 199 | end 200 | 201 | return __"@{LUAC_T}" 202 | end) 203 | 204 | local build = task "build" 205 | :description "Builds Lua" 206 | :requires(getvar "ALL_T") 207 | :perf(function(self) 208 | log.success "All done!" 209 | end) 210 | 211 | local compile = task "compile" 212 | :desc "Compile a single .o" 213 | :perf(function(self, out, original) 214 | log.warn("Compiling " .. out) 215 | 216 | local ok, msg = 217 | sh( 218 | "@{CC}", "@{CFLAGS}", "-c", "-o", original, out 219 | ) 220 | 221 | if not ok then 222 | error(msg) 223 | end 224 | 225 | return original 226 | end) 227 | 228 | local getlua = task "getlua" 229 | :description "Download and unzip Lua" 230 | :perform(function(self) 231 | if sh("wget", "https://www.lua.org/ftp/lua-5.3.5.tar.gz") 232 | and sh("tar xvzf", "lua-5.3.5.tar.gz") then 233 | return chdir "lua-5.3.5/src" and "lua-5.3.5/src" 234 | end 235 | end) 236 | 237 | -- luacheck: push ignore 631 238 | for file, deps in pairs { 239 | ["lapi.o"] = { "lapi.c", "lprefix.h", "lua.h", "luaconf.h", "lapi.h", "llimits.h", 240 | "lstate.h", "lobject.h", "ltm.h", "lzio.h", "lmem.h", "ldebug.h", 241 | "ldo.h", "lfunc.h", "lgc.h", "lstring.h", "ltable.h", "lundump.h", 242 | "lvm.h" }, 243 | ["lauxlib.o"] = { "lauxlib.c", "lprefix.h", "lua.h", "luaconf.h", "lauxlib.h", }, 244 | ["lbaselib.o"] = { "lbaselib.c", "lprefix.h", "lua.h", "luaconf.h", "lauxlib.h", "lualib.h" }, 245 | ["lbitlib.o"] = { "lbitlib.c", "lprefix.h", "lua.h", "luaconf.h", "lauxlib.h", "lualib.h" }, 246 | ["lcode.o"] = { "lcode.c", "lprefix.h", "lua.h", "luaconf.h", "lcode.h", "llex.h", 247 | "lobject.h", "llimits.h", "lzio.h", "lmem.h", "lopcodes.h", "lparser.h", 248 | "ldebug.h", "lstate.h", "ltm.h", "ldo.h", "lgc.h", "lstring.h", 249 | "ltable.h", "lvm.h" }, 250 | ["lcorolib.o"] = { "lcorolib.c", "lprefix.h", "lua.h", "luaconf.h", "lauxlib.h", "lualib.h" }, 251 | ["lctype.o"] = { "lctype.c", "lprefix.h", "lctype.h", "lua.h", "luaconf.h", "llimits.h" }, 252 | ["ldblib.o"] = { "ldblib.c", "lprefix.h", "lua.h", "luaconf.h", "lauxlib.h", "lualib.h" }, 253 | ["ldebug.o"] = { "ldebug.c", "lprefix.h", "lua.h", "luaconf.h", "lapi.h", "llimits.h", 254 | "lstate.h", "lobject.h", "ltm.h", "lzio.h", "lmem.h", "lcode.h", 255 | "llex.h", "lopcodes.h", "lparser.h", "ldebug.h", "ldo.h", "lfunc.h", 256 | "lstring.h", "lgc.h", "ltable.h", "lvm.h" }, 257 | ["ldo.o"] = { "ldo.c", "lprefix.h", "lua.h", "luaconf.h", "lapi.h", "llimits.h", 258 | "lstate.h", "lobject.h", "ltm.h", "lzio.h", "lmem.h", "ldebug.h", 259 | "ldo.h", "lfunc.h", "lgc.h", "lopcodes.h", "lparser.h", "lstring.h", 260 | "ltable.h", "lundump.h", "lvm.h" }, 261 | ["ldump.o"] = { "ldump.c", "lprefix.h", "lua.h", "luaconf.h", "lobject.h", "llimits.h", 262 | "lstate.h", "ltm.h", "lzio.h", "lmem.h", "lundump.h" }, 263 | ["lfunc.o"] = { "lfunc.c", "lprefix.h", "lua.h", "luaconf.h", "lfunc.h", "lobject.h", 264 | "llimits.h", "lgc.h", "lstate.h", "ltm.h", "lzio.h", "lmem.h" }, 265 | ["lgc.o"] = { "lgc.c", "lprefix.h", "lua.h", "luaconf.h", "ldebug.h", "lstate.h", 266 | "lobject.h", "llimits.h", "ltm.h", "lzio.h", "lmem.h", "ldo.h", 267 | "lfunc.h", "lgc.h", "lstring.h", "ltable.h" }, 268 | ["linit.o"] = { "linit.c", "lprefix.h", "lua.h", "luaconf.h", "lualib.h", "lauxlib.h" }, 269 | ["liolib.o"] = { "liolib.c", "lprefix.h", "lua.h", "luaconf.h", "lauxlib.h", "lualib.h" }, 270 | ["llex.o"] = { "llex.c", "lprefix.h", "lua.h", "luaconf.h", "lctype.h", "llimits.h", 271 | "ldebug.h", "lstate.h", "lobject.h", "ltm.h", "lzio.h", "lmem.h", 272 | "ldo.h", "lgc.h", "llex.h", "lparser.h", "lstring.h", "ltable.h" }, 273 | ["lmathlib.o"] = { "lmathlib.c", "lprefix.h", "lua.h", "luaconf.h", "lauxlib.h", "lualib.h" }, 274 | ["lmem.o"] = { "lmem.c", "lprefix.h", "lua.h", "luaconf.h", "ldebug.h", "lstate.h", 275 | "lobject.h", "llimits.h", "ltm.h", "lzio.h", "lmem.h", "ldo.h", 276 | "lgc.h" }, 277 | ["loadlib.o"] = { "loadlib.c", "lprefix.h", "lua.h", "luaconf.h", "lauxlib.h", "lualib.h" }, 278 | ["lobject.o"] = { "lobject.c", "lprefix.h", "lua.h", "luaconf.h", "lctype.h", "llimits.h", 279 | "ldebug.h", "lstate.h", "lobject.h", "ltm.h", "lzio.h", "lmem.h", 280 | "ldo.h", "lstring.h", "lgc.h", "lvm.h" }, 281 | ["lopcodes.o"] = { "lopcodes.c", "lprefix.h", "lopcodes.h", "llimits.h", "lua.h", "luaconf.h" }, 282 | ["loslib.o"] = { "loslib.c", "lprefix.h", "lua.h", "luaconf.h", "lauxlib.h", "lualib.h" }, 283 | ["lparser.o"] = { "lparser.c", "lprefix.h", "lua.h", "luaconf.h", "lcode.h", "llex.h", 284 | "lobject.h", "llimits.h", "lzio.h", "lmem.h", "lopcodes.h", "lparser.h", 285 | "ldebug.h", "lstate.h", "ltm.h", "ldo.h", "lfunc.h", "lstring.h", 286 | "lgc.h", "ltable.h" }, 287 | ["lstate.o"] = { "lstate.c", "lprefix.h", "lua.h", "luaconf.h", "lapi.h", "llimits.h", 288 | "lstate.h", "lobject.h", "ltm.h", "lzio.h", "lmem.h", "ldebug.h", 289 | "ldo.h", "lfunc.h", "lgc.h", "llex.h", "lstring.h", "ltable.h" }, 290 | ["lstring.o"] = { "lstring.c", "lprefix.h", "lua.h", "luaconf.h", "ldebug.h", "lstate.h", 291 | "lobject.h", "llimits.h", "ltm.h", "lzio.h", "lmem.h", "ldo.h", 292 | "lstring.h", "lgc.h" }, 293 | ["lstrlib.o"] = { "lstrlib.c", "lprefix.h", "lua.h", "luaconf.h", "lauxlib.h", "lualib.h" }, 294 | ["ltable.o"] = { "ltable.c", "lprefix.h", "lua.h", "luaconf.h", "ldebug.h", "lstate.h", 295 | "lobject.h", "llimits.h", "ltm.h", "lzio.h", "lmem.h", "ldo.h", 296 | "lgc.h", "lstring.h", "ltable.h", "lvm.h" }, 297 | ["ltablib.o"] = { "ltablib.c", "lprefix.h", "lua.h", "luaconf.h", "lauxlib.h", "lualib.h" }, 298 | ["ltm.o"] = { "ltm.c", "lprefix.h", "lua.h", "luaconf.h", "ldebug.h", "lstate.h", 299 | "lobject.h", "llimits.h", "ltm.h", "lzio.h", "lmem.h", "ldo.h", 300 | "lstring.h", "lgc.h", "ltable.h", "lvm.h" }, 301 | ["lua.o"] = { "lua.c", "lprefix.h", "lua.h", "luaconf.h", "lauxlib.h", "lualib.h" }, 302 | ["luac.o"] = { "luac.c", "lprefix.h", "lua.h", "luaconf.h", "lauxlib.h", "lobject.h", 303 | "llimits.h", "lstate.h", "ltm.h", "lzio.h", "lmem.h", "lundump.h", 304 | "ldebug.h", "lopcodes.h" }, 305 | ["lundump.o"] = { "lundump.c", "lprefix.h", "lua.h", "luaconf.h", "ldebug.h", "lstate.h", 306 | "lobject.h", "llimits.h", "ltm.h", "lzio.h", "lmem.h", "ldo.h", 307 | "lfunc.h", "lstring.h", "lgc.h", "lundump.h" }, 308 | ["lutf8lib.o"] = { "lutf8lib.c", "lprefix.h", "lua.h", "luaconf.h", "lauxlib.h", "lualib.h" }, 309 | ["lvm.o"] = { "lvm.c", "lprefix.h", "lua.h", "luaconf.h", "ldebug.h", "lstate.h", 310 | "lobject.h", "llimits.h", "ltm.h", "lzio.h", "lmem.h", "ldo.h", 311 | "lfunc.h", "lgc.h", "lopcodes.h", "lstring.h", "ltable.h", "lvm.h" }, 312 | ["lzio.o"] = { "lzio.c", "lprefix.h", "lua.h", "luaconf.h", "llimits.h", "lmem.h", 313 | "lstate.h", "lobject.h", "ltm.h", "lzio.h" }, 314 | } do -- luacheck: pop 315 | local _ = (outdated(deps, file) & compile):file(file) 316 | end 317 | 318 | -- Use sirocco to ask for missing input 319 | local List = require "sirocco.list" 320 | 321 | local plat = task "plat" 322 | :desc "Configure for specific platform" 323 | :perf(function(self) 324 | local platform = __"@{PLAT}" 325 | 326 | if not platform or platform == "none" then 327 | platform = List { 328 | prompt = "For which platform? ", 329 | multiple = false, 330 | required = true, 331 | items = getvar "PLATS", 332 | }:ask()[1] 333 | end 334 | 335 | if platform == "aix" then 336 | var { 337 | CC = "xlc", 338 | CFLAGS = "-O2 -DLUA_USE_POSIX -DLUA_USE_DLOPEN", 339 | SYSLIBS = "-ldl", 340 | SYSLDFLAGS = "-brtl -bexpall" 341 | } 342 | elseif platform == "bsd" then 343 | var { 344 | SYSCFLAGS = "-DLUA_USE_POSIX -DLUA_USE_DLOPEN", 345 | SYSLIBS = "-Wl,-E" 346 | } 347 | elseif platform == "c89" then 348 | log.warn "\n*** C89 does not guarantee 64-bit integers for Lua.\n" 349 | var { 350 | SYSCFLAGS = "-DLUA_USE_C89", 351 | CC = "gcc -std=c89" 352 | } 353 | elseif platform == "freebsd" then 354 | var { 355 | SYSCFLAGS ="-DLUA_USE_LINUX -DLUA_USE_READLINE -I/usr/include/edit", 356 | SYSLIBS = "-Wl,-E -ledit", 357 | CC = "cc" 358 | } 359 | elseif platform == "linux" then 360 | var { 361 | SYSCFLAGS = "-DLUA_USE_LINUX", 362 | SYSLIBS = "-Wl,-E -ldl -lreadline" 363 | } 364 | elseif platform == "macosx" then 365 | var { 366 | SYSCFLAGS = "-DLUA_USE_MACOSX", 367 | SYSLIBS = "-lreadline" 368 | } 369 | elseif platform == "mingw" then 370 | var { 371 | LUA_A = "lua53.dll", 372 | LUA_T = "lua.exe", 373 | LUAC_T = "luac.exe", 374 | AR = "@{CC} -shared -o", 375 | RANLIB = "strip --strip-unneeded", 376 | SYSCFLAGS = "-DLUA_BUILD_AS_DLL", 377 | SYSLIBS = "", 378 | SYSLDFLAGS = "-s" 379 | } 380 | elseif platform == "posix" then 381 | var { 382 | SYSCFLAGS="-DLUA_USE_POSIX" 383 | } 384 | elseif platform == "solaris" then 385 | var { 386 | SYSCFLAGS = "-DLUA_USE_POSIX -DLUA_USE_DLOPEN -D_REENTRANT", 387 | SYSLIBS = "-ldl" 388 | } 389 | end 390 | end) 391 | 392 | return { 393 | plan "all" 394 | :task( 395 | getlua .. plat .. build 396 | ), 397 | 398 | plan "getlua" 399 | :description "Get Lua" 400 | :task( 401 | getlua 402 | ), 403 | 404 | plan "build" 405 | :description "Build Lua" 406 | :task( 407 | build 408 | ), 409 | 410 | plan "clean" 411 | :description "Cleanup" 412 | :task( 413 | sht("rm -f", "@{ALL_T} @{ALL_O}") 414 | ) 415 | } 416 | -------------------------------------------------------------------------------- /examples/mingzip.fourmi.plan.lua: -------------------------------------------------------------------------------- 1 | -- fourmi -f example/mingzip.fourmi.plan.lua 2 | 3 | -- Example fourmi.lua 4 | 5 | local builtins = require "fourmi.builtins" 6 | local plan = require "fourmi.plan" 7 | local task = require "fourmi.task" 8 | local __ = builtins.__ 9 | local empty = builtins.task.empty 10 | local ls = builtins.task.ls 11 | local mv = builtins.task.mv 12 | local outdated = builtins.task.outdated 13 | local sh = builtins.sh 14 | local var = builtins.var 15 | 16 | -- Define vars 17 | 18 | var { 19 | min = "luamin", 20 | src = "./fourmi", 21 | dest = "~/tmp-code", 22 | } 23 | 24 | -- Define tasks 25 | 26 | local minify = task "minify" 27 | :description "Minify lua files with luamin" 28 | :perform(function(self, file) 29 | local minifiedFile = __"@{tmp}/" 30 | .. file 31 | :match "([^/]*)$" 32 | 33 | if not sh("@{min}", "-f", file, ">", minifiedFile) then 34 | error("Could not create minified file at: " .. minifiedFile) 35 | end 36 | 37 | return minifiedFile 38 | end) 39 | 40 | local gzip = task "gzip" 41 | :description "Zip file" 42 | :perform(function(self, file) 43 | sh( 44 | "gzip", "-k", "-f", file 45 | ) 46 | 47 | return file .. ".gz" 48 | end) 49 | 50 | -- Define plans 51 | 52 | return { 53 | plan "all" 54 | :description "Minify and gzip lua files" 55 | :task( 56 | ls("@{src}", "%.lua$") 57 | * (outdated "@{dest}/#{original}.gz" 58 | & minify >> gzip >> mv "@{dest}") 59 | ), 60 | 61 | plan "clean" 62 | :description "Cleanup" 63 | :task( 64 | empty "@{dest}" 65 | ) 66 | } 67 | -------------------------------------------------------------------------------- /fourmi-dev-1.rockspec: -------------------------------------------------------------------------------- 1 | 2 | package = "fourmi" 3 | version = "dev-1" 4 | rockspec_format = "3.0" 5 | 6 | source = { 7 | url = "git://github.com/giann/fourmi", 8 | } 9 | 10 | description = { 11 | summary = "🐜 A task runner", 12 | homepage = "https://github.com/giann/fourmi", 13 | license = "MIT/X11", 14 | } 15 | 16 | build = { 17 | modules = { 18 | ["fourmi"] = "fourmi/init.lua", 19 | ["fourmi.plan"] = "fourmi/plan.lua", 20 | ["fourmi.task"] = "fourmi/task.lua", 21 | ["fourmi.builtins"] = "fourmi/builtins.lua", 22 | ["fourmi.log"] = "fourmi/log.lua", 23 | }, 24 | type = "builtin", 25 | install = { 26 | bin = { 27 | "bin/fourmi" 28 | } 29 | } 30 | } 31 | 32 | dependencies = { 33 | "lua >= 5.3", 34 | "lua-term >= 0.7-1", 35 | "argparse >= 0.6.0-1", 36 | "luafilesystem >= 1.7.0-2", 37 | } 38 | -------------------------------------------------------------------------------- /fourmi/builtins.lua: -------------------------------------------------------------------------------- 1 | --- 2 | -- Common helpers and basic tasks 3 | -- @module fourmi.builtins 4 | -- @author Benoit Giannangeli 5 | -- @license MIT 6 | -- @copyright Benoit Giannangeli 2019 7 | 8 | local task = require "fourmi.task" 9 | local log = require "fourmi.log" 10 | local colors = require "term.colors" 11 | local lfs = require "lfs" 12 | local utils = require "fourmi.utils" 13 | local __ = utils.__ 14 | 15 | local builtins = { 16 | __ = __ 17 | } 18 | 19 | --- 20 | -- Runs a command captures stderr and returns it as error message 21 | -- @tparam string program command to run 22 | -- @tparam string ... program arguments If an arguments contains spaces it'll be quoted 23 | -- @treturn[1] boolean true if command succeded 24 | -- @treturn[2] string message in case of failure 25 | function builtins.sh(program, ...) 26 | program = __(program) 27 | 28 | local arguments = {...} 29 | for i, arg in ipairs(arguments) do 30 | local sarg = __(tostring(arg)) 31 | 32 | arguments[i] = sarg 33 | -- sarg:match "[^%s]%s+[^%s]" 34 | -- and string.format("%q", sarg) 35 | -- or sarg 36 | end 37 | 38 | arguments = table.concat(arguments, " ") 39 | 40 | local command = 41 | program .. " " .. arguments 42 | 43 | local stderr = os.tmpname() 44 | 45 | log.sys("$ " .. colors.bright(colors.magenta(program)) .. " " .. colors.magenta(arguments)) 46 | 47 | -- spawn program and yield when waitpid returns 48 | local ok, exit, status = os.execute( 49 | command .. " 2> " .. stderr, 50 | "r" 51 | ) 52 | 53 | if ok and exit == "exit" and status == 0 then 54 | return true 55 | else 56 | local err = io.open(stderr, "r") 57 | local failure = err:read "*a" 58 | 59 | err:close() 60 | os.remove(stderr) 61 | 62 | return false, 63 | "Command `" .. command .. "` failed with status code, " .. status .. ": " .. failure 64 | end 65 | end 66 | 67 | --- 68 | -- Returns true if a file is outdated 69 | -- @tparam string original If alone checks that it exists 70 | -- @tparam string target Outdated if not present or older than original 71 | function builtins.outdated(original, target) 72 | if original and target then 73 | local originalAttr = lfs.attributes(original) 74 | local targetAttr = lfs.attributes(target) 75 | 76 | return not targetAttr or originalAttr.change > targetAttr.change, 77 | target .. " already present and up-to-date" 78 | elseif original then 79 | return not lfs.attributes(original), 80 | original .. " already present" 81 | end 82 | end 83 | 84 | --- 85 | -- Set a fourmi variable 86 | -- @tparam string|table key or table of (key, value) 87 | -- @tparam string|number|boolean value 88 | function builtins.var(key, value) 89 | if type(key) ~= "table" then 90 | if _G.__fourmi.vars[key] ~= nil then 91 | log.debug(__"Overwriting `#{key}` with: `#{value}`") 92 | end 93 | 94 | _G.__fourmi.vars[key] = value 95 | else 96 | for k, v in pairs(key) do 97 | if _G.__fourmi.vars[k] ~= nil then 98 | log.debug(__"Overwriting `#{k}` with: `#{v}`") 99 | end 100 | 101 | _G.__fourmi.vars[k] = v 102 | end 103 | end 104 | end 105 | 106 | function builtins.getvar(key) 107 | local var = _G.__fourmi.vars[key] 108 | return type(var) == "string" and __(var) or var 109 | end 110 | 111 | builtins.chdir = function(path) 112 | log.warn("Current directory changed to: " .. colors.green(path)) 113 | return lfs.chdir(path) 114 | end 115 | 116 | --- Builtin tasks 117 | builtins.task = {} 118 | 119 | --- 120 | -- Create a task that runs a single shell command 121 | -- @tparam string command 122 | -- @tparam string ... 123 | -- @treturn task 124 | builtins.task.sh = function(command, ...) 125 | local args = {...} 126 | 127 | return task("$ " .. command .. " ", table.concat(args, " ")) 128 | :perform(function(tsk) 129 | local ok, message = builtins.sh(command, table.unpack(args)) 130 | 131 | if ok then 132 | log.warn(tsk.properties.successMessage or command .. " succeeded") 133 | elseif not tsk.properties.ignoreError then 134 | error(colors.yellow(tsk.properties.failureMessage or command .. " failed: " .. message)) 135 | else 136 | log.warn(tsk.properties.failureMessage or command .. " failed") 137 | end 138 | 139 | return ok, message 140 | end) 141 | end 142 | 143 | --- A task that list files 144 | builtins.task.ls = task "ls" 145 | :description "List files in a directory" 146 | :perform(function(self) 147 | local dir = __(self.options[1]) 148 | 149 | local items = {} 150 | 151 | for item in lfs.dir(dir) do 152 | if not self.options[2] 153 | or item:match(self.options[2]) then 154 | table.insert(items, dir .. "/" .. item) 155 | end 156 | end 157 | 158 | return table.unpack(items) 159 | end) 160 | 161 | -- A task that moves a file 162 | builtins.task.mv = task "mv" 163 | :description "Move a file" 164 | :perform(function(self, file) 165 | file = __(file) 166 | local dest = __(self.options[1]) .. "/" .. file:match "([^/]*)$" 167 | 168 | local ok, err = os.rename(file, dest) 169 | 170 | if ok then 171 | log.warn("Moved `" .. file .. "` to `" .. dest .. "`") 172 | return dest 173 | else 174 | error("Could not move `" .. file .. "` to `" .. dest .. "`: " .. err) 175 | end 176 | end) 177 | 178 | --- Empty files of a directory 179 | builtins.task.empty = task "empty" 180 | :description "Empty files of a directory" 181 | :perform(function(self) 182 | local dir = __(self.options[1]) 183 | local kind = lfs.attributes(dir).mode 184 | 185 | if kind == "directory" then 186 | local count = 0 187 | for file in lfs.dir(dir) do 188 | file = dir .. "/" .. file 189 | if (lfs.attributes(file) or {}).mode == "file" then 190 | local ok, err = os.remove(file) 191 | 192 | if ok then 193 | log.warn("\t`" .. file .. "` removed") 194 | count = count + 1 195 | else 196 | log.err("\tCould not remove `" .. file .. "`: " .. err) 197 | end 198 | end 199 | end 200 | 201 | if count > 0 then 202 | log.warn("Removed " .. count .. " files in `" .. dir .. "`") 203 | else 204 | log.success("Nothing to remove") 205 | end 206 | else 207 | error("`" .. dir .. "` is not a directory") 208 | end 209 | end) 210 | 211 | --- Filter out up-to-date files 212 | builtins.task.outdated = task "outdated" 213 | :description "Filter out up-to-date files" 214 | :property("quiet", true) 215 | :perform(function(self, original) 216 | original = original or self.options[1] 217 | local dest = original and self.options[1] or self.options[2] 218 | 219 | dest = dest and __(dest, { 220 | original = original:match "([^/]*)$" 221 | }) 222 | 223 | if builtins.outdated(original or dest, original and dest or nil) then 224 | return original 225 | end 226 | end) 227 | 228 | --- A task that returns true if original outdated with one of deps 229 | builtins.task.outdated = task "outdated" 230 | :description "Returns true if a file must be updated" 231 | :property("quiet", true) 232 | :perform(function(self) 233 | local originals = type(self.options[1]) ~= "table" and { self.options[1] } or self.options[1] 234 | local targets = type(self.options[2]) ~= "table" and { self.options[2] } or self.options[2] 235 | 236 | for _, original in ipairs(originals) do 237 | for _, target in ipairs(targets) do 238 | if builtins.outdated(original, target) then 239 | return original, target 240 | end 241 | end 242 | end 243 | 244 | return false 245 | end) 246 | 247 | return builtins 248 | -------------------------------------------------------------------------------- /fourmi/init.lua: -------------------------------------------------------------------------------- 1 | --- 2 | -- Main fourmi module 3 | -- @module fourmi 4 | -- @author Benoit Giannangeli 5 | -- @license MIT 6 | -- @copyright Benoit Giannangeli 2019 7 | 8 | local fourmi = { 9 | task = require "fourmi.task", 10 | plan = require "fourmi.plan", 11 | builtins = require "fourmi.builtins" 12 | } 13 | 14 | return fourmi 15 | -------------------------------------------------------------------------------- /fourmi/log.lua: -------------------------------------------------------------------------------- 1 | local colors = require "term.colors" 2 | 3 | local logColors = { 4 | info = colors.white, 5 | warn = colors.yellow, 6 | err = colors.red, 7 | debug = colors.blue, 8 | sys = colors.magenta, 9 | success = colors.green, 10 | } 11 | 12 | --- 13 | -- Log something 14 | -- @todo conf should say how much timestamp to show 15 | -- @tparam string channel 16 | -- @tparam string msg 17 | local function put(_, channel, msg) 18 | local tm = os.date "*t" 19 | tm = "[" 20 | -- .. string.format("%04d", tm.year) 21 | -- .. "-" .. string.format("%02d", tm.month) 22 | -- .. "-" .. string.format("%02d", tm.day) 23 | -- .. " " 24 | .. string.format("%02d", tm.hour) 25 | .. ":" 26 | .. string.format("%02d", tm.min) 27 | .. ":" 28 | .. string.format("%02d", tm.sec) 29 | .. "] " 30 | 31 | local leading = msg:match "^(\n*)" 32 | msg = msg:gsub("^(\n*)", "") 33 | msg = msg:gsub("\n", "\n" .. tm) 34 | 35 | print( 36 | leading .. 37 | colors.dim(colors.cyan( 38 | tm 39 | )) 40 | .. logColors[channel](msg) 41 | ) 42 | end 43 | 44 | local log = setmetatable({}, { 45 | __call = put 46 | }) 47 | 48 | for channel, _ in pairs(logColors) do 49 | log[channel] = function(msg) 50 | log(channel, msg) 51 | end 52 | end 53 | 54 | return log 55 | -------------------------------------------------------------------------------- /fourmi/plan.lua: -------------------------------------------------------------------------------- 1 | --- 2 | -- Plan module 3 | -- @classmod fourmi.plan 4 | -- @author Benoit Giannangeli 5 | -- @license MIT 6 | -- @copyright Benoit Giannangeli 2019 7 | 8 | local colors = require "term.colors" 9 | local log = require "fourmi.log" 10 | 11 | local planMt = { 12 | 13 | --- 14 | -- Run the plan 15 | -- @tparam plan self 16 | __call = function(self) 17 | local time = os.clock() 18 | 19 | log.info("\n" 20 | .. colors.green("🐜 Running plan " 21 | .. colors.bright(colors.blue(self.__name))) 22 | .. colors.dim(colors.cyan( 23 | (self.__description and "\n" .. self.__description .. ": " or " ") 24 | .. self.__task.__name 25 | )) 26 | ) 27 | 28 | if not self.task then 29 | error("Task is undefined for plan " .. self.__name) 30 | end 31 | 32 | local results = {self.__task:run()} 33 | 34 | log.info( 35 | "\n🐜 Plan " .. colors.bright(colors.blue(self.__name)) .. " completed with " 36 | .. colors.yellow(#results) .. " result" .. (#results > 1 and "s" or "") 37 | .. " in " .. colors.yellow(string.format("%.03f", os.clock() - time) .. "s") 38 | ) 39 | 40 | for _, res in ipairs(results) do 41 | log.info("\t→ " .. colors.dim(colors.cyan(tostring(res)))) 42 | end 43 | end, 44 | 45 | __index = { 46 | 47 | --- 48 | -- Set plan's task 49 | -- @tparam plan self 50 | -- @tparam task task 51 | task = function(self, task) 52 | self.__task = task 53 | 54 | return self 55 | end, 56 | 57 | --- 58 | -- Set plan's description 59 | -- @tparam plan self 60 | -- @tparam string description 61 | description = function(self, description) 62 | self.__description = description 63 | 64 | return self 65 | end, 66 | 67 | --- 68 | -- Set plan's name 69 | -- @tparam plan self 70 | -- @tparam string name 71 | name = function(self, name) 72 | self.__name = name 73 | 74 | return self 75 | end, 76 | 77 | -- Aliases 78 | desc = function(self, ...) 79 | return self:description(...) 80 | end, 81 | 82 | }, 83 | 84 | } 85 | 86 | local function plan(name) 87 | return setmetatable({ 88 | __name = name, 89 | }, planMt) 90 | end 91 | 92 | return plan 93 | -------------------------------------------------------------------------------- /fourmi/task.lua: -------------------------------------------------------------------------------- 1 | --- 2 | -- Task module 3 | -- @classmod fourmi.task 4 | -- @author Benoit Giannangeli 5 | -- @license MIT 6 | -- @copyright Benoit Giannangeli 2019 7 | 8 | local colors = require "term.colors" 9 | local log = require "fourmi.log" 10 | local utils = require "fourmi.utils" 11 | local flatten = utils.flatten 12 | local __ = utils.__ 13 | 14 | local function parallel(functions, ...) 15 | error "NYI" 16 | end 17 | 18 | local taskMt 19 | 20 | --- 21 | -- Task constructor 22 | -- @tparam string name Task's name 23 | -- @treturn task New task 24 | local function task(name) 25 | return setmetatable({ 26 | __name = name, 27 | run = function() end, 28 | options = {}, 29 | properties = {}, 30 | deps = {}, 31 | }, taskMt) 32 | end 33 | 34 | --- Lefts out options which are set via __call 35 | local function clone(tsk) 36 | local new = task(tsk.__name) 37 | :description(tsk.__description) 38 | :property(tsk.properties) 39 | :perform(tsk.run) 40 | 41 | new.deps = tsk.deps 42 | 43 | return new 44 | end 45 | 46 | taskMt = { 47 | --- 48 | -- Runs task1 in parallel of task2 49 | -- @todo NYI 50 | -- @tparam task task1 51 | -- @tparam task task2 52 | -- @treturn task 53 | __div = function(task1, task2) 54 | return task("(" .. task1.__name .. " / " .. task2.__name .. ")") 55 | :perform(function(self, ...) 56 | return parallel({task1.run, task2.run}, ...) 57 | end) 58 | :property("quiet", true) 59 | end, 60 | 61 | --- 62 | -- Runs task1 then task2 63 | -- @tparam task task1 64 | -- @tparam task task2 65 | -- @treturn task 66 | __concat = function(task1, task2) 67 | return task("(" .. task1.__name .. " .. " .. task2.__name .. ")") 68 | :perform(function(self, ...) 69 | return task1:run(...), 70 | task2:run(...) 71 | end) 72 | :property("quiet", true) 73 | end, 74 | 75 | --- 76 | -- Runs task2 with task1 ouput if any and first output is truthy value 77 | -- @tparam task task1 78 | -- @tparam task task2 79 | -- @treturn task 80 | __band = function(task1, task2) 81 | return task("(" .. task1.__name .. " & " .. task2.__name .. ")") 82 | :perform(function(self, ...) 83 | local resultask1 = table.pack(task1:run(...)) 84 | 85 | if resultask1 and resultask1[1] then 86 | return task2:run(table.unpack(resultask1)) 87 | end 88 | end) 89 | :property("quiet", true) 90 | end, 91 | 92 | --- 93 | -- Runs task2 only if task1 returns falsy value 94 | -- @tparam task task1 95 | -- @tparam task task2 96 | -- @treturn task 97 | __bor = function(task1, task2) 98 | return task("(" .. task1.__name .. " | " .. task2.__name .. ")") 99 | :perform(function(self, ...) 100 | return task1:run(...) or task2:run(...) 101 | end) 102 | :property("quiet", true) 103 | end, 104 | 105 | --- 106 | -- Pipes task1's output to task2 input 107 | -- @tparam task task1 108 | -- @tparam task task2 109 | -- @treturn task 110 | __shr = function(task1, task2) 111 | return task("(" .. task1.__name .. " >> " .. task2.__name .. ")") 112 | :perform(function(self, ...) 113 | return task2:run(task1:run(...)) 114 | end) 115 | :property("quiet", true) 116 | end, 117 | 118 | --- 119 | -- Pipes task2's output to task1 input 120 | -- @tparam task task2 121 | -- @tparam task task1 122 | -- @treturn task 123 | __shl = function(task2, task1) 124 | return task2 >> task1 125 | end, 126 | 127 | --- 128 | -- Runs task2 for each output of task1, task2 is silenced 129 | -- @tparam task task1 130 | -- @tparam task task2 131 | -- @treturn task 132 | __mul = function(task1, task2) 133 | return task("(" .. task1.__name .. " * " .. task2.__name .. ")") 134 | :perform(function(self, ...) 135 | local results = {} 136 | 137 | for _, result in ipairs {task1:run(...)} do 138 | local task2Res = task2:run(result) 139 | 140 | if task2Res ~= nil then 141 | table.insert( 142 | results, 143 | task2Res 144 | ) 145 | end 146 | end 147 | 148 | return table.unpack(results) 149 | end) 150 | :property("quiet", true) 151 | end, 152 | 153 | --- 154 | -- Runs task1 if a condition is met 155 | -- @tparam task task1 156 | -- @tparam boolean|function condition If a function, will be run when resulting task is invoked 157 | -- @treturn task 158 | __pow = function(task1, condition) 159 | return task(task1.__name .. " ^ (" .. tostring(condition) .. ")") 160 | :perform(function(self, ...) 161 | local ok, message 162 | 163 | if type(condition) == "function" then 164 | ok, message = condition(...) 165 | else 166 | ok = condition 167 | end 168 | 169 | if ok then 170 | return task1:run(...) 171 | else 172 | local args = {} 173 | for _, arg in ipairs {...} do 174 | table.insert(args, tostring(arg)) 175 | end 176 | 177 | log.warn("\nTask " .. task1.__name .. " ignored: " .. message) 178 | end 179 | end) 180 | :property("quiet", true) 181 | end, 182 | 183 | --- 184 | -- Returns a clone task with given options 185 | -- @tparam task self 186 | -- @param ... Task input 187 | __call = function(self, ...) 188 | local tsk = clone(self) 189 | 190 | tsk.options = {...} 191 | 192 | return tsk 193 | end, 194 | 195 | __index = { 196 | --- 197 | -- Set task's description 198 | -- @tparam task self 199 | -- @tparam string description 200 | description = function(self, description) 201 | self.__description = description 202 | 203 | return self 204 | end, 205 | 206 | --- 207 | -- Set task's name 208 | -- @tparam task self 209 | -- @tparam string name 210 | name = function(self, name) 211 | self.__name = name 212 | 213 | return self 214 | end, 215 | 216 | --- 217 | -- Set task's action 218 | -- @tparam task self 219 | -- @tparam string fn function 220 | perform = function(self, fn) 221 | self.run = function(self, ...) 222 | local time = os.clock() 223 | 224 | if not self.properties.quiet then 225 | log.info("\n" 226 | .. colors.green("🌿 Running task " 227 | .. colors.bright(colors.blue(self.__name)) 228 | .. colors.green .. " for " .. colors.bright(colors.yellow(table.concat({...}, ", ")))) 229 | .. (self.__description and colors.dim(colors.cyan("\n" .. self.__description)) or "") 230 | ) 231 | end 232 | 233 | -- Running dependencies 234 | for _, dep in ipairs(self.deps) do 235 | local t = _G.__fourmi.deps[dep] 236 | 237 | if t then 238 | t:run() 239 | else 240 | error("Could not satisfy dependency: " .. tostring(dep)) 241 | end 242 | end 243 | 244 | -- Running the task 245 | local results = {fn(self, ...)} 246 | 247 | if not self.properties.quiet then 248 | log.info( 249 | " Task " .. colors.bright(colors.blue(self.__name)) .. " completed with " 250 | .. colors.yellow(#results) .. " result" .. (#results > 1 and "s" or "") 251 | .. " in " .. colors.yellow(string.format("%.03f", os.clock() - time) .. "s") 252 | ) 253 | 254 | for _, res in ipairs(results) do 255 | log.info("\t→ " .. colors.blue(tostring(res))) 256 | end 257 | end 258 | 259 | return table.unpack(results) 260 | end 261 | 262 | return self 263 | end, 264 | 265 | --- 266 | -- Set a property 267 | -- @tparam task self 268 | -- @tparam string name Option's name 269 | -- @param value Option's value 270 | property = function(self, name, value) 271 | local properties = type(name) == "table" 272 | and name or {[name] = value} 273 | 274 | for n, v in pairs(properties) do 275 | self.properties[n] = v 276 | end 277 | 278 | return self 279 | end, 280 | 281 | --- 282 | -- Register task as producing `file` 283 | -- @tparam task self 284 | -- @tparam string file 285 | file = function(self, file) 286 | file = __(file) 287 | 288 | self.__file = file 289 | _G.__fourmi.deps[file] = self 290 | 291 | return self 292 | end, 293 | 294 | --- 295 | -- Inject file dependencies 296 | -- @tparam task self 297 | -- @tparam table deps 298 | requires = function(self, deps) 299 | assert( 300 | type(deps) == "table" or type(deps) == "string", 301 | "Dependency must be a table or a string" 302 | ) 303 | 304 | self.deps = flatten(deps) 305 | 306 | return self 307 | end, 308 | 309 | -- Aliases 310 | prop = function(self, ...) 311 | return self:property(...) 312 | end, 313 | 314 | perf = function(self, ...) 315 | return self:perform(...) 316 | end, 317 | 318 | desc = function(self, ...) 319 | return self:description(...) 320 | end, 321 | }, 322 | 323 | -- __tostring = function(self) 324 | -- return "Task " .. self.__name 325 | -- end 326 | } 327 | 328 | -- Non operators aliases 329 | taskMt.parallelTo = taskMt.__div 330 | taskMt.after = taskMt.__concat 331 | taskMt.success = taskMt.__band 332 | taskMt.failure = taskMt.__bor 333 | taskMt.into = taskMt.__shr 334 | taskMt.ouput = taskMt.__bxor 335 | taskMt.each = taskMt.__mul 336 | taskMt.meet = taskMt.__pow 337 | 338 | return task 339 | -------------------------------------------------------------------------------- /fourmi/utils.lua: -------------------------------------------------------------------------------- 1 | local function flatten(t, flat, seen) 2 | flat = flat or {} 3 | seen = seen or {} 4 | 5 | seen[t] = true 6 | 7 | for _, v in ipairs(t) do 8 | if type(v) == "table" and not seen[v] then 9 | flatten(v, flat, seen) 10 | else 11 | table.insert(flat, v) 12 | end 13 | end 14 | 15 | return flat 16 | end 17 | 18 | --- 19 | -- Recursive string interpolation helper 20 | -- - `${VARIABLE}` -> `os.getenv "VARIABLE"` 21 | -- - `#{variable}` -> `variable` in `context` or caller locals or `_G` 22 | -- - `~` -> `os.getenv "HOME"` 23 | -- - `@{variable}` -> `_G.__fourmi.vars[variable]` 24 | -- @tparam string str String to interpolate 25 | -- @tparam[opt] table context Table in which to search variables to interpolates 26 | -- @treturn string 27 | local function __(str, context) 28 | -- No context provided, build one from caller locals 29 | if not context and str:match "#{([A-Za-z_]+[A-Za-z_0-9]*)}" then 30 | context = {} 31 | local l = 1 32 | local key, value 33 | 34 | repeat 35 | key, value = debug.getlocal(2, l) 36 | l = l + 1 37 | 38 | if key ~= nil then 39 | context[key] = value 40 | end 41 | until not key 42 | end 43 | 44 | -- Interpolate ${} 45 | local env 46 | repeat 47 | env = str:match "%${([A-Za-z_]+[A-Za-z_0-9]*)}" 48 | 49 | str = env 50 | and str:gsub("%${" .. env .. "}", os.getenv(env) or "") 51 | or str 52 | until not env 53 | 54 | -- Interpolate #{} 55 | local var 56 | repeat 57 | var = str:match "#{([A-Za-z_]+[A-Za-z_0-9]*)}" 58 | 59 | if var then 60 | local value = context[var] 61 | if value == nil then 62 | value = _G[var] 63 | end 64 | if value == nil then 65 | value = "" 66 | end 67 | 68 | if type(value) == "table" then 69 | local tmp = {} 70 | for _, v in ipairs(flatten(value)) do 71 | table.insert( 72 | tmp, 73 | type(v) == "string" 74 | and __(v) 75 | or v 76 | ) 77 | end 78 | value = table.concat(tmp, " ") 79 | elseif type(value) == "string" then 80 | value = __(value) 81 | end 82 | 83 | str = str:gsub("#{" .. var .. "}", tostring(value)) 84 | end 85 | until not var 86 | 87 | -- Interpolate ~ 88 | str = str:gsub("~", os.getenv "HOME") 89 | 90 | -- Interpolate @{} 91 | var = nil 92 | repeat 93 | var = str:match "@{([A-Za-z_]+[A-Za-z_0-9]*)}" 94 | 95 | if var then 96 | local value = _G.__fourmi.vars[var] 97 | if value == nil then 98 | value = "" 99 | end 100 | 101 | if type(value) == "table" then 102 | local tmp = {} 103 | for _, v in ipairs(flatten(value)) do 104 | table.insert( 105 | tmp, 106 | type(v) == "string" 107 | and __(v) 108 | or v 109 | ) 110 | end 111 | value = table.concat(tmp, " ") 112 | elseif type(value) == "string" then 113 | value = __(value) 114 | end 115 | 116 | str = var 117 | and str:gsub("@{" .. var .. "}", tostring(value)) 118 | or str 119 | end 120 | until not var 121 | 122 | return str 123 | end 124 | 125 | return { 126 | flatten = flatten, 127 | __ = __ 128 | } 129 | --------------------------------------------------------------------------------