public void NoChangeWaveSetMeansAllChangeWavesAreEnabled(string featureWave) { using (TestEnvironment env = TestEnvironment.Create()) { ChangeWaves.ResetStateForTests(); ChangeWaves.AreFeaturesEnabled(featureWave).ShouldBe(true); string projectFile = $"" + $"<Project>" + $"<Target Name='HelloWorld' Condition=\"'$(MSBUILDDISABLEFEATURESFROMVERSION)' == '{ChangeWaves.EnableAllFeatures}' and $([MSBuild]::AreFeaturesEnabled('{featureWave}'))\">" + $"<Message Text='Hello World!'/>" + $"</Target>" + $"</Project>"; TransientTestFile file = env.CreateFile("proj.csproj", projectFile); ProjectCollection collection = new ProjectCollection(); MockLogger log = new MockLogger(); collection.RegisterLogger(log); collection.LoadProject(file.Path).Build().ShouldBeTrue(); log.AssertLogContains("Hello World!"); BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(); } }
internal void InitializeForTests(SdkResolverLoader resolverLoader = null, IList <SdkResolver> resolvers = null) { if (resolverLoader != null) { _sdkResolverLoader = resolverLoader; } _specificResolversManifestsRegistry = null; _generalResolversManifestsRegistry = null; _manifestToResolvers = null; _resolversList = null; if (resolvers != null) { if (ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_4)) { _specificResolversManifestsRegistry = new List <SdkResolverManifest>(); _generalResolversManifestsRegistry = new List <SdkResolverManifest>(); _manifestToResolvers = new Dictionary <SdkResolverManifest, IList <SdkResolver> >(); SdkResolverManifest sdkResolverManifest = new SdkResolverManifest(DisplayName: "TestResolversManifest", Path: null, ResolvableSdkRegex: null); _generalResolversManifestsRegistry.Add(sdkResolverManifest); _manifestToResolvers[sdkResolverManifest] = resolvers; } else { _resolversList = resolvers; } } }
public void CorrectlyDetermineDisabledFeatures() { using (TestEnvironment env = TestEnvironment.Create()) { env.SetChangeWave(ChangeWaves.LowestWave); BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(); foreach (string wave in ChangeWaves.AllWaves) { ChangeWaves.AreFeaturesEnabled(wave).ShouldBeFalse(); string projectFile = $"" + $"<Project>" + $"<Target Name='HelloWorld' Condition=\"$([MSBuild]::AreFeaturesEnabled('{wave}')) == false\">" + $"<Message Text='Hello World!'/>" + $"</Target>" + $"</Project>"; TransientTestFile file = env.CreateFile("proj.csproj", projectFile); ProjectCollection collection = new ProjectCollection(); MockLogger log = new MockLogger(); collection.RegisterLogger(log); Project p = collection.LoadProject(file.Path); p.Build().ShouldBeTrue(); log.AssertLogContains("Hello World!"); } BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(); } }
/// <summary> /// Helper function to build a simple project based on a particular change wave being set. /// Call SetChangeWave on your TestEnvironment before calling this function. /// </summary> /// <param name="testEnvironment">The TestEnvironment being used for this test.</param> /// <param name="versionToCheckAgainstCurrentChangeWave">The version to compare to the current set Change Wave.</param> /// <param name="currentChangeWaveShouldUltimatelyResolveTo">What the project property for the environment variable MSBuildDisableFeaturesFromVersion ultimately resolves to.</param> /// <param name="warningCodesLogShouldContain">An array of warning codes that should exist in the resulting log. Ex: "MSB4271".</param> private void buildSimpleProjectAndValidateChangeWave(TestEnvironment testEnvironment, Version versionToCheckAgainstCurrentChangeWave, Version currentChangeWaveShouldUltimatelyResolveTo, params string[] warningCodesLogShouldContain) { bool isThisWaveEnabled = versionToCheckAgainstCurrentChangeWave < currentChangeWaveShouldUltimatelyResolveTo || currentChangeWaveShouldUltimatelyResolveTo == ChangeWaves.EnableAllFeatures; ChangeWaves.AreFeaturesEnabled(versionToCheckAgainstCurrentChangeWave).ShouldBe(isThisWaveEnabled); string projectFile = $"" + $"<Project>" + $"<Target Name='HelloWorld' Condition=\"$([MSBuild]::AreFeaturesEnabled('{versionToCheckAgainstCurrentChangeWave}')) and '$(MSBUILDDISABLEFEATURESFROMVERSION)' == '{currentChangeWaveShouldUltimatelyResolveTo}'\">" + $"<Message Text='Hello World!'/>" + $"</Target>" + $"</Project>"; TransientTestFile file = testEnvironment.CreateFile("proj.csproj", projectFile); ProjectCollection collection = new ProjectCollection(); MockLogger log = new MockLogger(_output); collection.RegisterLogger(log); Project p = collection.LoadProject(file.Path); p.Build().ShouldBeTrue(); log.FullLog.Contains("Hello World!").ShouldBe(isThisWaveEnabled); if (warningCodesLogShouldContain != null) { log.WarningCount.ShouldBe(warningCodesLogShouldContain.Length); log.AssertLogContains(warningCodesLogShouldContain); } ChangeWaves.ResetStateForTests(); }
public void InvalidCallerForIsFeatureEnabledThrows(string waveToCheck) { using (TestEnvironment env = TestEnvironment.Create()) { env.SetChangeWave("16.8"); BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(); Shouldly.Should.Throw <InternalErrorException>(() => ChangeWaves.AreFeaturesEnabled(waveToCheck)); } }
/// <summary> /// Reads the application configuration file. /// NOTE: this is abstracted into a method to support unit testing GetToolsetDataFromConfiguration(). /// Unit tests wish to avoid reading (nunit.exe) application configuration file. /// </summary> private static Configuration ReadApplicationConfiguration() { if (ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_0)) { return(s_configurationCache.Value); } else { return(ReadOpenMappedExeConfiguration()); } }
public virtual SdkResult ResolveSdk(int submissionId, SdkReference sdk, LoggingContext loggingContext, ElementLocation sdkReferenceLocation, string solutionPath, string projectPath, bool interactive, bool isRunningInVisualStudio) { if (ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_4)) { return(ResolveSdkUsingResolversWithPatternsFirst(submissionId, sdk, loggingContext, sdkReferenceLocation, solutionPath, projectPath, interactive, isRunningInVisualStudio)); } else { return(ResolveSdkUsingAllResolvers(submissionId, sdk, loggingContext, sdkReferenceLocation, solutionPath, projectPath, interactive, isRunningInVisualStudio)); } }
private static XmlReader GetXmlReader(string file, StreamReader input, bool loadAsReadOnly, out Encoding encoding) { string uri = new UriBuilder(Uri.UriSchemeFile, string.Empty) { Path = file }.ToString(); XmlReader reader = null; if (ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave16_10) && loadAsReadOnly && !_disableReadOnlyLoad) { // Create an XML reader with IgnoreComments and IgnoreWhitespace set if we know that we won't be asked // to write the DOM back to a file. This is a performance optimization. XmlReaderSettings settings = new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore, IgnoreComments = true, IgnoreWhitespace = true, }; reader = XmlReader.Create(input, settings, uri); // Try to set Normalization to false. We do this to remain compatible with earlier versions of MSBuild // where we constructed the reader with 'new XmlTextReader()' which has normalization enabled by default. PropertyInfo normalizationPropertyInfo = GetNormalizationPropertyInfo(reader.GetType()); if (normalizationPropertyInfo != null) { normalizationPropertyInfo.SetValue(reader, false); } else { // Fall back to using XmlTextReader if the prop could not be bound. Debug.Fail("Could not set Normalization to false on the result of XmlReader.Create"); _disableReadOnlyLoad = true; reader.Dispose(); reader = null; } } if (reader == null) { reader = new XmlTextReader(uri, input) { DtdProcessing = DtdProcessing.Ignore }; } reader.Read(); encoding = input.CurrentEncoding; return(reader); }
internal static object GetRegistryValue(string keyName, string valueName, object defaultValue) { #if RUNTIME_TYPE_NETCORE // .NET Core MSBuild used to always return empty, so match that behavior // on non-Windows (no registry), and with a changewave (in case someone // had a registry property and it breaks when it lights up). if (!NativeMethodsShared.IsWindows || !ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_4)) { return(defaultValue); } #endif return(Registry.GetValue(keyName, valueName, defaultValue)); }
protected override IntPtr LoadUnmanagedDll(string unmanagedDllName) { if (ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_4)) { string?libraryPath = _resolver?.ResolveUnmanagedDllToPath(unmanagedDllName); if (libraryPath != null) { return(LoadUnmanagedDllFromPath(libraryPath)); } } return(base.LoadUnmanagedDll(unmanagedDllName)); }
private void SetResolverState(int submissionId, SdkResolver resolver, object state) { // Do not set state for resolution requests that are not associated with a valid build submission ID if (submissionId != BuildEventContext.InvalidSubmissionId) { ConcurrentDictionary <SdkResolver, object> resolverState = _resolverStateBySubmission.GetOrAdd( submissionId, _ => new ConcurrentDictionary <SdkResolver, object>( NativeMethodsShared.GetLogicalCoreCount(), ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_4) ? _specificResolversManifestsRegistry.Count + _generalResolversManifestsRegistry.Count : _resolversList.Count)); resolverState.AddOrUpdate(resolver, state, (sdkResolver, obj) => state); } }
public void NoChangeWaveSetMeansAllChangeWavesAreEnabled(string featureVersion) { using (TestEnvironment env = TestEnvironment.Create()) { // Reset static ChangeWave SetChangeWave(string.Empty, env); Version featureAsVersion = Version.Parse(featureVersion); ChangeWaves.AreFeaturesEnabled(featureAsVersion).ShouldBe(true); buildSimpleProjectAndValidateChangeWave(testEnvironment: env, versionToCheckAgainstCurrentChangeWave: featureAsVersion, currentChangeWaveShouldUltimatelyResolveTo: ChangeWaves.EnableAllFeatures, warningCodesLogShouldContain: null); } }
/// <summary> /// Gets the location of the directory used for diagnostic log files. /// </summary> /// <returns></returns> private static string GetDebugDumpPath() { string debugPath = // Cannot access change wave logic from these assemblies (https://github.com/dotnet/msbuild/issues/6707) #if CLR2COMPATIBILITY || MICROSOFT_BUILD_ENGINE_OM_UNITTESTS Environment.GetEnvironmentVariable("MSBUILDDEBUGPATH"); #else ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_0) ? DebugUtils.DebugPath : Environment.GetEnvironmentVariable("MSBUILDDEBUGPATH"); #endif return(!string.IsNullOrEmpty(debugPath) ? debugPath : Path.GetTempPath()); }
/// <summary> /// Scan for the end of the property expression /// </summary> /// <param name="expression">property expression to parse</param> /// <param name="index">current index to start from</param> /// <param name="indexResult">If successful, the index corresponds to the end of the property expression. /// In case of scan failure, it is the error position index.</param> /// <returns>result indicating whether or not the scan was successful.</returns> private static bool ScanForPropertyExpressionEnd(string expression, int index, out int indexResult) { int nestLevel = 0; bool whitespaceCheck = false; unsafe { fixed(char *pchar = expression) { while (index < expression.Length) { char character = pchar[index]; if (character == '(') { nestLevel++; whitespaceCheck = true; } else if (character == ')') { nestLevel--; whitespaceCheck = false; } else if (whitespaceCheck && char.IsWhiteSpace(character) && ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave16_10)) { indexResult = index; return(false); } // We have reached the end of the parenthesis nesting // this should be the end of the property expression // If it is not then the calling code will determine that if (nestLevel == 0) { indexResult = index; return(true); } else { index++; } } } } indexResult = index; return(true); }
internal static object GetRegistryValueFromView(string keyName, string valueName, object defaultValue, params object[] views) { #if RUNTIME_TYPE_NETCORE // .NET Core MSBuild used to always return empty, so match that behavior // on non-Windows (no registry), and with a changewave (in case someone // had a registry property and it breaks when it lights up). if (!NativeMethodsShared.IsWindows || !ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_4)) { return(defaultValue); } #endif if (views == null || views.Length == 0) { views = DefaultRegistryViews; } return(GetRegistryValueFromView(keyName, valueName, defaultValue, new ArraySegment <object>(views))); }
private static string GetLockedFileMessage(string file) { string message = string.Empty; try { if (NativeMethodsShared.IsWindows && ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_4)) { var processes = LockCheck.GetProcessesLockingFile(file); message = !string.IsNullOrEmpty(processes) ? ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword("Copy.FileLocked", processes) : String.Empty; } } catch (Exception) { // Never throw if we can't get the processes locking the file. } return(message); }
/// <summary> /// Execute the task. /// </summary> /// <returns></returns> public override bool Execute() { AssignedFiles = new ITaskItem[Files.Length]; if (Files.Length > 0) { // Compose a file in the root folder. // NOTE: at this point fullRootPath may or may not have a trailing // slash because Path.GetFullPath() does not add or remove it string fullRootPath = Path.GetFullPath(RootFolder); // Ensure trailing slash otherwise c:\bin appears to match part of c:\bin2\foo fullRootPath = FileUtilities.EnsureTrailingSlash(fullRootPath); string currentDirectory = Directory.GetCurrentDirectory(); // check if the root folder is the same as the current directory // NOTE: the path returned from Directory.GetCurrentDirectory() // does not have a trailing slash, but fullRootPath does bool isRootFolderSameAsCurrentDirectory = ((fullRootPath.Length - 1 /* exclude trailing slash */) == currentDirectory.Length) && (String.Compare( fullRootPath, 0, currentDirectory, 0, fullRootPath.Length - 1 /* don't compare trailing slash */, StringComparison.OrdinalIgnoreCase) == 0); for (int i = 0; i < Files.Length; ++i) { AssignedFiles[i] = new TaskItem(Files[i]); // If TargetPath is already set, it takes priority. // https://github.com/dotnet/msbuild/issues/2795 string targetPath = ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave16_10) ? Files[i].GetMetadata(ItemMetadataNames.targetPath) : null; // If TargetPath not already set, fall back to default behavior. if (string.IsNullOrEmpty(targetPath)) { targetPath = Files[i].GetMetadata(ItemMetadataNames.link); } if (string.IsNullOrEmpty(targetPath)) { if (// if the file path is relative !Path.IsPathRooted(Files[i].ItemSpec) && // if the file path doesn't contain any relative specifiers !Files[i].ItemSpec.Contains("." + Path.DirectorySeparatorChar) && // if the file path is already relative to the root folder isRootFolderSameAsCurrentDirectory) { // then just use the file path as-is // PERF NOTE: we do this to avoid calling Path.GetFullPath() below, // because that method consumes a lot of memory, esp. when we have // a lot of items coming through this task targetPath = Files[i].ItemSpec; } else { // PERF WARNING: Path.GetFullPath() is expensive in terms of memory; // we should avoid calling it whenever possible string itemSpecFullFileNamePath = Path.GetFullPath(Files[i].ItemSpec); if (String.Compare(fullRootPath, 0, itemSpecFullFileNamePath, 0, fullRootPath.Length, StringComparison.CurrentCultureIgnoreCase) == 0) { // The item spec file is in the "cone" of the RootFolder. Return the relative path from the cone root. targetPath = itemSpecFullFileNamePath.Substring(fullRootPath.Length); } else { // The item spec file is not in the "cone" of the RootFolder. Return the filename only. targetPath = Path.GetFileName(Files[i].ItemSpec); } } } AssignedFiles[i].SetMetadata(ItemMetadataNames.targetPath, EscapingUtilities.Escape(targetPath)); } } return(true); }
private static bool ScanForPropertyExpressionEnd(string expression, int index, out int indexResult) { int nestLevel = 0; bool whitespaceFound = false; bool nonIdentifierCharacterFound = false; indexResult = -1; unsafe { fixed(char *pchar = expression) { while (index < expression.Length) { char character = pchar[index]; if (character == '(') { nestLevel++; } else if (character == ')') { nestLevel--; } else if (char.IsWhiteSpace(character)) { whitespaceFound = true; indexResult = index; } else if (!XmlUtilities.IsValidSubsequentElementNameCharacter(character)) { nonIdentifierCharacterFound = true; } if (character == '$' && index < expression.Length - 1 && pchar[index + 1] == '(') { if (!ScanForPropertyExpressionEnd(expression, index + 1, out index)) { indexResult = index; return(false); } } // We have reached the end of the parenthesis nesting // this should be the end of the property expression // If it is not then the calling code will determine that if (nestLevel == 0) { if (whitespaceFound && !nonIdentifierCharacterFound && ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave16_10)) { return(false); } indexResult = index; return(true); } else { index++; } } } } indexResult = index; return(true); }
protected override ImmutableList <I> SelectItems(ImmutableList <ItemData> .Builder listBuilder, ImmutableHashSet <string> globsToIgnore) { var itemsToAdd = ImmutableList.CreateBuilder <I>(); Lazy <Func <string, bool> > excludeTester = null; ImmutableList <string> .Builder excludePatterns = ImmutableList.CreateBuilder <string>(); if (_excludes != null) { // STEP 4: Evaluate, split, expand and subtract any Exclude foreach (string exclude in _excludes) { string excludeExpanded = _expander.ExpandIntoStringLeaveEscaped(exclude, ExpanderOptions.ExpandPropertiesAndItems, _itemElement.ExcludeLocation); var excludeSplits = ExpressionShredder.SplitSemiColonSeparatedList(excludeExpanded); excludePatterns.AddRange(excludeSplits); } if (excludePatterns.Count > 0) { excludeTester = new Lazy <Func <string, bool> >(() => EngineFileUtilities.GetFileSpecMatchTester(excludePatterns, _rootDirectory)); } } ISet <string> excludePatternsForGlobs = null; foreach (var fragment in _itemSpec.Fragments) { if (fragment is ItemSpec <P, I> .ItemExpressionFragment itemReferenceFragment) { // STEP 3: If expression is "@(x)" copy specified list with its metadata, otherwise just treat as string var itemsFromExpression = _expander.ExpandExpressionCaptureIntoItems( itemReferenceFragment.Capture, _evaluatorData, _itemFactory, ExpanderOptions.ExpandItems, includeNullEntries: false, isTransformExpression: out _, elementLocation: _itemElement.IncludeLocation); itemsToAdd.AddRange( excludeTester != null ? itemsFromExpression.Where(item => !excludeTester.Value(item.EvaluatedInclude)) : itemsFromExpression); } else if (fragment is ValueFragment valueFragment) { string value = valueFragment.TextFragment; if (excludeTester?.Value(EscapingUtilities.UnescapeAll(value)) != true) { var item = _itemFactory.CreateItem(value, value, _itemElement.ContainingProject.FullPath); itemsToAdd.Add(item); } } else if (fragment is GlobFragment globFragment) { // If this item is behind a false condition and represents a full drive/filesystem scan, expanding it is // almost certainly undesired. It should be skipped to avoid evaluation taking an excessive amount of time. bool skipGlob = !_conditionResult && globFragment.IsFullFileSystemScan && !Traits.Instance.EscapeHatches.AlwaysEvaluateDangerousGlobs && ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave16_8); if (!skipGlob) { string glob = globFragment.TextFragment; if (excludePatternsForGlobs == null) { excludePatternsForGlobs = BuildExcludePatternsForGlobs(globsToIgnore, excludePatterns); } string[] includeSplitFilesEscaped; if (MSBuildEventSource.Log.IsEnabled()) { MSBuildEventSource.Log.ExpandGlobStart(_rootDirectory, glob, string.Join(", ", excludePatternsForGlobs)); } using (_lazyEvaluator._evaluationProfiler.TrackGlob(_rootDirectory, glob, excludePatternsForGlobs)) { includeSplitFilesEscaped = EngineFileUtilities.GetFileListEscaped( _rootDirectory, glob, excludePatternsForGlobs ); } if (MSBuildEventSource.Log.IsEnabled()) { MSBuildEventSource.Log.ExpandGlobStop(_rootDirectory, glob, string.Join(", ", excludePatternsForGlobs)); } foreach (string includeSplitFileEscaped in includeSplitFilesEscaped) { itemsToAdd.Add(_itemFactory.CreateItem(includeSplitFileEscaped, glob, _itemElement.ContainingProject.FullPath)); } } } else { throw new InvalidOperationException(fragment.GetType().ToString()); } } return(itemsToAdd.ToImmutable()); }
protected override Assembly?Load(AssemblyName assemblyName) { if (WellKnownAssemblyNames.Contains(assemblyName.Name !)) { // Force MSBuild assemblies to be loaded in the default ALC // and unify to the current version. return(null); } if (ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_4)) { // respect plugin.dll.json with the AssemblyDependencyResolver string?assemblyPath = _resolver?.ResolveAssemblyToPath(assemblyName); if (assemblyPath != null) { return(LoadFromAssemblyPath(assemblyPath)); } } // Fall back to the older MSBuild-on-Core behavior to continue to support // plugins that don't ship a .deps.json foreach (var cultureSubfolder in string.IsNullOrEmpty(assemblyName.CultureName) // If no culture is specified, attempt to load directly from // the known dependency paths. ? new[] { string.Empty } // Search for satellite assemblies in culture subdirectories // of the assembly search directories, but fall back to the // bare search directory if that fails. : new[] { assemblyName.CultureName, string.Empty }) { var candidatePath = Path.Combine(_directory, cultureSubfolder, $"{assemblyName.Name}.dll"); if (!FileSystems.Default.FileExists(candidatePath)) { continue; } AssemblyName candidateAssemblyName = AssemblyLoadContext.GetAssemblyName(candidatePath); if (candidateAssemblyName.Version != assemblyName.Version) { continue; } return(LoadFromAssemblyPath(candidatePath)); } // If the Assembly is provided via a file path, the following rules are used to load the assembly: // - the assembly from the user specified path is loaded, if it exists, into the custom ALC, or // - if the simple name of the assembly exists in the same folder as msbuild.exe, then that assembly gets loaded // into the default ALC (so it's shared with other uses). var assemblyNameInExecutableDirectory = Path.Combine(BuildEnvironmentHelper.Instance.CurrentMSBuildToolsDirectory, $"{assemblyName.Name}.dll"); if (FileSystems.Default.FileExists(assemblyNameInExecutableDirectory)) { return(AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyNameInExecutableDirectory)); } return(null); }
internal static bool AreFeaturesEnabled(Version wave) { return(ChangeWaves.AreFeaturesEnabled(wave)); }
internal static bool AreFeaturesEnabled(string wave) { return(string.IsNullOrEmpty(wave) ? false : ChangeWaves.AreFeaturesEnabled(wave)); }
/// <summary> /// Parse the given <paramref name="fileSpec" /> into a <see cref="MSBuildGlob" /> using a given /// <paramref name="globRoot" />. /// </summary> /// <param name="globRoot"> /// The root of the glob. /// The fixed directory part of the glob and the match arguments (<see cref="IsMatch" /> and <see cref="MatchInfo" />) /// will get normalized against this root. /// If empty, the current working directory is used. /// Cannot be null, and cannot contain invalid path arguments. /// </param> /// <param name="fileSpec">The string to parse</param> /// <returns></returns> public static MSBuildGlob Parse(string globRoot, string fileSpec) { ErrorUtilities.VerifyThrowArgumentNull(globRoot, nameof(globRoot)); ErrorUtilities.VerifyThrowArgumentNull(fileSpec, nameof(fileSpec)); ErrorUtilities.VerifyThrowArgumentInvalidPath(globRoot, nameof(globRoot)); if (string.IsNullOrEmpty(globRoot)) { globRoot = Directory.GetCurrentDirectory(); } globRoot = Strings.WeakIntern(FileUtilities.NormalizePath(globRoot).WithTrailingSlash()); var lazyState = new Lazy <GlobState>(() => { FileMatcher.Default.GetFileSpecInfo( fileSpec, out string fixedDirectoryPart, out string wildcardDirectoryPart, out string filenamePart, out bool needsRecursion, out bool isLegalFileSpec, (fixedDirPart, wildcardDirPart, filePart) => { var normalizedFixedPart = NormalizeTheFixedDirectoryPartAgainstTheGlobRoot(fixedDirPart, globRoot); return(normalizedFixedPart, wildcardDirPart, filePart); }); Regex regex = null; if (isLegalFileSpec) { string matchFileExpression = FileMatcher.RegularExpressionFromFileSpec(fixedDirectoryPart, wildcardDirectoryPart, filenamePart); lock (s_regexCache) { s_regexCache.TryGetValue(matchFileExpression, out regex); } if (regex == null) { RegexOptions regexOptions = FileMatcher.DefaultRegexOptions; // compile the regex since it's expected to be used multiple times // For the kind of regexes used here, compilation on .NET Framework tends to be expensive and not worth the small // run-time boost so it's enabled only on .NET Core by default. #if RUNTIME_TYPE_NETCORE bool compileRegex = true; #else bool compileRegex = !ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_0); #endif if (compileRegex) { regexOptions |= RegexOptions.Compiled; } Regex newRegex = new Regex(matchFileExpression, regexOptions); lock (s_regexCache) { if (!s_regexCache.TryGetValue(matchFileExpression, out regex)) { s_regexCache[matchFileExpression] = newRegex; } } regex ??= newRegex; } } return(new GlobState(globRoot, fileSpec, isLegalFileSpec, fixedDirectoryPart, wildcardDirectoryPart, filenamePart, needsRecursion, regex)); },
internal static object GetRegistryValueFromView(string keyName, string valueName, object defaultValue, ArraySegment <object> views) { #if RUNTIME_TYPE_NETCORE // .NET Core MSBuild used to always return empty, so match that behavior // on non-Windows (no registry), and with a changewave (in case someone // had a registry property and it breaks when it lights up). if (!NativeMethodsShared.IsWindows || !ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_4)) { return(defaultValue); } #endif // We will take on handing of default value // A we need to act on the null return from the GetValue call below // so we can keep searching other registry views object result = defaultValue; // If we haven't been passed any views, then we'll just use the default view if (views.Count == 0) { views = new ArraySegment <object>(DefaultRegistryViews); } foreach (object viewObject in views) { if (viewObject is string viewAsString) { string typeLeafName = typeof(RegistryView).Name + "."; string typeFullName = typeof(RegistryView).FullName + "."; // We'll allow the user to specify the leaf or full type name on the RegistryView enum viewAsString = viewAsString.Replace(typeFullName, "").Replace(typeLeafName, ""); // This may throw - and that's fine as the user will receive a controlled version // of that error. RegistryView view = (RegistryView)Enum.Parse(typeof(RegistryView), viewAsString, true); if (!NativeMethodsShared.IsWindows && !keyName.StartsWith("HKEY_CURRENT_USER", StringComparison.OrdinalIgnoreCase)) { // Fake common requests to HKLM that we can resolve // See if this asks for a specific SDK var m = RegistrySdkRegex.Value.Match(keyName); if (m.Success && m.Groups.Count >= 1 && valueName.Equals("InstallRoot", StringComparison.OrdinalIgnoreCase)) { return(Path.Combine(NativeMethodsShared.FrameworkBasePath, m.Groups[0].Value) + Path.DirectorySeparatorChar); } return(string.Empty); } using (RegistryKey key = GetBaseKeyFromKeyName(keyName, view, out string subKeyName)) { if (key != null) { using (RegistryKey subKey = key.OpenSubKey(subKeyName, false)) { // If we managed to retrieve the subkey, then move onto locating the value if (subKey != null) { result = subKey.GetValue(valueName); } // We've found a value, so stop looking if (result != null) { break; } } } } } } // We will have either found a result or defaultValue if one wasn't found at this point return(result); }