Add support for requiring Visual Studio's MSBuild from KoreBuild

This commit is contained in:
Nate McMaster 2017-10-27 14:11:06 -07:00
Родитель f977d575ec
Коммит ec1822a2c8
16 изменённых файлов: 797 добавлений и 6 удалений

Просмотреть файл

@ -1,18 +1,34 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27004.2002
VisualStudioVersion = 15.0.27004.2005
MinimumVisualStudioVersion = 15.0.26730.03
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A4F4353B-C3D2-40B0-909A-5B48A748EA76}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BF3E9C90-F129-4CE6-8F3B-F96831E4429B}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "files", "files", "{BF3E9C90-F129-4CE6-8F3B-F96831E4429B}"
ProjectSection(SolutionItems) = preProject
.appveyor.yml = .appveyor.yml
.editorconfig = .editorconfig
.gitattributes = .gitattributes
.gitignore = .gitignore
.travis.yml = .travis.yml
build.cmd = build.cmd
build.ps1 = build.ps1
build.sh = build.sh
build\common.props = build\common.props
CONTRIBUTING.md = CONTRIBUTING.md
build\dependencies.props = build\dependencies.props
global.json = global.json
korebuild.json = korebuild.json
LICENSE.txt = LICENSE.txt
NuGet.Config = NuGet.Config
push.cmd = push.cmd
push.ps1 = push.ps1
README.md = README.md
build\repo.props = build\repo.props
build\repo.targets = build\repo.targets
test.ps1 = test.ps1
test.sh = test.sh
version.props = version.props
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{60A938B2-D95A-403C-AA7A-3683AD64DFA0}"

Просмотреть файл

@ -36,8 +36,44 @@ docker-build | Runs the build inside docker. | .\run.ps1 docker-build {jessie\|w
default-build| Runs install-tools followed by msbuild (like build.cmd used to). | .\run.ps1 default-build /t:SomeTarget /p:Parameters
msbuild | Runs the build normally. | .\run.ps1 msbuild /t:SomeTarget /p:Parameters
### KoreBuild config
KoreBuild can be configured by adding a 'korebuild.json' file into the root folder of your repository.
Example:
```js
{
// add this for editor auto-completion :)
"$schema": "https://raw.githubusercontent.com/aspnet/BuildTools/dev/tools/korebuild.schema.json",
// specifies the channel used to update KoreBuild to new versions when you attempt to upgrade KoreBuild
"channel": "dev",
"toolsets": {
// All toolsets listed in this section are treated as required toolsets
"visualstudio": {
// defaults to `true`
"includePrerelease": false,
// see https://aka.ms/vs/workloads
"requiredWorkloads": [
"Microsoft.VisualStudio.Component.VSSDK"
],
// Default = no minimum version
"minVersion": "15.4",
// This tool is only required on Windows.
"required": [ "windows" ]
}
}
}
```
### Local testing
To test changes to this project locally we recomend you do:
```
```ps1
./test.ps1 -Command $CommandToTest -RepoPath C:\repo\to\test\against\
```

Просмотреть файл

@ -12,6 +12,7 @@
<SystemReflectionMetadataVersion>1.5.0</SystemReflectionMetadataVersion>
<TestSdkVersion>15.3.0</TestSdkVersion>
<XunitVersion>2.3.0-rc3-build3818</XunitVersion>
<VSWherePackageVersion>2.2.7</VSWherePackageVersion>
</PropertyGroup>
<!--

Просмотреть файл

@ -1,4 +1,4 @@
{
{
"$schema": "./tools/korebuild.schema.json",
"channel": "dev"
}

Просмотреть файл

@ -0,0 +1,93 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using KoreBuild.Tasks.Utilities;
using Microsoft.Build.Framework;
namespace KoreBuild.Tasks
{
/// <summary>
/// Finds Visual Studio.
/// </summary>
public class FindVisualStudio : Microsoft.Build.Utilities.Task
{
/// <summary>
/// The path to the korebuild.json file. (Optional)
/// </summary>
public string ConfigFile { get; set; }
/// <summary>
/// The base path to the installation of VS.
/// </summary>
[Output]
public string InstallationBasePath { get; set; }
/// <summary>
/// The path to MSBuild.exe (x86). It will be empty if this file does not exist.
/// </summary>
[Output]
public string MSBuildx86Path { get; set; }
/// <summary>
/// The path to MSBuild.exe (x64). It will be empty if this file does not exist.
/// </summary>
[Output]
public string MSBuildx64Path { get; set; }
public override bool Execute()
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
Log.LogError("Visual Studio cannot be found on non-Windows platforms");
return false;
}
VsInstallation vs;
if (!string.IsNullOrEmpty(ConfigFile))
{
if (!File.Exists(ConfigFile))
{
Log.LogError($"Could not load the korebuild config file from '{ConfigFile}'");
return false;
}
var settings = KoreBuildSettings.Load(ConfigFile);
var vsToolset = settings.Toolsets?.OfType<KoreBuildSettings.VisualStudioToolset>().FirstOrDefault();
if (vsToolset != null)
{
vs = VsWhere.FindLatestCompatibleInstallation(vsToolset, Log);
if (vs == null)
{
Log.LogError($"Could not find an installation of Visual Studio that satisifies the specified requirements in {ConfigFile}.");
return false;
}
}
else
{
vs = VsWhere.FindLatestInstallation(includePrerelease: true, log: Log);
}
}
else
{
vs = VsWhere.FindLatestInstallation(includePrerelease: true, log: Log);
}
if (vs == null)
{
Log.LogError($"Could not find any installation of Visual Studio.");
return false;
}
Log.LogMessage(MessageImportance.Normal, "Found {0} in {1}", vs.DisplayName, vs.InstallationPath);
InstallationBasePath = vs.InstallationPath;
MSBuildx86Path = vs.GetMSBuildx86SubPath();
MSBuildx64Path = vs.GetMSBuildx64SubPath();
return !Log.HasLoggedErrors;
}
}
}

Просмотреть файл

@ -0,0 +1,97 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO;
using System.Runtime.InteropServices;
using KoreBuild.Tasks.Utilities;
using Microsoft.Build.Framework;
namespace KoreBuild.Tasks
{
/// <summary>
/// Finds toolset information as listed in korebuild.json
/// </summary>
public class GetToolsets : Microsoft.Build.Utilities.Task
{
/// <summary>
/// The path to the korebuild.json file.
/// </summary>
[Required]
public string ConfigFile { get; set; }
/// <summary>
/// The path to MSBuild.exe (x86), if the 'visualstudio' toolset was specified. It will be empty if this file does not exist.
/// </summary>
[Output]
public string VisualStudioMSBuildx86Path { get; set; }
/// <summary>
/// The path to MSBuild.exe (x64), if the 'visualstudio' toolset was specified. It will be empty if this file does not exist.
/// </summary>
[Output]
public string VisualStudioMSBuildx64Path { get; set; }
public override bool Execute()
{
if (!File.Exists(ConfigFile))
{
Log.LogError($"Could not load the korebuild config file from '{ConfigFile}'");
return false;
}
var settings = KoreBuildSettings.Load(ConfigFile);
if (settings?.Toolsets == null)
{
Log.LogMessage(MessageImportance.Normal, "No recognized toolsets specified.");
return true;
}
foreach (var toolset in settings.Toolsets)
{
switch (toolset)
{
case KoreBuildSettings.VisualStudioToolset vs:
GetVisualStudio(vs);
break;
default:
Log.LogWarning("Toolset checks not implemented for " + toolset.GetType().Name);
break;
}
}
return !Log.HasLoggedErrors;
}
private void GetVisualStudio(KoreBuildSettings.VisualStudioToolset vsToolset)
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
if ((vsToolset.Required & KoreBuildSettings.RequiredPlatforms.Windows) != 0)
{
Log.LogError("Visual Studio is not available on non-Windows. Change korebuild.json to 'required: [\"windows\"]'.");
}
else
{
Log.LogMessage(MessageImportance.Low, "Skipping Visual Studio verification on non-Windows platforms.");
}
return;
}
var vs = VsWhere.FindLatestCompatibleInstallation(vsToolset, Log);
if (vs == null)
{
if (vsToolset.Required != KoreBuildSettings.RequiredPlatforms.None)
{
Log.LogError($"Could not find an installation of Visual Studio that satisifies the specified requirements in {ConfigFile}. This is required to build.");
}
return;
}
Log.LogMessage(MessageImportance.High, "Using {0} from {1}", vs.DisplayName, vs.InstallationPath);
VisualStudioMSBuildx86Path = vs.GetMSBuildx86SubPath();
VisualStudioMSBuildx64Path = vs.GetMSBuildx64SubPath();
}
}
}

Просмотреть файл

@ -10,6 +10,8 @@
<Content Include="*.targets" CopyToPublishDirectory="PreserveNewest" />
<Compile Include="..\..\shared\Microsoft.Extensions.CommandLineUtils.Sources\Utilities\*.cs" />
<Compile Include="..\..\shared\Utilities\MSBuildListSplitter.cs" />
<Compile Include="..\..\tools\KoreBuildSettings.cs" />
<Content Include="$(VSWhereDir)vswhere.exe" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" />
</ItemGroup>
<ItemGroup>
@ -20,6 +22,7 @@
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="$(MicrosoftBuildVersion)" PrivateAssets="All" />
<PackageReference Include="NuGet.Build.Tasks" Version="$(NuGetInMSBuildVersion)" PrivateAssets="All" />
<PackageReference Include="Newtonsoft.Json" Version="$(JsonNetInMSBuildVersion)" PrivateAssets="All" />
<PackageReference Include="vswhere" Version="$(VSWherePackageVersion)" PrivateAssets="All" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,35 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO;
namespace KoreBuild.Tasks.Utilities
{
/// <summary>
/// A DTO that is deserialized from the output of 'vswhere.exe -format json'
/// </summary>
internal class VsInstallation
{
public string DisplayName { get; set; }
public string InstallationPath { get; set; }
// Add methods for additional info inferred from the vswhere.exe output.
public string GetMSBuildx86SubPath()
{
var path = Path.Combine(InstallationPath, "MSBuild", "15.0", "Bin", "MSBuild.exe");
return File.Exists(path)
? path
: null;
}
public string GetMSBuildx64SubPath()
{
var path = Path.Combine(InstallationPath, "MSBuild", "15.0", "Bin", "amd64", "MSBuild.exe");
return File.Exists(path)
? path
: null;
}
}
}

Просмотреть файл

@ -0,0 +1,116 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Microsoft.Extensions.CommandLineUtils;
using Newtonsoft.Json;
namespace KoreBuild.Tasks.Utilities
{
internal class VsWhere
{
public static VsInstallation FindLatestInstallation(bool includePrerelease, TaskLoggingHelper log)
{
var args = new List<string>
{
"-latest",
};
if (includePrerelease)
{
args.Add("-prerelease");
}
return GetInstallations(args, log).FirstOrDefault();
}
public static VsInstallation FindLatestCompatibleInstallation(KoreBuildSettings.VisualStudioToolset toolset, TaskLoggingHelper log)
{
var args = new List<string> { "-latest" };
if (toolset.IncludePrerelease)
{
args.Add("-prerelease");
}
if (!string.IsNullOrEmpty(toolset.MinVersion))
{
args.Add("-version");
args.Add(toolset.MinVersion);
}
if (toolset.RequiredWorkloads != null)
{
foreach (var workload in toolset.RequiredWorkloads)
{
args.Add("-requires");
args.Add(workload);
}
}
return GetInstallations(args, log).FirstOrDefault();
}
private static VsInstallation[] GetInstallations(List<string> args, TaskLoggingHelper log)
{
args.Add("-format");
args.Add("json");
var vswhere = GetVsWherePath();
var process = new Process
{
StartInfo =
{
FileName = vswhere,
Arguments = ArgumentEscaper.EscapeAndConcatenate(args),
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true,
}
};
log.LogCommandLine(process.StartInfo.FileName + " " + process.StartInfo.Arguments);
try
{
process.Start();
process.WaitForExit();
}
catch (Exception ex)
{
log.LogError("vswhere failed." + ex.Message);
return Array.Empty<VsInstallation>();
}
var output = process.StandardOutput.ReadToEnd();
if (process.ExitCode != 0)
{
log.LogMessage(MessageImportance.Low, "vswhere output = " + output);
log.LogError("vswhere failed.");
return Array.Empty<VsInstallation>();
}
return JsonConvert.DeserializeObject<VsInstallation[]>(output);
}
private static string GetVsWherePath()
{
var searchPaths = new[]
{
Path.Combine(Path.GetDirectoryName(typeof(FindVisualStudio).Assembly.Location), "vswhere.exe"),
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Microsoft Visual Studio", "Installer", "vswhere.exe"),
};
var file = searchPaths.FirstOrDefault(File.Exists);
return file ?? "vswhere";
}
}
}

Просмотреть файл

@ -2,8 +2,10 @@
<UsingTask TaskName="KoreBuild.Tasks.ApplyNuGetPolicies" AssemblyFile="$(MSBuildThisFileDirectory)Internal.AspNetCore.KoreBuild.Tasks.dll" />
<UsingTask TaskName="KoreBuild.Tasks.CheckPackageReferences" AssemblyFile="$(MSBuildThisFileDirectory)Internal.AspNetCore.KoreBuild.Tasks.dll" />
<UsingTask TaskName="KoreBuild.Tasks.DownloadNuGetPackages" AssemblyFile="$(MSBuildThisFileDirectory)Internal.AspNetCore.KoreBuild.Tasks.dll" />
<UsingTask TaskName="KoreBuild.Tasks.FindVisualStudio" AssemblyFile="$(MSBuildThisFileDirectory)Internal.AspNetCore.KoreBuild.Tasks.dll" />
<UsingTask TaskName="KoreBuild.Tasks.GenerateDependenciesPropsFile" AssemblyFile="$(MSBuildThisFileDirectory)Internal.AspNetCore.KoreBuild.Tasks.dll" />
<UsingTask TaskName="KoreBuild.Tasks.GeneratePackageVersionPropsFile" AssemblyFile="$(MSBuildThisFileDirectory)Internal.AspNetCore.KoreBuild.Tasks.dll" />
<UsingTask TaskName="KoreBuild.Tasks.GenerateDependenciesPropsFile" AssemblyFile="$(MSBuildThisFileDirectory)Internal.AspNetCore.KoreBuild.Tasks.dll" />
<UsingTask TaskName="KoreBuild.Tasks.GetToolsets" AssemblyFile="$(MSBuildThisFileDirectory)Internal.AspNetCore.KoreBuild.Tasks.dll" />
<UsingTask TaskName="KoreBuild.Tasks.InstallDotNet" AssemblyFile="$(MSBuildThisFileDirectory)Internal.AspNetCore.KoreBuild.Tasks.dll" />
<UsingTask TaskName="KoreBuild.Tasks.PackNuSpec" AssemblyFile="$(MSBuildThisFileDirectory)Internal.AspNetCore.KoreBuild.Tasks.dll" />
<UsingTask TaskName="KoreBuild.Tasks.PushNuGetPackages" AssemblyFile="$(MSBuildThisFileDirectory)Internal.AspNetCore.KoreBuild.Tasks.dll" />

Просмотреть файл

@ -1,6 +1,7 @@
<Project>
<PropertyGroup>
<PrepareDependsOn>GetToolsets;$(PrepareDependsOn)</PrepareDependsOn>
<RestoreDependsOn>ApplyNuGetPolicies;InstallDotNet;CheckPackageReferences;$(RestoreDependsOn)</RestoreDependsOn>
<ApplyNuGetPoliciesDependsOn>
$(ApplyNuGetPoliciesDependsOn);
@ -9,6 +10,7 @@
</ApplyNuGetPoliciesDependsOn>
<DisablePackageReferenceRestrictions Condition=" '$(DisablePackageReferenceRestrictions)' == '' ">false</DisablePackageReferenceRestrictions>
<KoreBuildConfigFile Condition="'$(KoreBuildConfigFile)' == ''">$(RepositoryRoot)korebuild.json</KoreBuildConfigFile>
<DependencyVersionsFile Condition="'$(DependencyVersionsFile)' == ''">$(RepositoryRoot)build\dependencies.props</DependencyVersionsFile>
</PropertyGroup>
@ -108,4 +110,12 @@
Properties="$(SolutionProperties)" />
</Target>
<!-- This target asserts that all tools listed in korebuild.json are available. -->
<Target Name="GetToolsets" Condition="Exists($(KoreBuildConfigFile))">
<GetToolsets ConfigFile="$(KoreBuildConfigFile)">
<Output TaskParameter="VisualStudioMSBuildx86Path" PropertyName="VisualStudioMSBuildx86Path" />
<Output TaskParameter="VisualStudioMSBuildx64Path" PropertyName="VisualStudioMSBuildx64Path" />
</GetToolsets>
</Target>
</Project>

Просмотреть файл

@ -9,6 +9,7 @@
<ItemGroup>
<Compile Include="..\shared\*" />
<Content Include="..\..\files\KoreBuild\scripts\dotnet-install.*" CopyToOutputDirectory="PreserveNewest" />
<Content Include="$(VSWhereDir)vswhere.exe" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<ItemGroup>
@ -19,6 +20,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(TestSdkVersion)" />
<PackageReference Include="Moq" Version="$(MoqVersion)" />
<PackageReference Include="NuGet.Build.Tasks" Version="$(NuGetInMSBuildVersion)" />
<PackageReference Include="vswhere" Version="$(VsWherePackageVersion)" />
<PackageReference Include="xunit" Version="$(XunitVersion)" />
<PackageReference Include="xunit.runner.visualstudio" Version="$(XunitVersion)" />
</ItemGroup>

Просмотреть файл

@ -0,0 +1,100 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
using Xunit;
namespace KoreBuild.Tasks.Tests
{
public class KoreBuildSettingsTest : IDisposable
{
private readonly string _configFile;
public KoreBuildSettingsTest()
{
_configFile = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
}
public void Dispose()
{
if (File.Exists(_configFile))
{
File.Delete(_configFile);
}
}
[Fact]
public void ItDeserializesVisualStudioToolset()
{
File.WriteAllText(_configFile, @"
{
""toolsets"": {
""visualstudio"": {
""requiredWorkloads"": [ ""MyTestWorkload"" ],
""minVersion"": ""15.4"",
""includePrerelease"": false
}
}
}");
var settings = KoreBuildSettings.Load(_configFile);
var toolset = Assert.Single(settings.Toolsets);
var vs = Assert.IsType<KoreBuildSettings.VisualStudioToolset>(toolset);
Assert.Collection(vs.RequiredWorkloads, w => Assert.Equal("MyTestWorkload", w));
Assert.False(vs.IncludePrerelease);
Assert.Equal("15.4", vs.MinVersion);
}
[Fact]
public void ItDeserializesEmptyVisualStudioToolset()
{
File.WriteAllText(_configFile, @"
{
""toolsets"": {
""visualstudio"": {}
}
}
");
var settings = KoreBuildSettings.Load(_configFile);
var toolset = Assert.Single(settings.Toolsets);
var vs = Assert.IsType<KoreBuildSettings.VisualStudioToolset>(toolset);
Assert.NotNull(vs.RequiredWorkloads);
Assert.Empty(vs.RequiredWorkloads);
Assert.True(vs.IncludePrerelease);
Assert.Null(vs.MinVersion);
}
[Theory]
[InlineData("true", KoreBuildSettings.RequiredPlatforms.All)]
[InlineData("null", KoreBuildSettings.RequiredPlatforms.None)]
[InlineData("false", KoreBuildSettings.RequiredPlatforms.None)]
[InlineData(@"[""windows""]", KoreBuildSettings.RequiredPlatforms.Windows)]
[InlineData(@"[""linux""]", KoreBuildSettings.RequiredPlatforms.Linux)]
[InlineData(@"[""osx""]", KoreBuildSettings.RequiredPlatforms.MacOS)]
[InlineData(@"[""macos""]", KoreBuildSettings.RequiredPlatforms.MacOS)]
[InlineData(@"[""macos"", ""linux""]", KoreBuildSettings.RequiredPlatforms.MacOS | KoreBuildSettings.RequiredPlatforms.Linux)]
internal void ParsesPlatforms(string json, KoreBuildSettings.RequiredPlatforms platforms)
{
File.WriteAllText(_configFile, @"
{
""toolsets"": {
""visualstudio"": {
""required"": " + json + @"
}
}
}
");
var settings = KoreBuildSettings.Load(_configFile);
var toolset = Assert.Single(settings.Toolsets);
var vs = Assert.IsType<KoreBuildSettings.VisualStudioToolset>(toolset);
Assert.Equal(platforms, vs.Required);
}
}
}

Просмотреть файл

@ -0,0 +1,64 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO;
using System.Runtime.InteropServices;
using BuildTools.Tasks.Tests;
using Xunit;
using Xunit.Abstractions;
namespace KoreBuild.Tasks.Tests
{
public class VSWhereTests
{
private readonly ITestOutputHelper _output;
public VSWhereTests(ITestOutputHelper output)
{
_output = output;
}
[Fact]
public void ItFindsVisualStudio()
{
var engine = new MockEngine(_output) { ContinueOnError = true };
var task = new FindVisualStudio
{
BuildEngine = engine,
};
var result = task.Execute();
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
if (!result)
{
// Don't fail the test. aspnet/BuildTools might be building without a version of VS installed.
// We mostly want to make sure this task doesn't throw exceptions and fails gracefully
Assert.NotEmpty(engine.Errors);
}
else
{
Assert.NotEmpty(task.InstallationBasePath);
Assert.True(Directory.Exists(task.InstallationBasePath), "VS installation was not found");
// these output properties are only set if the file exists
if (!string.IsNullOrEmpty(task.MSBuildx64Path))
{
Assert.True(File.Exists(task.MSBuildx64Path), "MSBuild x64 was not found");
}
// these output properties are only set if the file exists
if (!string.IsNullOrEmpty(task.MSBuildx86Path))
{
Assert.True(File.Exists(task.MSBuildx86Path), "MSBuild x86 was not found");
}
}
}
else
{
Assert.False(result, "VSWhere should fail on non-windows platforms");
}
}
}
}

142
tools/KoreBuildSettings.cs Normal file
Просмотреть файл

@ -0,0 +1,142 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace KoreBuild
{
/// <summary>
/// Represents the korebuild.json file
/// /// </summary>
internal class KoreBuildSettings
{
public string Channel { get; set; }
public string ToolsSource { get; set; }
[JsonConverter(typeof(KoreBuildToolsetConverter))]
public IEnumerable<KoreBuildToolset> Toolsets { get; set; } = Array.Empty<KoreBuildToolset>();
[Flags]
public enum RequiredPlatforms
{
None = 0,
Windows = 1 << 0,
MacOS = 1 << 1,
Linux = 1 << 2,
All = Windows | MacOS | Linux,
}
public abstract class KoreBuildToolset
{
[JsonConverter(typeof(RequiredPlatformConverter))]
public RequiredPlatforms Required { get; set; } = RequiredPlatforms.All;
}
public class VisualStudioToolset : KoreBuildToolset
{
public bool IncludePrerelease { get; set; } = true;
public string MinVersion { get; set; }
public string[] RequiredWorkloads { get; set; } = Array.Empty<string>();
}
public static KoreBuildSettings Load(string filePath)
{
using (var file = File.OpenText(filePath))
using (var json = new JsonTextReader(file))
{
var serializer = new JsonSerializer();
return serializer.Deserialize<KoreBuildSettings>(json);
}
}
private class KoreBuildToolsetConverter : JsonConverter
{
public override bool CanConvert(Type objectType) => typeof(KoreBuildToolset).IsAssignableFrom(objectType);
public override bool CanWrite { get; } = false;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var toolsets = new List<KoreBuildToolset>();
var obj = JObject.Load(reader);
foreach (var prop in obj.Properties())
{
switch (prop.Name.ToLowerInvariant())
{
case "visualstudio":
var vs = prop.Value.ToObject<VisualStudioToolset>();
toolsets.Add(vs);
break;
default:
break;
}
}
return toolsets;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
private class RequiredPlatformConverter : JsonConverter
{
public override bool CanConvert(Type objectType) => typeof(RequiredPlatforms).IsAssignableFrom(objectType);
public override bool CanWrite { get; } = false;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
switch (reader.TokenType)
{
case JsonToken.Undefined:
case JsonToken.Null:
return RequiredPlatforms.None;
case JsonToken.Boolean:
var asBool = (bool)reader.Value;
return asBool
? RequiredPlatforms.All
: RequiredPlatforms.None;
case JsonToken.String:
return Parse(reader.Value as string);
case JsonToken.StartArray:
var jArray = JArray.ReadFrom(reader);
var platforms = RequiredPlatforms.None;
foreach (var value in jArray)
{
platforms |= Parse(value.Value<string>());
}
return platforms;
default:
throw new JsonReaderException("Unexpected format in korebuild.json at " + reader.Path + ". This should be a boolean or an array.");
}
}
private RequiredPlatforms Parse(string name)
{
switch (name.ToLowerInvariant())
{
case "windows":
return RequiredPlatforms.Windows;
case "linux":
return RequiredPlatforms.Linux;
case "osx":
case "macos":
return RequiredPlatforms.MacOS;
default:
throw new ArgumentException("Unrecognized plaform named " + name);
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
}
}

Просмотреть файл

@ -1,7 +1,72 @@
{
"title": "Config file for KoreBuild",
"$schema": "http://json-schema.org/draft-04/schema#",
"$schema": "http://json-schema.org/draft-06/hyper-schema",
"type": "object",
"definitions": {
"platforms": {
"oneOf": [
{
"properties": {
"required": {
"description": "Visual Studio is required to build this repo. Defaults to true if not specified.",
"type": "boolean",
"default": true
}
}
},
{
"properties": {
"required": {
"description": "Visual Studio is required to build this repo. Defaults to true if not specified.",
"type": "array",
"default": [
"windows",
"linux",
"macos"
],
"items": {
"type": "string",
"enum": [
"windows",
"linux",
"macos"
]
}
}
}
}
]
},
"visualstudio": {
"type": "object",
"description": "Defines the requirements for Visual Studio installation.",
"allOf": [
{
"$ref": "#/definitions/platforms"
},
{
"properties": {
"includePrerelease": {
"description": "Include pre-release versions when searching for a version of Visual Studio",
"type": "boolean",
"default": true
},
"requiredWorkloads": {
"type": "array",
"description": "A list of workloads that must be installed. See https://aka.ms/vs/workloads.",
"items": {
"type": "string"
}
},
"minVersion": {
"type": "string",
"description": "The minimum version of Visual Studio required."
}
}
}
]
}
},
"properties": {
"toolsSource": {
"description": "The remote source used to download KoreBuild. Can be a file path.",
@ -17,6 +82,15 @@
"rel/2.0.0",
"rel/2.0.2"
]
},
"toolsets": {
"description": "Lists required toolsets for this repository.",
"type": "object",
"properties": {
"visualstudio": {
"$ref": "#/definitions/visualstudio"
}
}
}
}
}