Example #1
0
        public static string FindJulia(bool force)
        {
            if (!force && !string.IsNullOrEmpty(JuliaPath) && File.Exists(JuliaPath))
            {
                return(JuliaPath);
            }

            string name = PlatformHelper.Is(Platform.Windows) ? "julia.exe" : "julia";
            string path;

            if (Mode == AhornHelperMode.System)
            {
                path = ProcessHelper.Read(
                    PlatformHelper.Is(Platform.Windows) ? "where.exe" : "which",
                    name,
                    out _
                    ).Trim().Split('\n').FirstOrDefault()?.Trim();
                if (!string.IsNullOrEmpty(path) && File.Exists(path))
                {
                    JuliaIsLocal = false;
                    return(JuliaPath = path);
                }

                if (PlatformHelper.Is(Platform.MacOS))
                {
                    // Sandboxing is hell and the Julia installation instructions make this path guaranteed on macOS.
                    path = "/usr/local/bin/julia";
                    if (File.Exists(path))
                    {
                        JuliaIsLocal = false;
                        return(JuliaPath = path);
                    }
                }

                if (PlatformHelper.Is(Platform.Windows))
                {
                    // Julia on Windows is a hot mess.
                    string localAppData      = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
                    IEnumerable <string> all = Directory.EnumerateDirectories(localAppData);

                    string localPrograms = Path.Combine(localAppData, "Programs");
                    if (Directory.Exists(localPrograms))
                    {
                        all = all.Concat(Directory.EnumerateDirectories(localPrograms));
                    }

                    string localJulias = Path.Combine(localPrograms, "Julia");
                    if (Directory.Exists(localJulias))
                    {
                        all = all.Concat(Directory.EnumerateDirectories(localJulias));
                    }

                    path = all
                           .Where(p => Path.GetFileName(p).StartsWith("Julia-") || Path.GetFileName(p).StartsWith("Julia "))
                           .OrderByDescending(p => Path.GetFileName(p.Substring(6)))
                           .FirstOrDefault();

                    if (!string.IsNullOrEmpty(path) && Directory.Exists(path))
                    {
                        path = Path.Combine(path, "bin", name);
                        if (File.Exists(path))
                        {
                            JuliaIsLocal = false;
                            return(JuliaPath = path);
                        }
                    }
                }
            }

            path = Path.Combine(RootPath, "julia", "bin", name);
            if (File.Exists(path))
            {
                JuliaIsLocal = true;
                return(JuliaPath = path);
            }

            return(null);
        }
        public override IEnumerator Run(bool beta)
        {
            yield return(Status("Preparing installation of Julia", false, "", false));

            string root = AhornHelper.RootPath;
            string tmp  = Path.Combine(root, "tmp");

            if (!Directory.Exists(tmp))
            {
                Directory.CreateDirectory(tmp);
            }

            string julia = Path.Combine(root, "julia");

            if (Directory.Exists(julia))
            {
                Directory.Delete(julia, true);
            }

            if (PlatformHelper.Is(Platform.Windows))
            {
                string zipPath = Path.Combine(tmp, "juliadownload.zip");
                if (File.Exists(zipPath))
                {
                    File.Delete(zipPath);
                }

                string url;

                if (PlatformHelper.Is(Platform.Bits64))
                {
                    url = beta ?
                          "https://julialang-s3.julialang.org/bin/winnt/x64/1.6/julia-1.6.0-rc3-win64.zip" :
                          "https://julialang-s3.julialang.org/bin/winnt/x64/1.5/julia-1.5.4-win64.zip";
                }
                else
                {
                    url = beta ?
                          "https://julialang-s3.julialang.org/bin/winnt/x86/1.6/julia-1.6.0-rc3-win32.zip" :
                          "https://julialang-s3.julialang.org/bin/winnt/x86/1.5/julia-1.5.4-win32.zip";
                }

                try {
                    yield return(Status($"Downloading {url}", false, "download", false));

                    using (FileStream zipStream = File.Open(zipPath, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete)) {
                        yield return(Download(url, 0, zipStream));

                        zipStream.Seek(0, SeekOrigin.Begin);

                        yield return(Status("Unzipping Julia", false, "download", false));

                        using (ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Read)) {
                            yield return(Unpack(zip, julia, beta ? "julia-1.6.0-rc3/" : "julia-1.5.4/"));
                        }
                    }
                } finally {
                    if (File.Exists(zipPath))
                    {
                        File.Delete(zipPath);
                    }
                }

                string launcher = Path.Combine(root, "launch-local-julia.bat");
                if (File.Exists(launcher))
                {
                    File.Delete(launcher);
                }
                File.WriteAllText(launcher, @"
@echo off
setlocal EnableDelayedExpansion
set ""JULIA_DEPOT_PATH=%~dp0\julia-depot""
set ""AHORN_GLOBALENV=%LocalAppData%\Ahorn\env""
set ""AHORN_ENV=%~dp0\ahorn-env""
""%~dp0\julia\bin\julia.exe"" %*
"
                                  .TrimStart().Replace("\r\n", "\n").Replace("\n", "\r\n")
                                  );
            }
            else if (PlatformHelper.Is(Platform.Linux))
            {
                string tarPath = Path.Combine(tmp, "juliadownload.tar.gz");
                if (File.Exists(tarPath))
                {
                    File.Delete(tarPath);
                }

                string url;

                string lsPath = ProcessHelper.Read("which", "ls", out _).Trim().Split('\n').FirstOrDefault()?.Trim();
                bool   musl   = !string.IsNullOrEmpty(lsPath) && ProcessHelper.Read("ldd", lsPath, out _).Contains("musl");

                if (PlatformHelper.Is(Platform.ARM))
                {
                    url = beta ?
                          "https://julialang-s3.julialang.org/bin/linux/aarch64/1.6/julia-1.6.0-rc3-linux-aarch64.tar.gz" :
                          "https://julialang-s3.julialang.org/bin/linux/aarch64/1.5/julia-1.5.4-linux-aarch64.tar.gz";
                }
                else if (musl)
                {
                    url = beta ?
                          "https://julialang-s3.julialang.org/bin/musl/x64/1.6/julia-1.6.0-rc3-musl-x86_64.tar.gz" :
                          "https://julialang-s3.julialang.org/bin/musl/x64/1.5/julia-1.5.4-musl-x86_64.tar.gz";
                }
                else if (PlatformHelper.Is(Platform.Bits64))
                {
                    url = beta ?
                          "https://julialang-s3.julialang.org/bin/linux/x64/1.6/julia-1.6.0-rc3-linux-x86_64.tar.gz" :
                          "https://julialang-s3.julialang.org/bin/linux/x64/1.5/julia-1.5.4-linux-x86_64.tar.gz";
                }
                else
                {
                    url = beta ?
                          "https://julialang-s3.julialang.org/bin/linux/x86/1.6/julia-1.6.0-rc3-linux-i686.tar.gz" :
                          "https://julialang-s3.julialang.org/bin/linux/x86/1.5/julia-1.5.4-linux-i686.tar.gz";
                }

                try {
                    yield return(Status($"Downloading {url}", false, "download", false));

                    using (FileStream tarStream = File.Open(tarPath, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete))
                        yield return(Download(url, 0, tarStream));

                    yield return(Status("Extracting Julia", false, "download", false));

                    yield return(Status("", false, "download", false));

                    using (Process process = ProcessHelper.Wrap("tar", $"-xvf \"{tarPath}\" -C \"{root}\"")) {
                        process.Start();
                        for (string line; (line = process.StandardOutput.ReadLine()) != null;)
                        {
                            yield return(Status(line, false, "download", true));
                        }
                        process.WaitForExit();
                        if (process.ExitCode != 0)
                        {
                            throw new Exception("tar encountered a fatal error:\n" + process.StandardError.ReadToEnd());
                        }
                        yield return(Status("Julia archive extracted", false, "download", true));
                    }

                    yield return(Status("Moving Julia", false, "download", false));

                    Directory.Move(Path.Combine(root, beta ? "julia-1.6.0-rc3" : "julia-1.5.4"), julia);
                } finally {
                    if (File.Exists(tarPath))
                    {
                        File.Delete(tarPath);
                    }
                }

                string launcher = Path.Combine(root, "launch-local-julia.sh");
                if (File.Exists(launcher))
                {
                    File.Delete(launcher);
                }
                File.WriteAllText(launcher, @"
#!/bin/sh
ROOTDIR=$(dirname ""$0"")
export JULIA_DEPOT_PATH=""${ROOTDIR}/julia-depot""
if [ ! -z ""${XDG_CONFIG_HOME}"" ]; then
    export AHORN_GLOBALENV=""${XDG_CONFIG_HOME}/Ahorn/env""
else
    export AHORN_GLOBALENV=""${HOME}/.config/Ahorn/env""
fi
export AHORN_ENV=""${ROOTDIR}/ahorn-env""
""${ROOTDIR}/julia/bin/julia"" $@
"
                                  .TrimStart().Replace("\r\n", "\n")
                                  );

                ProcessHelper.Read("chmod", $"a+x \"{launcher}\"", out _);
            }
            else if (PlatformHelper.Is(Platform.MacOS))
            {
                string dmgPath = Path.Combine(tmp, "juliadownload.dmg");
                if (File.Exists(dmgPath))
                {
                    File.Delete(dmgPath);
                }

                string mount = Path.Combine(tmp, "juliamount");
                if (Directory.Exists(mount))
                {
                    Directory.Delete(mount);
                }

                string url = beta ?
                             "https://julialang-s3.julialang.org/bin/mac/x64/1.6/julia-1.6.0-rc3-mac64.dmg" :
                             "https://julialang-s3.julialang.org/bin/mac/x64/1.5/julia-1.5.4-mac64.dmg";

                bool mounted = false;

                try {
                    yield return(Status($"Downloading {url}", false, "download", false));

                    using (FileStream dmgStream = File.Open(dmgPath, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete))
                        yield return(Download(url, 0, dmgStream));

                    yield return(Status("Mounting Julia", false, "download", false));

                    using (Process process = ProcessHelper.Wrap("hdiutil", $"attach -mountpoint \"{mount}\" \"{dmgPath}\"")) {
                        process.Start();
                        for (string line; (line = process.StandardOutput.ReadLine()) != null;)
                        {
                            yield return(Status(line, false, "download", false));
                        }
                        process.WaitForExit();
                        if (process.ExitCode != 0)
                        {
                            throw new Exception("hdiutil attach encountered a fatal error:\n" + process.StandardError.ReadToEnd());
                        }
                    }
                    mounted = true;

                    yield return(Status("Copying Julia", false, "download", false));

                    yield return(Status("", false, "download", false));

                    using (Process process = ProcessHelper.Wrap("cp", $"-rvf \"{Path.Combine(mount, beta ? "Julia-1.6.app" : "Julia-1.5.app", "Contents", "Resources", "julia")}\" \"{julia}\"")) {
                        process.Start();
                        for (string line; (line = process.StandardOutput.ReadLine()) != null;)
                        {
                            yield return(Status(line, false, "download", true));
                        }
                        process.WaitForExit();
                        if (process.ExitCode != 0)
                        {
                            throw new Exception("cp encountered a fatal error:\n" + process.StandardError.ReadToEnd());
                        }
                        yield return(Status("Julia copied", false, "download", true));
                    }

                    yield return(Status("Unmounting Julia", false, "download", false));

                    using (Process process = ProcessHelper.Wrap("hdiutil", $"detach \"{mount}\"")) {
                        process.Start();
                        for (string line; (line = process.StandardOutput.ReadLine()) != null;)
                        {
                            yield return(Status(line, false, "download", false));
                        }
                        process.WaitForExit();
                        if (process.ExitCode != 0)
                        {
                            throw new Exception("hdiutil detach encountered a fatal error:\n" + process.StandardError.ReadToEnd());
                        }
                    }
                    mounted = false;
                } finally {
                    if (mounted)
                    {
                        try {
                            using (Process process = ProcessHelper.Wrap("hdiutil", $"detach \"{mount}\"")) {
                                process.Start();
                                process.WaitForExit();
                                if (process.ExitCode != 0)
                                {
                                    throw new Exception("hdiutil detach encountered a fatal error:\n" + process.StandardError.ReadToEnd());
                                }
                            }
                        } catch (Exception e) {
                            Console.Error.WriteLine("Error unmounting Julia dmg in installer finally clause");
                            Console.Error.WriteLine(e);
                        }
                    }

                    if (File.Exists(dmgPath))
                    {
                        File.Delete(dmgPath);
                    }

                    if (Directory.Exists(mount))
                    {
                        Directory.Delete(mount);
                    }
                }

                string launcher = Path.Combine(root, "launch-local-julia.sh");
                if (File.Exists(launcher))
                {
                    File.Delete(launcher);
                }
                File.WriteAllText(launcher, @"
#!/bin/sh
ROOTDIR=$(dirname ""$0"")
export JULIA_DEPOT_PATH=""${ROOTDIR}/julia-depot""
if [ ! -z ""${XDG_CONFIG_HOME}"" ]; then
    export AHORN_GLOBALENV=""${XDG_CONFIG_HOME}/Ahorn/env""
else
    export AHORN_GLOBALENV=""${HOME}/.config/Ahorn/env""
fi
export AHORN_ENV=""${ROOTDIR}/ahorn-env""
""${ROOTDIR}/julia/bin/julia"" $@
"
                                  .TrimStart().Replace("\r\n", "\n")
                                  );

                ProcessHelper.Read("chmod", $"a+x \"{launcher}\"", out _);
            }
            else
            {
                throw new PlatformNotSupportedException($"Unsupported platform: {PlatformHelper.Current}");
            }
        }