| author | wenzelm | 
| Tue, 22 Oct 2024 12:45:38 +0200 | |
| changeset 81229 | e18600daa904 | 
| parent 80196 | 9308bc5f65d6 | 
| permissions | -rw-r--r-- | 
| 75555 
197a5b3a1ea2
promote "isabelle sync" to regular user-space tool, with proper documentation;
 wenzelm parents: 
75553diff
changeset | 1 | /* Title: Pure/Tools/sync.scala | 
| 75481 | 2 | Author: Makarius | 
| 3 | ||
| 4 | Synchronize Isabelle + AFP repositories. | |
| 5 | */ | |
| 6 | ||
| 7 | package isabelle | |
| 8 | ||
| 9 | ||
| 75549 | 10 | object Sync {
 | 
| 75552 
4aa3da02fd4d
sync session images, based on accidental local state;
 wenzelm parents: 
75549diff
changeset | 11 | /* session images */ | 
| 
4aa3da02fd4d
sync session images, based on accidental local state;
 wenzelm parents: 
75549diff
changeset | 12 | |
| 
4aa3da02fd4d
sync session images, based on accidental local state;
 wenzelm parents: 
75549diff
changeset | 13 | def find_images(options: Options, session_images: List[String], | 
| 
4aa3da02fd4d
sync session images, based on accidental local state;
 wenzelm parents: 
75549diff
changeset | 14 | dirs: List[Path] = Nil | 
| 
4aa3da02fd4d
sync session images, based on accidental local state;
 wenzelm parents: 
75549diff
changeset | 15 |   ): List[String] = {
 | 
| 
4aa3da02fd4d
sync session images, based on accidental local state;
 wenzelm parents: 
75549diff
changeset | 16 | if (session_images.isEmpty) Nil | 
| 
4aa3da02fd4d
sync session images, based on accidental local state;
 wenzelm parents: 
75549diff
changeset | 17 |     else {
 | 
| 78178 | 18 | val store = Store(options) | 
| 75552 
4aa3da02fd4d
sync session images, based on accidental local state;
 wenzelm parents: 
75549diff
changeset | 19 | val sessions_structure = Sessions.load_structure(options, dirs = dirs) | 
| 79662 | 20 |       sessions_structure.build_requirements(session_images).flatMap { name =>
 | 
| 21 | val session = store.get_session(name) | |
| 75552 
4aa3da02fd4d
sync session images, based on accidental local state;
 wenzelm parents: 
75549diff
changeset | 22 | val heap = | 
| 79662 | 23 | session.heap.map(_.expand).map(path => | 
| 75552 
4aa3da02fd4d
sync session images, based on accidental local state;
 wenzelm parents: 
75549diff
changeset | 24 | File.standard_path(path.dir.dir) + "/./" + path.dir.file_name + "/" + path.file_name) | 
| 79662 | 25 | val log_db = | 
| 26 | session.log_db.map(_.expand).map(path => | |
| 75552 
4aa3da02fd4d
sync session images, based on accidental local state;
 wenzelm parents: 
75549diff
changeset | 27 | File.standard_path(path.dir.dir.dir) + "/./" + path.dir.dir.file_name + "/" + | 
| 
4aa3da02fd4d
sync session images, based on accidental local state;
 wenzelm parents: 
75549diff
changeset | 28 | path.dir.file_name + "/" + path.file_name) | 
| 79662 | 29 | heap.toList ::: log_db.toList | 
| 75552 
4aa3da02fd4d
sync session images, based on accidental local state;
 wenzelm parents: 
75549diff
changeset | 30 | } | 
| 
4aa3da02fd4d
sync session images, based on accidental local state;
 wenzelm parents: 
75549diff
changeset | 31 | } | 
| 
4aa3da02fd4d
sync session images, based on accidental local state;
 wenzelm parents: 
75549diff
changeset | 32 | } | 
| 
4aa3da02fd4d
sync session images, based on accidental local state;
 wenzelm parents: 
75549diff
changeset | 33 | |
| 
4aa3da02fd4d
sync session images, based on accidental local state;
 wenzelm parents: 
75549diff
changeset | 34 | |
| 
4aa3da02fd4d
sync session images, based on accidental local state;
 wenzelm parents: 
75549diff
changeset | 35 | /* sync */ | 
| 
4aa3da02fd4d
sync session images, based on accidental local state;
 wenzelm parents: 
75549diff
changeset | 36 | |
| 80196 | 37 |   val DIRS: Path = Path.basic("dirs")
 | 
| 38 | val DIRS_ROOTS: Path = DIRS + Sessions.ROOTS | |
| 39 | ||
| 40 |   sealed case class Dir(name: String, root: Path, path: Path = Path.current, rev: String = "") {
 | |
| 41 | lazy val hg: Mercurial.Repository = Mercurial.repository(root) | |
| 42 | def check(): Unit = hg | |
| 43 | ||
| 44 | def source: Path = root + path | |
| 45 | def target: Path = DIRS + Path.basic(name) | |
| 46 | def roots_entry: String = ((if (name.isEmpty) Path.parent else Path.basic(name)) + path).implode | |
| 47 | } | |
| 48 | ||
| 49 | def afp_dir(base_dir: Path = AFP.BASE, rev: String = ""): Dir = | |
| 50 |     Dir("AFP", base_dir, path = AFP.main_dir(base_dir = Path.current), rev = rev)
 | |
| 51 | ||
| 52 | def afp_dirs(root: Option[Path] = None, rev: String = ""): List[Dir] = | |
| 53 | root.toList.map(base_dir => afp_dir(base_dir = base_dir, rev = rev)) | |
| 54 | ||
| 77783 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77518diff
changeset | 55 | def sync(options: Options, context: Rsync.Context, target: Path, | 
| 75487 | 56 | thorough: Boolean = false, | 
| 75553 | 57 | purge_heaps: Boolean = false, | 
| 75552 
4aa3da02fd4d
sync session images, based on accidental local state;
 wenzelm parents: 
75549diff
changeset | 58 | session_images: List[String] = Nil, | 
| 75492 | 59 | preserve_jars: Boolean = false, | 
| 75481 | 60 | dry_run: Boolean = false, | 
| 61 | rev: String = "", | |
| 80196 | 62 | dirs: List[Dir] = Nil | 
| 75481 | 63 |   ): Unit = {
 | 
| 77517 | 64 | val progress = context.progress | 
| 65 | ||
| 80191 | 66 | val self = Mercurial.self_repository() | 
| 80196 | 67 | dirs.foreach(_.check()) | 
| 68 | ||
| 69 |     val sync_dirs: List[Dir] = {
 | |
| 70 | val m = | |
| 71 | Multi_Map.from( | |
| 72 | for (dir <- dirs.iterator if dir.name.nonEmpty) yield dir.name -> dir.root.canonical) | |
| 73 |       for ((name, roots) <- m.iterator_list if roots.length > 1) {
 | |
| 74 |         error("Incoherent sync directory " + quote(name) + ":\n" +
 | |
| 75 | cat_lines(roots.reverse.map(p => " " + p.toString))) | |
| 76 | } | |
| 77 | Library.distinct(dirs, (d1: Dir, d2: Dir) => d1.name == d2.name) | |
| 78 | } | |
| 75481 | 79 | |
| 75492 | 80 |     val more_filter = if (preserve_jars) List("include *.jar", "protect *.jar") else Nil
 | 
| 81 | ||
| 80191 | 82 | def synchronize(src: Mercurial.Repository, dest: Path, r: String, | 
| 75511 | 83 | contents: List[File.Content] = Nil, filter: List[String] = Nil | 
| 84 |     ): Unit = {
 | |
| 80191 | 85 | src.sync(context, dest, rev = r, thorough = thorough, dry_run = dry_run, | 
| 75525 
68162e4f60a7
clarified signature: more explicit type Rsync.Context;
 wenzelm parents: 
75524diff
changeset | 86 | contents = contents, filter = filter ::: more_filter) | 
| 75511 | 87 | } | 
| 75481 | 88 | |
| 80191 | 89 |     progress.echo("\n* Isabelle:", verbose = true)
 | 
| 75553 | 90 |     val filter_heaps = if (purge_heaps) Nil else List("protect /heaps", "protect /heaps/**")
 | 
| 80196 | 91 | val filter_dirs = sync_dirs.map(dir => "protect /" + dir.target.implode) | 
| 80191 | 92 | synchronize(self, target, rev, | 
| 93 |       contents = List(File.content(Path.explode("etc/ISABELLE_ID"), self.id(rev = rev))),
 | |
| 80196 | 94 | filter = filter_heaps ::: filter_dirs) | 
| 75481 | 95 | |
| 80196 | 96 | context.ssh.make_directory(target + DIRS) | 
| 97 | context.ssh.write(target + DIRS_ROOTS, Library.terminate_lines(dirs.map(_.roots_entry))) | |
| 98 | ||
| 99 |     for (dir <- sync_dirs) {
 | |
| 100 |       progress.echo("\n* " + dir.name + ":", verbose = true)
 | |
| 101 | synchronize(dir.hg, target + dir.target, dir.rev) | |
| 75481 | 102 | } | 
| 75552 
4aa3da02fd4d
sync session images, based on accidental local state;
 wenzelm parents: 
75549diff
changeset | 103 | |
| 80196 | 104 | val images = find_images(options, session_images, dirs = dirs.map(_.source)) | 
| 75552 
4aa3da02fd4d
sync session images, based on accidental local state;
 wenzelm parents: 
75549diff
changeset | 105 |     if (images.nonEmpty) {
 | 
| 77518 
fda4da0f80f4
clarified signature: manage "verbose" flag via "progress";
 wenzelm parents: 
77517diff
changeset | 106 |       progress.echo("\n* Session images:", verbose = true)
 | 
| 77783 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77518diff
changeset | 107 |       val heaps = context.target(target + Path.explode("heaps")) + "/"
 | 
| 77518 
fda4da0f80f4
clarified signature: manage "verbose" flag via "progress";
 wenzelm parents: 
77517diff
changeset | 108 | Rsync.exec(context, thorough = thorough, dry_run = dry_run, | 
| 76617 | 109 |         args = List("--relative", "--") ::: images ::: List(heaps)).check
 | 
| 75552 
4aa3da02fd4d
sync session images, based on accidental local state;
 wenzelm parents: 
75549diff
changeset | 110 | } | 
| 75481 | 111 | } | 
| 112 | ||
| 113 | val isabelle_tool = | |
| 75549 | 114 |     Isabelle_Tool("sync", "synchronize Isabelle + AFP repositories",
 | 
| 75481 | 115 |       Scala_Project.here, { args =>
 | 
| 116 | var afp_root: Option[Path] = None | |
| 75553 | 117 | var purge_heaps = false | 
| 75552 
4aa3da02fd4d
sync session images, based on accidental local state;
 wenzelm parents: 
75549diff
changeset | 118 | var session_images = List.empty[String] | 
| 75492 | 119 | var preserve_jars = false | 
| 75487 | 120 | var thorough = false | 
| 75481 | 121 | var afp_rev = "" | 
| 122 | var dry_run = false | |
| 76136 
1bb677cceea4
let rsync re-use ssh connection via control path;
 wenzelm parents: 
76134diff
changeset | 123 | var ssh_port = 0 | 
| 75481 | 124 | var rev = "" | 
| 77783 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77518diff
changeset | 125 | var ssh_host = "" | 
| 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77518diff
changeset | 126 | var ssh_user = "" | 
| 75481 | 127 | var verbose = false | 
| 128 | ||
| 129 |         val getopts = Getopts("""
 | |
| 75549 | 130 | Usage: isabelle sync [OPTIONS] TARGET | 
| 75481 | 131 | |
| 132 | Options are: | |
| 75497 | 133 |     -A ROOT      include AFP with given root directory (":" for """ + AFP.BASE.implode + """)
 | 
| 75553 | 134 | -H purge heaps directory on target | 
| 75552 
4aa3da02fd4d
sync session images, based on accidental local state;
 wenzelm parents: 
75549diff
changeset | 135 | -I NAME include session heap image and build database | 
| 
4aa3da02fd4d
sync session images, based on accidental local state;
 wenzelm parents: 
75549diff
changeset | 136 | (based on accidental local state) | 
| 75492 | 137 | -J preserve *.jar files | 
| 75493 | 138 | -T thorough treatment of file content and directory times | 
| 75481 | 139 | -a REV explicit AFP revision (default: state of working directory) | 
| 77783 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77518diff
changeset | 140 | -s HOST SSH host name for remote target (default: local) | 
| 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77518diff
changeset | 141 | -u USER explicit SSH user name | 
| 75481 | 142 | -n no changes: dry-run | 
| 77783 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77518diff
changeset | 143 | -p PORT explicit SSH port | 
| 75481 | 144 | -r REV explicit revision (default: state of working directory) | 
| 145 | -v verbose | |
| 146 | ||
| 75555 
197a5b3a1ea2
promote "isabelle sync" to regular user-space tool, with proper documentation;
 wenzelm parents: 
75553diff
changeset | 147 | Synchronize Isabelle + AFP repositories, based on "isabelle hg_sync". | 
| 75481 | 148 | """, | 
| 75497 | 149 | "A:" -> (arg => afp_root = Some(if (arg == ":") AFP.BASE else Path.explode(arg))), | 
| 75553 | 150 | "H" -> (_ => purge_heaps = true), | 
| 75552 
4aa3da02fd4d
sync session images, based on accidental local state;
 wenzelm parents: 
75549diff
changeset | 151 | "I:" -> (arg => session_images = session_images ::: List(arg)), | 
| 75492 | 152 | "J" -> (_ => preserve_jars = true), | 
| 75487 | 153 | "T" -> (_ => thorough = true), | 
| 75481 | 154 | "a:" -> (arg => afp_rev = arg), | 
| 155 | "n" -> (_ => dry_run = true), | |
| 76136 
1bb677cceea4
let rsync re-use ssh connection via control path;
 wenzelm parents: 
76134diff
changeset | 156 | "p:" -> (arg => ssh_port = Value.Int.parse(arg)), | 
| 75481 | 157 | "r:" -> (arg => rev = arg), | 
| 77783 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77518diff
changeset | 158 | "s:" -> (arg => ssh_host = arg), | 
| 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77518diff
changeset | 159 | "u:" -> (arg => ssh_user = arg), | 
| 75481 | 160 | "v" -> (_ => verbose = true)) | 
| 161 | ||
| 162 | val more_args = getopts(args) | |
| 163 | val target = | |
| 164 |           more_args match {
 | |
| 77783 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77518diff
changeset | 165 | case List(target) => Path.explode(target) | 
| 75481 | 166 | case _ => getopts.usage() | 
| 167 | } | |
| 168 | ||
| 75552 
4aa3da02fd4d
sync session images, based on accidental local state;
 wenzelm parents: 
75549diff
changeset | 169 | val options = Options.init() | 
| 77518 
fda4da0f80f4
clarified signature: manage "verbose" flag via "progress";
 wenzelm parents: 
77517diff
changeset | 170 | val progress = new Console_Progress(verbose = verbose) | 
| 77783 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77518diff
changeset | 171 | |
| 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77518diff
changeset | 172 |         using(SSH.open_system(options, host = ssh_host, port = ssh_port, user = ssh_user)) { ssh =>
 | 
| 77795 | 173 | val context = Rsync.Context(progress = progress, ssh = ssh, stats = verbose) | 
| 77783 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77518diff
changeset | 174 | sync(options, context, target, thorough = thorough, purge_heaps = purge_heaps, | 
| 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77518diff
changeset | 175 | session_images = session_images, preserve_jars = preserve_jars, dry_run = dry_run, | 
| 80196 | 176 | rev = rev, dirs = afp_dirs(afp_root, afp_rev)) | 
| 77783 
fb61887c069a
clarified underlying SSH session of "isabelle hg_sync" and "isabelle sync";
 wenzelm parents: 
77518diff
changeset | 177 | } | 
| 75481 | 178 | } | 
| 179 | ) | |
| 180 | } |