Exemplo n.º 1
0
        private static void FilterUnneededProperties(Project project, IList <XElement> additionalPropertyGroups)
        {
            // special case handling for when condition-guarded props override global props not set to their defaults
            var globalOverrides = RetrieveGlobalOverrides(additionalPropertyGroups);

            if (!globalOverrides.TryGetValue("ProjectName", out var projectName))
            {
                if (!globalOverrides.TryGetValue("AssemblyName", out projectName))
                {
                    projectName = project.FilePath.Name.Replace(".csproj", "");
                }
            }

            var removeQueue = new List <XElement>();

            foreach (var propertyGroup in additionalPropertyGroups)
            {
                foreach (var child in propertyGroup.Elements())
                {
                    var parentCondition                   = propertyGroup.Attribute("Condition")?.Value.Trim() ?? "";
                    var hasParentCondition                = parentCondition.Length > 1;      // no sane condition is 1 char long
                    var parentConditionEvaluated          = ConditionEvaluator.GetNonAmbiguousConditionContracts(parentCondition);
                    var parentConditionHasPlatform        = parentConditionEvaluated.TryGetValue("Platform", out var parentConditionPlatform);
                    var parentConditionHasConfiguration   = parentConditionEvaluated.TryGetValue("Configuration", out var parentConditionConfiguration);
                    var parentConditionPlatformLower      = parentConditionPlatform?.ToLowerInvariant();
                    var parentConditionConfigurationLower = parentConditionConfiguration?.ToLowerInvariant();
                    var isDebugOnly   = parentConditionHasConfiguration && parentConditionConfigurationLower == "debug";
                    var isReleaseOnly = parentConditionHasConfiguration && parentConditionConfigurationLower == "release";

                    var tagLocalName        = child.Name.LocalName;
                    var valueLower          = child.Value.ToLowerInvariant();
                    var valueLowerTrim      = valueLower.Trim();
                    var hasGlobalOverride   = globalOverrides.TryGetValue(tagLocalName, out var globalOverride);
                    var globalOverrideLower = globalOverride?.ToLowerInvariant();

                    // Regex is the easiest way to replace string between two unknown chars preserving both as is
                    // so that bin\Debug\net472 is turned into bin\$(Configuration)\net472
                    // and bin/Debug\net472 is turned into bin/$(Configuration)\net472, preserving path separators
                    var configurationPathRegex = parentConditionHasConfiguration
                                                ? new Regex($@"([\\/]){parentConditionConfiguration}([\\/])")
                                                : null;

                    var childCondition = child.Attribute("Condition");
                    switch (tagLocalName)
                    {
                    // VS2013 NuGet bugs workaround
                    case "NuGetPackageImportStamp":
                    // used by Project2015To2017 and not needed anymore
                    case "OutputType" when !hasParentCondition:
                    case "Platforms" when !hasParentCondition:
                    case "Configurations" when !hasParentCondition:
                    case "TargetFrameworkVersion" when !hasParentCondition:
                    case "TargetFrameworkIdentifier" when !hasParentCondition:
                    case "TargetFrameworkProfile" when !hasParentCondition && valueLowerTrim.Length == 0:
                    // VCS properties
                    case "SccProjectName" when !hasParentCondition && valueLowerTrim.Length == 0:
                    case "SccLocalPath" when !hasParentCondition && valueLowerTrim.Length == 0:
                    case "SccAuxPath" when !hasParentCondition && valueLowerTrim.Length == 0:
                    case "SccProvider" when !hasParentCondition && valueLowerTrim.Length == 0:
                    // Project properties set to defaults (Microsoft.NET.Sdk)
                    case "OutputType" when ValidateDefaultValue("Library"):
                    case "FileAlignment" when ValidateDefaultValue("512"):
                    case "ErrorReport" when ValidateDefaultValue("prompt"):
                    case "Deterministic" when ValidateDefaultValue("true"):
                    case "WarningLevel" when ValidateDefaultValue("4"):
                    case "DebugType" when ValidateDefaultValue("portable"):
                    case "ResolveNuGetPackages" when ValidateDefaultValue("false"):
                    case "SkipImportNuGetProps" when ValidateDefaultValue("true"):
                    case "SkipImportNuGetBuildTargets" when ValidateDefaultValue("true"):
                    case "RestoreProjectStyle" when ValidateDefaultValue("packagereference"):
                    case "AllowUnsafeBlocks" when ValidateDefaultValue("false"):
                    case "TreatWarningsAsErrors" when ValidateDefaultValue("false"):
                    case "Prefer32Bit" when ValidateDefaultValue("false"):
                    case "SignAssembly" when ValidateDefaultValue("false"):
                    case "DelaySign" when ValidateDefaultValue("false"):
                    case "GeneratePackageOnBuild" when ValidateDefaultValue("false"):
                    case "PackageRequireLicenseAcceptance" when ValidateDefaultValue("false"):
                    case "DebugSymbols" when ValidateDefaultValue("false"):
                    case "CheckForOverflowUnderflow" when ValidateDefaultValue("false"):
                    case "AppendTargetFrameworkToOutputPath" when ValidateDefaultValue("true"):
                    case "AppDesignerFolder" when ValidateDefaultValue("properties"):
                    case "DefaultProjectTypeGuid" when ValidateDefaultValue("{fae04ec0-301f-11d3-bf4b-00c04f79efbc}"):
                    case "DefaultLanguageSourceExtension" when ValidateDefaultValue(".cs"):
                    case "Language" when ValidateDefaultValue("C#"):
                    case "TargetRuntime" when ValidateDefaultValue("managed"):
                    case "Utf8Output" when ValidateDefaultValue("true"):
                    case "PlatformName" when ValidateDefaultValue("$(platform)") ||
                        (parentConditionHasPlatform &&
                         ValidateDefaultValue(parentConditionPlatformLower)):
                    // Conditional platform default values
                    case "PlatformTarget" when parentConditionHasPlatform && child.Value == parentConditionPlatform &&
                        !hasGlobalOverride:
                    // Conditional configuration (Debug/Release) default values
                    case "DefineConstants" when isDebugOnly && ValidateDefaultConstants(valueLower, "debug", "trace") &&
                        !hasGlobalOverride:
                    case "DefineConstants" when isReleaseOnly && ValidateDefaultConstants(valueLower, "trace") &&
                        !hasGlobalOverride:
                    case "Optimize" when isDebugOnly && valueLower == "false" && !hasGlobalOverride:
                    case "Optimize" when isReleaseOnly && valueLower == "true" && !hasGlobalOverride:
                    case "DebugSymbols" when isDebugOnly && valueLower == "true" && !hasGlobalOverride:
                    // Default project values for Platform and Configuration
                    case "Platform" when !hasParentCondition && ValidateEmptyConditionValue(childCondition) &&
                        valueLower == "anycpu":
                    case "Configuration" when !hasParentCondition && ValidateEmptyConditionValue(childCondition) &&
                        valueLower == "debug":
                    // Extra ProjectName duplicates
                    case "RootNamespace" when !hasParentCondition && ValidateEmptyConditionValue(childCondition) &&
                        child.Value == projectName:
                    case "AssemblyName" when !hasParentCondition && ValidateEmptyConditionValue(childCondition) &&
                        child.Value == projectName:
                    {
                        removeQueue.Add(child);
                        break;
                    }

                    // Default configuration-specific paths
                    // todo: move duplicated paths from conditional groups to non-conditional one
                    case "OutputPath"
                        when parentConditionHasConfiguration && configurationPathRegex.IsMatch(child.Value):
                    {
                        child.Value = configurationPathRegex.Replace(child.Value, "$1$(Configuration)$2");
                        break;
                    }
                    }

                    // following local methods will capture parent scope
                    bool ValidateDefaultValue(string @default)
                    {
                        return((!hasParentCondition && valueLower == @default) ||
                               (hasParentCondition && ValidateConditionedDefaultValue(@default)));
                    }

                    bool ValidateConditionedDefaultValue(string @default)
                    {
                        return((valueLower == @default) && (!hasGlobalOverride || globalOverrideLower == @default));
                    }

                    // following local methods will not capture parent scope
                    bool ValidateEmptyConditionValue(XAttribute condition)
                    {
                        if (condition == null)
                        {
                            return(true);
                        }

                        var value = condition.Value;

                        return((value.Count(x => x == '=') == 2) && value.Contains("''"));
                    }

                    bool ValidateDefaultConstants(string value, params string[] expected)
                    {
                        var defines = value.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                        var set     = new HashSet <string>(defines);

                        foreach (var expecto in expected)
                        {
                            if (!set.Remove(expecto))
                            {
                                return(false);
                            }
                        }

                        return(set.Count == 0);
                    }
                }
            }

            // we cannot remove elements correctly while iterating through elements, 2nd pass is needed
            foreach (var child in removeQueue)
            {
                child.Remove();
            }
        }
Exemplo n.º 2
0
        private static (List <string>, List <string>) ReadConditionals(XElement[] unconditionalPropertyGroups,
                                                                       Project project)
        {
            var projectXml = project.ProjectDocument;

            var configurationSet = new HashSet <string>();
            var platformSet      = new HashSet <string>();

            var configurationsFromProperty = ParseFromProperty("Configurations");
            var platformsFromProperty      = ParseFromProperty("Platforms");

            var needConfigurations = configurationsFromProperty == null;

            if (!needConfigurations)
            {
                foreach (var configuration in configurationsFromProperty)
                {
                    configurationSet.Add(configuration);
                }
            }

            var needPlatforms = platformsFromProperty == null;

            if (!needPlatforms)
            {
                foreach (var platform in platformsFromProperty)
                {
                    platformSet.Add(platform);
                }
            }

            if (needConfigurations || needPlatforms)
            {
                foreach (var x in projectXml.Descendants())
                {
                    var condition = x.Attribute("Condition");
                    if (condition == null)
                    {
                        continue;
                    }

                    var conditionValue = condition.Value;
                    if (!conditionValue.Contains("$(Configuration)") && !conditionValue.Contains("$(Platform)"))
                    {
                        continue;
                    }

                    var conditionEvaluated = ConditionEvaluator.GetConditionValues(conditionValue);

                    if (needConfigurations && conditionEvaluated.TryGetValue("Configuration", out var configurations))
                    {
                        foreach (var configuration in configurations)
                        {
                            configurationSet.Add(configuration);
                        }
                    }

                    if (needPlatforms && conditionEvaluated.TryGetValue("Platform", out var platforms))
                    {
                        foreach (var platform in platforms)
                        {
                            platformSet.Add(platform);
                        }
                    }
                }
            }

            var configurationList = configurationSet.ToList();
            var platformList      = platformSet.ToList();

            configurationList.Sort();
            platformList.Sort();
            return(configurationList, platformList);

            string[] ParseFromProperty(string name) => unconditionalPropertyGroups.Elements(project.XmlNamespace + name)
            .FirstOrDefault()
            ?.Value
            .Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
        }