src/Pure/General/rsync.scala
author wenzelm
Thu, 15 Sep 2022 21:37:17 +0200
changeset 76166 dbafa8d688fb
parent 76136 1bb677cceea4
child 76170 5912209b4fb6
permissions -rw-r--r--
discontinued unclear timeout (stemming from jEdit JSch setup, see 14782d58a503), to make it work with native Windows ssh.exe;
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
75523
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
     1
/*  Title:      Pure/General/rsync.scala
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
     2
    Author:     Makarius
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
     3
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
     4
Support for rsync: see also https://rsync.samba.org
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
     5
*/
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
     6
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
     7
package isabelle
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
     8
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
     9
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
    10
object Rsync {
75527
a66fd84a30b7 clarified context with global defaults;
wenzelm
parents: 75526
diff changeset
    11
  sealed case class Context(progress: Progress,
76136
1bb677cceea4 let rsync re-use ssh connection via control path;
wenzelm
parents: 76131
diff changeset
    12
    ssh_port: Int = 0,
1bb677cceea4 let rsync re-use ssh connection via control path;
wenzelm
parents: 76131
diff changeset
    13
    ssh_control_path: String = "",
75527
a66fd84a30b7 clarified context with global defaults;
wenzelm
parents: 75526
diff changeset
    14
    archive: Boolean = true,
75554
be33ca6f45d7 more comments;
wenzelm
parents: 75534
diff changeset
    15
    protect_args: Boolean = true  // requires rsync 3.0.0, or later
75527
a66fd84a30b7 clarified context with global defaults;
wenzelm
parents: 75526
diff changeset
    16
  ) {
76136
1bb677cceea4 let rsync re-use ssh connection via control path;
wenzelm
parents: 76131
diff changeset
    17
    require(ssh_control_path.isEmpty || ssh_control_path == Bash.string(ssh_control_path),
1bb677cceea4 let rsync re-use ssh connection via control path;
wenzelm
parents: 76131
diff changeset
    18
      "Malformed socket path: " + quote(ssh_control_path))
1bb677cceea4 let rsync re-use ssh connection via control path;
wenzelm
parents: 76131
diff changeset
    19
1bb677cceea4 let rsync re-use ssh connection via control path;
wenzelm
parents: 76131
diff changeset
    20
    def command: String = {
1bb677cceea4 let rsync re-use ssh connection via control path;
wenzelm
parents: 76131
diff changeset
    21
      val ssh_command =
1bb677cceea4 let rsync re-use ssh connection via control path;
wenzelm
parents: 76131
diff changeset
    22
        "ssh" + (if (ssh_port > 0) " -p " + ssh_port else "") +
1bb677cceea4 let rsync re-use ssh connection via control path;
wenzelm
parents: 76131
diff changeset
    23
        (if (ssh_control_path.nonEmpty) " -o ControlPath=" + ssh_control_path else "")
1bb677cceea4 let rsync re-use ssh connection via control path;
wenzelm
parents: 76131
diff changeset
    24
1bb677cceea4 let rsync re-use ssh connection via control path;
wenzelm
parents: 76131
diff changeset
    25
      "rsync --rsh=" + Bash.string(ssh_command) +
75527
a66fd84a30b7 clarified context with global defaults;
wenzelm
parents: 75526
diff changeset
    26
        (if (archive) " --archive" else "") +
a66fd84a30b7 clarified context with global defaults;
wenzelm
parents: 75526
diff changeset
    27
        (if (protect_args) " --protect-args" else "")
76136
1bb677cceea4 let rsync re-use ssh connection via control path;
wenzelm
parents: 76131
diff changeset
    28
    }
75525
68162e4f60a7 clarified signature: more explicit type Rsync.Context;
wenzelm
parents: 75524
diff changeset
    29
  }
68162e4f60a7 clarified signature: more explicit type Rsync.Context;
wenzelm
parents: 75524
diff changeset
    30
75526
57b6a28e4eba tuned signature;
wenzelm
parents: 75525
diff changeset
    31
  def exec(
75525
68162e4f60a7 clarified signature: more explicit type Rsync.Context;
wenzelm
parents: 75524
diff changeset
    32
    context: Context,
75523
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
    33
    verbose: Boolean = false,
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
    34
    thorough: Boolean = false,
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
    35
    prune_empty_dirs: Boolean = false,
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
    36
    dry_run: Boolean = false,
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
    37
    clean: Boolean = false,
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
    38
    list: Boolean = false,
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
    39
    filter: List[String] = Nil,
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
    40
    args: List[String] = Nil
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
    41
  ): Process_Result = {
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
    42
    val script =
75525
68162e4f60a7 clarified signature: more explicit type Rsync.Context;
wenzelm
parents: 75524
diff changeset
    43
      context.command +
75523
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
    44
        (if (verbose) " --verbose" else "") +
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
    45
        (if (thorough) " --ignore-times" else " --omit-dir-times") +
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
    46
        (if (prune_empty_dirs) " --prune-empty-dirs" else "") +
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
    47
        (if (dry_run) " --dry-run" else "") +
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
    48
        (if (clean) " --delete-excluded" else "") +
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
    49
        (if (list) " --list-only --no-human-readable" else "") +
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
    50
        filter.map(s => " --filter=" + Bash.string(s)).mkString +
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
    51
        (if (args.nonEmpty) " " + Bash.strings(args) else "")
75525
68162e4f60a7 clarified signature: more explicit type Rsync.Context;
wenzelm
parents: 75524
diff changeset
    52
    context.progress.bash(script, echo = true)
75523
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
    53
  }
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
    54
75526
57b6a28e4eba tuned signature;
wenzelm
parents: 75525
diff changeset
    55
  def init(context: Context, target: String,
75523
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
    56
    contents: List[File.Content] = Nil
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
    57
  ): Unit =
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
    58
    Isabelle_System.with_tmp_dir("sync") { tmp_dir =>
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
    59
      val init_dir = Isabelle_System.make_directory(tmp_dir + Path.explode("init"))
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
    60
      contents.foreach(_.write(init_dir))
75534
1d937b12204d more robust: no change of directory attributes of initial test, notably target without .hg_sync meta data;
wenzelm
parents: 75527
diff changeset
    61
      exec(context.copy(archive = false),
1d937b12204d more robust: no change of directory attributes of initial test, notably target without .hg_sync meta data;
wenzelm
parents: 75527
diff changeset
    62
        thorough = true,
1d937b12204d more robust: no change of directory attributes of initial test, notably target without .hg_sync meta data;
wenzelm
parents: 75527
diff changeset
    63
        args =
1d937b12204d more robust: no change of directory attributes of initial test, notably target without .hg_sync meta data;
wenzelm
parents: 75527
diff changeset
    64
          List(if (contents.nonEmpty) "--archive" else "--dirs",
1d937b12204d more robust: no change of directory attributes of initial test, notably target without .hg_sync meta data;
wenzelm
parents: 75527
diff changeset
    65
            File.bash_path(init_dir) + "/.", target)).check
75523
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
    66
    }
75524
ff8012edac89 clarified signature;
wenzelm
parents: 75523
diff changeset
    67
ff8012edac89 clarified signature;
wenzelm
parents: 75523
diff changeset
    68
  def terminate(target: String): String =
ff8012edac89 clarified signature;
wenzelm
parents: 75523
diff changeset
    69
    if (target.endsWith(":") || target.endsWith("/")) target + "."
ff8012edac89 clarified signature;
wenzelm
parents: 75523
diff changeset
    70
    else if (target.endsWith(":.") || target.endsWith("/.")) target
ff8012edac89 clarified signature;
wenzelm
parents: 75523
diff changeset
    71
    else target + "/."
ff8012edac89 clarified signature;
wenzelm
parents: 75523
diff changeset
    72
ff8012edac89 clarified signature;
wenzelm
parents: 75523
diff changeset
    73
  def append(target: String, rest: String): String =
ff8012edac89 clarified signature;
wenzelm
parents: 75523
diff changeset
    74
    if (target.endsWith(":") || target.endsWith("/")) target + rest
ff8012edac89 clarified signature;
wenzelm
parents: 75523
diff changeset
    75
    else target + "/" + rest
75523
0dcaf0e5107b clarified modules;
wenzelm
parents:
diff changeset
    76
}