Skip to content
This repository has been archived by the owner on Jul 5, 2024. It is now read-only.

Commit

Permalink
Csq is compiling
Browse files Browse the repository at this point in the history
  • Loading branch information
caesay committed Dec 15, 2023
1 parent 123f91e commit 4226aa8
Show file tree
Hide file tree
Showing 19 changed files with 806 additions and 596 deletions.
11 changes: 6 additions & 5 deletions src/Squirrel.Csq/Commands/SystemCommandLineExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Text.RegularExpressions;
using NuGet.Common;
using NuGet.Versioning;
using Squirrel.NuGet;

namespace Squirrel.Csq.Commands;

Expand Down Expand Up @@ -36,7 +38,7 @@ public static CliOption<T> SetRequired<T>(this CliOption<T> option, bool isRequi

public static CliOption<T> SetDefault<T>(this CliOption<T> option, T defaultValue)
{
option.SetDefault(defaultValue);
option.DefaultValueFactory = (r) => defaultValue;
return option;
}

Expand Down Expand Up @@ -273,10 +275,9 @@ public static void MustBeValidFrameworkString(OptionResult result)
{
for (var i = 0; i < result.Tokens.Count; i++) {
var framework = result.Tokens[i].Value;
try {
Runtimes.ParseDependencyString(framework);
} catch (Exception e) {
result.AddError(e.Message);
bool valid = framework.Split(",").Select(Runtimes.GetRuntimeByName).All(x => x != null);
if (!valid) {
result.AddError($"Invalid target dependency string: {framework}.");
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/Squirrel.Csq/Commands/WindowsCommands.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@

using Squirrel.Packaging;

namespace Squirrel.Csq.Commands;

public class SigningCommand : BaseCommand
Expand Down
112 changes: 112 additions & 0 deletions src/Squirrel.Csq/Compat/EmbeddedRunner.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
using System.Runtime.Versioning;
using Squirrel.Csq.Commands;
using Squirrel.Deployment;
using Squirrel.Packaging.OSX;

namespace Squirrel.Csq.Compat;

public class EmbeddedRunner : ICommandRunner
{
private readonly ILogger _logger;

public EmbeddedRunner(ILogger logger)
{
_logger = logger;
}

[SupportedOSPlatform("osx")]
public Task ExecuteBundleOsx(BundleOsxCommand command)
{
var options = new BundleOsxOptions {
BundleId = command.BundleId,
PackAuthors = command.PackAuthors,
EntryExecutableName = command.EntryExecutableName,
Icon = command.Icon,
PackDirectory = command.PackDirectory,
PackId = command.PackId,
PackTitle = command.PackTitle,
PackVersion = command.PackVersion,
ReleaseDir = command.GetReleaseDirectory(),
};
new OsxCommands(_logger).Bundle(options);
return Task.CompletedTask;
}

public Task ExecuteGithubDownload(GitHubDownloadCommand command)
{
var options = new GitHubDownloadOptions {
Pre = command.Pre,
ReleaseDir = command.GetReleaseDirectory(),
RepoUrl = command.RepoUrl,
Token = command.Token,
};
return new GitHubRepository(_logger).DownloadRecentPackages(options);
}

public Task ExecuteGithubUpload(GitHubUploadCommand command)
{
var options = new GitHubUploadOptions {
ReleaseDir = command.GetReleaseDirectory(),
RepoUrl = command.RepoUrl,
Token = command.Token,
Publish = command.Publish,
ReleaseName = command.ReleaseName,
};
return new GitHubRepository(_logger).UploadMissingPackages(options);
}

public Task ExecuteHttpDownload(HttpDownloadCommand command)
{
var options = new HttpDownloadOptions {
ReleaseDir = command.GetReleaseDirectory(),
Url = command.Url,
};
return new SimpleWebRepository(_logger).DownloadRecentPackages(options);
}

public Task ExecutePackWindows(PackWindowsCommand command)
{
throw new NotImplementedException();
}

[SupportedOSPlatform("osx")]
public Task ExecuteReleasifyOsx(ReleasifyOsxCommand command)
{
throw new NotImplementedException();
}

public Task ExecuteReleasifyWindows(ReleasifyWindowsCommand command)
{
throw new NotImplementedException();
}

public Task ExecuteS3Download(S3DownloadCommand command)
{
var options = new S3Options {
Bucket = command.Bucket,
Endpoint = command.Endpoint,
KeyId = command.KeyId,
PathPrefix = command.PathPrefix,
Region = command.Region,
ReleaseDir = command.GetReleaseDirectory(),
Secret = command.Secret,
};
return new S3Repository(_logger).DownloadRecentPackages(options);
}

public Task ExecuteS3Upload(S3UploadCommand command)
{
var options = new S3UploadOptions {
Bucket = command.Bucket,
Endpoint = command.Endpoint,
KeyId = command.KeyId,
PathPrefix = command.PathPrefix,
Region = command.Region,
ReleaseDir = command.GetReleaseDirectory(),
Secret = command.Secret,
KeepMaxReleases = command.KeepMaxReleases,
Overwrite = command.Overwrite,
};
return new S3Repository(_logger).UploadMissingPackages(options);
}
}
17 changes: 17 additions & 0 deletions src/Squirrel.Csq/Compat/ICommandRunner.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.Runtime.Versioning;
using Squirrel.Csq.Commands;

namespace Squirrel.Csq.Compat;

public interface ICommandRunner
{
public Task ExecuteGithubDownload(GitHubDownloadCommand command);
public Task ExecuteGithubUpload(GitHubUploadCommand command);
public Task ExecuteHttpDownload(HttpDownloadCommand command);
public Task ExecuteS3Download(S3DownloadCommand command);
public Task ExecuteS3Upload(S3UploadCommand command);
public Task ExecuteBundleOsx(BundleOsxCommand command);
public Task ExecuteReleasifyOsx(ReleasifyOsxCommand command);
public Task ExecuteReleasifyWindows(ReleasifyWindowsCommand command);
public Task ExecutePackWindows(PackWindowsCommand command);
}
9 changes: 9 additions & 0 deletions src/Squirrel.Csq/Compat/IRunnerFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Squirrel.Csq.Commands;

namespace Squirrel.Csq.Compat;

public interface IRunnerFactory
{
public Task CreateAndExecuteAsync<T>(string commandName, T options) where T : BaseCommand;
public Task<ICommandRunner> CreateAsync();
}
105 changes: 105 additions & 0 deletions src/Squirrel.Csq/Compat/RunnerFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
using Microsoft.Extensions.Configuration;
using Squirrel.Csq.Commands;
using Squirrel.Csq.Updates;

namespace Squirrel.Csq.Compat;

public class RunnerFactory : IRunnerFactory
{
private const string CLOWD_PACKAGE_NAME = "Clowd.Squirrel";
private readonly ILogger _logger;
private readonly FileSystemInfo _solution;
private readonly IConfiguration _config;

public RunnerFactory(ILogger logger, FileSystemInfo solution, IConfiguration config)
{
_logger = logger;
_solution = solution;
this._config = config;
}

public async Task CreateAndExecuteAsync<T>(string commandName, T options) where T : BaseCommand
{
var runner = await CreateAsync();
var method = typeof(ICommandRunner).GetMethod(commandName);
await (Task) method.Invoke(runner, new object[] { options });
}

public async Task<ICommandRunner> CreateAsync()
{
if (_config.GetValue<bool?>("SKIP_UPDATE_CHECK") != true) {
var updateCheck = new UpdateChecker(_logger);
await updateCheck.CheckForUpdates();
}

var solutionDir = FindSolutionDirectory(_solution?.FullName);

if (solutionDir is null) {
throw new Exception($"Could not find '.sln'. Specify solution or solution directory with '--solution='.");
}

var version = new SquirrelVersionLocator(_logger).Search(solutionDir, CLOWD_PACKAGE_NAME);

if (version.Major == 4) {
var myVer = SquirrelRuntimeInfo.SquirrelNugetVersion;
if (version != myVer) {
_logger.Warn($"Installed SDK is {version}, while csq is {myVer}, this is not recommended.");
}
return new EmbeddedRunner(_logger);
}

if (version.Major == 2 && version.Minor > 7) {
_logger.Warn("Running in V2 compatibility mode. Not all features may be available.");

Dictionary<string, string> packageSearchPaths = new();
var nugetPackagesDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages");
packageSearchPaths.Add("nuget user profile cache", Path.Combine(nugetPackagesDir, CLOWD_PACKAGE_NAME.ToLower(), "{0}", "tools"));
packageSearchPaths.Add("visual studio packages cache", Path.Combine(solutionDir, "packages", CLOWD_PACKAGE_NAME + ".{0}", "tools"));
string squirrelExe = null;

foreach (var kvp in packageSearchPaths) {
var path = String.Format(kvp.Value, version);
if (Directory.Exists(path)) {
_logger.Debug($"Found {CLOWD_PACKAGE_NAME} {version} from {kvp.Key}");
var toolExePath = Path.Combine(path, "Squirrel.exe");
if (File.Exists(toolExePath)) {
squirrelExe = toolExePath;
break;
}
}
}

if (squirrelExe is null) {
throw new Exception($"Could not find {CLOWD_PACKAGE_NAME} {version} Squirrel.exe");
}

return new V2CompatRunner(_logger, squirrelExe);
}

throw new NotSupportedException($"Squirrel {version} is installed in this project, but not supported by this version of Csq. Supported versions are [> v2.8] and [> v4.0]");
}

private string FindSolutionDirectory(string slnArgument)
{
if (!String.IsNullOrWhiteSpace(slnArgument)) {
if (File.Exists(slnArgument) && slnArgument.EndsWith(".sln", StringComparison.InvariantCultureIgnoreCase)) {
// we were given a sln file as argument
return Path.GetDirectoryName(Path.GetFullPath(slnArgument));
}

if (Directory.Exists(slnArgument) && Directory.EnumerateFiles(slnArgument, "*.sln").Any()) {
return Path.GetFullPath(slnArgument);
}
}

// try to find the solution directory from cwd
var cwd = Environment.CurrentDirectory;
var slnSearchDirs = new string[] {
cwd,
Path.Combine(cwd, ".."),
Path.Combine(cwd, "..", ".."),
};

return slnSearchDirs.FirstOrDefault(d => Directory.EnumerateFiles(d, "*.sln").Any());
}
}
85 changes: 85 additions & 0 deletions src/Squirrel.Csq/Compat/SquirrelVersionLocator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using System.Xml.Linq;
using Microsoft.Build.Construction;
using NuGet.Versioning;

namespace Squirrel.Csq.Compat;

public class SquirrelVersionLocator
{
private readonly ILogger _logger;

public SquirrelVersionLocator(ILogger logger)
{
_logger = logger;
}

public NuGetVersion Search(string solutionDir, string packageName)
{
var dependencies = GetPackageVersionsFromDir(solutionDir, packageName).Distinct().ToArray();

if (dependencies.Length == 0) {
throw new Exception($"{packageName} nuget package was not found installed in solution.");
}

if (dependencies.Length > 1) {
throw new Exception($"Found multiple versions of {packageName} installed in solution ({string.Join(", ", dependencies)}). " +
$"Please consolidate to a single version.'");
}

var targetVersion = dependencies.Single();
return NuGetVersion.Parse(targetVersion);
}

IEnumerable<string> GetPackageVersionsFromDir(string rootDir, string packageName)
{
// old-style framework packages.config
foreach (var packagesFile in EnumerateFilesUntilSpecificDepth(rootDir, "packages.config", 3)) {
using var xmlStream = File.OpenRead(packagesFile);
var xdoc = XDocument.Load(xmlStream);

var sqel = xdoc.Root?.Elements().FirstOrDefault(e => e.Attribute("id")?.Value == packageName);
var ver = sqel?.Attribute("version");
if (ver == null) continue;

_logger.Debug($"{packageName} {ver.Value} referenced in {packagesFile}");

if (ver.Value.Contains('*'))
throw new Exception(
$"Wildcard versions are not supported in packages.config. Remove wildcard or upgrade csproj format to use PackageReference.");

yield return ver.Value;
}

// new-style csproj PackageReference
foreach (var projFile in EnumerateFilesUntilSpecificDepth(rootDir, "*.csproj", 3)) {
var proj = ProjectRootElement.Open(projFile);
if (proj == null) continue;

ProjectItemElement item = proj.Items.FirstOrDefault(i => i.ItemType == "PackageReference" && i.Include == packageName);
if (item == null) continue;

var version = item.Children.FirstOrDefault(x => x.ElementName == "Version") as ProjectMetadataElement;
if (version?.Value == null) continue;

_logger.Debug($"{packageName} {version.Value} referenced in {projFile}");

yield return version.Value;
}
}

static IEnumerable<string> EnumerateFilesUntilSpecificDepth(string rootPath, string searchPattern, int maxDepth, int currentDepth = 0)
{
var files = Directory.EnumerateFiles(rootPath, searchPattern, SearchOption.TopDirectoryOnly);
foreach (var f in files) {
yield return f;
}

if (currentDepth < maxDepth) {
foreach (var dir in Directory.EnumerateDirectories(rootPath)) {
foreach (var file in EnumerateFilesUntilSpecificDepth(dir, searchPattern, maxDepth, currentDepth + 1)) {
yield return file;
}
}
}
}
}
Loading

0 comments on commit 4226aa8

Please sign in to comment.