src/Pure/Tools/dotnet_setup.scala
changeset 76458 cc6fd1695294
child 76461 0869eacad310
equal deleted inserted replaced
76457:badbae70c51a 76458:cc6fd1695294
       
     1 /*  Title:      Pure/Tools/dotnet_setup.scala
       
     2     Author:     Makarius
       
     3 
       
     4 Dynamic setup of dotnet component.
       
     5 */
       
     6 
       
     7 package isabelle
       
     8 
       
     9 
       
    10 object Dotnet_Setup {
       
    11   /* platforms */
       
    12 
       
    13   sealed case class Platform_Info(
       
    14     family: Platform.Family.Value,
       
    15     name: String,
       
    16     os: String = "",
       
    17     arch: String = "x64",
       
    18     ext: String = "sh",
       
    19     exec: String = "bash",
       
    20     check: () => Unit = () => ())
       
    21 
       
    22   val all_platforms =
       
    23     List(
       
    24       Platform_Info(Platform.Family.linux_arm, "arm64-linux", os = "linux", arch = "arm64"),
       
    25       Platform_Info(Platform.Family.linux, "x86_64-linux", os = "linux"),
       
    26       Platform_Info(Platform.Family.macos, "arm64-darwin", os = "osx", arch = "arm64"),
       
    27       Platform_Info(Platform.Family.macos, "x86_64-darwin", os = "osx"),
       
    28       Platform_Info(Platform.Family.windows, "x86_64-windows",
       
    29         ext = "ps1",
       
    30         exec = "powershell -ExecutionPolicy ByPass",
       
    31         check = () => Isabelle_System.require_command("powershell", "-NoProfile -Command Out-Null")))
       
    32 
       
    33   def check_platform_spec(spec: String): String = {
       
    34     val all_specs =
       
    35       Library.distinct(all_platforms.map(_.family.toString) ::: all_platforms.map(_.name))
       
    36     if (all_specs.contains(spec)) spec
       
    37     else {
       
    38       error("Bad platform specification " + quote(spec) +
       
    39         "\n  expected " + commas_quote(all_specs))
       
    40     }
       
    41   }
       
    42 
       
    43 
       
    44   /* dotnet download and setup */
       
    45 
       
    46   def default_platform: String = Platform.Family.native(Platform.family)
       
    47   def default_target_dir: Path = Path.explode("$ISABELLE_COMPONENTS_BASE")
       
    48   def default_install_url: String = "https://dot.net/v1/dotnet-install"
       
    49   def default_version: String = "6.0.402"
       
    50 
       
    51   private val settings = """# -*- shell-script -*- :mode=shellscript:
       
    52 
       
    53 if [ -n "$ISABELLE_WINDOWS_PLATFORM64" -a -d "$COMPONENT/$ISABELLE_WINDOWS_PLATFORM64" ]; then
       
    54   ISABELLE_DOTNET="$COMPONENT/$ISABELLE_WINDOWS_PLATFORM64/dotnet.exe"
       
    55 elif [ -n "$ISABELLE_APPLE_PLATFORM64" -a -d "$COMPONENT/$ISABELLE_APPLE_PLATFORM64" ]; then
       
    56   ISABELLE_DOTNET="$COMPONENT/$ISABELLE_APPLE_PLATFORM64/dotnet"
       
    57 elif [ -d "$COMPONENT/$ISABELLE_PLATFORM64" ]; then
       
    58   ISABELLE_DOTNET="$COMPONENT/$ISABELLE_PLATFORM64/dotnet"
       
    59 fi
       
    60 """
       
    61 
       
    62   def dotnet_setup(
       
    63     platform_spec: String = default_platform,
       
    64     target_dir: Path = default_target_dir,
       
    65     install_url: String = default_install_url,
       
    66     version: String = default_version,
       
    67     dry_run: Boolean = false,
       
    68     verbose: Boolean = false,
       
    69     progress: Progress = new Progress
       
    70   ): Unit = {
       
    71     check_platform_spec(platform_spec)
       
    72 
       
    73     for {
       
    74       platform <- all_platforms
       
    75       if platform.family.toString == platform_spec || platform.name == platform_spec
       
    76     } {
       
    77       progress.expose_interrupt()
       
    78       platform.check()
       
    79 
       
    80 
       
    81       /* component directory */
       
    82 
       
    83       val component_dir =
       
    84         target_dir + Path.explode(if (version.isEmpty) "dotnet-latest" else "dotnet-" + version)
       
    85 
       
    86       if (!dry_run) {
       
    87         val etc_dir = Isabelle_System.make_directory(component_dir + Path.explode("etc"))
       
    88         val settings_path = etc_dir + Path.explode("settings")
       
    89         if (!settings_path.is_file) {
       
    90           progress.echo("Component " + component_dir.expand)
       
    91           File.write(settings_path, settings)
       
    92         }
       
    93 
       
    94         File.write(component_dir + Path.explode("README"),
       
    95           """This installation of Dotnet has been produced via "isabelle dotnet_setup".
       
    96 
       
    97 
       
    98         Makarius
       
    99         """ + Date.Format.date(Date.now()) + "\n")
       
   100 
       
   101         Components.update_components(true, component_dir)
       
   102       }
       
   103 
       
   104 
       
   105       /* platform directory */
       
   106 
       
   107       Isabelle_System.with_tmp_file("install", ext = platform.ext) { install =>
       
   108         Isabelle_System.download_file(install_url + "." + platform.ext, install)
       
   109 
       
   110         val platform_dir = component_dir + Path.explode(platform.name)
       
   111         if (platform_dir.is_dir) {
       
   112           progress.echo_warning("Platform " + platform.name + " already installed")
       
   113         }
       
   114         else {
       
   115           progress.echo("Platform " + platform.name + " ...")
       
   116           val script =
       
   117             platform.exec + " " + File.bash_platform_path(install) +
       
   118               (if (version.nonEmpty) " -Version " + Bash.string(version) else "") +
       
   119               " -Architecture " + Bash.string(platform.arch) +
       
   120               (if (platform.os.nonEmpty) " -OS " + Bash.string(platform.os) else "") +
       
   121               " -InstallDir " + Bash.string(platform.name) +
       
   122               (if (dry_run) " -DryRun" else "") +
       
   123               " -NoPath"
       
   124           progress.bash(script, echo = verbose,
       
   125             cwd = if (dry_run) null else component_dir.file).check
       
   126         }
       
   127       }
       
   128     }
       
   129   }
       
   130 
       
   131 
       
   132   /* Isabelle tool wrapper */
       
   133 
       
   134   val isabelle_tool =
       
   135     Isabelle_Tool("dotnet_setup", "dynamic setup of dotnet component (for Fsharp)",
       
   136       Scala_Project.here,
       
   137       { args =>
       
   138 
       
   139         var target_dir = default_target_dir
       
   140         var install_url = default_install_url
       
   141         var version = default_version
       
   142         var dry_run = false
       
   143         var platforms = List(default_platform)
       
   144         var verbose = false
       
   145 
       
   146         val getopts = Getopts("""
       
   147 Usage: isabelle dotnet_setup [OPTIONS]
       
   148 
       
   149   Options are:
       
   150     -D DIR       target directory (default: """ + default_target_dir.expand + """)
       
   151     -I URL       URL for install script without extension (default: """ + quote(default_install_url) + """)
       
   152     -V VERSION   version (empty means "latest", default: """ + quote(default_version) + """)
       
   153     -n           dry run: try download without installation
       
   154     -p PLATFORMS list of platforms as family or formal name, separated by commas
       
   155                  (default: """ + quote(default_platform) + """
       
   156     -v           verbose
       
   157 
       
   158   Download the Dotnet / Fsharp platform and configure it as Isabelle component.
       
   159 
       
   160   See also:
       
   161     https://fsharp.org
       
   162     https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-install-script
       
   163 
       
   164   Example:
       
   165     isabelle dotnet_setup
       
   166     isabelle dotnet fsi
       
   167 """,
       
   168           "D:" -> (arg => target_dir = Path.explode(arg)),
       
   169           "I:" -> (arg => install_url = arg),
       
   170           "V:" -> (arg => version = arg),
       
   171           "n" -> (_ => dry_run = true),
       
   172           "p:" -> (arg => platforms = Library.space_explode(',', arg).map(check_platform_spec)),
       
   173           "v" -> (_ => verbose = true))
       
   174 
       
   175         val more_args = getopts(args)
       
   176         if (more_args.nonEmpty) getopts.usage()
       
   177 
       
   178         val progress = new Console_Progress()
       
   179 
       
   180         for (platform <- platforms) {
       
   181           dotnet_setup(platform_spec = platform, target_dir = target_dir, install_url = install_url,
       
   182             version = version, dry_run = dry_run, verbose = verbose, progress = progress)
       
   183         }
       
   184       })
       
   185 }