aboutsummaryrefslogtreecommitdiff
path: root/src/Extensions/FileManagerExtensions.cs
blob: 6f497e71c2141ea5a1f0d8a3b4b115114b010fd1 (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
133
134
135
using System;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Runtime.CompilerServices;

using VNLib.Tools.Build.Executor.Model;
using VNLib.Tools.Build.Executor.Constants;
using static VNLib.Tools.Build.Executor.Constants.Config;

namespace VNLib.Tools.Build.Executor.Extensions
{

    internal static class FileManagerExtensions
    {

        private static readonly ConditionalWeakTable<IProject, object> _projectHashes = new();

        /// <summary>
        /// Gets all external dependencies for the current module
        /// </summary>
        /// <param name="module"></param>
        /// <returns>An array of project names of all the dependencies outside a given module</returns>
        public static string[] GetExternalDependencies(this IModuleData module)
        {
            /*
             * We need to get all child project dependencies that rely on projects
             * outside of the current module.
             * 
             * This assumes all projects within this model are properly linked
             * and assumed to be build together, as is 99% of the case, otherwise
             * custom build impl will happen at the Task level
             */

            //Get the project file names contained in the current module
            string[] selfProjects = module.Projects.Select(static p => p.ProjectFile.Name).ToArray();

            return module.Projects.SelectMany(static p => p.GetDependencies()).Where(dep => !selfProjects.Contains(dep)).ToArray();
        }

        /// <summary>
        /// Determines if any source files have changed
        /// </summary>
        /// <param name="commit">The most recent commit hash</param>
        public static async Task CheckSourceChangedAsync(this IModuleFileManager manager, IProject project, BuildConfig config, string commit)
        {
            //Compute current sum
            string sum = await project.GetSourceFileHashAsync(config);

            //Old sum file exists
            byte[]? sumData = await manager.ReadCheckSumAsync(project);

            //Try to read the old sum file
            if (sumData != null)
            {
                //Parse sum file
                using JsonDocument sumDoc = JsonDocument.Parse(sumData);

                //Get the sum
                string? hexSum = sumDoc.RootElement.GetProperty("sum").GetString();

                //Confirm the current sum and the found sum are equal
                if (sum.Equals(hexSum, StringComparison.OrdinalIgnoreCase))
                {
                    Log.Verbose("Project {p} source is {up}", project.ProjectName, "up-to-date");

                    //Project source is up-to-date
                    project.UpToDate = true;

                    //No changes made
                    return;
                }
            }

            Log.Verbose("Project {p} source is {up}", project.ProjectName, "changed");

            //Store sum change
            object sumChange = new Dictionary<string, string>()
            {
                { "sum", sum },
                { "commit", commit },
                { "modified", DateTimeOffset.UtcNow.ToString("s") }
            };

            //Store sum change for later
            _projectHashes.Add(project, sumChange);

            project.UpToDate = false;
        }

        /// <summary>
        /// Creates the module's output directory
        /// </summary>
        /// <param name="manager"></param>
        public static void CreateOutput(this IModuleFileManager manager)
        {
            //Create output directory for solution
            _ = Directory.CreateDirectory(manager.OutputDir);
        }

        /// <summary>
        /// Deletes the output's module directory
        /// </summary>
        /// <param name="manager"></param>
        public static void CleanOutput(this IModuleFileManager manager)
        {
            //Delete output directory for solution
            if (Directory.Exists(manager.OutputDir))
            {
                Directory.Delete(manager.OutputDir, true);
            }
        }
       

        /// <summary>
        /// Writes the source file checksum change to the project's sum file
        /// </summary>
        /// <param name="project"></param>
        /// <param name="project">The project to write the checksum for</param>
        /// <returns>A task that resolves when the sum file has been updated</returns>
        public static Task CommitSumChangeAsync(this IModuleFileManager manager, IProject project)
        {
            if (!_projectHashes.TryGetValue(project, out object? sumChange))
            {
                return Task.CompletedTask;
            }

            byte[] sumData = JsonSerializer.SerializeToUtf8Bytes(sumChange);

            return manager.WriteChecksumAsync(project, sumData);
        }
    }
}