///|
/// Migrate an existing docopt-style build interface without a second schema.
/// Expands sources into C compiler argv vectors. It deliberately does not run cc.
fn plan(argv : Array[String]) -> Json raise @docopt.DocoptError {
  let parser = @docopt.Parser::new(
    "Usage: plan (debug|release) [--define=KV]... [--out=DIR] ...\nOptions:\n  --define=KV  Preprocessor definition\n  --out=DIR  Output directory [default: build]",
  )
  let v = parser.parse(argv)
  let output = v.require_text("--out")
  guard output != "" else {
    raise @docopt.Conversion("output directory must not be empty")
  }
  let sources = v.texts("")
  let definitions = v.texts("--define")
  let jobs = []
  for index, source in sources {
    guard source.has_suffix(".c") && source.length() > 2 else {
      raise @docopt.Conversion("source must end in .c")
    }
    let args = ["cc", "-c", source, "-o", output + "/object-\{index}.o"]
    args.push(if v.flag("release") { "-O2" } else { "-g" })
    for definition in definitions {
      guard definition != "" && !definition.has_prefix("=") else {
        raise @docopt.Conversion("invalid definition")
      }
      args.push("-D" + definition)
    }
    jobs.push(
      (
        {
          "argv": Json(args),
          "display_posix": Json(@docopt.join_command(args)),
        } : Json),
    )
  }
  { "jobs": Json::array(jobs), "object_count": Json(sources.length()) }
}

///|
fn main {
  let args = @cliargs.arguments()
  println(
    plan(
      if args.is_empty() {
        ["release", "--define=LOG=1", "src/main.c"]
      } else {
        args
      },
    ).stringify(),
  ) catch {
    e => println("error: " + e.message())
  }
}