-
Notifications
You must be signed in to change notification settings - Fork 2.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce MSBuild tasks programability #12364
Changes from all commits
005b627
681d5e9
07e14bc
2481095
f24094b
189e8fc
f368274
ab18542
86bf637
72c6202
fdbb298
c9fc713
13d3f66
4957796
0fde7bc
f08266d
7b6221b
28f5382
6fa7c2b
43205b6
c9577ec
0a1b8b6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
using System.Security.Cryptography; | ||
using Microsoft.Build.Framework; | ||
using MSBuildTask = Microsoft.Build.Utilities.Task; | ||
|
||
namespace OrchardCore.Build.Tasks; | ||
|
||
public class CopyTranslationFilesTask : MSBuildTask | ||
{ | ||
public string SourceFilePath { get; set; } | ||
|
||
[Required] | ||
public string DestinationFolderPath { get; set; } | ||
|
||
public override bool Execute() | ||
{ | ||
if (string.IsNullOrEmpty(SourceFilePath)) | ||
{ | ||
return true; | ||
} | ||
|
||
if (!Directory.Exists(DestinationFolderPath)) | ||
{ | ||
Directory.CreateDirectory(DestinationFolderPath); | ||
} | ||
|
||
var fileInfo = new FileInfo(SourceFilePath); | ||
var destinationFilePath = Path.Combine(DestinationFolderPath, fileInfo.Name); | ||
|
||
if (File.Exists(destinationFilePath)) | ||
{ | ||
// Skip unchanged files | ||
var sourceFileHash = GetFileHashAsync(fileInfo.FullName); | ||
var destinationFileHash = GetFileHashAsync(destinationFilePath); | ||
|
||
if (sourceFileHash.Equals(destinationFileHash, StringComparison.OrdinalIgnoreCase)) | ||
{ | ||
return true; | ||
} | ||
} | ||
|
||
fileInfo.CopyTo(destinationFilePath, overwrite: true); | ||
|
||
return true; | ||
} | ||
|
||
private static string GetFileHashAsync(string fileName) | ||
{ | ||
using var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read); | ||
var hash = SHA256.HashData(stream); | ||
|
||
return Convert.ToBase64String(hash); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
using MSBuildTask = Microsoft.Build.Utilities.Task; | ||
|
||
namespace OrchardCore.Build.Tasks; | ||
|
||
public abstract class CreateWebFolderTask : MSBuildTask | ||
{ | ||
public abstract string FolderName { get; } | ||
|
||
public override bool Execute() | ||
{ | ||
if (!Directory.Exists(FolderName)) | ||
{ | ||
Directory.CreateDirectory(FolderName); | ||
|
||
var dummyFilePath = Path.Combine(FolderName, ".placeholder"); | ||
|
||
if (!File.Exists(dummyFilePath)) | ||
{ | ||
File.WriteAllText(dummyFilePath, string.Empty); | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
namespace OrchardCore.Build.Tasks; | ||
|
||
public class CreateLocalizationFolderTask : CreateWebFolderTask | ||
{ | ||
public override string FolderName => "Localization"; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
namespace OrchardCore.Build.Tasks; | ||
|
||
public class CreateWebRootFolderTask : CreateWebFolderTask | ||
{ | ||
public override string FolderName => "wwwroot"; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<Import Project="..\OrchardCore.Build\OrchardCore.Commons.targets" /> | ||
<Import Project="..\OrchardCore.Build\Dependencies.props" /> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>$(CommonTargetFrameworks)</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.Build.Utilities.Core" /> | ||
</ItemGroup> | ||
|
||
</Project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,12 @@ | ||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
|
||
<PropertyGroup> | ||
<_OrchardCoreBuildTasksAssembly>$(ProjectDir)..\..\OrchardCore.Build.Tasks\bin\$(Configuration)\$(CommonTargetFrameworks)\OrchardCore.Build.Tasks.dll</_OrchardCoreBuildTasksAssembly> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will only work if the DLL already exists but if you build the solution for the first time, it won't. Won't this cause issues? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It should be there during the build, if it's not there the GitHub Workflow will not pass There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, but this line will be executed even when one just opens the solution and anything else that MSBuild does, not just during a build. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will only work if the DLL already exists but if you build the solution for the first time, it won't. Won't this cause issues?> This is weird, could you please try the following:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you please make sure you test all of such scenarios? And in VS, the solution should still work with F5. |
||
</PropertyGroup> | ||
|
||
<UsingTask TaskName="CopyTranslationFilesTask" AssemblyFile="$(_OrchardCoreBuildTasksAssembly)" /> | ||
<UsingTask TaskName="CreateLocalizationFolderTask" AssemblyFile="$(_OrchardCoreBuildTasksAssembly)" /> | ||
<UsingTask TaskName="CreateWebRootFolderTask" AssemblyFile="$(_OrchardCoreBuildTasksAssembly)" /> | ||
|
||
<ItemGroup> | ||
<Compile Remove="App_Data\**;Localization\**" /> | ||
|
@@ -12,25 +20,19 @@ | |
<RelativePath>App_Data\%(RecursiveDir)%(Filename)%(Extension)</RelativePath> | ||
</ResolvedFileToPublish> | ||
</ItemGroup> | ||
|
||
|
||
<Target Name="MakeWebRootFolder" BeforeTargets="MakeLocalizationFolder" Condition="!Exists('wwwroot/.placeholder')"> | ||
<WriteLinesToFile Lines="" Encoding="Unicode" File="wwwroot/.placeholder" /> | ||
<Target Name="MakeWebRootFolder" BeforeTargets="MakeLocalizationFolder"> | ||
<CreateWebRootFolderTask /> | ||
</Target> | ||
|
||
<Target Name="MakeLocalizationFolder" BeforeTargets="Build" Condition="!Exists('Localization/.placeholder')"> | ||
<WriteLinesToFile Lines="" Encoding="Unicode" File="Localization/.placeholder" /> | ||
<Target Name="MakeLocalizationFolder" BeforeTargets="Build"> | ||
<CreateLocalizationFolderTask /> | ||
</Target> | ||
|
||
<Target | ||
Name="CopyPackageTranslationFiles" | ||
AfterTargets="Build"> | ||
<Target Name="CopyPackageTranslationFiles" AfterTargets="Build"> | ||
<Message Text="Copying translation files: $(MSBuildProjectName)" Importance="high" /> | ||
<Copy | ||
SourceFiles="%(PackageTranslationFiles.FullPath)" | ||
DestinationFolder="Localization\%(RecursiveDir)" | ||
Condition="'@(PackageTranslationFiles)' != ''" | ||
SkipUnchangedFiles="true" | ||
OverwriteReadOnlyFiles="false" /> | ||
<CopyTranslationFilesTask Condition="'@(PackageTranslationFiles)' != ''" SourceFilePath="%(PackageTranslationFiles.FullPath)" DestinationFolderPath="Localization\%(RecursiveDir)" /> | ||
</Target> | ||
|
||
<Target Name="PublishTranslationFiles" BeforeTargets="GetCopyToPublishDirectoryItems"> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
namespace OrchardCore.Build.Tasks.Tests; | ||
|
||
public class CopyTranslationFilesTaskTests | ||
{ | ||
private const string LocalizationFolder = "Localization"; | ||
|
||
private readonly Mock<IBuildEngine> _buildEngine; | ||
private readonly List<BuildErrorEventArgs> _errors = []; | ||
|
||
public CopyTranslationFilesTaskTests() | ||
{ | ||
_buildEngine = new Mock<IBuildEngine>(); | ||
_buildEngine.Setup(be => be.LogErrorEvent(It.IsAny<BuildErrorEventArgs>())) | ||
.Callback<BuildErrorEventArgs>(e => _errors.Add(e)); | ||
} | ||
|
||
[Theory] | ||
[InlineData("ar.po")] | ||
[InlineData("fr.po")] | ||
public void ShouldCopyTranslationFilesToLocalizationFolder(string file) | ||
{ | ||
// Arrange | ||
var filePath = Path.Combine(LocalizationFolder, file); | ||
var task = new CopyTranslationFilesTask | ||
{ | ||
BuildEngine = _buildEngine.Object, | ||
SourceFilePath = file, | ||
DestinationFolderPath = LocalizationFolder, | ||
}; | ||
|
||
TryCreateLocalizationFolder(); | ||
|
||
// Act | ||
var result = task.Execute(); | ||
|
||
// Assert | ||
Assert.True(result); | ||
Assert.Empty(_errors); | ||
Assert.True(File.Exists(filePath)); | ||
} | ||
|
||
private static void TryCreateLocalizationFolder() | ||
{ | ||
if (Directory.Exists(LocalizationFolder)) | ||
{ | ||
Directory.Delete(LocalizationFolder, recursive: true); | ||
} | ||
|
||
Directory.CreateDirectory(LocalizationFolder); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
namespace OrchardCore.Build.Tasks.Tests; | ||
|
||
public class CreateWebRootFolderTaskTests | ||
{ | ||
private const string WebRootFolder = "wwwroot"; | ||
|
||
private static readonly string _placeholderFile = $"{WebRootFolder}\\.placeholder"; | ||
|
||
private readonly Mock<IBuildEngine> _buildEngine; | ||
private readonly List<BuildErrorEventArgs> _errors = []; | ||
|
||
public CreateWebRootFolderTaskTests() | ||
{ | ||
_buildEngine = new Mock<IBuildEngine>(); | ||
_buildEngine.Setup(be => be.LogErrorEvent(It.IsAny<BuildErrorEventArgs>())) | ||
.Callback<BuildErrorEventArgs>(e => _errors.Add(e)); | ||
|
||
TryDeleteLocalizationFolder(); | ||
} | ||
|
||
[Fact] | ||
public void ShouldCreateLocalizationFolder() | ||
{ | ||
// Arrange | ||
var task = new CreateWebRootFolderTask | ||
{ | ||
BuildEngine = _buildEngine.Object | ||
}; | ||
|
||
// Act | ||
var result = task.Execute(); | ||
|
||
// Assert | ||
Assert.True(result); | ||
Assert.Empty(_errors); | ||
Assert.True(Directory.Exists(WebRootFolder)); | ||
Assert.True(File.Exists(_placeholderFile)); | ||
} | ||
|
||
[Fact] | ||
public void ShouldNotCreateLocalizationFolderIfExists() | ||
{ | ||
// Arrange | ||
var task = new CreateWebRootFolderTask | ||
{ | ||
BuildEngine = _buildEngine.Object | ||
}; | ||
Directory.CreateDirectory(WebRootFolder); | ||
|
||
var createdTime1 = new DirectoryInfo(_placeholderFile).CreationTimeUtc; | ||
|
||
// Act & Assert | ||
task.Execute(); | ||
Assert.True(Directory.Exists(WebRootFolder)); | ||
|
||
var createdTime2 = new DirectoryInfo(_placeholderFile).CreationTimeUtc; | ||
Assert.Equal(createdTime1, createdTime2); | ||
} | ||
|
||
[Fact] | ||
public void IgnorePlaceholderFileIfExists() | ||
{ | ||
// Arrange | ||
var task = new CreateWebRootFolderTask | ||
{ | ||
BuildEngine = _buildEngine.Object | ||
}; | ||
|
||
// Act & Assert | ||
task.Execute(); | ||
|
||
var createdTime1 = new FileInfo(_placeholderFile).CreationTimeUtc; | ||
task.Execute(); | ||
|
||
var createdTime2 = new FileInfo(_placeholderFile).CreationTimeUtc; | ||
Assert.Equal(createdTime1, createdTime2); | ||
} | ||
|
||
private static void TryDeleteLocalizationFolder() | ||
{ | ||
if (Directory.Exists(WebRootFolder)) | ||
{ | ||
Directory.Delete(WebRootFolder, true); | ||
} | ||
} | ||
} |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move this under the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It was there, but I'd like to separate them into another project to make it clear that's not related to the actual OC tests project There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All tests and even benchmarks are there, so this is suitable (and it's already in the same file system folder). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Benchmarks in a separated project, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't understand, sorry. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The benchmarks in a separate project There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm talking about moving this project to the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just noticed that while I was preparing a demo |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<Import Project="..\..\src\OrchardCore.Build\Dependencies.props" /> | ||
|
||
<PropertyGroup> | ||
<TargetFrameworks>$(CommonTargetFrameworks)</TargetFrameworks> | ||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This won't be needed if #15764 is merged first. |
||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<FrameworkReference Include="Microsoft.AspNetCore.App" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\..\src\OrchardCore.Build.Tasks\OrchardCore.Build.Tasks.csproj" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.NET.Test.Sdk" /> | ||
<PackageReference Include="Moq" /> | ||
<PackageReference Include="xunit" /> | ||
<PackageReference Include="xunit.analyzers" /> | ||
<PackageReference Include="xunit.runner.visualstudio" /> | ||
<DotNetCliToolReference Include="dotnet-xunit" Version="$(DotNetXunitVersion)" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<EmbeddedResource Include="ar.po"> | ||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||
</EmbeddedResource> | ||
<EmbeddedResource Include="fr.po"> | ||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||
</EmbeddedResource> | ||
</ItemGroup> | ||
|
||
<Import Project="..\..\src\OrchardCore.Build\OrchardCore.Commons.targets" /> | ||
|
||
</Project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
global using System; | ||
global using System.Collections.Generic; | ||
global using System.IO; | ||
global using Microsoft.Build.Framework; | ||
global using Moq; | ||
global using Xunit; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
msgctxt "OrchardCore.Tests.Localization.PortableObjectStringLocalizerFactoryTests.Model" | ||
msgid "Hello" | ||
msgstr "مرحبا" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
msgctxt "OrchardCore.Tests.Localization.PortableObjectStringLocalizerFactoryTests.Model" | ||
msgid "Hello" | ||
msgstr "Bonjour" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a folder name, not a path, such as
wwwroot
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How it's used is a path. Passing
wwwroot/some/sub/folder
to it would work too.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, it could be