File: src\LocateDotNet.cs
Web Access
Project: src\src\Microsoft.DotNet.Arcade.Sdk\Microsoft.DotNet.Arcade.Sdk.csproj (Microsoft.DotNet.Arcade.Sdk)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
 
namespace Microsoft.DotNet.Arcade.Sdk
{
    public class LocateDotNet : Microsoft.Build.Utilities.Task
    {
        private static readonly string s_cacheKey = "LocateDotNet-FCDFF825-F35B-4601-9CB5-74DCA498B589";
 
        private sealed class CacheEntry
        {
            public readonly DateTime LastWrite;
            public readonly string Paths;
            public readonly string Value;
 
            public CacheEntry(DateTime lastWrite, string paths, string value)
            {
                LastWrite = lastWrite;
                Paths = paths;
                Value = value;
            }
        }
 
        [Required]
        public string RepositoryRoot { get; set; }
 
        [Output]
        public string DotNetPath { get; set; }
 
        public override bool Execute()
        {
            ExecuteImpl();
            return !Log.HasLoggedErrors;
        }
 
        private void ExecuteImpl()
        {
            var globalJsonPath = Path.Combine(RepositoryRoot, "global.json");
 
            var lastWrite = File.GetLastWriteTimeUtc(globalJsonPath);
            var paths = Environment.GetEnvironmentVariable("PATH");
 
            var cachedResult = (CacheEntry)BuildEngine4.GetRegisteredTaskObject(s_cacheKey, RegisteredTaskObjectLifetime.Build);
            if (cachedResult != null && lastWrite == cachedResult.LastWrite && paths == cachedResult.Paths)
            {
                Log.LogMessage(MessageImportance.Low, $"Reused cached value.");
                DotNetPath = cachedResult.Value;
                return;
            }
 
            var globalJson = File.ReadAllText(globalJsonPath);
 
            // avoid Newtonsoft.Json dependency
            var match = Regex.Match(globalJson, @"""dotnet""\s*:\s*""([^""]+)""");
            if (!match.Success)
            {
                Log.LogError($"Unable to determine dotnet version from file '{globalJsonPath}'.");
                return;
            }
 
            var sdkVersion = match.Groups[1].Value;
 
            var fileName = (Path.DirectorySeparatorChar == '\\') ? "dotnet.exe" : "dotnet";
            var dotNetDir = paths.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault(p => File.Exists(Path.Combine(p, fileName)));
 
            if (dotNetDir == null || !Directory.Exists(Path.Combine(dotNetDir, "sdk", sdkVersion)))
            {
                Log.LogError($"Unable to find dotnet with SDK version '{sdkVersion}'");
                return;
            }
 
            DotNetPath = Path.GetFullPath(Path.Combine(dotNetDir, fileName));
            BuildEngine4.RegisterTaskObject(s_cacheKey, new CacheEntry(lastWrite, paths, DotNetPath), RegisteredTaskObjectLifetime.Build, allowEarlyCollection: true);
        }
    }
}