# \[ANN\] Makeitso.jl: Make-like behaviour for Julia scripts

**URL:** https://discourse.julialang.org/t/ann-makeitso-jl-make-like-behaviour-for-julia-scripts/18243
**Category:** Package Announcements
**Created:** [December 3, 2018, 11:04am UTC](https://discourse.julialang.org/t/ann-makeitso-jl-make-like-behaviour-for-julia-scripts/18243 "2018-12-03T11:04:02Z")
**Posts on this page:** 1
**Page:** 1

<div class="post-metadata">

### Author: ![krcools](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/krcools/32/10212_2.png) [@krcools](https://discourse.julialang.org/u/krcools)
#### Post date: [December 3, 2018, 11:04am UTC](https://discourse.julialang.org/t/ann-makeitso-jl-make-like-behaviour-for-julia-scripts/18243/1 "2018-12-03T11:04:02Z")

</div>

> **[GitHub - krcools/Makeitso.jl: Make-like behaviour for Julia scripts](https://github.com/krcools/Makeitso.jl)**
>
> Make-like behaviour for Julia scripts

# Makeitso.jl

Make-like behavior to facilitate writing long-running scripts. The idea is that the user supplies the dependencies between targets and recipes for how to make a target from those dependencies. If changes happen to one recipe (cf. if one edits one source file in a make project), only affected variables will be recomputed.

Upon computation, a backup is written to disk. This means that even when the work on the script is resumed in a different session (because you went home, julia crashed, you ran out of memory, you share the data with coworkers over e.g. Dropbox), only missing and out-of-date targets will be recomputed.

This saves a lot of time and unnecessary reruns of computations. It also relieves the programmer of having to track all dependencies and keep a clear picture of the workspace in their head.

## Example

```julia
using Makeitso

@target A ()->1:10
@target B ()->[-4,-3,-2,-1,0,1,2,3,4,5]
@target C (A,B)->A.+B
@target D (A,B,C)->A.+B.+C

x = (@make D)[end]
@assert x == 30

@target B ()->pi
println("--- Recipe for B modified! ---")

x = (@make D)[end]
@assert x ≈ (20+2pi)

```

This script describes the dependencies between targets `A,B,C,D`. Upon calling `@make D` all targets `D` depends on are built. Copies are written to disk in case we want to continue work in a future julia session. Assuming this is the first run ever (i.e. no copies on disk exist), the script results in the following output:

```julia-auto
[Info: level 1 dep A: computed from dependencies [initial computation].
[Info: level 1 dep B: computed from dependencies [initial computation].
[ Info: level 2 dep A: retrieved from memory cache.
[ Info: level 2 dep B: retrieved from memory cache.
[Info: level 1 dep C: computed from dependencies [initial computation].
[Info: level 0 dep D: computed from dependencies [initial computation].
--- Recipe for B modified! ---
[ Info: level 1 dep A: retrieved from memory cache.
[Info: level 1 dep B: computed from dependencies [initial computation].
[ Info: level 2 dep A: retrieved from memory cache.
[ Info: level 2 dep B: retrieved from memory cache.
[Info: level 1 dep C: computed from dependencies [memory cache out-of-date].
[Info: level 0 dep D: computed from dependencies [memory cache out-of-date]

```

Note in particular that modifiying target `B` has the desired effect of recomputing `B,C,D`. An immediate second run will not require `A` to be recomputed, but `B,C,D` will, because we reverted to the original recipe for `B`:

```julia-auto
[ Info: level 1 dep A: retrieved from memory cache.
[Info: level 1 dep B: computed from dependencies [initial computation].
[ Info: level 2 dep A: retrieved from memory cache.
[ Info: level 2 dep B: retrieved from memory cache.
[Info: level 1 dep C: computed from dependencies [memory cache out-of-date].
[Info: level 0 dep D: computed from dependencies [memory cache out-of-date].
--- Recipe for B modified! ---
[ Info: level 1 dep A: retrieved from memory cache.
[Info: level 1 dep B: computed from dependencies [initial computation].
[ Info: level 2 dep A: retrieved from memory cache.
[ Info: level 2 dep B: retrieved from memory cache.
[Info: level 1 dep C: computed from dependencies [memory cache out-of-date].
[Info: level 0 dep D: computed from dependencies [memory cache out-of-date].

```

Likewise, killing the session and running the script again will see `A` restored from disk, but the other targets are recomputed twice (one for each version of the recipe for `B`):

```julia-auto
Julia has exited. Press Enter to start a new session.
Starting Julia...
julia> include("examples/hello.jl")
[ Info: level 1 dep A: restored from disk.
[Info: level 1 dep B: computed from dependencies [recipe modified].
[ Info: level 2 dep A: retrieved from memory cache.
[ Info: level 2 dep B: retrieved from memory cache.
[Info: level 1 dep C: computed from dependencies [store out-of-date].
[Info: level 0 dep D: computed from dependencies [store out-of-date].
--- Recipe for B modified! ---
[ Info: level 1 dep A: retrieved from memory cache.
[Info: level 1 dep B: computed from dependencies [initial computation].
[ Info: level 2 dep A: retrieved from memory cache.
[ Info: level 2 dep B: retrieved from memory cache.
[Info: level 1 dep C: computed from dependencies [memory cache out-of-date].
[Info: level 0 dep D: computed from dependencies [memory cache out-of-date].

```

## Notes

- Recipe validity is tracked by storing the hash of the corresponding julia `Expr`
- The `@target` macro creates a variable `target_A` etc. in the module namespace, excluding these names as valid variable names in your script.
- Recipes resulting in `nothing` are not valid as `nothing` indicates absence of an in-memory cached value.
- Up-dates to _normal_ non-target variables are not tracked and changes to them will not trigger recomputation of dependents. Functions taking zero arguments and returning a constant value are the appropriate way to allow for changeable parameters.
