-
-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
2e0065e
commit 002cc49
Showing
21 changed files
with
1,272 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
using System; | ||
using System.Linq; | ||
using DotnetAffected.Abstractions; | ||
using DotnetAffected.Core; | ||
using Microsoft.Build.Execution; | ||
using Microsoft.Build.Framework; | ||
using Microsoft.Build.Utilities; | ||
using System.Collections; | ||
using System.Collections.Generic; | ||
|
||
namespace DotnetAffected.Tasks | ||
{ | ||
public class AffectedTask : Microsoft.Build.Utilities.Task | ||
{ | ||
[Required] | ||
public string Root { get; set; } | ||
|
||
[Required] | ||
public ITaskItem[] Projects { get; set; } | ||
|
||
public ITaskItem[] AssumeChanges { get; set; } | ||
|
||
public ITaskItem[]? FilterClasses { get; set; } | ||
|
||
[Output] | ||
public ITaskItem[] FilterInstances { get; private set; } | ||
|
||
[Output] | ||
public string[] ModifiedProjects { get; private set; } | ||
|
||
[Output] | ||
public int ModifiedProjectsCount { get; private set; } | ||
|
||
public override bool Execute() | ||
{ | ||
try | ||
{ | ||
var affectedOptions = new AffectedOptions(Root); | ||
|
||
var graph = new ProjectGraphFactory(affectedOptions).BuildProjectGraph(); | ||
IChangesProvider changesProvider = AssumeChanges?.Any() == true | ||
? new AssumptionChangesProvider(graph, AssumeChanges.Select(c => c.ItemSpec)) | ||
: new GitChangesProvider(); | ||
|
||
var executor = new AffectedExecutor(affectedOptions, | ||
graph, | ||
changesProvider, | ||
new PredictionChangedProjectsProvider(graph, affectedOptions)); | ||
|
||
var results = executor.Execute(); | ||
var modifiedProjectInstances = new HashSet<ProjectInstance>(); | ||
var modifiedProjects = new List<string>(); | ||
var filterInstances = new List<ITaskItem>(); | ||
var filterTypes = BuildFilterClassMetadata(); | ||
|
||
foreach (var node in results.ProjectsWithChangedFiles.Concat(results.AffectedProjects)) | ||
{ | ||
if (modifiedProjectInstances.Add(node.ProjectInstance)) | ||
{ | ||
modifiedProjects.Add(node.ProjectInstance.FullPath); | ||
|
||
if (filterTypes.Length > 0) | ||
{ | ||
var projectInstance = node.ProjectInstance; | ||
foreach (var filterType in filterTypes) | ||
{ | ||
var taskItem = new TaskItem(projectInstance.FullPath); | ||
filterInstances.Add(taskItem); | ||
|
||
foreach (var kvp in filterType) | ||
taskItem.SetMetadata(kvp.Key, projectInstance.GetProperty(kvp.Key)?.EvaluatedValue ?? kvp.Value); | ||
} | ||
} | ||
} | ||
} | ||
|
||
FilterInstances = filterInstances.ToArray(); | ||
ModifiedProjects = modifiedProjects.ToArray(); | ||
ModifiedProjectsCount = ModifiedProjects.Length; | ||
} | ||
catch (Exception? e) | ||
{ | ||
while (e is not null) | ||
{ | ||
Log.LogErrorFromException(e); | ||
e = e.InnerException; | ||
} | ||
} | ||
|
||
return !Log.HasLoggedErrors; | ||
} | ||
|
||
private Dictionary<string, string>[] BuildFilterClassMetadata() | ||
{ | ||
Dictionary<string, string> Selector(ITaskItem filter) | ||
{ | ||
var t = new Dictionary<string, string>(); | ||
foreach (var obj in filter.CloneCustomMetadata()) | ||
{ | ||
var entry = (DictionaryEntry)obj; | ||
t[(string)entry.Key] = entry.Value as string ?? ""; | ||
} | ||
|
||
t["AffectedFilterClassName"] = filter.ItemSpec; | ||
return t; | ||
} | ||
|
||
return FilterClasses is null | ||
? Array.Empty<Dictionary<string, string>>() | ||
: FilterClasses.Select(Selector).ToArray(); | ||
} | ||
|
||
static AffectedTask() | ||
{ | ||
Lib2GitNativePathHelper.ResolveCustomNativeLibraryPath(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<Import Project="$(PackageDefaultsPropsPath)" /> | ||
|
||
<PropertyGroup> | ||
<RootNamespace>DotnetAffected.Tasks</RootNamespace> | ||
|
||
<ExcludeFromSourceBuild>true</ExcludeFromSourceBuild> | ||
|
||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> | ||
<PackageType>MSBuildSdk</PackageType> | ||
<IncludeBuildOutput>false</IncludeBuildOutput> | ||
<IsPackable>true</IsPackable> | ||
|
||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems> | ||
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking> | ||
<PackTasks>true</PackTasks> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<Compile Include="AffectedTask.cs" /> | ||
<Compile Include="Lib2GitNativePathHelper.cs" /> | ||
<None Include="Sdk\**" Pack="true" PackagePath="Sdk\" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.Build.Prediction" /> | ||
|
||
<PackageReference Include="Microsoft.Build.Framework" Publish="false" /> | ||
<PackageReference Include="Microsoft.Build.Utilities.Core" Publish="false" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="$(SourcesPath)/DotnetAffected.Core/DotnetAffected.Core.csproj" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<!-- | ||
Update all PackageReference and ProjectReference Items to have | ||
PrivateAssets="All" and default Publish to true. | ||
This removes the dependency nodes from the generated nuspec and | ||
forces the publish output to contain the dlls. | ||
--> | ||
<PackageReference Update="@(PackageReference)"> | ||
<PrivateAssets>All</PrivateAssets> | ||
<Publish Condition="'%(PackageReference.Publish)' == ''">true</Publish> | ||
<ExcludeAssets Condition="'%(PackageReference.Publish)' == 'false'">runtime</ExcludeAssets> | ||
</PackageReference> | ||
<ProjectReference Update="@(ProjectReference)"> | ||
<PrivateAssets>All</PrivateAssets> | ||
<Publish Condition="'%(ProjectReference.Publish)' == ''">true</Publish> | ||
</ProjectReference> | ||
|
||
<!-- | ||
Do not include assemblies that MSBuild ships with in the package. | ||
--> | ||
<PackageReference Update="Microsoft.Build" PrivateAssets="All" Publish="false" ExcludeAssets="runtime" /> | ||
<PackageReference Update="Microsoft.Build.Utilities.Core" PrivateAssets="All" Publish="false" ExcludeAssets="runtime" /> | ||
</ItemGroup> | ||
|
||
<PropertyGroup> | ||
<TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);_AddBuildOutputToPackageCore</TargetsForTfmSpecificContentInPackage> | ||
</PropertyGroup> | ||
|
||
<Target Name="_AddBuildOutputToPackageCore" DependsOnTargets="Publish"> | ||
<!-- | ||
Create a clone for OSX/Linux libraries that starts with "lib2git". | ||
The clone will have the first 3 characters of the filename removed ("lib"). | ||
This is required to load the libraries since LibGit2Sharp will search for them without the "lib" | ||
--> | ||
<ItemGroup> | ||
<NativeLibGitToCopy Include="$(PublishDir)runtimes/**/native/libgit2*.*" /> | ||
</ItemGroup> | ||
<Copy SourceFiles="@(NativeLibGitToCopy)" DestinationFiles="@(NativeLibGitToCopy->Replace('libgit2','git2'))" /> | ||
|
||
<!-- | ||
Publish .NET Core assets and include them in the package under tools directory. | ||
--> | ||
<ItemGroup> | ||
<TfmSpecificPackageFile Include="$(PublishDir)**" PackagePath="tools/$(TargetFramework)/%(RecursiveDir)%(FileName)%(Extension)" /> | ||
</ItemGroup> | ||
</Target> | ||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
using System; | ||
using System.IO; | ||
using System.Runtime.InteropServices; | ||
|
||
namespace DotnetAffected.Tasks | ||
{ | ||
public static class Lib2GitNativePathHelper | ||
{ | ||
public static void ResolveCustomNativeLibraryPath() | ||
{ | ||
var assemblyDirectory = Path.GetDirectoryName(typeof(LibGit2Sharp.GlobalSettings).Assembly.Location); | ||
var runtimesDirectory = Path.Combine(assemblyDirectory ?? "", "runtimes"); | ||
|
||
if (!Directory.Exists(runtimesDirectory)) | ||
return; | ||
|
||
var processorArchitecture = RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant(); | ||
|
||
var (os, libExtension) = GetNativeInfo(); | ||
|
||
foreach (var runtimeFolder in Directory.GetDirectories(runtimesDirectory, $"{os}*-{processorArchitecture}")) | ||
{ | ||
var libFolder = Path.Combine(runtimeFolder, "native"); | ||
|
||
foreach (var libFilePath in Directory.GetFiles(libFolder, $"*{libExtension}")) | ||
{ | ||
if (IsLibraryLoadable(libFilePath)) | ||
{ | ||
LibGit2Sharp.GlobalSettings.NativeLibraryPath = libFolder; | ||
return; | ||
} | ||
|
||
} | ||
} | ||
} | ||
|
||
private static (string Os, string LibExtension) GetNativeInfo() | ||
{ | ||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) | ||
return ("linux", ".so"); | ||
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) | ||
return ("osx", ".dylib"); | ||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) | ||
return ("win", ".dll"); | ||
|
||
throw new PlatformNotSupportedException(); | ||
} | ||
|
||
private static bool IsLibraryLoadable(string libPath) | ||
{ | ||
if (File.Exists(libPath) && NativeLibrary.TryLoad(libPath, out var ptr)) | ||
{ | ||
NativeLibrary.Free(ptr); | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
|
||
|
||
`DotnetAffected.Core` uses `LibGit2Sharp` to analyse changes using the git history/log. | ||
|
||
`LibGit2Sharp` uses native, os based, git libraries to perform all git operations. | ||
|
||
Loading of the native libraries is based on predefined locations relative to the application/assembly. | ||
It works fine when used in the context of an application. | ||
With a Task however, it does not. | ||
|
||
A task is loaded in the context of the build running it. | ||
When the task activates `LibGit2Sharp`, it will try to load the native libraries, however in the | ||
context of the build application which does not have access to the native libraries. | ||
|
||
This is known issue when using dependencies in a build library using Tasks. | ||
The solution is to omit dependency declaration and instead physically include them with the | ||
shipped library. | ||
|
||
With `LibGit2Sharp` we also need to tell it where to look for the native libraries. | ||
`LibGit2Sharp`' will load the file from the path we provide, however for osx/linux it will | ||
try to load a file which does not exists since it comes with a `lib` prefix which the | ||
os native library resolver knows how to handle but with custom load it fails. | ||
|
||
To workaround that we just add an additional build step to the library to also have a copy | ||
of the native library without the `lib` prefix. | ||
|
||
So we have 2 files for each implementation, for example: `libgit2-XYZ.so` and `git2-XYZ.so` |
Oops, something went wrong.