using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Security.Cryptography;
using VNLib.Tools.Build.Executor.Model;
using VNLib.Tools.Build.Executor.Constants;
namespace VNLib.Tools.Build.Executor.Extensions
{
internal static class ProjectExtensions
{
///
/// Gets the project dependencies for the given project
///
///
/// The list of project dependencies
public static string[] GetDependencies(this IProject project)
{
//Get the project file names (not paths) that are dependencies
return project.ProjectData.GetProjectRefs().Select(static r => Path.GetFileName(r)).ToArray();
}
private static bool IsSourceFile(BuildConfig conf, string fileName)
{
for (int i = 0; i < conf.SourceFileEx.Length; i++)
{
if (fileName.EndsWith(conf.SourceFileEx[i]))
{
return true;
}
}
return false;
}
private static bool IsExcludedDir(BuildConfig conf, string path)
{
for (int i = 0; i < conf.ExcludedSourceDirs.Length; i++)
{
if (path.Contains(conf.ExcludedSourceDirs[i]))
{
return true;
}
}
return false;
}
public static IEnumerable GetProjectBuildFiles(this IProject project, BuildConfig config)
{
//See if an output dir is specified
string? outDir = project.ProjectData["output_dir"] ?? project.ProjectData["output"];
//If an output dir is specified, only get files from that dir
if(!string.IsNullOrWhiteSpace(outDir))
{
//realtive file path
outDir = Path.Combine(project.WorkingDir.FullName, outDir);
if (Directory.Exists(outDir))
{
return new DirectoryInfo(outDir)
.EnumerateFiles(config.OutputFileType, SearchOption.TopDirectoryOnly);
}
}
return project.WorkingDir.EnumerateFiles(config.OutputFileType, SearchOption.AllDirectories);
}
///
/// Gets the sha256 hash of all the source files within the project
///
/// A task that resolves the hexadecimal string of the sha256 hash of all the project source files
public static async Task GetSourceFileHashAsync(this IProject project, BuildConfig config)
{
//Get all
FileInfo[] sourceFiles = project.WorkingDir!.EnumerateFiles("*.*", SearchOption.AllDirectories)
//Get all source files, c/c#/c++ source files, along with .xproj files (project files)
.Where(n => IsSourceFile(config, n.Name))
//Exclude the obj intermediate output dir
.Where(f => !IsExcludedDir(config, f.DirectoryName ?? ""))
.ToArray();
//Get a scratch file to append file source code to
await using FileStream scratch = new(
$"{config.Index.ScratchDir.FullName}/{Path.GetRandomFileName()}",
FileMode.OpenOrCreate,
FileAccess.ReadWrite,
FileShare.None,
8192,
FileOptions.DeleteOnClose
);
//Itterate over all source files
foreach (FileInfo sourceFile in sourceFiles)
{
//Open the source file stream
await using FileStream source = sourceFile.OpenRead();
//Append the data to the file stream
await source.CopyToAsync(scratch);
}
//Flush the stream to disk and roll back to start
await scratch.FlushAsync();
scratch.Seek(0, SeekOrigin.Begin);
byte[] hash;
//Create a sha 256 hash of the file
using (SHA256 alg = SHA256.Create())
{
//Hash the file
hash = await alg.ComputeHashAsync(scratch);
}
//Get hex of the hash
return Convert.ToHexString(hash);
}
public static string GetSafeProjectName(this IProject project)
{
return project.ProjectName
.Replace('/', '-')
.Replace('\\','-');
}
}
}