Skip to content

Commit

Permalink
Support downstream projects with configuring cache for Elm make results
Browse files Browse the repository at this point in the history
Support improving robustness and speed with a cache for the results from successful invocations of Elm make.
Adapt to increase the robustness of the compilation of Elm programs after last week's observation: For users from China, the dependencies on network access in Elm led to impractical delays or failure.
  • Loading branch information
Viir committed Dec 6, 2022
1 parent 6ddc5ce commit 4bd0b69
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 36 deletions.
94 changes: 93 additions & 1 deletion implement/elm-fullstack/ElmFullstack/Elm019Binaries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ public static class Elm019Binaries
{
static public string? overrideElmMakeHomeDirectory = null;

static public IFileStore? elmMakeResultCacheFileStoreDefault = null;

static string? elmHomeDirectory;

public record ElmMakeOk(ReadOnlyMemory<byte> producedFile);
Expand All @@ -34,6 +36,81 @@ static public Result<string, ElmMakeOk> ElmMakeToHtml(
string? elmMakeCommandAppendix = null) =>
ElmMake(elmCodeFiles, pathToFileWithElmEntryPoint, "file-for-elm-make-output.html", elmMakeCommandAppendix);

/// <inheritdoc cref="ElmMakeIgnoringCachedResults"/>
static public Result<string, ElmMakeOk> ElmMake(
IImmutableDictionary<IReadOnlyList<string>, ReadOnlyMemory<byte>> elmCodeFiles,
IReadOnlyList<string> pathToFileWithElmEntryPoint,
string outputFileName,
string? elmMakeCommandAppendix = null) =>
ElmMake(
elmCodeFiles: elmCodeFiles,
pathToFileWithElmEntryPoint: pathToFileWithElmEntryPoint,
outputFileName: outputFileName,
elmMakeCommandAppendix: elmMakeCommandAppendix,
resultCacheFileStore: elmMakeResultCacheFileStoreDefault);

/// <inheritdoc cref="ElmMakeIgnoringCachedResults"/>
static public Result<string, ElmMakeOk> ElmMake(
IImmutableDictionary<IReadOnlyList<string>, ReadOnlyMemory<byte>> elmCodeFiles,
IReadOnlyList<string> pathToFileWithElmEntryPoint,
string outputFileName,
string? elmMakeCommandAppendix,
IFileStore? resultCacheFileStore)
{
var elmCodeFilesHash =
CommonConversion.StringBase16(
Composition.GetHash(Composition.FromTreeWithStringPath(Composition.SortedTreeFromSetOfBlobsWithStringPath(elmCodeFiles))));

var requestIdentifer = new ElmMakeRequestIdentifier(
elmCodeFilesHash: elmCodeFilesHash,
pathToFileWithElmEntryPoint: pathToFileWithElmEntryPoint,
outputFileName: outputFileName,
elmMakeCommandAppendix: elmMakeCommandAppendix);

var requestHash =
CommonConversion.StringBase16(
CommonConversion.HashSHA256(System.Text.Encoding.UTF8.GetBytes(System.Text.Json.JsonSerializer.Serialize(requestIdentifer))));

var cacheEntryPath = ImmutableList.Create(requestHash);

try
{
var cacheEntryFile =
resultCacheFileStore?.GetFileContent(cacheEntryPath);

if (cacheEntryFile is not null)
{
var resultFromCache =
System.Text.Json.JsonSerializer.Deserialize<Result<string, ElmMakeOkJsonStructure>>(cacheEntryFile!.ToArray())
?.Map(AsElmMakeOk);

if (resultFromCache is Result<string, ElmMakeOk>.Ok resultOk)
return resultFromCache;
}
}
catch { }

var result =
ElmMakeIgnoringCachedResults(
elmCodeFiles,
pathToFileWithElmEntryPoint,
outputFileName,
elmMakeCommandAppendix);

if (resultCacheFileStore is not null)
{
try
{
resultCacheFileStore.SetFileContent(
cacheEntryPath,
System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(result.Map(AsJsonStructure)));
}
catch { }
}

return result;
}

/*
2019-12-14: Switch to modeling file paths as a list of string instead of a string, to avoid that problem reported earlier and described below:
Expand All @@ -57,7 +134,7 @@ I cannot find that directory though! Is it missing? Is there a typo?
/// <summary>
/// Use the 'elm make' command on the elm executable file.
/// </summary>
static public Result<string, ElmMakeOk> ElmMake(
static public Result<string, ElmMakeOk> ElmMakeIgnoringCachedResults(
IImmutableDictionary<IReadOnlyList<string>, ReadOnlyMemory<byte>> elmCodeFiles,
IReadOnlyList<string> pathToFileWithElmEntryPoint,
string outputFileName,
Expand Down Expand Up @@ -175,4 +252,19 @@ static public string GetElmHomeDirectory()
Directory.CreateDirectory(elmHomeDirectory);
return elmHomeDirectory;
}

record ElmMakeRequestIdentifier(
string elmCodeFilesHash,
IReadOnlyList<string> pathToFileWithElmEntryPoint,
string outputFileName,
string? elmMakeCommandAppendix);

record ElmMakeOkJsonStructure(
string producedFileBase64);

static ElmMakeOk AsElmMakeOk(ElmMakeOkJsonStructure cacheEntry) =>
new(producedFile: Convert.FromBase64String(cacheEntry.producedFileBase64));

static ElmMakeOkJsonStructure AsJsonStructure(ElmMakeOk cacheEntry) =>
new(producedFileBase64: Convert.ToBase64String(cacheEntry.producedFile!.ToArray())!);
}
42 changes: 14 additions & 28 deletions implement/elm-fullstack/Pine/CacheByFileName.cs
Original file line number Diff line number Diff line change
@@ -1,42 +1,28 @@
using System.IO;
using System;
using System.Collections.Immutable;
using System.Linq;

namespace Pine;

public record CacheByFileName(string CacheDirectory)
public record CacheByFileName(IFileStore FileStore)
{
public byte[] GetOrUpdate(string fileName, System.Func<byte[]> getNew) =>
GetOrTryAdd(fileName, getNew)!;
public ReadOnlyMemory<byte> GetOrUpdate(string fileName, Func<ReadOnlyMemory<byte>> getNew) =>
GetOrTryAdd(fileName, () => getNew())!.Value;

public byte[]? GetOrTryAdd(string fileName, System.Func<byte[]?> tryBuild)
public ReadOnlyMemory<byte>? GetOrTryAdd(string fileName, Func<ReadOnlyMemory<byte>?> tryBuild)
{
var cacheFilePath = Path.Combine(CacheDirectory, fileName);
var entryPath = ImmutableList.Create(fileName);

if (File.Exists(cacheFilePath))
{
try
{
return File.ReadAllBytes(cacheFilePath);
}
catch { }
}
var fromCache = FileStore.GetFileContent(entryPath);

if (fromCache is not null)
return new ReadOnlyMemory<byte>(fromCache.ToArray());

var file = tryBuild();

if (file is not null)
if (file.HasValue)
{
try
{
var directory = Path.GetDirectoryName(cacheFilePath);

if (directory != null)
Directory.CreateDirectory(directory);

File.WriteAllBytes(cacheFilePath, file);
}
catch (System.Exception e)
{
System.Console.WriteLine("Failed to write cache entry: " + e.ToString());
}
FileStore.SetFileContent(entryPath, file.Value.ToArray());
}

return file;
Expand Down
4 changes: 2 additions & 2 deletions implement/elm-fullstack/Pine/GitPartialForCommitServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ static public Task Run(

var fileCache = new CacheByFileName
(
CacheDirectory: Path.Combine(fileCacheDirectory, ZipArchivePathPrefix.TrimStart('/'))
new FileStoreFromSystemIOFile(Path.Combine(fileCacheDirectory, ZipArchivePathPrefix.TrimStart('/')))
);

/*
Expand Down Expand Up @@ -64,7 +64,7 @@ static public Task Run(
gitCloneUrlPrefixes.Count + " prefixes are supported: " + string.Join(", ", gitCloneUrlPrefixes));
}

byte[] loadWithFreshClone()
System.ReadOnlyMemory<byte> loadWithFreshClone()
{
var files =
LoadFromGitHubOrGitLab.GetRepositoryFilesPartialForCommitViaEnvironmentGitCheckout(
Expand Down
2 changes: 1 addition & 1 deletion implement/elm-fullstack/Pine/LoadFromGitHubOrGitLab.cs
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ static Result<string, IImmutableDictionary<IReadOnlyList<string>, ReadOnlyMemory
not null => Result<string, IImmutableDictionary<IReadOnlyList<string>, ReadOnlyMemory<byte>>>.ok(
Composition.ToFlatDictionaryWithPathComparer(
Composition.SortedTreeFromSetOfBlobsWithCommonFilePath(
ZipArchive.EntriesFromZipArchive(fromExternalCache))
ZipArchive.EntriesFromZipArchive(fromExternalCache.Value))
.EnumerateBlobsTransitive())),

_ => localCache.Value
Expand Down
9 changes: 7 additions & 2 deletions implement/elm-fullstack/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,17 @@ namespace ElmFullstack;

public class Program
{
static public string AppVersionId => "2022-12-05";
static public string AppVersionId => "2022-12-06";

static int AdminInterfaceDefaultPort => 4000;

static int Main(string[] args)
{
Elm019Binaries.overrideElmMakeHomeDirectory = ElmMakeHomeDirectoryPath;
Elm019Binaries.elmMakeResultCacheFileStoreDefault = ElmMakeResultCacheFileStoreDefault;

LoadFromGitHubOrGitLab.RepositoryFilesPartialForCommitCacheDefault =
new CacheByFileName(CacheDirectory: Path.Combine(Filesystem.CacheDirectory, "git", "partial-for-commit", "zip"));
new CacheByFileName(new FileStoreFromSystemIOFile(Path.Combine(Filesystem.CacheDirectory, "git", "partial-for-commit", "zip")));

var app = new CommandLineApplication
{
Expand Down Expand Up @@ -1420,6 +1421,10 @@ static CommandArgument ProcessSiteArgumentOnCommand(CommandLineApplication cmd)
static public string ElmMakeHomeDirectoryPath =>
Path.Combine(Filesystem.CacheDirectory, "elm-make-home");

static public IFileStore ElmMakeResultCacheFileStoreDefault =>
new FileStoreFromSystemIOFile(
Path.Combine(Filesystem.CacheDirectory, "elm-make-result-cache", AppVersionId));

static public void DotNetConsoleWriteLineUsingColor(string line, ConsoleColor color)
{
var colorBefore = Console.ForegroundColor;
Expand Down
4 changes: 2 additions & 2 deletions implement/elm-fullstack/elm-fullstack.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
<TargetFramework>net7.0</TargetFramework>
<RootNamespace>ElmFullstack</RootNamespace>
<AssemblyName>elm-fs</AssemblyName>
<AssemblyVersion>2022.1205.0.0</AssemblyVersion>
<FileVersion>2022.1205.0.0</FileVersion>
<AssemblyVersion>2022.1206.0.0</AssemblyVersion>
<FileVersion>2022.1206.0.0</FileVersion>
<Nullable>enable</Nullable>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
</PropertyGroup>
Expand Down

0 comments on commit 4bd0b69

Please sign in to comment.