Add support for specifying NodeJS as a required toolset

This commit is contained in:
Nate McMaster 2017-10-30 11:10:08 -07:00
Родитель 7e24ef1862
Коммит 502f38938f
7 изменённых файлов: 225 добавлений и 7 удалений

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

@ -42,6 +42,8 @@ KoreBuild can be configured by adding a 'korebuild.json' file into the root fold
Example:
```js
// NB: Don't actually use comments in JSON files. PowerShell's ConvertFrom-Json will throw an error.
{
// add this for editor auto-completion :)
"$schema": "https://raw.githubusercontent.com/aspnet/BuildTools/dev/tools/korebuild.schema.json",
@ -66,6 +68,11 @@ Example:
// This tool is only required on Windows.
"required": [ "windows" ]
},
"nodejs": {
"required": true,
"minVersion": "8.0"
}
}
}

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

@ -1,6 +1,8 @@
// 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.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using KoreBuild.Tasks.Utilities;
@ -20,17 +22,26 @@ namespace KoreBuild.Tasks
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.
/// The path to MSBuild.exe (x86) if the 'visualstudio' toolset was specified in korebuild.json.
/// It will be empty if not specified or 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.
/// The path to MSBuild.exe (x64) if the 'visualstudio' toolset was specified in korebuild.json.
/// It will be empty if not specified or if this file does not exist.
/// </summary>
[Output]
public string VisualStudioMSBuildx64Path { get; set; }
/// <summary>
/// The path to NodeJS.exe if the 'nodejs' toolset was specified in korebuild.json.
/// It will be empty if not specified.
/// </summary>
[Output]
public string NodeJSPath { get; set; }
public override bool Execute()
{
if (!File.Exists(ConfigFile))
@ -54,6 +65,9 @@ namespace KoreBuild.Tasks
case KoreBuildSettings.VisualStudioToolset vs:
GetVisualStudio(vs);
break;
case KoreBuildSettings.NodeJSToolset node:
GetNode(node);
break;
default:
Log.LogWarning("Toolset checks not implemented for " + toolset.GetType().Name);
break;
@ -93,5 +107,94 @@ namespace KoreBuild.Tasks
VisualStudioMSBuildx86Path = vs.GetMSBuildx86SubPath();
VisualStudioMSBuildx64Path = vs.GetMSBuildx64SubPath();
}
private void GetNode(KoreBuildSettings.NodeJSToolset nodeToolset)
{
var nodePath = EnvironmentHelper.GetCommandOnPath("nodejs") ?? EnvironmentHelper.GetCommandOnPath("node");
var required = IsRequiredOnThisPlatform(nodeToolset.Required);
if (string.IsNullOrEmpty(nodePath))
{
LogFailure(
isError: required,
message: $"Could not find NodeJS on PATH.");
return;
}
Log.LogMessage(MessageImportance.Low, "Found NodeJS in " + nodePath);
if (nodeToolset.MinVersion == null)
{
NodeJSPath = nodePath;
return;
}
var process = Process.Start(new ProcessStartInfo
{
FileName = nodePath,
Arguments = "--version",
RedirectStandardOutput = true,
});
process.WaitForExit();
if (process.ExitCode != 0)
{
LogFailure(
isError: required,
message: $"Found NodeJS in '{nodePath}', but could not determine the version of NodeJS installed. 'node --version' failed.");
return;
}
var nodeVersionString = process.StandardOutput.ReadToEnd()?.Trim()?.TrimStart('v');
if (!Version.TryParse(nodeVersionString, out var nodeVersion))
{
LogFailure(
isError: required,
message: $"Found NodeJS in '{nodePath}', but could not determine the version of NodeJS installed. 'node --version' returned '{nodeVersionString}'.");
return;
}
if (nodeVersion < nodeToolset.MinVersion)
{
LogFailure(
isError: required,
message: $"Found NodeJS in '{nodePath}', but its version '{nodeVersionString}' did not meet the required minimum version '{nodeToolset.MinVersion}' as specified in '{ConfigFile}'");
return;
}
Log.LogMessage(MessageImportance.High, "Using NodeJS {0} from {1}", nodeVersionString, nodePath);
NodeJSPath = nodePath;
}
private void LogFailure(bool isError, string message)
{
if (isError)
{
Log.LogError(message);
}
else
{
Log.LogWarning(message);
}
}
private bool IsRequiredOnThisPlatform(KoreBuildSettings.RequiredPlatforms platforms)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return (platforms & KoreBuildSettings.RequiredPlatforms.Windows) != 0;
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return (platforms & KoreBuildSettings.RequiredPlatforms.Linux) != 0;
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return (platforms & KoreBuildSettings.RequiredPlatforms.MacOS) != 0;
}
return platforms != KoreBuildSettings.RequiredPlatforms.None;
}
}
}

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

@ -0,0 +1,38 @@
// 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 System.Linq;
using System.Runtime.InteropServices;
namespace KoreBuild.Tasks.Utilities
{
internal class EnvironmentHelper
{
private static string[] _searchPaths;
private static string[] _executableExtensions;
static EnvironmentHelper()
{
_searchPaths = Environment.GetEnvironmentVariable("PATH")
.Split(Path.PathSeparator)
.Select(p => p.Trim('"'))
.ToArray();
_executableExtensions = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? Environment.GetEnvironmentVariable("PATHEXT").Split(';').Select(e => e.ToLower().Trim('"')).ToArray()
: Array.Empty<string>();
}
public static string GetCommandOnPath(string exeName)
{
return _searchPaths.Join(
_executableExtensions,
p => true,
e => true,
(p, e) => Path.Combine(p, exeName + e))
.FirstOrDefault(File.Exists);
}
}
}

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

@ -115,6 +115,7 @@
<GetToolsets ConfigFile="$(KoreBuildConfigFile)">
<Output TaskParameter="VisualStudioMSBuildx86Path" PropertyName="VisualStudioMSBuildx86Path" />
<Output TaskParameter="VisualStudioMSBuildx64Path" PropertyName="VisualStudioMSBuildx64Path" />
<Output TaskParameter="NodeJSPath" PropertyName="NodeJSPath" />
</GetToolsets>
</Target>

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

@ -3,6 +3,7 @@
using System;
using System.IO;
using Newtonsoft.Json;
using Xunit;
namespace KoreBuild.Tasks.Tests
@ -96,5 +97,37 @@ namespace KoreBuild.Tasks.Tests
var vs = Assert.IsType<KoreBuildSettings.VisualStudioToolset>(toolset);
Assert.Equal(platforms, vs.Required);
}
[Fact]
public void ItDeserializesNodeJSToolsetWithVersion()
{
File.WriteAllText(_configFile, @"
{
""toolsets"": {
""nodejs"": {
""minVersion"": ""8.0""
}
}
}");
var settings = KoreBuildSettings.Load(_configFile);
var toolset = Assert.Single(settings.Toolsets);
var node = Assert.IsType<KoreBuildSettings.NodeJSToolset>(toolset);
Assert.Equal(new Version(8, 0), node.MinVersion);
}
[Fact]
public void ItFailsIfVersionIsNotValid()
{
File.WriteAllText(_configFile, @"
{
""toolsets"": {
""nodejs"": {
""minVersion"": ""banana""
}
}
}");
Assert.Throws<JsonSerializationException>(() => KoreBuildSettings.Load(_configFile));
}
}
}

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

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
namespace KoreBuild
@ -42,6 +43,13 @@ namespace KoreBuild
public string MinVersion { get; set; }
public string[] RequiredWorkloads { get; set; } = Array.Empty<string>();
}
public class NodeJSToolset : KoreBuildToolset
{
[JsonConverter(typeof(VersionConverter))]
public Version MinVersion { get; set; }
}
public static KoreBuildSettings Load(string filePath)
{
using (var file = File.OpenText(filePath))
@ -65,14 +73,22 @@ namespace KoreBuild
foreach (var prop in obj.Properties())
{
KoreBuildToolset toolset;
switch (prop.Name.ToLowerInvariant())
{
case "visualstudio":
var vs = prop.Value.ToObject<VisualStudioToolset>();
toolsets.Add(vs);
toolset = prop.Value.ToObject<VisualStudioToolset>();
break;
case "nodejs":
toolset = prop.Value.ToObject<NodeJSToolset>();
break;
default:
break;
continue;
}
if (toolset != null)
{
toolsets.Add(toolset);
}
}

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

@ -8,7 +8,7 @@
{
"properties": {
"required": {
"description": "Visual Studio is required to build this repo. Defaults to true if not specified.",
"description": "This toolset is required to build this repo. Defaults to true if not specified.",
"type": "boolean",
"default": true
}
@ -17,7 +17,7 @@
{
"properties": {
"required": {
"description": "Visual Studio is required to build this repo. Defaults to true if not specified.",
"description": "This toolset is required to build this repo. Defaults to true if not specified.",
"type": "array",
"default": [
"windows",
@ -37,6 +37,23 @@
}
]
},
"nodejs": {
"type": "object",
"description": "Defines the requirements for a NodeJS installation.",
"allOf": [
{
"$ref": "#/definitions/platforms"
},
{
"properties": {
"minVersion": {
"type": "string",
"description": "The minimum version of NodeJS required. Must contain at least <major>.<minor>, but can also be <major>.<minor>.<patch>"
}
}
}
]
},
"visualstudio": {
"type": "object",
"description": "Defines the requirements for Visual Studio installation.",
@ -89,6 +106,9 @@
"properties": {
"visualstudio": {
"$ref": "#/definitions/visualstudio"
},
"nodejs": {
"$ref": "#/definitions/nodejs"
}
}
}