aboutsummaryrefslogtreecommitdiff
path: root/src/Extensions/ProjectExtensions.cs
blob: 9bb55c1d39f0392a2bb96d33343395b70c41b98f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132

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
    {

        /// <summary>
        /// Gets the project dependencies for the given project
        /// </summary>
        /// <param name="project"></param>
        /// <returns>The list of project dependencies</returns>
        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<FileInfo> 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);
        }

        /// <summary>
        /// Gets the sha256 hash of all the source files within the project
        /// </summary>
        /// <returns>A task that resolves the hexadecimal string of the sha256 hash of all the project source files</returns>
        public static async Task<string> 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('\\','-');
        }
    }
}