1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-23 18:18:00 +02:00
Beef/IDE/src/util/PackMan.bf

412 lines
10 KiB
Beef
Raw Normal View History

2024-10-21 09:18:07 -04:00
#pragma warning disable 168
2021-02-25 08:10:21 -08:00
using System;
using IDE.Util;
2024-10-21 09:18:07 -04:00
using System.Collections;
using System.Security.Cryptography;
using System.IO;
using Beefy.utils;
using System.Threading;
2021-02-25 08:10:21 -08:00
namespace IDE.util
{
class PackMan
{
2024-10-21 09:18:07 -04:00
public class WorkItem
2021-02-25 08:10:21 -08:00
{
2024-10-21 09:18:07 -04:00
public enum Kind
{
None,
FindVersion,
Clone,
Checkout
}
2021-02-25 08:10:21 -08:00
2024-10-21 09:18:07 -04:00
public Kind mKind;
public String mProjectName ~ delete _;
public String mURL ~ delete _;
public List<String> mConstraints ~ DeleteContainerAndItems!(_);
public String mTag ~ delete _;
public String mHash ~ delete _;
public String mPath ~ delete _;
public GitManager.GitInstance mGitInstance ~ _?.ReleaseRef();
public ~this()
2021-02-25 08:10:21 -08:00
{
2024-10-21 09:18:07 -04:00
mGitInstance?.Cancel();
2021-02-25 08:10:21 -08:00
}
}
2024-10-21 09:18:07 -04:00
public List<WorkItem> mWorkItems = new .() ~ DeleteContainerAndItems!(_);
public bool mInitialized;
public String mManagedPath ~ delete _;
public bool mFailed;
public void Fail(StringView error)
{
gApp.OutputErrorLine(error);
if (!mFailed)
{
mFailed = true;
gApp.[Friend]FlushDeferredLoadProjects();
}
}
public bool CheckInit()
{
2024-10-22 09:06:54 -04:00
if ((gApp.mWorkspace.mProjectLoadState != .Preparing) && (mWorkItems.IsEmpty))
{
// Clear failed state
mFailed = false;
}
2024-10-21 09:18:07 -04:00
if (mInitialized)
return true;
if (gApp.mBeefConfig.mManagedLibPath.IsEmpty)
return false;
mManagedPath = new .(gApp.mBeefConfig.mManagedLibPath);
mInitialized = true;
return true;
}
public void GetPath(StringView url, StringView hash, String outPath)
{
//var urlHash = SHA256.Hash(url.ToRawData()).ToString(.. scope .());
//outPath.AppendF($"{mManagedPath}/{urlHash}/{hash}");
outPath.AppendF($"{mManagedPath}/{hash}");
}
2021-02-25 08:10:21 -08:00
public bool CheckLock(StringView projectName, String outPath)
{
2024-10-21 09:18:07 -04:00
if (!CheckInit())
return false;
if (gApp.mWantUpdateVersionLocks != null)
{
if ((gApp.mWantUpdateVersionLocks.IsEmpty) || (gApp.mWantUpdateVersionLocks.ContainsAlt(projectName)))
return false;
}
if (!gApp.mWorkspace.mProjectLockMap.TryGetAlt(projectName, ?, var lock))
return false;
switch (lock)
{
case .Git(let url, let tag, let hash):
var path = GetPath(url, hash, .. scope .());
var managedFilePath = scope $"{path}/BeefManaged.toml";
if (File.Exists(managedFilePath))
{
outPath.Append(path);
outPath.Append("/BeefProj.toml");
return true;
}
default:
}
2021-02-25 08:10:21 -08:00
return false;
}
2024-10-21 09:18:07 -04:00
public void CloneCompleted(StringView projectName, StringView url, StringView tag, StringView hash, StringView path)
{
gApp.mWorkspace.SetLock(projectName, .Git(new .(url), new .(tag), new .(hash)));
StructuredData sd = scope .();
sd.CreateNew();
sd.Add("FileVersion", 1);
sd.Add("Version", tag);
sd.Add("GitURL", url);
sd.Add("GitTag", tag);
sd.Add("GitHash", hash);
var tomlText = sd.ToTOML(.. scope .());
var managedFilePath = scope $"{path}/BeefManaged.toml";
File.WriteAllText(managedFilePath, tomlText).IgnoreError();
}
public void GetWithHash(StringView projectName, StringView url, StringView tag, StringView hash)
{
if (!CheckInit())
return;
String destPath = GetPath(url, hash, .. scope .());
var urlPath = Path.GetDirectoryPath(destPath, .. scope .());
Directory.CreateDirectory(urlPath).IgnoreError();
if (Directory.Exists(destPath))
{
var managedFilePath = scope $"{destPath}/BeefManaged.toml";
if (File.Exists(managedFilePath))
{
if (gApp.mVerbosity >= .Normal)
{
if (tag.IsEmpty)
gApp.OutputLine($"Git selecting library '{projectName}' at {hash.Substring(0, 7)}");
else
gApp.OutputLine($"Git selecting library '{projectName}' tag '{tag}' at {hash.Substring(0, 7)}");
}
CloneCompleted(projectName, url, tag, hash, destPath);
ProjectReady(projectName, destPath);
return;
}
String tempDir = new $"{destPath}__{(int32)Internal.GetTickCountMicro():X}";
//if (Directory.DelTree(destPath) case .Err)
if (Directory.Move(destPath, tempDir) case .Err)
{
delete tempDir;
Fail(scope $"Failed to remove directory '{destPath}'");
return;
}
ThreadPool.QueueUserWorkItem(new () =>
{
Directory.DelTree(tempDir);
}
~
{
delete tempDir;
});
}
if (gApp.mVerbosity >= .Normal)
{
if (tag.IsEmpty)
gApp.OutputLine($"Git cloning library '{projectName}' at {hash.Substring(0, 7)}...");
else
gApp.OutputLine($"Git cloning library '{projectName}' tag '{tag}' at {hash.Substring(0, 7)}");
}
WorkItem workItem = new .();
workItem.mKind = .Clone;
workItem.mProjectName = new .(projectName);
workItem.mURL = new .(url);
workItem.mTag = new .(tag);
workItem.mHash = new .(hash);
workItem.mPath = new .(destPath);
mWorkItems.Add(workItem);
}
public void GetWithVersion(StringView projectName, StringView url, SemVer semVer)
{
if (!CheckInit())
return;
bool ignoreLock = false;
if (gApp.mWantUpdateVersionLocks != null)
{
if ((gApp.mWantUpdateVersionLocks.IsEmpty) || (gApp.mWantUpdateVersionLocks.ContainsAlt(projectName)))
ignoreLock = true;
}
if ((!ignoreLock) && (gApp.mWorkspace.mProjectLockMap.TryGetAlt(projectName, ?, var lock)))
{
switch (lock)
{
case .Git(let checkURL, let tag, let hash):
if (checkURL == url)
GetWithHash(projectName, url, tag, hash);
return;
default:
}
}
if (gApp.mVerbosity >= .Normal)
gApp.OutputLine($"Git retrieving version list for '{projectName}'");
WorkItem workItem = new .();
workItem.mKind = .FindVersion;
workItem.mProjectName = new .(projectName);
workItem.mURL = new .(url);
2024-10-22 09:06:54 -04:00
if (!semVer.IsEmpty)
2024-10-21 09:18:07 -04:00
workItem.mConstraints = new .() { new String(semVer.mVersion) };
mWorkItems.Add(workItem);
}
public void UpdateGitConstraint(StringView url, SemVer semVer)
{
for (var workItem in mWorkItems)
{
if ((workItem.mKind == .FindVersion) && (workItem.mURL == url))
{
if (workItem.mConstraints == null)
workItem.mConstraints = new .();
workItem.mConstraints.Add(new String(semVer.mVersion));
}
}
}
public void Checkout(StringView projectName, StringView url, StringView path, StringView tag, StringView hash)
{
if (!CheckInit())
return;
WorkItem workItem = new .();
workItem.mKind = .Checkout;
workItem.mProjectName = new .(projectName);
workItem.mURL = new .(url);
workItem.mTag = new .(tag);
workItem.mHash = new .(hash);
workItem.mPath = new .(path);
mWorkItems.Add(workItem);
}
public void ProjectReady(StringView projectName, StringView path)
{
if (var project = gApp.mWorkspace.FindProject(projectName))
{
String projectPath = scope $"{path}/BeefProj.toml";
project.mProjectPath.Set(projectPath);
gApp.RetryProjectLoad(project, false);
}
}
public void Update()
{
bool executingGit = false;
// First handle active git items
for (var workItem in mWorkItems)
{
if (workItem.mGitInstance == null)
continue;
if (!workItem.mGitInstance.mDone)
{
executingGit = true;
continue;
}
if (!workItem.mGitInstance.mFailed)
{
switch (workItem.mKind)
{
case .FindVersion:
gApp.CompilerLog("");
StringView bestTag = default;
StringView bestHash = default;
for (var tag in workItem.mGitInstance.mTagInfos)
{
2024-10-22 09:06:54 -04:00
if ((tag.mTag == "HEAD") &&
((workItem.mConstraints == null) || (workItem.mConstraints.Contains("HEAD"))))
{
2024-10-21 09:18:07 -04:00
bestHash = tag.mHash;
2024-10-22 09:06:54 -04:00
break;
}
2024-10-21 09:18:07 -04:00
else if (workItem.mConstraints != null)
{
bool hasMatch = false;
for (var constraint in workItem.mConstraints)
{
if (SemVer.IsVersionMatch(tag.mTag, constraint))
{
hasMatch = true;
break;
}
}
if (hasMatch)
{
if ((bestTag.IsEmpty) || (SemVer.Compare(tag.mTag, bestTag) > 0))
{
bestTag = tag.mTag;
bestHash = tag.mHash;
}
}
}
}
if (bestHash != default)
{
GetWithHash(workItem.mProjectName, workItem.mURL, bestTag, bestHash);
}
else
{
String constraints = scope .();
for (var constraint in workItem.mConstraints)
{
if (!constraints.IsEmpty)
constraints.Append(", ");
constraints.Append('\'');
constraints.Append(constraint);
constraints.Append('\'');
}
2024-10-22 09:06:54 -04:00
Fail(scope $"Failed to locate version for '{workItem.mProjectName}' with constraints {constraints}");
2024-10-21 09:18:07 -04:00
}
case .Clone:
Checkout(workItem.mProjectName, workItem.mURL, workItem.mPath, workItem.mTag, workItem.mHash);
case .Checkout:
CloneCompleted(workItem.mProjectName, workItem.mURL, workItem.mTag, workItem.mHash, workItem.mPath);
ProjectReady(workItem.mProjectName, workItem.mPath);
if (gApp.mVerbosity >= .Normal)
gApp.OutputLine($"Git cloning library '{workItem.mProjectName}' done.");
default:
}
}
2024-10-22 09:06:54 -04:00
else
{
Fail(scope $"Failed to retrieve project '{workItem.mProjectName}' at '{workItem.mURL}'");
}
2024-10-21 09:18:07 -04:00
@workItem.Remove();
delete workItem;
}
if (!executingGit)
{
// First handle active git items
for (var workItem in mWorkItems)
{
if (workItem.mGitInstance != null)
continue;
switch (workItem.mKind)
{
case .FindVersion:
workItem.mGitInstance = gApp.mGitManager.GetTags(workItem.mURL)..AddRef();
case .Checkout:
workItem.mGitInstance = gApp.mGitManager.Checkout(workItem.mPath, workItem.mHash)..AddRef();
case .Clone:
workItem.mGitInstance = gApp.mGitManager.Clone(workItem.mURL, workItem.mPath)..AddRef();
default:
}
}
}
}
public void GetHashFromFilePath(StringView filePath, String path)
{
if (mManagedPath == null)
return;
if (!filePath.StartsWith(mManagedPath))
return;
StringView hashPart = filePath.Substring(mManagedPath.Length);
if (hashPart.Length < 42)
return;
hashPart.RemoveFromStart(1);
hashPart.Length = 40;
path.Append(hashPart);
}
public void CancelAll()
{
if (mWorkItems.IsEmpty)
return;
Fail("Aborted project transfer");
mWorkItems.ClearAndDeleteItems();
}
2021-02-25 08:10:21 -08:00
}
}