Skip to content

Commit c35e707

Browse files
committed
moving tasks to separate project library, the dll is dynamically loaded
1 parent fe649fe commit c35e707

17 files changed

+648
-99
lines changed

src/GitVersion.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GitVersionTask", "GitVersio
3434
EndProject
3535
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GitVersion.Tool", "GitVersionExe\GitVersion.Tool.csproj", "{929263FD-5CD2-42E1-BF3D-E0C1B0320DA4}"
3636
EndProject
37+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitVersionTask.MsBuild", "GitVersionTask.MsBuild\GitVersionTask.MsBuild.csproj", "{0F1AEC4E-E81D-4F84-B2E8-3415A1A4DBF4}"
38+
EndProject
3739
Global
3840
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3941
Debug|Any CPU = Debug|Any CPU
@@ -68,6 +70,10 @@ Global
6870
{929263FD-5CD2-42E1-BF3D-E0C1B0320DA4}.Debug|Any CPU.Build.0 = Debug|Any CPU
6971
{929263FD-5CD2-42E1-BF3D-E0C1B0320DA4}.Release|Any CPU.ActiveCfg = Release|Any CPU
7072
{929263FD-5CD2-42E1-BF3D-E0C1B0320DA4}.Release|Any CPU.Build.0 = Release|Any CPU
73+
{0F1AEC4E-E81D-4F84-B2E8-3415A1A4DBF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
74+
{0F1AEC4E-E81D-4F84-B2E8-3415A1A4DBF4}.Debug|Any CPU.Build.0 = Debug|Any CPU
75+
{0F1AEC4E-E81D-4F84-B2E8-3415A1A4DBF4}.Release|Any CPU.ActiveCfg = Release|Any CPU
76+
{0F1AEC4E-E81D-4F84-B2E8-3415A1A4DBF4}.Release|Any CPU.Build.0 = Release|Any CPU
7177
EndGlobalSection
7278
GlobalSection(SolutionProperties) = preSolution
7379
HideSolutionNode = FALSE
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFrameworks>net461;netstandard2.0</TargetFrameworks>
5+
</PropertyGroup>
6+
7+
<ItemGroup>
8+
<PackageReference Include="Microsoft.Build" Version="15.9.20" />
9+
<PackageReference Include="Microsoft.Build.Tasks.Core" Version="15.9.20" />
10+
<PackageReference Include="Microsoft.DotNet.PlatformAbstractions" Version="2.1.0" />
11+
</ItemGroup>
12+
13+
<ItemGroup>
14+
<InternalsVisibleTo Include="GitVersionTask" />
15+
</ItemGroup>
16+
17+
<ItemGroup>
18+
<ProjectReference Include="..\GitVersionCore\GitVersionCore.csproj" />
19+
</ItemGroup>
20+
21+
</Project>

src/GitVersionTask/GitVersionTaskBase.cs renamed to src/GitVersionTask.MsBuild/GitVersionTaskBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace GitVersionTask
1+
namespace GitVersionTask.MsBuild
22
{
33
using Microsoft.Build.Framework;
44
using Microsoft.Build.Utilities;
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// This code originally copied from https://raw.githubusercontent.com/dotnet/sourcelink/master/src/Microsoft.Build.Tasks.Git/GitLoaderContext.cs
2+
#if !NET461
3+
namespace GitVersionTask.MsBuild.LibGit2Sharp
4+
{
5+
using System;
6+
using System.IO;
7+
using System.Linq;
8+
using System.Reflection;
9+
using System.Runtime.InteropServices;
10+
using System.Runtime.Loader;
11+
using RuntimeEnvironment = Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment;
12+
13+
public sealed class GitLoaderContext : AssemblyLoadContext
14+
{
15+
private readonly string[] assemblies;
16+
public static GitLoaderContext Instance { get; private set; }
17+
18+
private GitLoaderContext(string[] assemblies) => this.assemblies = assemblies;
19+
20+
public static void Init(params string[] assemblies) => Instance = new GitLoaderContext(assemblies);
21+
22+
protected override Assembly Load(AssemblyName assemblyName)
23+
{
24+
if (assemblies.Contains(assemblyName.Name))
25+
{
26+
var path = Path.Combine(Path.GetDirectoryName(typeof(GitLoaderContext).Assembly.Location), assemblyName.Name + ".dll");
27+
return LoadFromAssemblyPath(path);
28+
}
29+
30+
return Default.LoadFromAssemblyName(assemblyName);
31+
}
32+
33+
protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
34+
{
35+
var modulePtr = IntPtr.Zero;
36+
37+
if (unmanagedDllName.StartsWith("git2-", StringComparison.Ordinal) ||
38+
unmanagedDllName.StartsWith("libgit2-", StringComparison.Ordinal))
39+
{
40+
var directory = GetNativeLibraryDirectory();
41+
var extension = GetNativeLibraryExtension();
42+
43+
if (!unmanagedDllName.EndsWith(extension, StringComparison.Ordinal))
44+
{
45+
unmanagedDllName += extension;
46+
}
47+
48+
var nativeLibraryPath = Path.Combine(directory, unmanagedDllName);
49+
if (!File.Exists(nativeLibraryPath))
50+
{
51+
nativeLibraryPath = Path.Combine(directory, "lib" + unmanagedDllName);
52+
}
53+
54+
modulePtr = LoadUnmanagedDllFromPath(nativeLibraryPath);
55+
}
56+
57+
return modulePtr != IntPtr.Zero ? modulePtr : base.LoadUnmanagedDll(unmanagedDllName);
58+
}
59+
60+
private static string GetNativeLibraryDirectory()
61+
{
62+
var dir = Path.GetDirectoryName(typeof(GitLoaderContext).Assembly.Location);
63+
return Path.Combine(dir, "runtimes", RuntimeIdMap.GetNativeLibraryDirectoryName(RuntimeEnvironment.GetRuntimeIdentifier()), "native");
64+
}
65+
66+
private static string GetNativeLibraryExtension()
67+
{
68+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
69+
{
70+
return ".dll";
71+
}
72+
73+
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
74+
{
75+
return ".dylib";
76+
}
77+
78+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
79+
{
80+
return ".so";
81+
}
82+
83+
throw new PlatformNotSupportedException();
84+
}
85+
}
86+
}
87+
#endif
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// This code originally copied and adapted from https://raw.githubusercontent.com/dotnet/sourcelink/master/src/Microsoft.Build.Tasks.Git/TaskImplementation.cs
2+
3+
namespace GitVersionTask.MsBuild.LibGit2Sharp
4+
{
5+
using System;
6+
using System.Collections.Generic;
7+
using System.IO;
8+
using System.Reflection;
9+
10+
public class LibGit2SharpLoader
11+
{
12+
private static string taskDirectory;
13+
14+
public static LibGit2SharpLoader Instance { get; private set; }
15+
public Assembly Assembly { get; }
16+
17+
public static void LoadAssembly(string tasksAssembly) => Instance = new LibGit2SharpLoader(tasksAssembly);
18+
19+
private LibGit2SharpLoader(string tasksAssembly)
20+
{
21+
taskDirectory = Path.GetDirectoryName(typeof(LibGit2SharpLoader).Assembly.Location);
22+
#if NET461
23+
nullVersion = new Version(0, 0, 0, 0);
24+
loaderLog = new List<string>();
25+
26+
AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolve;
27+
28+
var assemblyName = typeof(LibGit2SharpLoader).Assembly.GetName();
29+
assemblyName.Name = tasksAssembly;
30+
Assembly = Assembly.Load(assemblyName);
31+
#else
32+
var operationsPath = Path.Combine(taskDirectory, tasksAssembly + ".dll");
33+
Assembly = GitLoaderContext.Instance.LoadFromAssemblyPath(operationsPath);
34+
#endif
35+
}
36+
37+
38+
#if NET461
39+
40+
private static Version nullVersion;
41+
42+
private static List<string> loaderLog;
43+
44+
private static void Log(ResolveEventArgs args, string outcome)
45+
{
46+
lock (loaderLog)
47+
{
48+
loaderLog.Add($"Loading '{args.Name}' referenced by '{args.RequestingAssembly}': {outcome}.");
49+
}
50+
}
51+
52+
public static string[] GetLog()
53+
{
54+
lock (loaderLog)
55+
{
56+
return loaderLog.ToArray();
57+
}
58+
}
59+
60+
private static Assembly AssemblyResolve(object sender, ResolveEventArgs args)
61+
{
62+
// Limit resolution scope to minimum to affect the rest of msbuild as little as possible.
63+
// Only resolve System.* assemblies from the task directory that are referenced with 0.0.0.0 version (from netstandard.dll).
64+
65+
var referenceName = new AssemblyName(args.Name);
66+
if (!referenceName.Name.StartsWith("System.", StringComparison.OrdinalIgnoreCase))
67+
{
68+
Log(args, "not System");
69+
return null;
70+
}
71+
72+
if (referenceName.Version != nullVersion)
73+
{
74+
Log(args, "not null version");
75+
return null;
76+
}
77+
78+
var referencePath = Path.Combine(taskDirectory, referenceName.Name + ".dll");
79+
if (!File.Exists(referencePath))
80+
{
81+
Log(args, $"file '{referencePath}' not found");
82+
return null;
83+
}
84+
85+
Log(args, $"loading from '{referencePath}'");
86+
return Assembly.Load(AssemblyName.GetAssemblyName(referencePath));
87+
}
88+
#endif
89+
}
90+
}

0 commit comments

Comments
 (0)