wenzelm@48276: /* Title: Pure/System/build.scala wenzelm@48276: Author: Makarius wenzelm@48276: wenzelm@48276: Build and manage Isabelle sessions. wenzelm@48276: */ wenzelm@48276: wenzelm@48276: package isabelle wenzelm@48276: wenzelm@48276: wenzelm@48335: import java.io.File wenzelm@48335: wenzelm@48337: import scala.collection.mutable wenzelm@48340: import scala.annotation.tailrec wenzelm@48337: wenzelm@48335: wenzelm@48276: object Build wenzelm@48276: { wenzelm@48276: /* command line entry point */ wenzelm@48276: wenzelm@48276: private object Bool wenzelm@48276: { wenzelm@48276: def unapply(s: String): Option[Boolean] = wenzelm@48276: s match { wenzelm@48276: case "true" => Some(true) wenzelm@48276: case "false" => Some(false) wenzelm@48276: case _ => None wenzelm@48276: } wenzelm@48276: } wenzelm@48276: wenzelm@48340: private object Chunks wenzelm@48340: { wenzelm@48340: private def chunks(list: List[String]): List[List[String]] = wenzelm@48340: list.indexWhere(_ == "\n") match { wenzelm@48340: case -1 => List(list) wenzelm@48340: case i => wenzelm@48340: val (chunk, rest) = list.splitAt(i) wenzelm@48340: chunk :: chunks(rest.tail) wenzelm@48340: } wenzelm@48340: def unapplySeq(list: List[String]): Option[List[List[String]]] = Some(chunks(list)) wenzelm@48340: } wenzelm@48340: wenzelm@48276: def main(args: Array[String]) wenzelm@48276: { wenzelm@48335: val rc = wenzelm@48335: try { wenzelm@48335: args.toList match { wenzelm@48340: case Bool(all_sessions) :: Bool(build_images) :: Bool(list_only) :: wenzelm@48340: Chunks(more_dirs, options, sessions) => wenzelm@48340: build(all_sessions, build_images, list_only, wenzelm@48340: more_dirs.map(Path.explode), options, sessions) wenzelm@48340: case _ => error("Bad arguments:\n" + cat_lines(args)) wenzelm@48276: } wenzelm@48335: } wenzelm@48335: catch { wenzelm@48335: case exn: Throwable => java.lang.System.err.println(Exn.message(exn)); 2 wenzelm@48335: } wenzelm@48335: sys.exit(rc) wenzelm@48276: } wenzelm@48276: wenzelm@48276: wenzelm@48276: /* build */ wenzelm@48276: wenzelm@48276: def build(all_sessions: Boolean, build_images: Boolean, list_only: Boolean, wenzelm@48340: more_dirs: List[Path], options: List[String], sessions: List[String]): Int = wenzelm@48276: { wenzelm@48340: println("more_dirs = " + more_dirs.toString) wenzelm@48276: println("options = " + options.toString) wenzelm@48276: println("sessions = " + sessions.toString) wenzelm@48276: wenzelm@48340: find_sessions(more_dirs) foreach println wenzelm@48335: wenzelm@48335: 0 wenzelm@48276: } wenzelm@48280: wenzelm@48280: wenzelm@48337: /** session information **/ wenzelm@48334: wenzelm@48334: type Options = List[(String, Option[String])] wenzelm@48334: wenzelm@48280: case class Session_Info( wenzelm@48334: dir: Path, wenzelm@48334: name: String, wenzelm@48334: parent: String, wenzelm@48334: description: String, wenzelm@48334: options: Options, wenzelm@48334: theories: List[(Options, String)], wenzelm@48334: files: List[String]) wenzelm@48334: wenzelm@48337: private val pure_info = wenzelm@48337: Session_Info(Path.current, "Pure", "", "", Nil, List((Nil, "Pure")), Nil) wenzelm@48337: wenzelm@48337: wenzelm@48337: /* parsing */ wenzelm@48337: wenzelm@48337: val ROOT_NAME = "ROOT" wenzelm@48337: wenzelm@48337: private case class Session_Entry( wenzelm@48337: name: String, wenzelm@48337: reset: Boolean, wenzelm@48337: path: Option[String], wenzelm@48337: parent: String, wenzelm@48337: description: String, wenzelm@48337: options: Options, wenzelm@48337: theories: List[(Options, List[String])], wenzelm@48337: files: List[String]) wenzelm@48337: wenzelm@48334: private object Parser extends Parse.Parser wenzelm@48334: { wenzelm@48334: val SESSION = "session" wenzelm@48334: val IN = "in" wenzelm@48334: val DESCRIPTION = "description" wenzelm@48334: val OPTIONS = "options" wenzelm@48334: val THEORIES = "theories" wenzelm@48334: val FILES = "files" wenzelm@48334: wenzelm@48334: val syntax = wenzelm@48336: Outer_Syntax.empty + "!" + "(" + ")" + "+" + "," + "=" + "[" + "]" + wenzelm@48336: SESSION + IN + DESCRIPTION + OPTIONS + THEORIES + FILES wenzelm@48334: wenzelm@48337: val session_entry: Parser[Session_Entry] = wenzelm@48334: { wenzelm@48334: val session_name = atom("session name", _.is_name) wenzelm@48334: val theory_name = atom("theory name", _.is_name) wenzelm@48280: wenzelm@48334: val option = wenzelm@48334: name ~ opt(keyword("=") ~! name ^^ { case _ ~ x => x }) ^^ { case x ~ y => (x, y) } wenzelm@48337: val options = keyword("[") ~> repsep(option, keyword(",")) <~ keyword("]") wenzelm@48334: wenzelm@48334: val theories = wenzelm@48334: keyword(THEORIES) ~! ((options | success(Nil)) ~ rep1(theory_name)) ^^ wenzelm@48334: { case _ ~ (x ~ y) => (x, y) } wenzelm@48334: wenzelm@48334: ((keyword(SESSION) ~! session_name) ^^ { case _ ~ x => x }) ~ wenzelm@48336: (keyword("!") ^^^ true | success(false)) ~ wenzelm@48337: (opt(keyword(IN) ~! string ^^ { case _ ~ x => x })) ~ wenzelm@48334: (keyword("=") ~> session_name <~ keyword("+")) ~ wenzelm@48334: (keyword(DESCRIPTION) ~! text ^^ { case _ ~ x => x } | success("")) ~ wenzelm@48334: (keyword(OPTIONS) ~! options ^^ { case _ ~ x => x } | success(Nil)) ~ wenzelm@48337: rep1(theories) ~ wenzelm@48336: (keyword(FILES) ~! rep1(path) ^^ { case _ ~ x => x } | success(Nil)) ^^ wenzelm@48337: { case a ~ b ~ c ~ d ~ e ~ f ~ g ~ h => Session_Entry(a, b, c, d, e, f, g, h) } wenzelm@48334: } wenzelm@48334: wenzelm@48337: def parse_entries(root: File): List[Session_Entry] = wenzelm@48334: { wenzelm@48335: val toks = syntax.scan(Standard_System.read_file(root)) wenzelm@48337: parse_all(rep(session_entry), Token.reader(toks, root.toString)) match { wenzelm@48334: case Success(result, _) => result wenzelm@48334: case bad => error(bad.toString) wenzelm@48334: } wenzelm@48334: } wenzelm@48334: } wenzelm@48280: wenzelm@48337: wenzelm@48337: /* find session */ wenzelm@48337: wenzelm@48340: def find_sessions(more_dirs: List[Path]): List[Session_Info] = wenzelm@48280: { wenzelm@48337: val infos = new mutable.ListBuffer[Session_Info] wenzelm@48337: infos += pure_info wenzelm@48337: wenzelm@48280: for { wenzelm@48340: (dir, strict) <- Isabelle_System.components().map((_, false)) ++ more_dirs.map((_, true)) wenzelm@48280: root = Isabelle_System.platform_file(dir + Path.basic(ROOT_NAME)) wenzelm@48340: _ = (strict && !root.isFile && error("Bad session root file: " + quote(root.toString))) wenzelm@48280: if root.isFile wenzelm@48337: entry <- Parser.parse_entries(root) wenzelm@48337: } wenzelm@48337: { wenzelm@48337: try { wenzelm@48337: val parent = wenzelm@48337: infos.find(_.name == entry.parent) getOrElse wenzelm@48337: error("Unknown parent session: " + quote(entry.parent)) wenzelm@48337: val full_name = wenzelm@48337: if (entry.reset) entry.name wenzelm@48337: else parent.name + "-" + entry.name wenzelm@48339: wenzelm@48339: if (entry.name == "") error("Bad session name") wenzelm@48339: if (infos.exists(_.name == full_name)) wenzelm@48339: error("Duplicate session name: " + quote(full_name)) wenzelm@48339: wenzelm@48337: val path = wenzelm@48337: entry.path match { wenzelm@48337: case Some(p) => Path.explode(p) wenzelm@48337: case None => Path.basic(entry.name) wenzelm@48337: } wenzelm@48337: val thys = entry.theories.map({ case (x, ys) => ys.map(y => (x, y)) }).flatten wenzelm@48337: val info = wenzelm@48337: Session_Info(dir + path, full_name, entry.parent, entry.description, wenzelm@48337: entry.options, thys, entry.files) wenzelm@48339: wenzelm@48337: infos += info wenzelm@48337: } wenzelm@48337: catch { wenzelm@48337: case ERROR(msg) => wenzelm@48337: error(msg + "\nThe error(s) above occurred in session entry " + wenzelm@48337: quote(entry.name) + " (file " + quote(root.toString) + ")") wenzelm@48337: } wenzelm@48337: } wenzelm@48337: infos.toList wenzelm@48280: } wenzelm@48276: } wenzelm@48276: