├── Project2015To2017.Tests
├── TestFiles
│ ├── Deletions
│ │ ├── a.txt
│ │ ├── Test1.csproj
│ │ ├── Test2.csproj
│ │ ├── Test3.csproj
│ │ ├── Test4.csproj
│ │ ├── AssemblyInfo.txt
│ │ ├── EmptyFolder
│ │ │ └── a.txt
│ │ ├── NonEmptyFolder
│ │ │ └── a.txt
│ │ └── NonEmptyFolder2
│ │ │ └── a.txt
│ ├── Solutions
│ │ ├── TextFile.txt
│ │ ├── ClassLibrary
│ │ │ ├── Class1.cs
│ │ │ ├── Properties
│ │ │ │ └── AssemblyInfo.cs
│ │ │ └── ClassLibrary.csproj
│ │ └── sampleSolution.testsln
│ ├── FileInclusion
│ │ ├── AnotherFile.cs
│ │ ├── Program.cs
│ │ ├── Folder
│ │ │ ├── FileIncludedByWildcard.cs
│ │ │ └── FileInFolder.cs
│ │ ├── WildcardFolder
│ │ │ └── WildcardSubFolder
│ │ │ │ └── SubFolderWildcardFile.cs
│ │ ├── app.config
│ │ ├── IncludedFile.cs
│ │ ├── Class1.cs
│ │ ├── SourceFileSubTypeCode.cs
│ │ ├── SourceFileAsResource.cs
│ │ ├── packages.config
│ │ ├── Properties
│ │ │ ├── AssemblyInfo.cs
│ │ │ └── Resources.Designer.cs
│ │ ├── Resources.Designer.cs
│ │ ├── SourceFileWithDesigner.Designer.cs
│ │ └── fileExclusion.testcsproj
│ ├── AssemblyInfoHandling
│ │ ├── Empty
│ │ │ └── Properties
│ │ │ │ └── AssemblyInfo.cs
│ │ ├── ClassDataLeft
│ │ │ └── Properties
│ │ │ │ └── AssemblyInfo.cs
│ │ └── Redundant
│ │ │ └── Properties
│ │ │ └── AssemblyInfo.cs
│ ├── OtherTestProjects
│ │ ├── test.nuspec
│ │ ├── OtherTestClass.cs
│ │ ├── packages.config
│ │ ├── AssemblyInfo.cs
│ │ └── containsTestSDK.testcsproj
│ ├── AltNugetConfig
│ │ ├── nuget.config
│ │ └── ProjectFolder
│ │ │ └── packages.config
│ └── nuSpecs
│ │ └── noNamespace.nuspec
├── OtherPackagesConfig
│ └── packages.config
├── Project2015To2017.Tests.csproj.DotSettings
├── NuSpecReaderTest.cs
├── SolutionReaderTests.cs
├── ProjectReferenceReadTest.cs
├── AssemblyFilterDefaultTransformationTest.cs
├── DummyLogger.cs
├── AssemblyInfoReadTest.cs
├── ImportsTargetsFilterPackageReferencesTransformationTest.cs
├── PrimaryProjectPropertiesUpdateTransformationTest.cs
├── W001IllegalProjectTypeDiagnosticTest.cs
├── AssemblyFilterPackageReferencesTransformationTest.cs
├── AssemblyFilterHintedPackageReferencesTransformationTest.cs
├── ProgramTest.cs
├── NuGetPackageTransformationTest.cs
├── UnsupportedProjectTypesTest.cs
├── TargetFrameworkReplaceTransformationTest.cs
└── TestProjectPackageReferenceTransformationTest.cs
├── .editorconfig
├── Project2015To2017.Core
├── Analysis
│ ├── IReporterOptions.cs
│ ├── LoggerReporterOptions.cs
│ ├── IDiagnosticLocation.cs
│ ├── IDiagnosticResult.cs
│ ├── IDiagnostic.cs
│ ├── DiagnosticLocation.cs
│ ├── IllegalDiagnosticStateException.cs
│ ├── AnalysisOptions.cs
│ ├── DiagnosticResult.cs
│ ├── IReporter.cs
│ ├── DiagnosticSet.cs
│ ├── AnalysisExtensions.cs
│ ├── Diagnostics
│ │ ├── W002MissingProjectFileDiagnostic.cs
│ │ ├── W011UnsupportedConditionalDiagnostic.cs
│ │ ├── W001IllegalProjectTypeDiagnostic.cs
│ │ └── W010ConfigurationsMismatchDiagnostic.cs
│ ├── ReporterBase.cs
│ ├── LoggerReporter.cs
│ ├── Analyzer.cs
│ └── DiagnosticBase.cs
├── Definition
│ ├── IReference.cs
│ ├── ApplicationType.cs
│ ├── PackageReference.cs
│ ├── AssemblyReference.cs
│ ├── ProjectReference.cs
│ ├── PackageConfiguration.cs
│ ├── Solution.cs
│ └── Project.cs
├── Transforms
│ ├── TargetTransformationExecutionMoment.cs
│ ├── ITransformationWithTargetMoment.cs
│ ├── ITransformationWithDependencies.cs
│ ├── IModernOnlyProjectTransformation.cs
│ ├── ITransformation.cs
│ ├── ILegacyOnlyProjectTransformation.cs
│ ├── BasicReadTransformationSet.cs
│ ├── EmptyGroupRemoveTransformation.cs
│ ├── TargetFrameworkReplaceTransformation.cs
│ ├── PropertyDeduplicationTransformation.cs
│ ├── NuGetPackageTransformation.cs
│ └── PrimaryProjectPropertiesUpdateTransformation.cs
├── Caching
│ ├── IProjectCache.cs
│ ├── NoProjectCache.cs
│ └── DefaultProjectCache.cs
├── ITransformationSet.cs
├── NoopTransformationSet.cs
├── NoopLogger.cs
├── Reading
│ ├── Conditionals
│ │ ├── OperandExpressionNode.cs
│ │ ├── IConditionEvaluationState.cs
│ │ ├── EqualExpressionNode.cs
│ │ ├── NotEqualExpressionNode.cs
│ │ ├── CharacterUtilities.cs
│ │ ├── OrExpressionNode.cs
│ │ ├── AndExpressionNode.cs
│ │ ├── FunctionCallExpressionNode.cs
│ │ ├── NotExpressionNode.cs
│ │ ├── LessThanExpressionNode.cs
│ │ ├── GreaterThanExpressionNode.cs
│ │ ├── LessThanOrEqualExpressionNode.cs
│ │ ├── GreaterThanOrEqualExpressionNode.cs
│ │ ├── NumericComparisonExpressionNode.cs
│ │ ├── GenericExpressionNode.cs
│ │ └── NumericExpressionNode.cs
│ ├── ConditionEvaluator.ModernCache.cs
│ ├── ConditionEvaluator.LegacyCache.cs
│ ├── upstream.md
│ ├── ConditionEvaluationStateImpl.cs
│ ├── AssemblyInfoReader.cs
│ └── NuSpecReader.cs
├── Project2015To2017.Core.csproj
├── ChainTransformationSet.cs
├── ConversionOptions.cs
└── UnsupportedProjectTypes.cs
├── Project2015To2017
├── ProcessSingleItemCallback.cs
├── PatternProcessor.cs
├── Project2015To2017.csproj
└── ProjectConverterExtensions.cs
├── Project2015To2017.Migrate2017.Library
├── Project2015To2017.Migrate2017.Library.csproj
├── Diagnostics
│ ├── W034ReferenceAliasesDiagnostic.cs
│ ├── W030LegacyDebugTypesDiagnostic.cs
│ ├── W031MSBuildSdkVersionSpecificationDiagnostic.cs
│ ├── W032OldLanguageVersionDiagnostic.cs
│ ├── W033ObsoletePortableClassLibrariesDiagnostic.cs
│ └── W020MicrosoftCSharpDiagnostic.cs
├── Transforms
│ ├── AssemblyFilterPackageReferencesTransformation.cs
│ ├── AssemblyFilterDefaultTransformation.cs
│ ├── AssemblyFilterHintedPackageReferencesTransformation.cs
│ ├── TestProjectPackageReferenceTransformation.cs
│ └── XamlPagesTransformation.cs
├── Vs15DiagnosticSet.cs
└── Vs15TransformationSet.cs
├── Project2015To2017.sln.DotSettings
├── Project2015To2017.Console
├── Project2015To2017.Console.csproj
├── Options.cs
└── Program.cs
├── LICENSE
├── Directory.Build.props
├── Project2015To2017.Migrate2017.Tool
├── Project2015To2017.Migrate2017.Tool.csproj
└── CommandLogic.cs
├── .gitattributes
├── appveyor.yml
├── README.md
└── Project2015To2017.sln
/Project2015To2017.Tests/TestFiles/Deletions/a.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/Deletions/Test1.csproj:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/Deletions/Test2.csproj:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/Deletions/Test3.csproj:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/Deletions/Test4.csproj:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/Solutions/TextFile.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/Deletions/AssemblyInfo.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/Deletions/EmptyFolder/a.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/FileInclusion/AnotherFile.cs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/FileInclusion/Program.cs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/Deletions/NonEmptyFolder/a.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/Deletions/NonEmptyFolder2/a.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/OtherPackagesConfig/packages.config:
--------------------------------------------------------------------------------
1 | definitely not xml
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/AssemblyInfoHandling/Empty/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/FileInclusion/Folder/FileIncludedByWildcard.cs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/FileInclusion/WildcardFolder/WildcardSubFolder/SubFolderWildcardFile.cs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*]
2 | indent_style = tab
3 | tab_width = 4
4 | charset = utf-8
5 | trim_trailing_whitespace = true
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/AssemblyInfoHandling/ClassDataLeft/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | public class Test
2 | {
3 |
4 | }
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/FileInclusion/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/FileInclusion/IncludedFile.cs:
--------------------------------------------------------------------------------
1 | namespace Project2015To2017Tests.TestFiles
2 | {
3 | class IncludedFile
4 | {
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Analysis/IReporterOptions.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace Project2015To2017.Analysis
4 | {
5 | public interface IReporterOptions
6 | {
7 | }
8 | }
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/FileInclusion/Folder/FileInFolder.cs:
--------------------------------------------------------------------------------
1 | namespace Project2015To2017Tests.TestFiles.Folder
2 | {
3 | class FileInFolder
4 | {
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Analysis/LoggerReporterOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Project2015To2017.Analysis
2 | {
3 | public sealed class LoggerReporterOptions : IReporterOptions
4 | {
5 | }
6 | }
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/OtherTestProjects/test.nuspec:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/baruchiro/CsprojToVs2017/master/Project2015To2017.Tests/TestFiles/OtherTestProjects/test.nuspec
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/OtherTestProjects/OtherTestClass.cs:
--------------------------------------------------------------------------------
1 | namespace Project2015To2017Tests.TestFiles.OtherTestProjects
2 | {
3 | public class OtherTestClass
4 | {
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Project2015To2017/ProcessSingleItemCallback.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace Project2015To2017
4 | {
5 | public delegate void ProcessSingleItemCallback(FileInfo file, string extension);
6 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/Definition/IReference.cs:
--------------------------------------------------------------------------------
1 | using System.Xml.Linq;
2 |
3 | namespace Project2015To2017.Definition
4 | {
5 | public interface IReference
6 | {
7 | XElement DefinitionElement { get; }
8 | }
9 | }
--------------------------------------------------------------------------------
/Project2015To2017/PatternProcessor.cs:
--------------------------------------------------------------------------------
1 | namespace Project2015To2017
2 | {
3 | public delegate bool PatternProcessor(ProjectConverter converter, string pattern, ProcessSingleItemCallback callback, Facility self);
4 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/Transforms/TargetTransformationExecutionMoment.cs:
--------------------------------------------------------------------------------
1 | namespace Project2015To2017.Transforms
2 | {
3 | public enum TargetTransformationExecutionMoment : byte
4 | {
5 | Normal = 0,
6 | Early,
7 | Late
8 | }
9 | }
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/AssemblyInfoHandling/Redundant/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using somenamespace;
2 | using someOtherNameSpace;
3 |
4 | //some comment
5 |
6 | /*
7 | * A multi-line comment
8 | * none of these need keeping
9 | */
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/FileInclusion/Class1.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Project2015To2017Tests.TestFiles.FileInclusion
6 | {
7 | class Class1
8 | {
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Transforms/ITransformationWithTargetMoment.cs:
--------------------------------------------------------------------------------
1 | namespace Project2015To2017.Transforms
2 | {
3 | public interface ITransformationWithTargetMoment : ITransformation
4 | {
5 | TargetTransformationExecutionMoment ExecutionMoment { get; }
6 | }
7 | }
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/FileInclusion/SourceFileSubTypeCode.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Project2015To2017Tests.TestFiles.FileInclusion
6 | {
7 | class SourceFileSubTypeCode
8 | {
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Analysis/IDiagnosticLocation.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace Project2015To2017.Analysis
4 | {
5 | public interface IDiagnosticLocation
6 | {
7 | FileSystemInfo Source { get; }
8 | uint SourceLine { get; }
9 | string SourcePath { get; }
10 | }
11 | }
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/FileInclusion/SourceFileAsResource.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Project2015To2017Tests.TestFiles.FileInclusion
6 | {
7 | class SourceFileAsResource
8 | {
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Transforms/ITransformationWithDependencies.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Project2015To2017.Transforms
4 | {
5 | public interface ITransformationWithDependencies : ITransformation
6 | {
7 | IReadOnlyCollection DependOn { get; }
8 | }
9 | }
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/AltNugetConfig/nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/Solutions/ClassLibrary/Class1.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace ClassLibrary
8 | {
9 | public class Class1
10 | {
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Caching/IProjectCache.cs:
--------------------------------------------------------------------------------
1 | namespace Project2015To2017.Caching
2 | {
3 | public interface IProjectCache
4 | {
5 | void Add(string key, Definition.Project project);
6 |
7 | bool TryGetValue(string key, out Definition.Project project);
8 |
9 | void Purge();
10 | }
11 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/Analysis/IDiagnosticResult.cs:
--------------------------------------------------------------------------------
1 | using Project2015To2017.Definition;
2 |
3 | namespace Project2015To2017.Analysis
4 | {
5 | public interface IDiagnosticResult
6 | {
7 | string Code { get; }
8 | IDiagnosticLocation Location { get; }
9 | string Message { get; }
10 | Project Project { get; }
11 | }
12 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/Definition/ApplicationType.cs:
--------------------------------------------------------------------------------
1 | namespace Project2015To2017.Definition
2 | {
3 | public enum ApplicationType
4 | {
5 | Unknown = 0,
6 | ClassLibrary = 1,
7 | ConsoleApplication = 2,
8 | WindowsApplication = 3,
9 | TestProject = 4,
10 | AppContainerExe = 5
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Analysis/IDiagnostic.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Project2015To2017.Definition;
3 |
4 | namespace Project2015To2017.Analysis
5 | {
6 | public interface IDiagnostic
7 | {
8 | bool SkipForLegacyProject { get; }
9 | bool SkipForModernProject { get; }
10 |
11 | IReadOnlyList Analyze(Project project);
12 | }
13 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/ITransformationSet.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Microsoft.Extensions.Logging;
3 | using Project2015To2017.Transforms;
4 |
5 | namespace Project2015To2017
6 | {
7 | public interface ITransformationSet
8 | {
9 | IReadOnlyCollection Transformations(
10 | ILogger logger,
11 | ConversionOptions conversionOptions);
12 | }
13 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/Transforms/IModernOnlyProjectTransformation.cs:
--------------------------------------------------------------------------------
1 | namespace Project2015To2017.Transforms
2 | {
3 | ///
4 | /// A transformation implementing this interface is supposed to be executed
5 | /// only on projects under CPS (in ordinary situations)
6 | ///
7 | public interface IModernOnlyProjectTransformation : ITransformation
8 | {
9 |
10 | }
11 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/Transforms/ITransformation.cs:
--------------------------------------------------------------------------------
1 | using Project2015To2017.Definition;
2 |
3 | namespace Project2015To2017.Transforms
4 | {
5 | public interface ITransformation
6 | {
7 | ///
8 | /// Alter the provided project in some way
9 | ///
10 | ///
11 | void Transform(Project definition);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Definition/PackageReference.cs:
--------------------------------------------------------------------------------
1 | using System.Xml.Linq;
2 |
3 | namespace Project2015To2017.Definition
4 | {
5 | public sealed class PackageReference : IReference
6 | {
7 | public string Id { get; set; }
8 | public string Version { get; set; }
9 | public bool IsDevelopmentDependency { get; set; }
10 |
11 | public XElement DefinitionElement { get; set; }
12 | }
13 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/Transforms/ILegacyOnlyProjectTransformation.cs:
--------------------------------------------------------------------------------
1 | namespace Project2015To2017.Transforms
2 | {
3 | ///
4 | /// A transformation implementing this interface is supposed to be executed
5 | /// only on projects under legacy project system (in ordinary situations)
6 | ///
7 | public interface ILegacyOnlyProjectTransformation : ITransformation
8 | {
9 |
10 | }
11 | }
--------------------------------------------------------------------------------
/Project2015To2017.Tests/Project2015To2017.Tests.csproj.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | CSharp72
--------------------------------------------------------------------------------
/Project2015To2017.Migrate2017.Library/Project2015To2017.Migrate2017.Library.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard1.3;netstandard2.0
5 | Project2015To2017.Migrate2017
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Project2015To2017.sln.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/FileInclusion/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/OtherTestProjects/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/OtherTestProjects/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 |
6 | [assembly: AssemblyTitle("Title")]
7 | [assembly: AssemblyCompany("Company.")]
8 | [assembly: AssemblyProduct("Product")]
9 | [assembly: AssemblyCopyright("Copyright © 2012-2016 Generic Inc.")]
10 | [assembly: ComVisible(false)]
11 | [assembly: CLSCompliant(true)]
12 | [assembly: AssemblyInformationalVersion("7.61.0")]
13 | [assembly: AssemblyVersion("7.0")]
--------------------------------------------------------------------------------
/Project2015To2017.Core/Definition/AssemblyReference.cs:
--------------------------------------------------------------------------------
1 | using System.Xml.Linq;
2 |
3 | namespace Project2015To2017.Definition
4 | {
5 | // Reference
6 | public sealed class AssemblyReference : IReference
7 | {
8 | // Attributes
9 | public string Include { get; set; }
10 |
11 | // Elements
12 | public string EmbedInteropTypes { get; set; }
13 | public string HintPath { get; set; }
14 | public string Private { get; set; }
15 | public string SpecificVersion { get; set; }
16 |
17 | public XElement DefinitionElement { get; set; }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/FileInclusion/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 |
6 | [assembly: AssemblyTitle("Title")]
7 | [assembly: AssemblyCompany("Company.")]
8 | [assembly: AssemblyProduct("Product")]
9 | [assembly: AssemblyCopyright("Copyright © 2012-2016 Generic Inc.")]
10 | [assembly: ComVisible(false)]
11 | [assembly: CLSCompliant(true)]
12 | [assembly: AssemblyInformationalVersion("7.61.0")]
13 | [assembly: AssemblyVersion("7.0")]
--------------------------------------------------------------------------------
/Project2015To2017.Tests/NuSpecReaderTest.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using Microsoft.VisualStudio.TestTools.UnitTesting;
3 | using Project2015To2017.Reading;
4 |
5 | namespace Project2015To2017.Tests
6 | {
7 | [TestClass]
8 | public class NuSpecReaderTest
9 | {
10 | [TestMethod]
11 | public void LoadsNuSpecWithNoNamespace()
12 | {
13 | var reader = new NuSpecReader(NoopLogger.Instance);
14 | var nuspec = reader.Read(new FileInfo(@"TestFiles\nuSpecs\dummy.csproj"));
15 |
16 | Assert.IsNotNull(nuspec);
17 | }
18 |
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Caching/NoProjectCache.cs:
--------------------------------------------------------------------------------
1 | using Project2015To2017.Definition;
2 |
3 | namespace Project2015To2017.Caching
4 | {
5 | public sealed class NoProjectCache : IProjectCache
6 | {
7 | public static IProjectCache Instance => new NoProjectCache();
8 |
9 | private NoProjectCache()
10 | {
11 |
12 | }
13 |
14 | public void Add(string key, Project project)
15 | {
16 | }
17 |
18 | public bool TryGetValue(string key, out Project project)
19 | {
20 | project = null;
21 | return false;
22 | }
23 |
24 | public void Purge()
25 | {
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/Analysis/DiagnosticLocation.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace Project2015To2017.Analysis
4 | {
5 | public sealed class DiagnosticLocation : IDiagnosticLocation
6 | {
7 | public uint SourceLine { get; set; }
8 | public FileSystemInfo Source { get; set; }
9 | public string SourcePath { get; set; }
10 |
11 | public DiagnosticLocation()
12 | {
13 | }
14 |
15 | public DiagnosticLocation(IDiagnosticLocation location)
16 | {
17 | this.SourceLine = location.SourceLine;
18 | this.Source = location.Source;
19 | this.SourcePath = location.SourcePath;
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/Analysis/IllegalDiagnosticStateException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Project2015To2017.Analysis
4 | {
5 | public sealed class IllegalDiagnosticStateException : InvalidOperationException
6 | {
7 | ///
8 | public IllegalDiagnosticStateException()
9 | {
10 | }
11 |
12 | ///
13 | public IllegalDiagnosticStateException(string message) : base(message)
14 | {
15 | }
16 |
17 | ///
18 | public IllegalDiagnosticStateException(string message, Exception innerException) : base(message, innerException)
19 | {
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/Analysis/AnalysisOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Collections.Immutable;
3 |
4 | namespace Project2015To2017.Analysis
5 | {
6 | public sealed class AnalysisOptions
7 | {
8 | ///
9 | /// Including ID of diagnostics in this list will make analyzer skip their execution and therefore output
10 | ///
11 | public ImmutableHashSet Diagnostics { get; }
12 |
13 | public AnalysisOptions(IEnumerable diagnostics = null)
14 | {
15 | this.Diagnostics = (diagnostics ?? DiagnosticSet.All).ToImmutableHashSet();
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/AltNugetConfig/ProjectFolder/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/NoopTransformationSet.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Microsoft.Extensions.Logging;
4 | using Project2015To2017.Transforms;
5 |
6 | namespace Project2015To2017
7 | {
8 | public class NoopTransformationSet : ITransformationSet
9 | {
10 | public static readonly NoopTransformationSet Instance = new NoopTransformationSet();
11 |
12 | private NoopTransformationSet()
13 | {
14 | }
15 |
16 | public IReadOnlyCollection Transformations(
17 | ILogger logger,
18 | ConversionOptions conversionOptions)
19 | {
20 | return Array.Empty();
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/OtherTestProjects/containsTestSDK.testcsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Exe
6 | UnitTest
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Analysis/DiagnosticResult.cs:
--------------------------------------------------------------------------------
1 | using Project2015To2017.Definition;
2 |
3 | namespace Project2015To2017.Analysis
4 | {
5 | public sealed class DiagnosticResult : IDiagnosticResult
6 | {
7 | public string Code { get; internal set; }
8 | public string Message { get; internal set; }
9 | public Project Project { get; internal set; }
10 | public IDiagnosticLocation Location { get; internal set; }
11 |
12 | public DiagnosticResult()
13 | {
14 | }
15 |
16 | public DiagnosticResult(IDiagnosticResult result)
17 | {
18 | this.Code = result.Code;
19 | this.Message = result.Message;
20 | this.Project = result.Project;
21 | this.Location = result.Location;
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/Project2015To2017.Tests/SolutionReaderTests.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using Microsoft.Extensions.Logging;
3 | using Microsoft.VisualStudio.TestTools.UnitTesting;
4 | using Project2015To2017.Reading;
5 |
6 | namespace Project2015To2017.Tests
7 | {
8 | [TestClass]
9 | public class SolutionReaderTests
10 | {
11 | [TestMethod]
12 | public void ReadsSolutionFileSuccessfully()
13 | {
14 | var testFile = @"TestFiles/Solutions/sampleSolution.testsln";
15 |
16 | var logger = new DummyLogger {MinimumLogLevel = LogLevel.Warning};
17 |
18 | SolutionReader.Instance.Read(testFile, logger);
19 |
20 | //Should be no warnings or errors
21 | Assert.IsFalse(logger.LogEntries.Any());
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Caching/DefaultProjectCache.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Concurrent;
2 | using Project2015To2017.Definition;
3 |
4 | namespace Project2015To2017.Caching
5 | {
6 | public sealed class DefaultProjectCache : IProjectCache
7 | {
8 | private readonly ConcurrentDictionary dictionary = new ConcurrentDictionary();
9 |
10 | public void Add(string key, Project project)
11 | {
12 | this.dictionary.AddOrUpdate(key, project, (s, p) => p);
13 | }
14 |
15 | public void Purge()
16 | {
17 | this.dictionary.Clear();
18 | }
19 |
20 | public bool TryGetValue(string key, out Project project)
21 | {
22 | return this.dictionary.TryGetValue(key, out project);
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/Analysis/IReporter.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Project2015To2017.Definition;
3 |
4 | namespace Project2015To2017.Analysis
5 | {
6 | public interface IReporter where TOptions : IReporterOptions
7 | {
8 | ///
9 | /// Default options for any project
10 | ///
11 | TOptions DefaultOptions { get; }
12 |
13 | ///
14 | /// Do the actual issue reporting
15 | ///
16 | /// Diagnostics to report
17 | /// Options for the reporter
18 | void Report(IReadOnlyList results, TOptions reporterOptions);
19 |
20 | TOptions CreateOptionsForProject(Project project);
21 | }
22 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/NoopLogger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.Extensions.Logging;
3 |
4 | namespace Project2015To2017
5 | {
6 | public sealed class NoopLogger : ILogger, IDisposable
7 | {
8 | public static ILogger Instance = new NoopLogger();
9 |
10 | private NoopLogger()
11 | {
12 |
13 | }
14 |
15 | public IDisposable BeginScope(TState state)
16 | {
17 | return this;
18 | }
19 |
20 | public void Dispose()
21 | {
22 | throw new NotImplementedException();
23 | }
24 |
25 | public bool IsEnabled(LogLevel logLevel)
26 | {
27 | return false;
28 | }
29 |
30 | public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter)
31 | {
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/Transforms/BasicReadTransformationSet.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Microsoft.Extensions.Logging;
3 |
4 | namespace Project2015To2017.Transforms
5 | {
6 | public class BasicReadTransformationSet : ITransformationSet
7 | {
8 | public static readonly BasicReadTransformationSet Instance = new BasicReadTransformationSet();
9 |
10 | private BasicReadTransformationSet()
11 | {
12 | }
13 |
14 | public IReadOnlyCollection Transformations(
15 | ILogger logger,
16 | ConversionOptions conversionOptions)
17 | {
18 | return new ITransformation[]
19 | {
20 | new NuGetPackageTransformation(),
21 | new AssemblyAttributeTransformation(logger, conversionOptions.KeepAssemblyInfo),
22 | };
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/Project2015To2017.Tests/ProjectReferenceReadTest.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Linq;
3 | using Microsoft.VisualStudio.TestTools.UnitTesting;
4 | using Project2015To2017.Reading;
5 |
6 | namespace Project2015To2017.Tests
7 | {
8 | [TestClass]
9 | public class ProjectReferenceReadTest
10 | {
11 | [TestMethod]
12 | public void TransformsProjectReferences()
13 | {
14 | var project = new ProjectReader().Read(Path.Combine("TestFiles", "OtherTestProjects", "net46console.testcsproj"));
15 |
16 | Assert.AreEqual(2, project.ProjectReferences.Count);
17 | Assert.IsTrue(project.ProjectReferences.Any(x => x.Include == @"..\SomeOtherProject\SomeOtherProject.csproj" && x.Aliases == "global,one"));
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/nuSpecs/noNamespace.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $id$
5 | $version$
6 | title
7 | some author
8 | true
9 | a nice description.
10 | That description, shortened.
11 | some tags API
12 | $copyright$
13 | someurl
14 |
15 | Some long
16 | text
17 | with newlines
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Definition/ProjectReference.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Xml.Linq;
4 |
5 | namespace Project2015To2017.Definition
6 | {
7 | public sealed class ProjectReference : IReference
8 | {
9 | public string ProjectName { get; set; }
10 | public Guid ProjectGuid { get; set; }
11 | public string ProjectTypeGuid { get; set; }
12 |
13 | public string Include { get; set; }
14 | public string Aliases { get; set; }
15 | public bool EmbedInteropTypes { get; set; }
16 |
17 | public FileInfo ProjectFile { get; set; }
18 | public XElement DefinitionElement { get; set; }
19 |
20 | ///
21 | /// Extension of the project file, if any
22 | ///
23 | public string Extension => ProjectFile?.Extension ?? Path.GetExtension(Include);
24 | }
25 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/Reading/Conditionals/OperandExpressionNode.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 |
4 | namespace Project2015To2017.Reading.Conditionals
5 | {
6 | ///
7 | /// Base class for all nodes that are operands (are leaves in the parse tree)
8 | ///
9 | internal abstract class OperandExpressionNode : GenericExpressionNode
10 | {
11 | #region REMOVE_COMPAT_WARNING
12 |
13 | internal override bool DetectAnd()
14 | {
15 | return false;
16 | }
17 |
18 | internal override bool DetectOr()
19 | {
20 | return false;
21 | }
22 | #endregion
23 |
24 | }
25 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/Reading/ConditionEvaluator.ModernCache.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.Caching;
2 |
3 | namespace Project2015To2017.Reading
4 | {
5 | ///
6 | ///
7 | ///
8 | internal static partial class ConditionEvaluator
9 | {
10 | private static bool TryGetCachedOrCreateState(string condition, out ConditionEvaluationStateImpl state)
11 | {
12 | state = MemoryCache.Default[condition] as ConditionEvaluationStateImpl;
13 |
14 | if (state != null)
15 | {
16 | return true;
17 | }
18 |
19 | state = new ConditionEvaluationStateImpl(condition);
20 |
21 | MemoryCache.Default.Add(condition, state, ObjectCache.InfiniteAbsoluteExpiration);
22 |
23 | return false;
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/Reading/ConditionEvaluator.LegacyCache.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Project2015To2017.Reading
4 | {
5 | ///
6 | ///
7 | ///
8 | internal static partial class ConditionEvaluator
9 | {
10 | private static readonly Dictionary Cache = new Dictionary();
11 |
12 | private static bool TryGetCachedOrCreateState(string condition, out ConditionEvaluationStateImpl state)
13 | {
14 | if (Cache.TryGetValue(condition, out state))
15 | {
16 | return true;
17 | }
18 |
19 | state = new ConditionEvaluationStateImpl(condition);
20 |
21 | Cache.Add(condition, state);
22 |
23 | return false;
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/Project2015To2017.Tests/AssemblyFilterDefaultTransformationTest.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using Microsoft.VisualStudio.TestTools.UnitTesting;
4 | using Project2015To2017.Definition;
5 | using Project2015To2017.Migrate2017.Transforms;
6 |
7 | namespace Project2015To2017.Tests
8 | {
9 | [TestClass]
10 | public class AssemblyFilterDefaultTransformationTest
11 | {
12 | [TestMethod]
13 | public void PreventEmptyAssemblyReferences()
14 | {
15 | var project = new Project
16 | {
17 | AssemblyReferences = new List
18 | {
19 | new AssemblyReference
20 | {
21 | Include = "System"
22 | }
23 | },
24 | FilePath = new FileInfo("test.cs")
25 | };
26 |
27 | new AssemblyFilterDefaultTransformation().Transform(project);
28 |
29 | Assert.AreEqual(0, project.AssemblyReferences.Count);
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/Project2015To2017.Console/Project2015To2017.Console.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net461;netcoreapp2.1
5 | netcoreapp2.1
6 | True
7 |
8 | Project2015To2017.Cli
9 | Project2015To2017.Cli
10 | Exe
11 | csproj-to-2017
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Definition/PackageConfiguration.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using System.Xml.Linq;
4 |
5 | namespace Project2015To2017.Definition
6 | {
7 | public sealed class PackageConfiguration
8 | {
9 | public string Id { get; set; }
10 | public string Version { get; set; }
11 | public string Authors { get; set; }
12 | public string Description { get; set; }
13 | public string Copyright { get; set; }
14 | public string LicenseUrl { get; set; }
15 | public string ProjectUrl { get; set; }
16 | public string IconUrl { get; set; }
17 | public string Tags { get; set; }
18 | public string ReleaseNotes { get; set; }
19 | public bool RequiresLicenseAcceptance { get; set; }
20 | public IList Dependencies { get; set; }
21 | public FileInfo NuspecFile { get; set; }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/DummyLogger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Microsoft.Extensions.Logging;
4 |
5 | namespace Project2015To2017.Tests
6 | {
7 | internal class DummyLogger : ILogger
8 | {
9 | private readonly List logs = new List();
10 | public IReadOnlyList LogEntries => this.logs;
11 |
12 | public LogLevel MinimumLogLevel { get; set; } = LogLevel.Error;
13 |
14 | public IDisposable BeginScope(TState state)
15 | {
16 | throw new NotImplementedException();
17 | }
18 |
19 | public bool IsEnabled(LogLevel logLevel)
20 | {
21 | return logLevel >= MinimumLogLevel;
22 | }
23 |
24 | public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter)
25 | {
26 | if (IsEnabled(logLevel))
27 | {
28 | this.logs.Add(formatter(state, exception));
29 | }
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/AssemblyInfoReadTest.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using Microsoft.VisualStudio.TestTools.UnitTesting;
3 | using Project2015To2017.Reading;
4 |
5 | namespace Project2015To2017.Tests
6 | {
7 | [TestClass]
8 | public class AssemblyInfoReadTest
9 | {
10 | [TestMethod]
11 | public void FindsAttributes()
12 | {
13 | var project = new ProjectReader().Read(Path.Combine("TestFiles", "OtherTestProjects", "net46console.testcsproj"));
14 |
15 | Assert.IsNotNull(project.AssemblyAttributes.Company);
16 | Assert.IsNotNull(project.AssemblyAttributes.Copyright);
17 | Assert.IsNotNull(project.AssemblyAttributes.InformationalVersion);
18 | Assert.IsNotNull(project.AssemblyAttributes.Product);
19 | Assert.AreEqual("Title", project.AssemblyAttributes.Title);
20 | Assert.IsNotNull(project.AssemblyAttributes.Version);
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Project2015To2017/Project2015To2017.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard1.3;netstandard2.0
5 | Project2015To2017
6 | $(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Project2015To2017.Core.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard1.3;netstandard2.0
5 | Project2015To2017
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Project2015To2017.Migrate2017.Library/Diagnostics/W034ReferenceAliasesDiagnostic.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using Project2015To2017.Analysis;
4 | using Project2015To2017.Definition;
5 |
6 | namespace Project2015To2017.Migrate2017.Diagnostics
7 | {
8 | public sealed class W034ReferenceAliasesDiagnostic : DiagnosticBase
9 | {
10 | public W034ReferenceAliasesDiagnostic() : base(34)
11 | {
12 | }
13 |
14 | public override IReadOnlyList Analyze(Project project)
15 | {
16 | var list = new List();
17 |
18 | foreach (var reference in project.ProjectReferences.Where(x => !string.IsNullOrEmpty(x.Aliases)))
19 | {
20 | list.Add(
21 | CreateDiagnosticResult(project,
22 | $"ProjectReference ['{reference.Include}'] aliases are a feature of low support. Consider dropping their usage.",
23 | project.FilePath)
24 | .LoadLocationFromElement(reference.DefinitionElement));
25 | }
26 |
27 | return list;
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/ChainTransformationSet.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Microsoft.Extensions.Logging;
4 | using Project2015To2017.Transforms;
5 |
6 | namespace Project2015To2017
7 | {
8 | public class ChainTransformationSet : ITransformationSet
9 | {
10 | private readonly IReadOnlyCollection sets;
11 |
12 | public ChainTransformationSet(IReadOnlyCollection sets)
13 | {
14 | this.sets = sets ?? throw new ArgumentNullException(nameof(sets));
15 | }
16 |
17 | public ChainTransformationSet(params ITransformationSet[] sets)
18 | : this((IReadOnlyCollection) sets)
19 | {
20 | }
21 |
22 | public IReadOnlyCollection Transformations(ILogger logger, ConversionOptions conversionOptions)
23 | {
24 | var res = new List();
25 | foreach (var set in sets)
26 | {
27 | res.AddRange(set.Transformations(logger, conversionOptions));
28 | }
29 |
30 | return res;
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/Reading/Conditionals/IConditionEvaluationState.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Project2015To2017.Reading.Conditionals
4 | {
5 | internal interface IConditionEvaluationState
6 | {
7 | ///
8 | /// Table of conditioned properties and their values.
9 | /// Used to populate configuration lists in some project systems.
10 | /// If this is null, as it is for command line builds, conditioned properties
11 | /// are not recorded.
12 | ///
13 | Dictionary> ConditionedPropertiesInProject { get; }
14 |
15 | ///
16 | /// May return null if the expression would expand to non-empty and it broke out early.
17 | /// Otherwise, returns the correctly expanded expression.
18 | ///
19 | string ExpandIntoStringBreakEarly(string expression);
20 |
21 | ///
22 | /// Expands the specified expression into a string.
23 | ///
24 | string ExpandIntoString(string expression);
25 | }
26 | }
--------------------------------------------------------------------------------
/Project2015To2017.Migrate2017.Library/Diagnostics/W030LegacyDebugTypesDiagnostic.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Project2015To2017.Analysis;
3 | using Project2015To2017.Definition;
4 |
5 | namespace Project2015To2017.Migrate2017.Diagnostics
6 | {
7 | public sealed class W030LegacyDebugTypesDiagnostic : DiagnosticBase
8 | {
9 | public W030LegacyDebugTypesDiagnostic() : base(30)
10 | {
11 | }
12 |
13 | public override IReadOnlyList Analyze(Project project)
14 | {
15 | var list = new List();
16 |
17 | foreach (var x in project.ProjectDocument.Descendants(project.XmlNamespace + "DebugType"))
18 | {
19 | if (x.Value.Equals("portable", Extensions.BestAvailableStringIgnoreCaseComparison))
20 | continue;
21 |
22 | list.Add(
23 | CreateDiagnosticResult(project,
24 | $"Consider migrating to 'portable' debug type, cross-platform alternative to legacy Windows PDBs.",
25 | project.FilePath)
26 | .LoadLocationFromElement(x));
27 | }
28 |
29 | return list;
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Hans van Bakel
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Analysis/DiagnosticSet.cs:
--------------------------------------------------------------------------------
1 | using Project2015To2017.Analysis.Diagnostics;
2 | using System.Collections.Generic;
3 |
4 | namespace Project2015To2017.Analysis
5 | {
6 | public sealed class DiagnosticSet : HashSet
7 | {
8 | public static readonly IDiagnostic W001 = new W001IllegalProjectTypeDiagnostic();
9 | public static readonly IDiagnostic W002 = new W002MissingProjectFileDiagnostic();
10 | public static readonly IDiagnostic W010 = new W010ConfigurationsMismatchDiagnostic();
11 | public static readonly IDiagnostic W011 = new W011UnsupportedConditionalDiagnostic();
12 |
13 | public static readonly DiagnosticSet NoneDefault = new DiagnosticSet();
14 |
15 | public static readonly DiagnosticSet System = new DiagnosticSet
16 | {
17 | W001,
18 | W002,
19 | };
20 |
21 | public static readonly DiagnosticSet GenericProjectIssues = new DiagnosticSet
22 | {
23 | W010,
24 | W011,
25 | };
26 |
27 | public static readonly DiagnosticSet All = new DiagnosticSet();
28 |
29 | static DiagnosticSet()
30 | {
31 | All.UnionWith(System);
32 | All.UnionWith(GenericProjectIssues);
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/Analysis/AnalysisExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Xml;
3 | using System.Xml.Linq;
4 |
5 | namespace Project2015To2017.Analysis
6 | {
7 | public static class AnalysisExtensions
8 | {
9 | public static string GetSourcePath(this IDiagnosticLocation self)
10 | {
11 | if (self == null) throw new ArgumentNullException(nameof(self));
12 |
13 | return self.SourcePath ?? self.Source?.FullName;
14 | }
15 |
16 | public static IDiagnosticResult LoadLocationFromElement(this IDiagnosticResult self, XElement element)
17 | {
18 | if (self == null) throw new ArgumentNullException(nameof(self));
19 | if (element == null) throw new ArgumentNullException(nameof(element));
20 |
21 | if (self.Location.SourceLine != uint.MaxValue)
22 | {
23 | return self;
24 | }
25 |
26 | if (element is IXmlLineInfo elementOnLine && elementOnLine.HasLineInfo())
27 | {
28 | return new DiagnosticResult(self)
29 | {
30 | Location = new DiagnosticLocation(self.Location)
31 | {
32 | SourceLine = (uint) elementOnLine.LineNumber
33 | }
34 | };
35 | }
36 |
37 | return self;
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/Analysis/Diagnostics/W002MissingProjectFileDiagnostic.cs:
--------------------------------------------------------------------------------
1 | using Project2015To2017.Definition;
2 | using System;
3 | using System.Collections.Generic;
4 |
5 | namespace Project2015To2017.Analysis.Diagnostics
6 | {
7 | public sealed class W002MissingProjectFileDiagnostic : DiagnosticBase
8 | {
9 | public override bool SkipForLegacyProject => true;
10 | public override bool SkipForModernProject => true;
11 | public override IReadOnlyList Analyze(Project project) =>
12 | throw new InvalidOperationException("W002 is not an executable diagnostic");
13 |
14 | public static IReadOnlyList CreateResult(ProjectReference @ref, Solution solution = null)
15 | {
16 | return new[]
17 | {
18 | new DiagnosticResult
19 | {
20 | Code = "W002",
21 | Message =
22 | $"Referenced project file '{@ref.Include}' was not found at '{@ref.ProjectFile.FullName}'.",
23 | Location = new DiagnosticLocation
24 | {
25 | Source = solution?.FilePath
26 | }
27 | }
28 | };
29 | }
30 |
31 | public W002MissingProjectFileDiagnostic() : base(2)
32 | {
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Project2015To2017.Migrate2017.Library/Transforms/AssemblyFilterPackageReferencesTransformation.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using Project2015To2017.Definition;
4 | using Project2015To2017.Transforms;
5 |
6 | namespace Project2015To2017.Migrate2017.Transforms
7 | {
8 | public sealed class AssemblyFilterPackageReferencesTransformation : ILegacyOnlyProjectTransformation
9 | {
10 | public void Transform(Project definition)
11 | {
12 | var packageReferences =
13 | definition.PackageReferences ?? new List();
14 |
15 | var packageIds = packageReferences
16 | .Select(x => x.Id)
17 | .ToList();
18 |
19 | var (assemblyReferences, removeQueue) =
20 | definition
21 | .AssemblyReferences
22 | //We don't need to keep any references to package files as these are
23 | //now generated dynamically at build time
24 | .Split(assemblyReference => !packageIds.Contains(assemblyReference.Include));
25 |
26 | foreach (var assemblyReference in removeQueue)
27 | {
28 | assemblyReference.DefinitionElement?.Remove();
29 | }
30 |
31 | definition.AssemblyReferences = assemblyReferences;
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 7.3
5 | hvanbakel et. al.
6 | Project2015To2017
7 | https://github.com/hvanbakel/CsprojToVs2017
8 | https://github.com/hvanbakel/CsprojToVs2017/blob/master/LICENSE
9 | https://github.com/hvanbakel/CsprojToVs2017
10 | Copyright Hans van Bakel
11 | Tool for converting a MSBuild project file to VS2017 format and beyond.
12 | dotnet csproj fsproj vbproj msbuild conversion vs2015 vs14 vs15 vs2017
13 | 3.0.4
14 | 3.0.0.0
15 |
16 |
17 | true
18 | true
19 | $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Transforms/EmptyGroupRemoveTransformation.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Xml.Linq;
4 | using Microsoft.Extensions.Logging;
5 | using Project2015To2017.Definition;
6 |
7 | namespace Project2015To2017.Transforms
8 | {
9 | public class EmptyGroupRemoveTransformation
10 | : ITransformationWithTargetMoment, ITransformationWithDependencies
11 | {
12 | public void Transform(Project definition)
13 | {
14 | definition.PropertyGroups = FilterNonEmpty(definition.PropertyGroups);
15 | definition.ItemGroups = FilterNonEmpty(definition.ItemGroups).ToList();
16 | }
17 |
18 | private static IReadOnlyList FilterNonEmpty(IEnumerable groups)
19 | {
20 | var (keep, remove) = groups
21 | .Split(x => x.HasElements
22 | || (x.HasAttributes && x.Attributes().Any(a => a.Name.LocalName != "Condition")));
23 | foreach (var element in remove)
24 | {
25 | element.Remove();
26 | }
27 |
28 | return keep;
29 | }
30 |
31 | public TargetTransformationExecutionMoment ExecutionMoment =>
32 | TargetTransformationExecutionMoment.Late;
33 |
34 | public IReadOnlyCollection DependOn => new[]
35 | {
36 | typeof(PrimaryProjectPropertiesUpdateTransformation).Name,
37 | };
38 | }
39 | }
--------------------------------------------------------------------------------
/Project2015To2017.Migrate2017.Library/Diagnostics/W031MSBuildSdkVersionSpecificationDiagnostic.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Project2015To2017.Analysis;
4 | using Project2015To2017.Definition;
5 |
6 | namespace Project2015To2017.Migrate2017.Diagnostics
7 | {
8 | public sealed class W031MSBuildSdkVersionSpecificationDiagnostic : DiagnosticBase
9 | {
10 | public W031MSBuildSdkVersionSpecificationDiagnostic() : base(31)
11 | {
12 | }
13 |
14 | public override IReadOnlyList Analyze(Project project)
15 | {
16 | var root = project.ProjectDocument.Root ?? throw new ArgumentNullException(nameof(project));
17 | var sdk = root.Attribute("Sdk")?.Value?.Trim();
18 |
19 | if (string.IsNullOrEmpty(sdk))
20 | {
21 | return Array.Empty();
22 | }
23 |
24 | if (!sdk.Contains("/"))
25 | {
26 | return Array.Empty();
27 | }
28 |
29 | return new[]
30 | {
31 | CreateDiagnosticResult(project,
32 | $"You have MSBuild SDK version specified in your project file ({sdk}). This is considered a bad practice. A recommended approach would be using global.json file for centralized SDK version management.",
33 | project.FilePath)
34 | .LoadLocationFromElement(root)
35 | };
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/Analysis/ReporterBase.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Project2015To2017.Definition;
3 |
4 | namespace Project2015To2017.Analysis
5 | {
6 | public abstract class ReporterBase : IReporter where TOptions : IReporterOptions
7 | {
8 | public abstract TOptions DefaultOptions { get; }
9 |
10 | protected abstract void Report(IDiagnosticResult result, TOptions reporterOptions);
11 |
12 | ///
13 | public void Report(IReadOnlyList results, TOptions reporterOptions)
14 | {
15 | if (results == null || results.Count == 0)
16 | {
17 | return;
18 | }
19 |
20 | foreach (var result in results)
21 | {
22 | var targetResult = result;
23 | if (result.Location?.Source != null)
24 | {
25 | var sourcePath = result.Project.TryFindBestRootDirectory()
26 | ?.GetRelativePathTo(result.Location.Source);
27 | targetResult = new DiagnosticResult(result)
28 | {
29 | Location = new DiagnosticLocation(result.Location)
30 | {
31 | SourcePath = sourcePath
32 | }
33 | };
34 | }
35 |
36 | Report(targetResult, reporterOptions);
37 | }
38 | }
39 |
40 | public virtual TOptions CreateOptionsForProject(Project project)
41 | {
42 | return this.DefaultOptions;
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/Transforms/TargetFrameworkReplaceTransformation.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Project2015To2017.Definition;
3 |
4 | namespace Project2015To2017.Transforms
5 | {
6 | public sealed class TargetFrameworkReplaceTransformation : ITransformationWithTargetMoment
7 | {
8 | public TargetFrameworkReplaceTransformation(
9 | IReadOnlyList targetFrameworks,
10 | bool appendTargetFrameworkToOutputPath = true)
11 | {
12 | this.TargetFrameworks = targetFrameworks;
13 | this.AppendTargetFrameworkToOutputPath = appendTargetFrameworkToOutputPath;
14 | }
15 |
16 | public void Transform(Project definition)
17 | {
18 | if (null == definition)
19 | {
20 | return;
21 | }
22 |
23 | if (this.TargetFrameworks != null && this.TargetFrameworks.Count > 0)
24 | {
25 | definition.TargetFrameworks.Clear();
26 | foreach (var targetFramework in this.TargetFrameworks)
27 | {
28 | definition.TargetFrameworks.Add(targetFramework);
29 | }
30 | }
31 |
32 | definition.AppendTargetFrameworkToOutputPath = this.AppendTargetFrameworkToOutputPath;
33 | }
34 |
35 | public IReadOnlyList TargetFrameworks { get; }
36 | public bool AppendTargetFrameworkToOutputPath { get; }
37 |
38 | public TargetTransformationExecutionMoment ExecutionMoment =>
39 | TargetTransformationExecutionMoment.Early;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Project2015To2017.Migrate2017.Library/Diagnostics/W032OldLanguageVersionDiagnostic.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Project2015To2017.Analysis;
3 | using Project2015To2017.Definition;
4 |
5 | namespace Project2015To2017.Migrate2017.Diagnostics
6 | {
7 | public sealed class W032OldLanguageVersionDiagnostic : DiagnosticBase
8 | {
9 | public W032OldLanguageVersionDiagnostic() : base(32)
10 | {
11 | }
12 |
13 | public override IReadOnlyList Analyze(Project project)
14 | {
15 | var list = new List();
16 |
17 | foreach (var x in project.ProjectDocument.Descendants(project.XmlNamespace + "LangVersion"))
18 | {
19 | // last 2 versions + default
20 | var version = x.Value;
21 | if (version.Equals("7.2", Extensions.BestAvailableStringIgnoreCaseComparison)) continue;
22 | if (version.Equals("7.3", Extensions.BestAvailableStringIgnoreCaseComparison)) continue;
23 | if (version.Equals("latest", Extensions.BestAvailableStringIgnoreCaseComparison)) continue;
24 | if (version.Equals("default", Extensions.BestAvailableStringIgnoreCaseComparison)) continue;
25 |
26 | list.Add(
27 | CreateDiagnosticResult(project,
28 | $"Consider upgrading language version to the latest ({version}).",
29 | project.FilePath)
30 | .LoadLocationFromElement(x));
31 | }
32 |
33 | return list;
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/Definition/Solution.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using NuGet.Configuration;
4 |
5 | namespace Project2015To2017.Definition
6 | {
7 | public sealed class Solution
8 | {
9 | public FileInfo FilePath { get; set; }
10 | public IReadOnlyList ProjectPaths { get; set; }
11 | public DirectoryInfo SolutionFolder => this.FilePath.Directory;
12 | public IReadOnlyList UnsupportedProjectPaths { get; set; }
13 |
14 | ///
15 | /// The directory where nuget stores its extracted packages for the solution.
16 | /// In general this is the 'packages' folder within the solution oflder, but
17 | /// it can be overridden, which is accounted for here.
18 | ///
19 | public DirectoryInfo NuGetPackagesPath
20 | {
21 | get
22 | {
23 | var solutionFolder = this.SolutionFolder.FullName;
24 |
25 | var nuGetSettings = Settings.LoadDefaultSettings(solutionFolder);
26 | var repositoryPathSetting = SettingsUtility.GetRepositoryPath(nuGetSettings);
27 |
28 | //return the explicitly set path, or if there isn't one, then assume the 'packages' folder is in the solution folder
29 | var path = repositoryPathSetting ?? Path.GetFullPath(Path.Combine(solutionFolder, "packages"));
30 |
31 | return new DirectoryInfo(Extensions.MaybeAdjustFilePath(path, solutionFolder));
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Project2015To2017.Migrate2017.Library/Diagnostics/W033ObsoletePortableClassLibrariesDiagnostic.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Collections.Immutable;
3 | using System.Linq;
4 | using Project2015To2017.Analysis;
5 | using Project2015To2017.Definition;
6 |
7 | namespace Project2015To2017.Migrate2017.Diagnostics
8 | {
9 | public sealed class W033ObsoletePortableClassLibrariesDiagnostic : DiagnosticBase
10 | {
11 | public W033ObsoletePortableClassLibrariesDiagnostic() : base(33)
12 | {
13 | }
14 |
15 | public override IReadOnlyList Analyze(Project project)
16 | {
17 | var comparison = Extensions.BestAvailableStringIgnoreCaseComparison;
18 | var pcls = project.TargetFrameworks.Where(x => x.StartsWith("portable-", comparison)).ToImmutableHashSet();
19 |
20 | // not all profiles can be mapped to .NET Standard (thanks to Silverlight & Framework 4.0)
21 | // we could skip emitting diagnostics in such cases, but we don't
22 | // instead we suggest dropping such old targets (WinXP can still be covered by net40 in TargetFrameworks)
23 |
24 | var list = new List(pcls.Count);
25 |
26 | foreach (var pcl in pcls)
27 | {
28 | list.Add(
29 | CreateDiagnosticResult(project,
30 | $"PCL profiles are obsolete. Consider migrating to .NET Standard.",
31 | project.FilePath));
32 | }
33 |
34 | return list;
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/Reading/Conditionals/EqualExpressionNode.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 |
4 | using System;
5 | using System.Diagnostics;
6 |
7 | namespace Project2015To2017.Reading.Conditionals
8 | {
9 | ///
10 | /// Compares for equality
11 | ///
12 | [DebuggerDisplay("{DebuggerDisplay,nq}")]
13 | internal sealed class EqualExpressionNode : MultipleComparisonNode
14 | {
15 | ///
16 | /// Compare numbers
17 | ///
18 | protected override bool Compare(double left, double right)
19 | {
20 | return left == right;
21 | }
22 |
23 | ///
24 | /// Compare booleans
25 | ///
26 | protected override bool Compare(bool left, bool right)
27 | {
28 | return left == right;
29 | }
30 |
31 | ///
32 | /// Compare strings
33 | ///
34 | protected override bool Compare(string left, string right)
35 | {
36 | return String.Equals(left, right, StringComparison.OrdinalIgnoreCase);
37 | }
38 |
39 | internal override string DebuggerDisplay => $"(== {this.LeftChild.DebuggerDisplay} {this.RightChild.DebuggerDisplay})";
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Reading/Conditionals/NotEqualExpressionNode.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 |
4 | using System;
5 | using System.Diagnostics;
6 |
7 | namespace Project2015To2017.Reading.Conditionals
8 | {
9 | ///
10 | /// Compares for inequality
11 | ///
12 | [DebuggerDisplay("{DebuggerDisplay,nq}")]
13 | internal sealed class NotEqualExpressionNode : MultipleComparisonNode
14 | {
15 | ///
16 | /// Compare numbers
17 | ///
18 | protected override bool Compare(double left, double right)
19 | {
20 | return left != right;
21 | }
22 |
23 | ///
24 | /// Compare booleans
25 | ///
26 | protected override bool Compare(bool left, bool right)
27 | {
28 | return left != right;
29 | }
30 |
31 | ///
32 | /// Compare strings
33 | ///
34 | protected override bool Compare(string left, string right)
35 | {
36 | return !String.Equals(left, right, StringComparison.OrdinalIgnoreCase);
37 | }
38 |
39 | internal override string DebuggerDisplay => $"(!= {this.LeftChild.DebuggerDisplay} {this.RightChild.DebuggerDisplay})";
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Project2015To2017.Migrate2017.Library/Transforms/AssemblyFilterDefaultTransformation.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Immutable;
2 | using System.Linq;
3 | using Project2015To2017.Definition;
4 | using Project2015To2017.Transforms;
5 |
6 | namespace Project2015To2017.Migrate2017.Transforms
7 | {
8 | public sealed class AssemblyFilterDefaultTransformation : ILegacyOnlyProjectTransformation
9 | {
10 | public void Transform(Project definition)
11 | {
12 | if (definition.AssemblyReferences == null)
13 | {
14 | definition.AssemblyReferences = ImmutableArray.Empty;
15 | return;
16 | }
17 |
18 | var (assemblyReferences, removeQueue) = definition.AssemblyReferences
19 | .Split(IsNonDefaultIncludedAssemblyReference);
20 |
21 | foreach (var assemblyReference in removeQueue)
22 | {
23 | assemblyReference.DefinitionElement?.Remove();
24 | }
25 |
26 | definition.AssemblyReferences = assemblyReferences;
27 | }
28 |
29 |
30 | private static bool IsNonDefaultIncludedAssemblyReference(AssemblyReference assemblyReference)
31 | {
32 | var name = assemblyReference.Include;
33 | return !new[]
34 | {
35 | "System",
36 | "System.Core",
37 | "System.Data",
38 | "System.Drawing",
39 | "System.IO.Compression.FileSystem",
40 | "System.Numerics",
41 | "System.Runtime.Serialization",
42 | "System.Xml",
43 | "System.Xml.Linq"
44 | }.Contains(name);
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/Reading/Conditionals/CharacterUtilities.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 |
4 | namespace Project2015To2017.Reading.Conditionals
5 | {
6 | internal static class CharacterUtilities
7 | {
8 | static internal bool IsNumberStart(char candidate)
9 | {
10 | return (candidate == '+' || candidate == '-' || candidate == '.' || char.IsDigit(candidate));
11 | }
12 |
13 | static internal bool IsSimpleStringStart(char candidate)
14 | {
15 | return (candidate == '_' || char.IsLetter(candidate));
16 | }
17 |
18 | static internal bool IsSimpleStringChar(char candidate)
19 | {
20 | return (IsSimpleStringStart(candidate) || char.IsDigit(candidate));
21 | }
22 |
23 | static internal bool IsHexAlphabetic(char candidate)
24 | {
25 | return (candidate == 'a' || candidate == 'b' || candidate == 'c' || candidate == 'd' || candidate == 'e' || candidate == 'f' ||
26 | candidate == 'A' || candidate == 'B' || candidate == 'C' || candidate == 'D' || candidate == 'E' || candidate == 'F');
27 | }
28 |
29 | static internal bool IsHexDigit(char candidate)
30 | {
31 | return (char.IsDigit(candidate) || IsHexAlphabetic(candidate));
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Project2015To2017.Migrate2017.Tool/Project2015To2017.Migrate2017.Tool.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net461;netcoreapp2.1
5 | netcoreapp2.1
6 | True
7 |
8 | dotnet-migrate-2017
9 | Project2015To2017.Migrate2017.Tool
10 | Project2015To2017.Migrate2017.Tool
11 | Exe
12 |
13 | $(RestoreAdditionalProjectSources);
14 | https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Analysis/Diagnostics/W011UnsupportedConditionalDiagnostic.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using Project2015To2017.Definition;
4 | using Project2015To2017.Reading;
5 | using Project2015To2017.Reading.Conditionals;
6 |
7 | namespace Project2015To2017.Analysis.Diagnostics
8 | {
9 | public sealed class W011UnsupportedConditionalDiagnostic : DiagnosticBase
10 | {
11 | public W011UnsupportedConditionalDiagnostic() : base(11)
12 | {
13 | }
14 |
15 | public override IReadOnlyList Analyze(Project project)
16 | {
17 | var list = new List();
18 | foreach (var x in project.ProjectDocument.Descendants())
19 | {
20 | var condition = x.Attribute("Condition");
21 | if (condition == null)
22 | {
23 | continue;
24 | }
25 |
26 | var conditionState = ConditionEvaluator.GetConditionState(condition.Value);
27 |
28 | var pairs = conditionState.UnsupportedNodes
29 | .Select(n => n.GetType().Name.Replace("ExpressionNode", ""))
30 | .GroupBy(n => n)
31 | .Select(n => (n.Key, n.Count()));
32 |
33 | foreach (var (key, value) in pairs)
34 | {
35 | var countContext = value > 1 ? $"({value} occurrences)" : "";
36 | list.Add(CreateDiagnosticResult(project,
37 | $"Unsupported '{key}' expression in conditional {countContext}",
38 | project.FilePath)
39 | .LoadLocationFromElement(x));
40 | }
41 | }
42 |
43 | return list;
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/Project2015To2017.Tests/ImportsTargetsFilterPackageReferencesTransformationTest.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using Microsoft.VisualStudio.TestTools.UnitTesting;
3 | using Project2015To2017.Migrate2017.Transforms;
4 | using Project2015To2017.Reading;
5 |
6 | namespace Project2015To2017.Tests
7 | {
8 | [TestClass]
9 | public class ImportsTargetsFilterPackageReferencesTransformationTest
10 | {
11 | [TestMethod]
12 | public void DedupeImportsFromPackagesAlternativePackagesFolder()
13 | {
14 | var projFile = @"TestFiles\AltNugetConfig\ProjectFolder\net46console.testcsproj";
15 |
16 | var project = new ProjectReader().Read(projFile);
17 |
18 | var transformation = new ImportsTargetsFilterPackageReferencesTransformation();
19 |
20 | //Then attempt to clear any referencing the nuget packages folder
21 | transformation.Transform(project);
22 |
23 | var expectedRemaining = new []
24 | {
25 | @"",
26 | @""
27 | };
28 |
29 | var remainingImports = project.Imports
30 | .Select(x => x.ToString())
31 | .ToList();
32 |
33 | //The only ones left which point to another folder
34 | Assert.AreEqual(2, remainingImports.Count);
35 | CollectionAssert.AreEqual(expectedRemaining, remainingImports);
36 |
37 | Assert.IsFalse(project.Targets.Any());
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/Reading/Conditionals/OrExpressionNode.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 |
4 | using System.Diagnostics;
5 |
6 | namespace Project2015To2017.Reading.Conditionals
7 | {
8 | ///
9 | /// Performs logical OR on children
10 | /// Does not update conditioned properties table
11 | ///
12 | [DebuggerDisplay("{DebuggerDisplay,nq}")]
13 | internal sealed class OrExpressionNode : OperatorExpressionNode
14 | {
15 | ///
16 | /// Evaluate as boolean
17 | ///
18 | internal override bool BoolEvaluate(IConditionEvaluationState state)
19 | {
20 | if (this.LeftChild.BoolEvaluate(state))
21 | {
22 | // Short circuit
23 | return true;
24 | }
25 | else
26 | {
27 | return this.RightChild.BoolEvaluate(state);
28 | }
29 | }
30 |
31 | internal override string DebuggerDisplay => $"(or {this.LeftChild.DebuggerDisplay} {this.RightChild.DebuggerDisplay})";
32 |
33 | #region REMOVE_COMPAT_WARNING
34 | private bool _possibleOrCollision = true;
35 | internal override bool PossibleOrCollision
36 | {
37 | set { this._possibleOrCollision = value; }
38 | get { return this._possibleOrCollision; }
39 | }
40 | #endregion
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Reading/upstream.md:
--------------------------------------------------------------------------------
1 | # upstream links
2 | To support advanced and always precise Condition attribute parsing we use parts of MSBuild code licensed under MIT. This document is designated to assist in updating imported code or alike purposes.
3 |
4 | ## Code version
5 | The code was brought in on July 21st 2018. The latest commit ID for Conditionals\ directory was *f147a76*.
6 |
7 | ## ConditionEvaluator
8 | Has parts of [ConditionEvaluator](https://github.com/Microsoft/msbuild/blob/master/src/Build/Evaluation/ConditionEvaluator.cs) code in "MSBuild Conditional routine" region.
9 |
10 | Changes:
11 | * Some method doc changes (usage section was incorrect)
12 | * SinglePropertyRegex was wrapped in Lazy
13 | * IConditionEvaluationState was moved out to the outer scope and then to Conditionals\ directory
14 |
15 | ## Conditionals\ directory
16 | Most of it is based on [Microsoft.Build.Evaluation\Conditionals](https://github.com/Microsoft/msbuild/tree/master/src/Build/Evaluation/Conditionals), with some parts from [Microsoft.Build.Shared](https://github.com/Microsoft/msbuild/tree/master/src/Shared).
17 |
18 | Changes:
19 | * Removed some deprecated code for compat with old MSBuild expression parser (too many dependencies)
20 | * Removed many verify-guards so that if in doubt an exception will likely be thrown (we don't need user-oriented error reporting facilities if conditionals contain syntax errors)
21 | * Included some utility classes (CharacterUtilities, ConversionUtilities, ErrorUtilities)
22 | * Changed namespace to match new location
--------------------------------------------------------------------------------
/Project2015To2017.Core/Reading/Conditionals/AndExpressionNode.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 |
4 | using System.Diagnostics;
5 |
6 | namespace Project2015To2017.Reading.Conditionals
7 | {
8 | ///
9 | /// Performs logical AND on children
10 | /// Does not update conditioned properties table
11 | ///
12 | [DebuggerDisplay("{DebuggerDisplay,nq}")]
13 | internal sealed class AndExpressionNode : OperatorExpressionNode
14 | {
15 | ///
16 | /// Evaluate as boolean
17 | ///
18 | internal override bool BoolEvaluate(IConditionEvaluationState state)
19 | {
20 | if (!this.LeftChild.BoolEvaluate(state))
21 | {
22 | // Short circuit
23 | return false;
24 | }
25 | else
26 | {
27 | return this.RightChild.BoolEvaluate(state);
28 | }
29 | }
30 |
31 | internal override string DebuggerDisplay => $"(and {this.LeftChild.DebuggerDisplay} {this.RightChild.DebuggerDisplay})";
32 |
33 | #region REMOVE_COMPAT_WARNING
34 | private bool _possibleAndCollision = true;
35 | internal override bool PossibleAndCollision
36 | {
37 | set { this._possibleAndCollision = value; }
38 | get { return this._possibleAndCollision; }
39 | }
40 | #endregion
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Reading/ConditionEvaluationStateImpl.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Project2015To2017.Reading.Conditionals;
4 |
5 | namespace Project2015To2017.Reading
6 | {
7 | internal sealed class ConditionEvaluationStateImpl : IConditionEvaluationState
8 | {
9 | ///
10 | public Dictionary> ConditionedPropertiesInProject { get; } =
11 | new Dictionary>();
12 |
13 | public GenericExpressionNode Node { get; set; }
14 |
15 | public bool Evaluated { get; set; }
16 |
17 | public ICollection UnsupportedNodes { get; set; } =
18 | Array.Empty();
19 |
20 | public string Condition { get; }
21 |
22 | public ConditionEvaluationStateImpl(string condition)
23 | {
24 | this.Condition = condition ?? throw new ArgumentNullException(nameof(condition));
25 | }
26 |
27 | ///
28 | public string ExpandIntoStringBreakEarly(string expression)
29 | {
30 | return expression;
31 | }
32 |
33 | ///
34 | public string ExpandIntoString(string expression)
35 | {
36 | return expression;
37 | }
38 |
39 | public void Evaluate()
40 | {
41 | try
42 | {
43 | // it makes little sense for condition to be that short
44 | if (this.Condition.Length >= 2)
45 | {
46 | this.Node.Evaluate(this); // return value ignored
47 | }
48 | }
49 | catch (Exception)
50 | {
51 | // ignored
52 | }
53 | finally
54 | {
55 | this.Evaluated = true;
56 | }
57 | }
58 | }
59 | }
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/Solutions/ClassLibrary/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("ClassLibrary")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("ClassLibrary")]
13 | [assembly: AssemblyCopyright("Copyright © 2018")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("0efb1378-b556-4ac1-b4e7-676ed896d863")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/Project2015To2017.Migrate2017.Library/Vs15DiagnosticSet.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using Project2015To2017.Analysis;
5 | using Project2015To2017.Analysis.Diagnostics;
6 | using Project2015To2017.Migrate2017.Diagnostics;
7 | using static Project2015To2017.Analysis.DiagnosticSet;
8 |
9 | namespace Project2015To2017.Migrate2017
10 | {
11 | public static class Vs15DiagnosticSet
12 | {
13 | public static readonly IDiagnostic W020 = new W020MicrosoftCSharpDiagnostic();
14 | public static readonly IDiagnostic W021 = new W021SystemNuGetPackagesDiagnostic();
15 |
16 | public static readonly IDiagnostic W030 = new W030LegacyDebugTypesDiagnostic();
17 | public static readonly IDiagnostic W031 = new W031MSBuildSdkVersionSpecificationDiagnostic();
18 | public static readonly IDiagnostic W032 = new W032OldLanguageVersionDiagnostic();
19 | public static readonly IDiagnostic W033 = new W033ObsoletePortableClassLibrariesDiagnostic();
20 | public static readonly IDiagnostic W034 = new W034ReferenceAliasesDiagnostic();
21 |
22 | public static readonly DiagnosticSet ModernIssues = new DiagnosticSet
23 | {
24 | W020,
25 | W021,
26 | };
27 |
28 | public static readonly DiagnosticSet ModernizationTips = new DiagnosticSet
29 | {
30 | W030,
31 | W031,
32 | W032,
33 | W033,
34 | W034,
35 | };
36 |
37 | public static readonly DiagnosticSet All = new DiagnosticSet();
38 |
39 | static Vs15DiagnosticSet()
40 | {
41 | All.UnionWith(DiagnosticSet.All);
42 | All.UnionWith(ModernIssues);
43 | All.UnionWith(ModernizationTips);
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Analysis/LoggerReporter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 | using Microsoft.Extensions.Logging;
4 |
5 | namespace Project2015To2017.Analysis
6 | {
7 | public sealed class LoggerReporter : ReporterBase
8 | {
9 | private readonly ILogger logger;
10 |
11 | public LoggerReporter(ILogger logger)
12 | {
13 | this.logger = logger;
14 | }
15 |
16 | public override LoggerReporterOptions DefaultOptions => new LoggerReporterOptions();
17 |
18 | ///
19 | protected override void Report(IDiagnosticResult result, LoggerReporterOptions reporterOptions)
20 | {
21 | var consoleHeader = $"{result.Code}: ";
22 | string linePadding;
23 | {
24 | var pad = new StringBuilder(consoleHeader.Length, consoleHeader.Length);
25 | pad.Append(' ', consoleHeader.Length);
26 | linePadding = pad.ToString();
27 | }
28 | var message = result.Message.Trim();
29 |
30 | var sourcePath = result.Location.GetSourcePath();
31 | var sourceLine = result.Location.SourceLine;
32 | switch (sourcePath)
33 | {
34 | case string _ when sourceLine != uint.MaxValue:
35 | message = $"{sourcePath}:{sourceLine}: {message}";
36 | break;
37 | case string _ when sourceLine == uint.MaxValue:
38 | message = $"{sourcePath}: {message}";
39 | break;
40 | case null when sourceLine != uint.MaxValue:
41 | message = $"{sourceLine}: {message}";
42 | break;
43 | case null when sourceLine == uint.MaxValue:
44 | default:
45 | break;
46 | }
47 |
48 | this.logger.LogInformation(consoleHeader + Environment.NewLine + message);
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Reading/Conditionals/FunctionCallExpressionNode.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 |
4 | using System;
5 | using System.Collections.Generic;
6 |
7 | namespace Project2015To2017.Reading.Conditionals
8 | {
9 | ///
10 | /// Evaluates a function expression, such as "Exists('foo')"
11 | ///
12 | internal sealed class FunctionCallExpressionNode : OperatorExpressionNode
13 | {
14 | private readonly List _arguments;
15 | public readonly string _functionName;
16 |
17 | internal FunctionCallExpressionNode(string functionName, List arguments)
18 | {
19 | this._functionName = functionName;
20 | this._arguments = arguments;
21 | }
22 |
23 | ///
24 | /// Evaluate node as boolean
25 | ///
26 | internal override bool BoolEvaluate(IConditionEvaluationState state)
27 | {
28 | if (String.Compare(this._functionName, "exists", StringComparison.OrdinalIgnoreCase) == 0)
29 | {
30 | return true;
31 | }
32 |
33 | if (String.Compare(this._functionName, "HasTrailingSlash", StringComparison.OrdinalIgnoreCase) == 0)
34 | {
35 | // often used to append slash to path so return false to enable this codepath
36 | return false;
37 | }
38 |
39 | // We haven't implemented any other "functions"
40 |
41 | return false;
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/PrimaryProjectPropertiesUpdateTransformationTest.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Xml.Linq;
3 | using Microsoft.VisualStudio.TestTools.UnitTesting;
4 | using Project2015To2017.Definition;
5 | using Project2015To2017.Transforms;
6 |
7 | namespace Project2015To2017.Tests
8 | {
9 | [TestClass]
10 | public class PrimaryProjectPropertiesUpdateTransformationTest
11 | {
12 | [TestMethod]
13 | public void OutputAppendTargetFrameworkToOutputPathTrue()
14 | {
15 | var project = new Project
16 | {
17 | IsModernProject = true,
18 | AppendTargetFrameworkToOutputPath = true,
19 | PropertyGroups = new[] { new XElement("PropertyGroup") },
20 | FilePath = new FileInfo("test.cs")
21 | };
22 |
23 | new PrimaryProjectPropertiesUpdateTransformation().Transform(project);
24 |
25 | var appendTargetFrameworkToOutputPath = project.Property("AppendTargetFrameworkToOutputPath");
26 | Assert.IsNull(appendTargetFrameworkToOutputPath);
27 | }
28 |
29 | [TestMethod]
30 | public void OutputAppendTargetFrameworkToOutputPathFalse()
31 | {
32 | var project = new Project
33 | {
34 | IsModernProject = true,
35 | AppendTargetFrameworkToOutputPath = false,
36 | PropertyGroups = new[] { new XElement("PropertyGroup") },
37 | FilePath = new FileInfo("test.cs")
38 | };
39 |
40 | new PrimaryProjectPropertiesUpdateTransformation().Transform(project);
41 |
42 | var appendTargetFrameworkToOutputPath = project.Property("AppendTargetFrameworkToOutputPath");
43 | Assert.IsNotNull(appendTargetFrameworkToOutputPath);
44 | Assert.AreEqual("false", appendTargetFrameworkToOutputPath.Value);
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/Project2015To2017.Migrate2017.Tool/CommandLogic.cs:
--------------------------------------------------------------------------------
1 | using DotNet.Globbing;
2 | using Serilog;
3 | using System.Collections.Generic;
4 | using System.IO;
5 |
6 | namespace Project2015To2017.Migrate2017.Tool
7 | {
8 | public class CommandLogic
9 | {
10 | private readonly PatternProcessor globProcessor = (converter, pattern, callback, self) =>
11 | {
12 | Log.Verbose("Falling back to globbing");
13 | self.DoProcessableFileSearch();
14 | var glob = Glob.Parse(pattern);
15 | Log.Verbose("Parsed glob {Glob}", glob);
16 | foreach (var (path, extension) in self.Files)
17 | {
18 | if (!glob.IsMatch(path)) continue;
19 | var file = new FileInfo(path);
20 | callback(file, extension);
21 | }
22 |
23 | return true;
24 | };
25 |
26 | private readonly Facility facility;
27 |
28 | public CommandLogic()
29 | {
30 | var genericLogger = new Serilog.Extensions.Logging.SerilogLoggerProvider().CreateLogger(nameof(Serilog));
31 | facility = new Facility(genericLogger, globProcessor);
32 | }
33 |
34 | public void ExecuteEvaluate(
35 | IReadOnlyCollection items,
36 | ConversionOptions conversionOptions)
37 | {
38 | facility.ExecuteEvaluate(items, conversionOptions);
39 | }
40 |
41 | public void ExecuteMigrate(
42 | IReadOnlyCollection items,
43 | bool noBackup,
44 | ConversionOptions conversionOptions)
45 | {
46 | facility.ExecuteMigrate(items, noBackup, conversionOptions);
47 | }
48 |
49 | public void ExecuteAnalyze(
50 | IReadOnlyCollection items,
51 | ConversionOptions conversionOptions)
52 | {
53 | facility.ExecuteAnalyze(items, conversionOptions);
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/Project2015To2017.Console/Options.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using CommandLine;
4 |
5 | namespace Project2015To2017.Console
6 | {
7 | public class Options
8 | {
9 | [Value(0)]
10 | public IEnumerable Files { get; set; }
11 |
12 | [Option('d', "dry-run", Default = false, HelpText = "Will not update any files, just outputs all the messages")]
13 | public bool DryRun { get; set; } = false;
14 |
15 | [Option('n', "no-backup", Default = false, HelpText = "Will not create a backup folder")]
16 | public bool NoBackup { get; set; } = false;
17 |
18 | [Option('a', "assembly-info", Default = false, HelpText = "Keep Assembly Info in a file")]
19 | public bool AssemblyInfo { get; set; } = false;
20 |
21 | [Option('t', "target-frameworks", Separator = ';', HelpText = "Specific target frameworks")]
22 | public IEnumerable TargetFrameworks { get; set; }
23 |
24 | [Option('o', "output-path", Default = false, HelpText = "Will not create a subfolder with the target framework in the output path")]
25 | public bool NoTargetFrameworkToOutputPath { get; set; } = false;
26 |
27 | [Option('f', "force", Default = false, HelpText = "Will force an upgrade even though certain preconditions might not have been met")]
28 | public bool Force { get; set; } = false;
29 |
30 | public ConversionOptions ConversionOptions
31 | => new ConversionOptions
32 | {
33 | KeepAssemblyInfo = AssemblyInfo,
34 | TargetFrameworks = TargetFrameworks?.ToList(),
35 | AppendTargetFrameworkToOutputPath = !NoTargetFrameworkToOutputPath,
36 | ProjectCache = new Caching.DefaultProjectCache(),
37 | Force = this.Force
38 | };
39 | }
40 | }
--------------------------------------------------------------------------------
/Project2015To2017.Migrate2017.Library/Vs15TransformationSet.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Microsoft.Extensions.Logging;
3 | using Project2015To2017.Migrate2017.Transforms;
4 | using Project2015To2017.Transforms;
5 |
6 | namespace Project2015To2017.Migrate2017
7 | {
8 | public class Vs15TransformationSet : ITransformationSet
9 | {
10 | public static readonly Vs15TransformationSet TrueInstance = new Vs15TransformationSet();
11 |
12 | public static readonly ITransformationSet Instance = new ChainTransformationSet(
13 | BasicReadTransformationSet.Instance,
14 | TrueInstance);
15 |
16 | private Vs15TransformationSet()
17 | {
18 | }
19 |
20 | public IReadOnlyCollection Transformations(
21 | ILogger logger,
22 | ConversionOptions conversionOptions)
23 | {
24 | return new ITransformation[]
25 | {
26 | // Generic
27 | new TargetFrameworkReplaceTransformation(
28 | conversionOptions.TargetFrameworks,
29 | conversionOptions.AppendTargetFrameworkToOutputPath),
30 | new PropertyDeduplicationTransformation(),
31 | new PropertySimplificationTransformation(),
32 | new PrimaryProjectPropertiesUpdateTransformation(),
33 | new EmptyGroupRemoveTransformation(),
34 | // VS15 migration
35 | new TestProjectPackageReferenceTransformation(logger),
36 | new AssemblyFilterPackageReferencesTransformation(),
37 | new AssemblyFilterHintedPackageReferencesTransformation(),
38 | new AssemblyFilterDefaultTransformation(),
39 | new ImportsTargetsFilterPackageReferencesTransformation(),
40 | new FileTransformation(logger),
41 | new XamlPagesTransformation(logger),
42 | };
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/Transforms/PropertyDeduplicationTransformation.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Collections.Immutable;
3 | using System.Linq;
4 | using Project2015To2017.Definition;
5 |
6 | namespace Project2015To2017.Transforms
7 | {
8 | public sealed class PropertyDeduplicationTransformation : ITransformationWithDependencies
9 | {
10 | public void Transform(Project definition)
11 | {
12 | var props = definition
13 | .ConditionalGroups()
14 | .Select(x => (
15 | x,
16 | x.Elements()
17 | .Where(c => !c.HasElements)
18 | .Select(c => c.Name.LocalName)
19 | .ToImmutableHashSet()
20 | ))
21 | .ToImmutableArray();
22 |
23 | if (props.Length == 0)
24 | {
25 | return;
26 | }
27 |
28 | var intersection = props.First().Item2;
29 | foreach (var (_, nameSet) in props.Skip(1))
30 | {
31 | intersection = intersection.Intersect(nameSet);
32 | }
33 |
34 | if (intersection.IsEmpty)
35 | {
36 | return;
37 | }
38 |
39 | foreach (var commonKey in intersection)
40 | {
41 | var properties = props.Select(x => x.Item1.Element(x.Item1.Name.Namespace + commonKey)).ToImmutableArray();
42 | var values = properties.Select(x => x.Value).ToImmutableHashSet();
43 | if (values.Count != 1) continue;
44 |
45 | foreach (var property in properties)
46 | {
47 | property.Remove();
48 | }
49 |
50 | var sourceForCopy = properties.First();
51 | definition.PrimaryPropertyGroup().Add(sourceForCopy);
52 | }
53 | }
54 |
55 | public IReadOnlyCollection DependOn => new[]
56 | {
57 | typeof(PropertySimplificationTransformation).Name,
58 | };
59 | }
60 | }
--------------------------------------------------------------------------------
/Project2015To2017.Tests/W001IllegalProjectTypeDiagnosticTest.cs:
--------------------------------------------------------------------------------
1 | using System.Xml.Linq;
2 | using Microsoft.VisualStudio.TestTools.UnitTesting;
3 | using Project2015To2017.Analysis.Diagnostics;
4 | using Project2015To2017.Definition;
5 | using Project2015To2017.Reading;
6 |
7 | namespace Project2015To2017.Tests
8 | {
9 | [TestClass]
10 | public class W001IllegalProjectTypeDiagnosticTest
11 | {
12 | [TestMethod]
13 | public void GeneratesResultForXamarinAndroid()
14 | {
15 | var project = new Project
16 | {
17 | ProjectDocument = new XDocument(
18 | new XElement("Project",
19 | new XElement(Project.XmlLegacyNamespace + "PropertyGroup",
20 | new XElement(
21 | XName.Get("ProjectTypeGuids", Project.XmlLegacyNamespace.NamespaceName),
22 | "{EFBA0AD7-5A72-4C68-AF49-83D382785DCF}"))))
23 | };
24 |
25 | ProjectPropertiesReader.ReadPropertyGroups(project);
26 |
27 | var results = new W001IllegalProjectTypeDiagnostic().Analyze(project);
28 | Assert.AreEqual(1, results.Count);
29 | }
30 |
31 | [TestMethod]
32 | public void DoesNotGenerateResultForValidType()
33 | {
34 | var project = new Project
35 | {
36 | ProjectDocument = new XDocument(
37 | new XElement("Project",
38 | new XElement(Project.XmlLegacyNamespace + "PropertyGroup",
39 | new XElement(
40 | XName.Get("ProjectTypeGuids", Project.XmlLegacyNamespace.NamespaceName),
41 | "{318C4C53-4319-472D-A480-6540F3D375FD}"))))
42 | };
43 |
44 | ProjectPropertiesReader.ReadPropertyGroups(project);
45 |
46 | var results = new W001IllegalProjectTypeDiagnostic().Analyze(project);
47 | Assert.IsNotNull(results);
48 | Assert.AreEqual(0, results.Count);
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/Reading/Conditionals/NotExpressionNode.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 |
4 | using System.Diagnostics;
5 |
6 | namespace Project2015To2017.Reading.Conditionals
7 | {
8 | ///
9 | /// Performs logical NOT on left child
10 | /// Does not update conditioned properties table
11 | ///
12 | [DebuggerDisplay("{DebuggerDisplay,nq}")]
13 | internal sealed class NotExpressionNode : OperatorExpressionNode
14 | {
15 | ///
16 | /// Evaluate as boolean
17 | ///
18 | internal override bool BoolEvaluate(IConditionEvaluationState state)
19 | {
20 | return !this.LeftChild.BoolEvaluate(state);
21 | }
22 |
23 | internal override bool CanBoolEvaluate(IConditionEvaluationState state)
24 | {
25 | return this.LeftChild.CanBoolEvaluate(state);
26 | }
27 |
28 | ///
29 | /// Returns unexpanded value with '!' prepended. Useful for error messages.
30 | ///
31 | internal override string GetUnexpandedValue(IConditionEvaluationState state)
32 | {
33 | return "!" + this.LeftChild.GetUnexpandedValue(state);
34 | }
35 |
36 | ///
37 | /// Returns expanded value with '!' prepended. Useful for error messages.
38 | ///
39 | internal override string GetExpandedValue(IConditionEvaluationState state)
40 | {
41 | return "!" + this.LeftChild.GetExpandedValue(state);
42 | }
43 |
44 | internal override string DebuggerDisplay => $"(not {this.LeftChild.DebuggerDisplay})";
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Project2015To2017.Migrate2017.Library/Transforms/AssemblyFilterHintedPackageReferencesTransformation.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Linq;
3 | using Project2015To2017.Definition;
4 | using Project2015To2017.Transforms;
5 |
6 | namespace Project2015To2017.Migrate2017.Transforms
7 | {
8 | public sealed class AssemblyFilterHintedPackageReferencesTransformation : ILegacyOnlyProjectTransformation
9 | {
10 | public void Transform(Project definition)
11 | {
12 | if (definition.PackageReferences == null || definition.PackageReferences.Count == 0)
13 | {
14 | return;
15 | }
16 |
17 | var projectPath = definition.ProjectFolder.FullName;
18 |
19 | var nugetRepositoryPath = definition.NuGetPackagesPath.FullName;
20 |
21 | var packageReferenceIds = definition.PackageReferences.Select(x => x.Id).ToArray();
22 |
23 | var packagePaths = packageReferenceIds.Select(packageId => Path.Combine(nugetRepositoryPath, packageId).ToLower())
24 | .ToArray();
25 |
26 | var (filteredAssemblies, removeQueue) = definition.AssemblyReferences
27 | .Split(assembly => !packagePaths.Any(
28 | packagePath => AssemblyMatchesPackage(assembly, packagePath)
29 | )
30 | );
31 |
32 | foreach (var assemblyReference in removeQueue)
33 | {
34 | assemblyReference.DefinitionElement?.Remove();
35 | }
36 |
37 | definition.AssemblyReferences = filteredAssemblies;
38 |
39 | bool AssemblyMatchesPackage(AssemblyReference assembly, string packagePath)
40 | {
41 | var hintPath = assembly.HintPath;
42 | if (hintPath == null)
43 | {
44 | return false;
45 | }
46 |
47 | hintPath = Extensions.MaybeAdjustFilePath(hintPath, projectPath);
48 |
49 | var fullHintPath = Path.IsPathRooted(hintPath) ? hintPath : Path.GetFullPath(Path.Combine(projectPath, hintPath));
50 |
51 | return fullHintPath.ToLowerInvariant().StartsWith(Extensions.MaybeAdjustFilePath(packagePath, projectPath));
52 | }
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Analysis/Diagnostics/W001IllegalProjectTypeDiagnostic.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Project2015To2017.Definition;
3 | using System.Collections.Generic;
4 | using System.Collections.Immutable;
5 | using System.Linq;
6 |
7 | namespace Project2015To2017.Analysis.Diagnostics
8 | {
9 | public sealed class W001IllegalProjectTypeDiagnostic : DiagnosticBase
10 | {
11 | private static readonly Dictionary TypeGuids = new Dictionary
12 | {
13 | ["{EFBA0AD7-5A72-4C68-AF49-83D382785DCF}"] = "Xamarin.Android",
14 | ["{6BC8ED88-2882-458C-8E55-DFD12B67127B}"] = "Xamarin.iOS",
15 | ["{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A}"] = "UAP/UWP",
16 | };
17 |
18 | ///
19 | public override IReadOnlyList Analyze(Project project)
20 | {
21 | var list = new List(TypeGuids.Count + 1);
22 | if (project.IsWindowsFormsProject)
23 | {
24 | list.Add(CreateDiagnosticResult(project,
25 | "Windows Forms support in CPS is in early stages and support might depend on your working environment.",
26 | project.FilePath));
27 | }
28 |
29 | // try to get project type - may not exist
30 | var typeElement = project.Property("ProjectTypeGuids");
31 | if (typeElement == null)
32 | {
33 | return Array.Empty();
34 | }
35 |
36 | // parse the CSV list
37 | var guidTypes = typeElement.Value
38 | .Split(';')
39 | .Select(x => x.Trim().ToUpperInvariant())
40 | .ToImmutableHashSet();
41 |
42 | foreach (var item in TypeGuids)
43 | {
44 | if (!guidTypes.Contains(item.Key)) continue;
45 |
46 | list.Add(
47 | CreateDiagnosticResult(project,
48 | $"Project type {item.Value} is not tested thoroughly and support might depend on your working environment.",
49 | project.FilePath)
50 | .LoadLocationFromElement(typeElement));
51 | }
52 |
53 | return list;
54 | }
55 |
56 | public W001IllegalProjectTypeDiagnostic() : base(1)
57 | {
58 | }
59 | }
60 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/ConversionOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Collections.Immutable;
3 | using Project2015To2017.Transforms;
4 |
5 | namespace Project2015To2017
6 | {
7 | public sealed class ConversionOptions
8 | {
9 | ///
10 | /// Project cache, if any. When null no caching is used.
11 | ///
12 | public Caching.IProjectCache ProjectCache { get; set; }
13 | ///
14 | /// Whether to keep the AssemblyInfo.cs file, or to
15 | /// move the attributes into the project file
16 | ///
17 | public bool KeepAssemblyInfo { get; set; }
18 | ///
19 | /// Change the target framework to a specific framework, or to
20 | /// multi target frameworks
21 | ///
22 | public IReadOnlyList TargetFrameworks { get; set; }
23 | ///
24 | /// Append the target framework to the output path
25 | ///
26 | public bool AppendTargetFrameworkToOutputPath { get; set; } = true;
27 | ///
28 | /// A collection of transforms executed before the execution of default ones
29 | ///
30 | public IReadOnlyList PreDefaultTransforms { get; set; } = ImmutableArray.Empty;
31 | ///
32 | /// A collection of transforms executed after the execution of default ones
33 | ///
34 | public IReadOnlyList PostDefaultTransforms { get; set; } = ImmutableArray.Empty;
35 | ///
36 | /// A collection of transform class names executed despite being intended for different project system,
37 | /// like forcing run on already converted project.
38 | ///
39 | public IReadOnlyList ForceDefaultTransforms { get; set; } = ImmutableArray.Empty;
40 |
41 | ///
42 | /// Force conversion ignoring any checks we might do that prevent a conversion.
43 | ///
44 | public bool Force { get; set; }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Project2015To2017.Migrate2017.Library/Transforms/TestProjectPackageReferenceTransformation.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using Microsoft.Extensions.Logging;
3 | using Project2015To2017.Definition;
4 | using Project2015To2017.Transforms;
5 |
6 | namespace Project2015To2017.Migrate2017.Transforms
7 | {
8 | public sealed class TestProjectPackageReferenceTransformation : ILegacyOnlyProjectTransformation
9 | {
10 | private readonly ILogger logger;
11 |
12 | public TestProjectPackageReferenceTransformation(ILogger logger = null)
13 | {
14 | this.logger = logger ?? NoopLogger.Instance;
15 | }
16 |
17 | public void Transform(Project definition)
18 | {
19 | var existingPackageReferences = definition.PackageReferences;
20 |
21 | if (definition.Type != ApplicationType.TestProject ||
22 | existingPackageReferences.Any(x => x.Id == "Microsoft.NET.Test.Sdk")) return;
23 |
24 | var testReferences = new[]
25 | {
26 | new PackageReference {Id = "Microsoft.NET.Test.Sdk", Version = "15.0.0"},
27 | new PackageReference {Id = "MSTest.TestAdapter", Version = "1.1.11"},
28 | new PackageReference {Id = "MSTest.TestFramework", Version = "1.1.11"}
29 | };
30 |
31 | var versions = definition.TargetFrameworks?
32 | .Select(f => int.TryParse(f.Replace("net", string.Empty), out int result) ? result : default(int?))
33 | .Where(x => x.HasValue)
34 | .Select(v => v < 100 ? v * 10 : v);
35 |
36 | if (versions != null)
37 | {
38 | if (versions.Any(v => v < 450))
39 | {
40 | logger.LogWarning("Target framework net40 is not compatible with the MSTest NuGet packages. Please consider updating the target framework of your test project(s)");
41 | }
42 | }
43 |
44 | var adjustedPackageReferences = existingPackageReferences
45 | .Concat(testReferences)
46 | .ToArray();
47 |
48 | foreach (var reference in testReferences)
49 | {
50 | logger.LogInformation($"Adding NuGet reference to {reference.Id}, version {reference.Version}.");
51 | }
52 |
53 | definition.PackageReferences = adjustedPackageReferences;
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Project2015To2017.Migrate2017.Library/Diagnostics/W020MicrosoftCSharpDiagnostic.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Project2015To2017.Analysis;
5 | using Project2015To2017.Definition;
6 |
7 | namespace Project2015To2017.Migrate2017.Diagnostics
8 | {
9 | public sealed class W020MicrosoftCSharpDiagnostic : DiagnosticBase
10 | {
11 | private static readonly string[] IncompatiblePrefixes = { "net1", "net2", "net3" };
12 |
13 | ///
14 | public override IReadOnlyList Analyze(Project project)
15 | {
16 | var reference = project.AssemblyReferences.FirstOrDefault(x => string.Equals(x.Include, "Microsoft.CSharp", StringComparison.OrdinalIgnoreCase));
17 | if (reference == null)
18 | {
19 | return Array.Empty();
20 | }
21 |
22 | var net40Found = false;
23 |
24 | var list = new List();
25 | foreach (var framework in project.TargetFrameworks.Where(x => x.StartsWith("net", StringComparison.OrdinalIgnoreCase)))
26 | {
27 | if (framework.StartsWith("net40"))
28 | {
29 | net40Found = true;
30 | continue;
31 | }
32 |
33 | foreach (var incompatiblePrefix in IncompatiblePrefixes)
34 | {
35 | if (!framework.StartsWith(incompatiblePrefix))
36 | {
37 | continue;
38 | }
39 |
40 | list.Add(
41 | CreateDiagnosticResult(project,
42 | $"'Microsoft.CSharp' assembly is incompatible with TargetFramework '{incompatiblePrefix}', version no less than 4.0 is expected.",
43 | project.FilePath)
44 | .LoadLocationFromElement(reference.DefinitionElement));
45 | }
46 | }
47 |
48 | if (!net40Found)
49 | {
50 | list.Add(
51 | CreateDiagnosticResult(project,
52 | "A better way to reference 'Microsoft.CSharp' assembly is using 'Microsoft.CSharp' NuGet package. It will simplify porting to other runtimes.",
53 | project.FilePath)
54 | .LoadLocationFromElement(reference.DefinitionElement));
55 | }
56 |
57 | return list;
58 | }
59 |
60 | public W020MicrosoftCSharpDiagnostic() : base(20)
61 | {
62 | }
63 | }
64 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/Analysis/Analyzer.cs:
--------------------------------------------------------------------------------
1 | using Project2015To2017.Definition;
2 | using Project2015To2017.Reading;
3 | using System;
4 | using Project2015To2017.Analysis.Diagnostics;
5 |
6 | namespace Project2015To2017.Analysis
7 | {
8 | public sealed class Analyzer
9 | where TReporter : class, IReporter
10 | where TReporterOptions : IReporterOptions
11 | {
12 | private readonly AnalysisOptions _options;
13 | private readonly TReporter _reporter;
14 |
15 | public Analyzer(TReporter reporter, AnalysisOptions options = null)
16 | {
17 | this._reporter = reporter ?? throw new ArgumentNullException(nameof(reporter));
18 | this._options = options ?? new AnalysisOptions();
19 | }
20 |
21 | public void Analyze(Project project)
22 | {
23 | if (project == null)
24 | {
25 | throw new ArgumentNullException(nameof(project));
26 | }
27 |
28 | foreach (var diagnostic in this._options.Diagnostics)
29 | {
30 | if (diagnostic.SkipForModernProject && project.IsModernProject)
31 | {
32 | continue;
33 | }
34 |
35 | if (diagnostic.SkipForLegacyProject && !project.IsModernProject)
36 | {
37 | continue;
38 | }
39 |
40 | var reporterOptions = this._reporter.CreateOptionsForProject(project);
41 | this._reporter.Report(diagnostic.Analyze(project), reporterOptions);
42 | }
43 | }
44 |
45 | public void Analyze(Solution solution)
46 | {
47 | if (solution == null)
48 | {
49 | throw new ArgumentNullException(nameof(solution));
50 | }
51 |
52 | if (solution.ProjectPaths == null)
53 | {
54 | return;
55 | }
56 |
57 | var projectReader = new ProjectReader(NoopLogger.Instance);
58 | foreach (var projectPath in solution.ProjectPaths)
59 | {
60 | if (!projectPath.ProjectFile.Exists)
61 | {
62 | if (this._options.Diagnostics.Contains(DiagnosticSet.W002))
63 | {
64 | this._reporter.Report(
65 | W002MissingProjectFileDiagnostic.CreateResult(projectPath, solution),
66 | this._reporter.DefaultOptions);
67 | }
68 | continue;
69 | }
70 |
71 | var project = projectReader.Read(projectPath.ProjectFile);
72 |
73 | Analyze(project);
74 | }
75 | }
76 | }
77 | }
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/Solutions/sampleSolution.testsln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.26124.0
5 | MinimumVisualStudioVersion = 15.0.26124.0
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassLibrary", "ClassLibrary\ClassLibrary.csproj", "{0EFB1378-B556-4AC1-B4E7-676ED896D863}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AFolder", "AFolder", "{4C39B892-D594-4976-8930-046197229232}"
9 | ProjectSection(SolutionItems) = preProject
10 | TextFile.txt = TextFile.txt
11 | EndProjectSection
12 | EndProject
13 | Global
14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
15 | Debug|Any CPU = Debug|Any CPU
16 | Debug|x64 = Debug|x64
17 | Debug|x86 = Debug|x86
18 | Release|Any CPU = Release|Any CPU
19 | Release|x64 = Release|x64
20 | Release|x86 = Release|x86
21 | EndGlobalSection
22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
23 | {0EFB1378-B556-4AC1-B4E7-676ED896D863}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
24 | {0EFB1378-B556-4AC1-B4E7-676ED896D863}.Debug|Any CPU.Build.0 = Debug|Any CPU
25 | {0EFB1378-B556-4AC1-B4E7-676ED896D863}.Debug|x64.ActiveCfg = Debug|Any CPU
26 | {0EFB1378-B556-4AC1-B4E7-676ED896D863}.Debug|x64.Build.0 = Debug|Any CPU
27 | {0EFB1378-B556-4AC1-B4E7-676ED896D863}.Debug|x86.ActiveCfg = Debug|Any CPU
28 | {0EFB1378-B556-4AC1-B4E7-676ED896D863}.Debug|x86.Build.0 = Debug|Any CPU
29 | {0EFB1378-B556-4AC1-B4E7-676ED896D863}.Release|Any CPU.ActiveCfg = Release|Any CPU
30 | {0EFB1378-B556-4AC1-B4E7-676ED896D863}.Release|Any CPU.Build.0 = Release|Any CPU
31 | {0EFB1378-B556-4AC1-B4E7-676ED896D863}.Release|x64.ActiveCfg = Release|Any CPU
32 | {0EFB1378-B556-4AC1-B4E7-676ED896D863}.Release|x64.Build.0 = Release|Any CPU
33 | {0EFB1378-B556-4AC1-B4E7-676ED896D863}.Release|x86.ActiveCfg = Release|Any CPU
34 | {0EFB1378-B556-4AC1-B4E7-676ED896D863}.Release|x86.Build.0 = Release|Any CPU
35 | EndGlobalSection
36 | GlobalSection(SolutionProperties) = preSolution
37 | HideSolutionNode = FALSE
38 | EndGlobalSection
39 | GlobalSection(ExtensibilityGlobals) = postSolution
40 | SolutionGuid = {36F34A40-2599-425D-82D8-1084A2C9AEA5}
41 | EndGlobalSection
42 | EndGlobal
43 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Analysis/DiagnosticBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Globalization;
4 | using System.IO;
5 | using Project2015To2017.Definition;
6 |
7 | namespace Project2015To2017.Analysis
8 | {
9 | public abstract class DiagnosticBase : IEquatable, IDiagnostic
10 | {
11 | public uint Id { get; }
12 | public string DiagnosticCode { get; }
13 |
14 | public virtual bool SkipForLegacyProject => false;
15 | public virtual bool SkipForModernProject => false;
16 |
17 | protected DiagnosticBase(uint id)
18 | {
19 | this.Id = id;
20 | this.DiagnosticCode = $"W{id.ToString(CultureInfo.InvariantCulture).PadLeft(3, '0')}";
21 | }
22 |
23 | public abstract IReadOnlyList Analyze(Project project);
24 |
25 | ///
26 | /// Report found issue using user-selected means of logging
27 | ///
28 | /// Project in which the issue was found
29 | /// Informative message about the issue
30 | /// File or directory for user reference
31 | /// File line for user reference
32 | protected DiagnosticResult CreateDiagnosticResult(Project project, string message, FileSystemInfo source = null, uint sourceLine = uint.MaxValue)
33 | {
34 | if (source == null && sourceLine != uint.MaxValue)
35 | {
36 | throw new ArgumentNullException(nameof(source));
37 | }
38 |
39 | return new DiagnosticResult
40 | {
41 | Code = DiagnosticCode,
42 | Message = message,
43 | Location = new DiagnosticLocation
44 | {
45 | Source = source,
46 | SourceLine = sourceLine
47 | },
48 | Project = project
49 | };
50 | }
51 |
52 | public override int GetHashCode()
53 | {
54 | return (int)this.Id;
55 | }
56 |
57 | public override string ToString()
58 | {
59 | return this.DiagnosticCode;
60 | }
61 |
62 | public bool Equals(DiagnosticBase other)
63 | {
64 | if (other is null) return false;
65 | if (ReferenceEquals(this, other)) return true;
66 | return this.Id == other.Id;
67 | }
68 |
69 | public override bool Equals(object obj)
70 | {
71 | if (obj is null) return false;
72 | if (ReferenceEquals(this, obj)) return true;
73 | return obj is DiagnosticBase other && Equals(other);
74 | }
75 | }
76 | }
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/Solutions/ClassLibrary/ClassLibrary.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | 0efb1378-b556-4ac1-b4e7-676ed896d863
8 | Library
9 | Properties
10 | ClassLibrary
11 | ClassLibrary
12 | v4.6.1
13 | 512
14 | true
15 |
16 |
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | pdbonly
27 | true
28 | bin\Release\
29 | TRACE
30 | prompt
31 | 4
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/UnsupportedProjectTypes.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using Project2015To2017.Definition;
4 |
5 | namespace Project2015To2017
6 | {
7 | ///
8 | /// Helper library to filter out unsupported project types
9 | ///
10 | public static class UnsupportedProjectTypes
11 | {
12 | ///
13 | /// Check for unsupported ProjectTypeGuids in project
14 | ///
15 | /// source project document to check
16 | ///
17 | public static bool IsUnsupportedProjectType(Project project)
18 | {
19 | if (project == null) throw new ArgumentNullException(nameof(project));
20 | var xmlDocument = project.ProjectDocument;
21 |
22 | // try to get project type - may not exist
23 | var typeElement = xmlDocument.Descendants(project.XmlNamespace + "ProjectTypeGuids").FirstOrDefault();
24 | // no matching tag found, project should be okay to convert
25 | if (typeElement == null) return false;
26 |
27 | // parse the CSV list
28 | var guidTypes = typeElement.Value.Split(';').Select(x => x.Trim());
29 |
30 | // if any guid matches an unsupported type, return true
31 | return (from guid in guidTypes
32 | from unsupported in unsupportedGuids
33 | where guid.Equals(unsupported, StringComparison.CurrentCultureIgnoreCase)
34 | select unsupported).Any();
35 | }
36 |
37 | ///
38 | /// Guids that cannot be converted
39 | ///
40 | ///
41 | /// Types of projects that are not supported:
42 | /// https://github.com/dotnet/project-system/blob/master/docs/feature-comparison.md
43 | /// The GUIDs taken from
44 | /// https://www.codeproject.com/Reference/720512/List-of-Visual-Studio-Project-Type-GUIDs
45 | /// Note that the list here is in upper case but project file guids are normally lower case
46 | /// This list does not include Windows Forms apps, these have no type guid
47 | ///
48 | private static readonly string[] unsupportedGuids =
49 | {
50 | "{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}", // ASP.NET 5
51 | "{603C0E0B-DB56-11DC-BE95-000D561079B0}", // ASP.NET MVC 1
52 | "{F85E285D-A4E0-4152-9332-AB1D724D3325}", // ASP.NET MVC 2
53 | "{E53F8FEA-EAE0-44A6-8774-FFD645390401}", // ASP.NET MVC 3
54 | "{E3E379DF-F4C6-4180-9B81-6769533ABE47}", // ASP.NET MVC 4
55 | "{349C5851-65DF-11DA-9384-00065B846F21}", // ASP.NET MVC 5
56 | };
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/AssemblyFilterPackageReferencesTransformationTest.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using System.Linq;
4 | using Microsoft.VisualStudio.TestTools.UnitTesting;
5 | using Project2015To2017.Definition;
6 | using Project2015To2017.Migrate2017.Transforms;
7 | using Project2015To2017.Reading;
8 |
9 | namespace Project2015To2017.Tests
10 | {
11 | [TestClass]
12 | public class AssemblyFilterPackageReferencesTransformationTest
13 | {
14 | [TestMethod]
15 | public void TransformsAssemblyReferences()
16 | {
17 | var project = new ProjectReader().Read(Path.Combine("TestFiles", "OtherTestProjects", "net46console.testcsproj"));
18 | var transformation = new AssemblyFilterPackageReferencesTransformation();
19 |
20 | transformation.Transform(project);
21 |
22 | Assert.AreEqual(12, project.AssemblyReferences.Count);
23 | Assert.IsTrue(project.AssemblyReferences.Any(x => x.Include == @"System.Xml.Linq"));
24 | Assert.IsTrue(project.AssemblyReferences.Any(x => x.Include == @"Microsoft.CSharp"));
25 | }
26 |
27 | [TestMethod]
28 | public void RemoveExtraAssemblyReferences()
29 | {
30 | var project = new Project
31 | {
32 | AssemblyReferences = new List
33 | {
34 | new AssemblyReference
35 | {
36 | Include = "Test.Package",
37 | EmbedInteropTypes = "false",
38 | HintPath = @"..\packages\Test.Package.dll",
39 | Private = "false",
40 | SpecificVersion = "false"
41 | }
42 | ,
43 | new AssemblyReference
44 | {
45 | Include = "Other.Package",
46 | EmbedInteropTypes = "false",
47 | HintPath = @"..\packages\Other.Package.dll",
48 | Private = "false",
49 | SpecificVersion = "false"
50 | }
51 | },
52 | PackageReferences = new List
53 | {
54 | new PackageReference
55 | {
56 | Id = "Test.Package",
57 | IsDevelopmentDependency = false,
58 | Version = "1.2.3"
59 | }
60 | ,
61 | new PackageReference
62 | {
63 | Id = "Another.Package",
64 | IsDevelopmentDependency = false,
65 | Version = "3.2.1"
66 | }
67 | }
68 | };
69 |
70 | var transformation = new AssemblyFilterPackageReferencesTransformation();
71 |
72 | transformation.Transform(project);
73 |
74 | Assert.AreEqual(1, project.AssemblyReferences.Count);
75 | Assert.AreEqual(2, project.PackageReferences.Count);
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/AssemblyFilterHintedPackageReferencesTransformationTest.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using Microsoft.VisualStudio.TestTools.UnitTesting;
4 | using Project2015To2017.Definition;
5 | using Project2015To2017.Migrate2017.Transforms;
6 | using Project2015To2017.Reading;
7 |
8 | namespace Project2015To2017.Tests
9 | {
10 | [TestClass]
11 | public class AssemblyFilterHintedPackageReferencesTransformationTest
12 | {
13 | [TestMethod]
14 | public void HandlesNoPackagesConfig()
15 | {
16 | var project = new Project();
17 |
18 | var transformation = new AssemblyFilterHintedPackageReferencesTransformation();
19 | transformation.Transform(project);
20 | }
21 |
22 | [TestMethod]
23 | public void DedupeReferencesFromPackages()
24 | {
25 | var project = new Project
26 | {
27 | AssemblyReferences = new List
28 | {
29 | new AssemblyReference {
30 | Include = "Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL",
31 | HintPath = @"..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll"
32 | },
33 | new AssemblyReference {
34 |
35 | Include = "System.Data.DataSetExtensions"
36 | },
37 | new AssemblyReference {
38 |
39 | Include = "Owin",
40 | HintPath = @"..\packages\Owin.1.0\lib\net40\Owin.dll"
41 | }
42 | },
43 | PackageReferences = new[]
44 | {
45 | new PackageReference {
46 | Id = "Newtonsoft.Json",
47 | Version = "1.0.0"
48 | }
49 | },
50 | FilePath = new FileInfo(@".\dummy.csproj")
51 | };
52 |
53 | var transformation = new AssemblyFilterHintedPackageReferencesTransformation();
54 |
55 | transformation.Transform(project);
56 |
57 | Assert.AreEqual(2, project.AssemblyReferences.Count);
58 | }
59 |
60 | [TestMethod]
61 | public void DedupeReferencesFromPackagesAlternativePackagesFolder()
62 | {
63 | var projFile = @"TestFiles\AltNugetConfig\ProjectFolder\net46console.testcsproj";
64 |
65 | var project = new ProjectReader().Read(projFile);
66 |
67 | var transformation = new AssemblyFilterHintedPackageReferencesTransformation();
68 |
69 | //Then attempt to clear any referencing the nuget packages folder
70 | transformation.Transform(project);
71 |
72 | //The only one left which points to another folder
73 | Assert.AreEqual(1, project.AssemblyReferences.Count);
74 | Assert.IsTrue(project.AssemblyReferences[0].Include.StartsWith("Owin"));
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/Project2015To2017.Console/Program.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using CommandLine;
4 | using Microsoft.Extensions.Logging;
5 | using Project2015To2017.Analysis;
6 | using Project2015To2017.Definition;
7 | using Project2015To2017.Migrate2017;
8 | using Project2015To2017.Transforms;
9 |
10 | namespace Project2015To2017.Console
11 | {
12 | internal static class Program
13 | {
14 | static void Main(string[] args)
15 | {
16 | Parser.Default.ParseArguments(args)
17 | .WithParsed(ConvertProject);
18 | }
19 |
20 | private static void ConvertProject(Options options)
21 | {
22 | var conversionOptions = options.ConversionOptions;
23 |
24 | var convertedProjects = new List();
25 |
26 | ILogger logger = new ConsoleLogger("console", (s, l) => l >= LogLevel.Information, true);
27 |
28 | logger.LogError("csproj-to-2017 is obsolete and will be removed very soon. Migrate to Project2015To2017.Migrate2017.Tool (dotnet migrate-2017) as soon as possible.");
29 |
30 | foreach (var file in options.Files)
31 | {
32 | var projects = new ProjectConverter(logger, Vs15TransformationSet.Instance, conversionOptions)
33 | .Convert(file, logger)
34 | .Where(x => x != null)
35 | .ToList();
36 | convertedProjects.AddRange(projects);
37 | }
38 |
39 | System.Console.Out.Flush();
40 |
41 | var analyzer = new Analyzer(new LoggerReporter(logger));
42 | foreach (var project in convertedProjects)
43 | {
44 | analyzer.Analyze(project);
45 | }
46 |
47 | if (options.DryRun)
48 | {
49 | return;
50 | }
51 |
52 | var doBackup = !options.NoBackup;
53 |
54 | var writer = new Writing.ProjectWriter(logger, x => x.Delete(), _ => { });
55 | foreach (var project in convertedProjects)
56 | {
57 | writer.Write(project, doBackup);
58 | }
59 |
60 | System.Console.Out.Flush();
61 |
62 | logger.LogInformation("### Performing 2nd pass to analyze converted projects...");
63 |
64 | conversionOptions.ProjectCache?.Purge();
65 |
66 | convertedProjects.Clear();
67 |
68 | foreach (var file in options.Files)
69 | {
70 | var projects = new ProjectConverter(logger, BasicReadTransformationSet.Instance, conversionOptions)
71 | .Convert(file, logger)
72 | .Where(x => x != null)
73 | .ToList();
74 | convertedProjects.AddRange(projects);
75 | }
76 |
77 | System.Console.Out.Flush();
78 |
79 | foreach (var project in convertedProjects)
80 | {
81 | analyzer.Analyze(project);
82 | }
83 | }
84 | }
85 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/Reading/Conditionals/LessThanExpressionNode.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 |
4 | using System;
5 | using System.Diagnostics;
6 |
7 | namespace Project2015To2017.Reading.Conditionals
8 | {
9 | ///
10 | /// Compares for left < right
11 | ///
12 | [DebuggerDisplay("{DebuggerDisplay,nq}")]
13 | internal sealed class LessThanExpressionNode : NumericComparisonExpressionNode
14 | {
15 | ///
16 | /// Compare numerically
17 | ///
18 | protected override bool Compare(double left, double right)
19 | {
20 | return left < right;
21 | }
22 |
23 | ///
24 | /// Compare Versions. This is only intended to compare version formats like "A.B.C.D" which can otherwise not be compared numerically
25 | ///
26 | ///
27 | protected override bool Compare(Version left, Version right)
28 | {
29 | return left < right;
30 | }
31 |
32 | ///
33 | /// Compare mixed numbers and Versions
34 | ///
35 | protected override bool Compare(Version left, double right)
36 | {
37 | if (left.Major != right)
38 | {
39 | return left.Major < right;
40 | }
41 |
42 | // If they have same "major" number, then that means we are comparing something like "6.X.Y.Z" to "6". Version treats the objects with more dots as
43 | // "larger" regardless of what those dots are (e.g. 6.0.0.0 > 6 is a true statement)
44 | return false;
45 | }
46 |
47 | ///
48 | /// Compare mixed numbers and Versions
49 | ///
50 | protected override bool Compare(double left, Version right)
51 | {
52 | if (right.Major != left)
53 | {
54 | return left < right.Major;
55 | }
56 |
57 | // If they have same "major" number, then that means we are comparing something like "6.X.Y.Z" to "6". Version treats the objects with more dots as
58 | // "larger" regardless of what those dots are (e.g. 6.0.0.0 > 6 is a true statement)
59 | return true;
60 | }
61 |
62 | internal override string DebuggerDisplay => $"(< {this.LeftChild.DebuggerDisplay} {this.RightChild.DebuggerDisplay})";
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Reading/Conditionals/GreaterThanExpressionNode.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 |
4 | using System;
5 | using System.Diagnostics;
6 |
7 | namespace Project2015To2017.Reading.Conditionals
8 | {
9 | ///
10 | /// Compares for left > right
11 | ///
12 | [DebuggerDisplay("{DebuggerDisplay,nq}")]
13 | internal sealed class GreaterThanExpressionNode : NumericComparisonExpressionNode
14 | {
15 | ///
16 | /// Compare numerically
17 | ///
18 | protected override bool Compare(double left, double right)
19 | {
20 | return left > right;
21 | }
22 |
23 | ///
24 | /// Compare Versions. This is only intended to compare version formats like "A.B.C.D" which can otherwise not be compared numerically
25 | ///
26 | ///
27 | protected override bool Compare(Version left, Version right)
28 | {
29 | return left > right;
30 | }
31 |
32 | ///
33 | /// Compare mixed numbers and Versions
34 | ///
35 | protected override bool Compare(Version left, double right)
36 | {
37 | if (left.Major != right)
38 | {
39 | return left.Major > right;
40 | }
41 |
42 | // If they have same "major" number, then that means we are comparing something like "6.X.Y.Z" to "6". Version treats the objects with more dots as
43 | // "larger" regardless of what those dots are (e.g. 6.0.0.0 > 6 is a true statement)
44 | return true;
45 | }
46 |
47 | ///
48 | /// Compare mixed numbers and Versions
49 | ///
50 | protected override bool Compare(double left, Version right)
51 | {
52 | if (right.Major != left)
53 | {
54 | return left > right.Major;
55 | }
56 |
57 | // If they have same "major" number, then that means we are comparing something like "6.X.Y.Z" to "6". Version treats the objects with more dots as
58 | // "larger" regardless of what those dots are (e.g. 6.0.0.0 > 6 is a true statement)
59 | return false;
60 | }
61 |
62 | internal override string DebuggerDisplay => $"(> {this.LeftChild.DebuggerDisplay} {this.RightChild.DebuggerDisplay})";
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Reading/Conditionals/LessThanOrEqualExpressionNode.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 |
4 | using System;
5 | using System.Diagnostics;
6 |
7 | namespace Project2015To2017.Reading.Conditionals
8 | {
9 | ///
10 | /// Compares for left <= right
11 | ///
12 | [DebuggerDisplay("{DebuggerDisplay,nq}")]
13 | internal sealed class LessThanOrEqualExpressionNode : NumericComparisonExpressionNode
14 | {
15 | ///
16 | /// Compare numerically
17 | ///
18 | protected override bool Compare(double left, double right)
19 | {
20 | return left <= right;
21 | }
22 |
23 | ///
24 | /// Compare Versions. This is only intended to compare version formats like "A.B.C.D" which can otherwise not be compared numerically
25 | ///
26 | ///
27 | protected override bool Compare(Version left, Version right)
28 | {
29 | return left <= right;
30 | }
31 |
32 | ///
33 | /// Compare mixed numbers and Versions
34 | ///
35 | protected override bool Compare(Version left, double right)
36 | {
37 | if (left.Major != right)
38 | {
39 | return left.Major <= right;
40 | }
41 |
42 | // If they have same "major" number, then that means we are comparing something like "6.X.Y.Z" to "6". Version treats the objects with more dots as
43 | // "larger" regardless of what those dots are (e.g. 6.0.0.0 > 6 is a true statement)
44 | return false;
45 | }
46 |
47 | ///
48 | /// Compare mixed numbers and Versions
49 | ///
50 | protected override bool Compare(double left, Version right)
51 | {
52 | if (right.Major != left)
53 | {
54 | return left <= right.Major;
55 | }
56 |
57 | // If they have same "major" number, then that means we are comparing something like "6.X.Y.Z" to "6". Version treats the objects with more dots as
58 | // "larger" regardless of what those dots are (e.g. 6.0.0.0 > 6 is a true statement)
59 | return true;
60 | }
61 |
62 | internal override string DebuggerDisplay => $"(<= {this.LeftChild.DebuggerDisplay} {this.RightChild.DebuggerDisplay})";
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Reading/Conditionals/GreaterThanOrEqualExpressionNode.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 |
4 | using System;
5 | using System.Diagnostics;
6 |
7 | namespace Project2015To2017.Reading.Conditionals
8 | {
9 | ///
10 | /// Compares for left >= right
11 | ///
12 | [DebuggerDisplay("{DebuggerDisplay,nq}")]
13 | internal sealed class GreaterThanOrEqualExpressionNode : NumericComparisonExpressionNode
14 | {
15 | ///
16 | /// Compare numerically
17 | ///
18 | protected override bool Compare(double left, double right)
19 | {
20 | return left >= right;
21 | }
22 |
23 | ///
24 | /// Compare Versions. This is only intended to compare version formats like "A.B.C.D" which can otherwise not be compared numerically
25 | ///
26 | ///
27 | protected override bool Compare(Version left, Version right)
28 | {
29 | return left >= right;
30 | }
31 |
32 | ///
33 | /// Compare mixed numbers and Versions
34 | ///
35 | protected override bool Compare(Version left, double right)
36 | {
37 | if (left.Major != right)
38 | {
39 | return left.Major >= right;
40 | }
41 |
42 | // If they have same "major" number, then that means we are comparing something like "6.X.Y.Z" to "6". Version treats the objects with more dots as
43 | // "larger" regardless of what those dots are (e.g. 6.0.0.0 > 6 is a true statement)
44 | return true;
45 | }
46 |
47 | ///
48 | /// Compare mixed numbers and Versions
49 | ///
50 | protected override bool Compare(double left, Version right)
51 | {
52 | if (right.Major != left)
53 | {
54 | return left >= right.Major;
55 | }
56 |
57 | // If they have same "major" number, then that means we are comparing something like "6.X.Y.Z" to "6". Version treats the objects with more dots as
58 | // "larger" regardless of what those dots are (e.g. 6.0.0.0 > 6 is a true statement)
59 | return false;
60 | }
61 |
62 | internal override string DebuggerDisplay => $"(>= {this.LeftChild.DebuggerDisplay} {this.RightChild.DebuggerDisplay})";
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/Project2015To2017/ProjectConverterExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Logging;
2 | using Project2015To2017.Definition;
3 | using Project2015To2017.Reading;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.IO;
7 | using System.Linq;
8 |
9 | namespace Project2015To2017
10 | {
11 | public static class ProjectConverterExtensions
12 | {
13 | public static IEnumerable Convert(this ProjectConverter self, string target, ILogger logger = default)
14 | {
15 | if (logger == null) logger = NoopLogger.Instance;
16 |
17 | var extension = Path.GetExtension(target) ?? throw new ArgumentNullException(nameof(target));
18 | if (extension.Length > 0)
19 | {
20 | var file = new FileInfo(target);
21 |
22 | switch (extension)
23 | {
24 | case ".sln":
25 | {
26 | var solution = SolutionReader.Instance.Read(file, logger);
27 | foreach (var project in self.ProcessSolutionFile(solution))
28 | {
29 | yield return project;
30 | }
31 | break;
32 | }
33 | case string s when ProjectConverter.ProjectFileMappings.ContainsKey(extension):
34 | {
35 | yield return self.ProcessProjectFile(file, null);
36 | break;
37 | }
38 | default:
39 | logger.LogCritical("Please specify a project or solution file.");
40 | break;
41 | }
42 |
43 | yield break;
44 | }
45 |
46 | // Process the only solution in given directory
47 | var solutionFiles = Directory.EnumerateFiles(target, "*.sln", SearchOption.TopDirectoryOnly).ToArray();
48 | if (solutionFiles.Length == 1)
49 | {
50 | var solution = SolutionReader.Instance.Read(solutionFiles[0], logger);
51 | foreach (var project in self.ProcessSolutionFile(solution))
52 | {
53 | yield return project;
54 | }
55 |
56 | yield break;
57 | }
58 |
59 | var projectsProcessed = 0;
60 | // Process all csprojs found in given directory
61 | foreach (var fileExtension in ProjectConverter.ProjectFileMappings.Keys)
62 | {
63 | var projectFiles = Directory.EnumerateFiles(target, "*" + fileExtension, SearchOption.AllDirectories).ToArray();
64 | if (projectFiles.Length == 0)
65 | {
66 | continue;
67 | }
68 |
69 | if (projectFiles.Length > 1)
70 | {
71 | logger.LogInformation($"Multiple project files found under directory {target}:");
72 | }
73 |
74 | logger.LogInformation(string.Join(Environment.NewLine, projectFiles));
75 |
76 | foreach (var projectFile in projectFiles)
77 | {
78 | // todo: rewrite both directory enumerations to use FileInfo instead of raw strings
79 | yield return self.ProcessProjectFile(new FileInfo(projectFile), null);
80 | projectsProcessed++;
81 | }
82 | }
83 |
84 | if (projectsProcessed == 0)
85 | {
86 | logger.LogCritical("Please specify a project file.");
87 | }
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Analysis/Diagnostics/W010ConfigurationsMismatchDiagnostic.cs:
--------------------------------------------------------------------------------
1 | using Project2015To2017.Definition;
2 | using Project2015To2017.Reading;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Xml.Linq;
7 |
8 | namespace Project2015To2017.Analysis.Diagnostics
9 | {
10 | public sealed class W010ConfigurationsMismatchDiagnostic : DiagnosticBase
11 | {
12 | ///
13 | public override IReadOnlyList Analyze(Project project)
14 | {
15 | var configurationSet = new HashSet();
16 | var platformSet = new HashSet();
17 |
18 | var configurationsFromProperty = ParseFromProperty("Configurations") ?? new[] { "Debug", "Release" };
19 | var platformsFromProperty = ParseFromProperty("Platforms") ?? new[] { "AnyCPU" };
20 |
21 | foreach (var configuration in configurationsFromProperty)
22 | {
23 | configurationSet.Add(configuration);
24 | }
25 |
26 | foreach (var platform in platformsFromProperty)
27 | {
28 | platformSet.Add(platform);
29 | }
30 |
31 | var list = new List();
32 | foreach (var x in project.ProjectDocument.Descendants())
33 | {
34 | var condition = x.Attribute("Condition");
35 | if (condition == null)
36 | {
37 | continue;
38 | }
39 |
40 | var conditionValue = condition.Value;
41 | if (!conditionValue.Contains("$(Configuration)") && !conditionValue.Contains("$(Platform)"))
42 | {
43 | continue;
44 | }
45 |
46 | var conditionEvaluated = ConditionEvaluator.GetConditionValues(conditionValue);
47 |
48 | if (conditionEvaluated.TryGetValue("Configuration", out var configurations))
49 | {
50 | foreach (var configuration in configurations)
51 | {
52 | if (!configurationSet.Contains(configuration))
53 | {
54 | list.Add(
55 | CreateDiagnosticResult(project,
56 | $"Configuration '{configuration}' is used in project file but not mentioned in $(Configurations).",
57 | project.FilePath)
58 | .LoadLocationFromElement(x));
59 | }
60 | }
61 | }
62 |
63 | if (conditionEvaluated.TryGetValue("Platform", out var platforms))
64 | {
65 | foreach (var platform in platforms)
66 | {
67 | if (!platformSet.Contains(platform))
68 | {
69 | list.Add(
70 | CreateDiagnosticResult(project,
71 | $"Platform '{platform}' is used in project file but not mentioned in $(Platforms).",
72 | project.FilePath)
73 | .LoadLocationFromElement(x));
74 | }
75 | }
76 | }
77 | }
78 |
79 | return list;
80 |
81 | string[] ParseFromProperty(string name) => project.Property(name)
82 | ?.Value
83 | .Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
84 | }
85 |
86 | public W010ConfigurationsMismatchDiagnostic() : base(10)
87 | {
88 | }
89 | }
90 | }
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | version: 4.0.{build}
2 | skip_tags: true
3 | image:
4 | - Visual Studio 2017
5 | configuration:
6 | - Release
7 | pull_requests:
8 | do_not_increment_build_number: true
9 | skip_branch_with_pr: true
10 | environment:
11 | LOGGER: '/l:"C:\Program Files\AppVeyor\BuildAgent\dotnetcore\Appveyor.MSBuildLogger.dll"'
12 | LIBRARY: './Project2015To2017/Project2015To2017.csproj'
13 | LEGACYCLITOOL: './Project2015To2017.Console/Project2015To2017.Console.csproj'
14 | CLITOOL: './Project2015To2017.Migrate2017.Tool/Project2015To2017.Migrate2017.Tool.csproj'
15 | build_script:
16 | - cmd: dotnet build %LOGGER% -v m -c %configuration%
17 | - cmd: dotnet pack %LIBRARY% %LOGGER% -v m -c %configuration% --no-build
18 | - cmd: dotnet pack %LEGACYCLITOOL% %LOGGER% -v m -c %configuration% --no-build /p:Pack=true
19 | - cmd: dotnet pack %CLITOOL% %LOGGER% -v m -c %configuration% --no-build /p:Pack=true
20 | - cmd: dotnet publish %CLITOOL% %LOGGER% -v m -c %configuration% -f netcoreapp2.1 -o ./out/netcoreapp2.1 --no-build
21 | - cmd: dotnet publish %CLITOOL% %LOGGER% -v m -c %configuration% -f net461 -o ./out/net461 --no-build
22 | after_build:
23 | - cmd: 7z a ./Project2015To2017/out/dotnet-migrate-2017.zip ./Project2015To2017.Migrate2017.Tool/out/*
24 | test_script:
25 | - cmd: dotnet test Project2015To2017.Tests/Project2015To2017.Tests.csproj -c %configuration% --no-build --test-adapter-path:. --logger:Appveyor
26 |
27 | artifacts:
28 | - path: ./Project2015To2017.Console/bin/$(configuration)/Project2015To2017.Cli.**.nupkg
29 | name: global-tool-legacy
30 | - path: ./Project2015To2017.Migrate2017.Tool/bin/$(configuration)/Project2015To2017.Migrate2017.Tool.**.nupkg
31 | name: global-tool
32 | - path: ./Project2015To2017/out/dotnet-migrate-2017.zip
33 | name: out-zip
34 | - path: Project2015To2017\bin\$(configuration)\Project2015To2017.**.nupkg
35 | name: nupkg
36 |
37 | deploy:
38 | - provider: NuGet
39 | api_key:
40 | secure: i6ZaMLxF/Jbkh7SgDJuFIwpcrXl1BcNI5vd5Re33EEQV7PrZ71F6MpuIDF7glY/o
41 | skip_symbols: false
42 | artifact: nupkg
43 |
44 | - provider: NuGet
45 | api_key:
46 | secure: +o0/XtevmiegEOJLY9u6bm57gbFvGT/0mraL78vBCtEjNkdjWkZEQGxiL//UP88S
47 | skip_symbols: false
48 | artifact: global-tool-legacy
49 |
50 | - provider: NuGet
51 | api_key:
52 | secure: 5MwxrP9zjSXiJ2WUmdQSR98h+ys28LHiDzVBaFmrDCDvBhvNf+QqDaNDs/+LhVQU
53 | skip_symbols: false
54 | artifact: global-tool
55 |
56 | - provider: GitHub
57 | release: dotnet-migrate-2017-v$(APPVEYOR_BUILD_VERSION)
58 | description: 'Automatically built release v$(APPVEYOR_BUILD_VERSION).'
59 | auth_token:
60 | secure: "LYFGDMnT4oJLpCsZUkhL/oTnG3ZpQnsjaTc10DfTf/jbkXtzctRkXEu2ea3YnyqP"
61 | artifact: out-zip
62 | draft: true
63 | prerelease: true
64 | on:
65 | branch: master # deploy on tag push only
66 |
67 | # build cache to preserve files/folders between builds
68 | cache:
69 | - packages -> **\packages.config # preserve "packages" directory in the root of build folder but will reset it if packages.config is modified
70 | - '%LocalAppData%\NuGet\Cache'
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/FileInclusion/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace Project2015To2017Tests.TestFiles.FileInclusion {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Project2015To2017Tests.TestFiles.FileInclusion.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/FileInclusion/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace Project2015To2017Tests.TestFiles.FileInclusion.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Project2015To2017Tests.TestFiles.FileInclusion.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/FileInclusion/SourceFileWithDesigner.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace Project2015To2017Tests.TestFiles.FileInclusion {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class SourceFileWithDesigner {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal SourceFileWithDesigner() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Project2015To2017Tests.TestFiles.FileInclusion.SourceFileWithDesigner", typeof(SourceFileWithDesigner).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/ProgramTest.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using System.Linq;
4 | using Microsoft.VisualStudio.TestTools.UnitTesting;
5 | using Project2015To2017.Reading;
6 | using Project2015To2017.Writing;
7 |
8 | namespace Project2015To2017.Tests
9 | {
10 | [TestClass]
11 | public class ProgramTest
12 | {
13 | [TestMethod]
14 | public void ValidatesFileIsWritable()
15 | {
16 | var projectFile = Path.Combine("TestFiles", "OtherTestProjects", "readonly.testcsproj");
17 | var copiedProjectFile = Path.Combine("TestFiles", "OtherTestProjects", $"{nameof(ValidatesFileIsWritable)}.readonly");
18 |
19 | if (File.Exists(copiedProjectFile))
20 | {
21 | File.SetAttributes(copiedProjectFile, FileAttributes.Normal);
22 | File.Delete(copiedProjectFile);
23 | }
24 |
25 | try
26 | {
27 | File.Copy(projectFile, copiedProjectFile);
28 |
29 | File.SetAttributes(copiedProjectFile, FileAttributes.ReadOnly);
30 |
31 | var logger = new DummyLogger();
32 | var project = new ProjectReader(logger).Read(copiedProjectFile);
33 |
34 | Assert.IsFalse(logger.LogEntries.Any(x => x.Contains("Aborting as could not write to project file")));
35 |
36 | var writer = new ProjectWriter(logger);
37 |
38 | writer.Write(project, makeBackups: false);
39 |
40 | Assert.IsTrue(logger.LogEntries.Any(x => x.Contains("Aborting as could not write to project file")));
41 | }
42 | finally
43 | {
44 | if (File.Exists(copiedProjectFile))
45 | {
46 | File.SetAttributes(copiedProjectFile, FileAttributes.Normal);
47 | File.Delete(copiedProjectFile);
48 | }
49 | }
50 | }
51 |
52 | [TestMethod]
53 | public void ValidatesFileIsWritableAfterCheckout()
54 | {
55 | var logs = new List();
56 |
57 | var projectFile = Path.Combine("TestFiles", "OtherTestProjects", "readonly.testcsproj");
58 | var copiedProjectFile = Path.Combine("TestFiles", "OtherTestProjects", $"{nameof(ValidatesFileIsWritableAfterCheckout)}.readonly");
59 |
60 | if (File.Exists(copiedProjectFile))
61 | {
62 | File.SetAttributes(copiedProjectFile, FileAttributes.Normal);
63 | File.Delete(copiedProjectFile);
64 | }
65 |
66 | try
67 | {
68 | File.Copy(projectFile, copiedProjectFile);
69 |
70 | File.SetAttributes(copiedProjectFile, FileAttributes.ReadOnly);
71 |
72 | var project = new ProjectReader().Read(copiedProjectFile);
73 |
74 | var projectWriter = new ProjectWriter(_ => { }, file => File.SetAttributes(file.FullName, FileAttributes.Normal));
75 |
76 | projectWriter.Write(project, makeBackups: false);
77 |
78 | Assert.IsFalse(logs.Any(x => x.Contains("Aborting as could not write to project file")));
79 | }
80 | finally
81 | {
82 | if (File.Exists(copiedProjectFile))
83 | {
84 | File.SetAttributes(copiedProjectFile, FileAttributes.Normal);
85 | File.Delete(copiedProjectFile);
86 | }
87 | }
88 | }
89 |
90 | [TestMethod]
91 | public void ValidatesFileExists()
92 | {
93 | Assert.IsFalse(ProjectConverter.Validate(new FileInfo(Path.Combine("TestFiles", "OtherTestProjects", "nonexistent.testcsproj")), NoopLogger.Instance));
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Reading/Conditionals/NumericComparisonExpressionNode.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 |
4 | using System;
5 |
6 | namespace Project2015To2017.Reading.Conditionals
7 | {
8 | ///
9 | /// Evaluates a numeric comparison, such as less-than, or greater-or-equal-than
10 | /// Does not update conditioned properties table.
11 | ///
12 | internal abstract class NumericComparisonExpressionNode : OperatorExpressionNode
13 | {
14 | ///
15 | /// Compare numbers
16 | ///
17 | protected abstract bool Compare(double left, double right);
18 |
19 | ///
20 | /// Compare Versions. This is only intended to compare version formats like "A.B.C.D" which can otherwise not be compared numerically
21 | ///
22 | protected abstract bool Compare(Version left, Version right);
23 |
24 | ///
25 | /// Compare mixed numbers and Versions
26 | ///
27 | protected abstract bool Compare(Version left, double right);
28 |
29 | ///
30 | /// Compare mixed numbers and Versions
31 | ///
32 | protected abstract bool Compare(double left, Version right);
33 |
34 | ///
35 | /// Evaluate as boolean
36 | ///
37 | internal override bool BoolEvaluate(IConditionEvaluationState state)
38 | {
39 | bool isLeftNum = this.LeftChild.CanNumericEvaluate(state);
40 | bool isLeftVersion = this.LeftChild.CanVersionEvaluate(state);
41 | bool isRightNum = this.RightChild.CanNumericEvaluate(state);
42 | bool isRightVersion = this.RightChild.CanVersionEvaluate(state);
43 | bool isNumeric = isLeftNum && isRightNum;
44 | bool isVersion = isLeftVersion && isRightVersion;
45 |
46 | // If the values identify as numeric, make that comparison instead of the Version comparison since numeric has a stricter definition
47 | if (isNumeric)
48 | {
49 | return Compare(this.LeftChild.NumericEvaluate(state), this.RightChild.NumericEvaluate(state));
50 | }
51 | else if (isVersion)
52 | {
53 | return Compare(this.LeftChild.VersionEvaluate(state), this.RightChild.VersionEvaluate(state));
54 | }
55 |
56 | // If the numbers are of a mixed type, call that specific Compare method
57 | if (isLeftNum && isRightVersion)
58 | {
59 | return Compare(this.LeftChild.NumericEvaluate(state), this.RightChild.VersionEvaluate(state));
60 | }
61 | else if (isLeftVersion && isRightNum)
62 | {
63 | return Compare(this.LeftChild.VersionEvaluate(state), this.RightChild.NumericEvaluate(state));
64 | }
65 |
66 | // Throw error here as this code should be unreachable
67 | ErrorUtilities.ThrowInternalErrorUnreachable();
68 | return false;
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Reading/AssemblyInfoReader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Linq;
4 | using System.Xml.Linq;
5 | using Microsoft.CodeAnalysis.CSharp;
6 | using Microsoft.CodeAnalysis.CSharp.Syntax;
7 | using Microsoft.Extensions.Logging;
8 | using Project2015To2017.Definition;
9 |
10 | namespace Project2015To2017.Reading
11 | {
12 | public sealed class AssemblyInfoReader
13 | {
14 | private readonly ILogger logger;
15 |
16 | public AssemblyInfoReader(ILogger logger)
17 | {
18 | this.logger = logger;
19 | }
20 |
21 | public AssemblyAttributes Read(Project project)
22 | {
23 | var projectPath = project.ProjectFolder.FullName;
24 |
25 | var compileElements = project.ItemGroups
26 | .SelectMany(x => x.Descendants(project.XmlNamespace + "Compile"))
27 | .ToList();
28 |
29 | var missingCount = 0u;
30 |
31 | var assemblyInfoFiles = compileElements
32 | .Attributes("Include")
33 | .Select(x => x.Value.ToString())
34 | .Where(x => !x.Contains("*"))
35 | .Select(x =>
36 | {
37 | var filePath = Path.IsPathRooted(x) ? x : Path.GetFullPath(Path.Combine(projectPath, x));
38 | return new FileInfo(Extensions.MaybeAdjustFilePath(filePath, projectPath));
39 | }
40 | )
41 | .Where(IsAssemblyInfoFile)
42 | .Where(x =>
43 | {
44 | if (x.Exists)
45 | {
46 | return true;
47 | }
48 |
49 | missingCount++;
50 | this.logger.LogWarning($@"AssemblyInfo file '{x.FullName}' not found");
51 | return false;
52 | }
53 | )
54 | .ToList();
55 |
56 | if (assemblyInfoFiles.Count == 0)
57 | {
58 | this.logger.LogWarning($@"Could not read from assemblyinfo, no assemblyinfo file found");
59 |
60 | return null;
61 | }
62 |
63 | if (assemblyInfoFiles.Count + missingCount > 1)
64 | {
65 | var fileList = string.Join($",{Environment.NewLine}", assemblyInfoFiles.Select(x => x.FullName));
66 | this.logger.LogWarning($@"Could not read from assemblyinfo, multiple assemblyinfo files found:{Environment.NewLine}{fileList}");
67 |
68 | project.HasMultipleAssemblyInfoFiles = true;
69 | return null;
70 | }
71 |
72 | var assemblyInfoFile = assemblyInfoFiles[0];
73 | var assemblyInfoFileName = assemblyInfoFile.FullName;
74 |
75 | this.logger.LogInformation($"Reading assembly info from {assemblyInfoFileName}.");
76 |
77 | var text = File.ReadAllText(assemblyInfoFileName);
78 |
79 | var tree = CSharpSyntaxTree.ParseText(text);
80 |
81 | var root = (CompilationUnitSyntax) tree.GetRoot();
82 |
83 | var assemblyAttributes = new AssemblyAttributes
84 | {
85 | File = assemblyInfoFile,
86 | FileContents = root
87 | };
88 |
89 | return assemblyAttributes;
90 | }
91 |
92 | private static bool IsAssemblyInfoFile(FileInfo x)
93 | {
94 | var nameLower = x.Name.ToLower();
95 | if (nameLower == "assemblyinfo.cs")
96 | return true;
97 | return nameLower.EndsWith(".cs") && nameLower.Contains("assemblyinfo");
98 | }
99 | }
100 | }
--------------------------------------------------------------------------------
/Project2015To2017.Core/Reading/Conditionals/GenericExpressionNode.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 |
4 | using System;
5 |
6 | namespace Project2015To2017.Reading.Conditionals
7 | {
8 | ///
9 | /// Base class for all expression nodes.
10 | ///
11 | public abstract class GenericExpressionNode
12 | {
13 | internal abstract bool CanBoolEvaluate(IConditionEvaluationState state);
14 | internal abstract bool CanNumericEvaluate(IConditionEvaluationState state);
15 | internal abstract bool CanVersionEvaluate(IConditionEvaluationState state);
16 | internal abstract bool BoolEvaluate(IConditionEvaluationState state);
17 | internal abstract double NumericEvaluate(IConditionEvaluationState state);
18 | internal abstract Version VersionEvaluate(IConditionEvaluationState state);
19 |
20 | ///
21 | /// Returns true if this node evaluates to an empty string,
22 | /// otherwise false.
23 | /// (It may be cheaper to determine whether an expression will evaluate
24 | /// to empty than to fully evaluate it.)
25 | /// Implementations should cache the result so that calls after the first are free.
26 | ///
27 | internal virtual bool EvaluatesToEmpty(IConditionEvaluationState state)
28 | {
29 | return false;
30 | }
31 |
32 | ///
33 | /// Value after any item and property expressions are expanded
34 | ///
35 | ///
36 | internal abstract string GetExpandedValue(IConditionEvaluationState state);
37 |
38 | ///
39 | /// Value before any item and property expressions are expanded
40 | ///
41 | ///
42 | internal abstract string GetUnexpandedValue(IConditionEvaluationState state);
43 |
44 | ///
45 | /// If any expression nodes cache any state for the duration of evaluation,
46 | /// now's the time to clean it up
47 | ///
48 | internal abstract void ResetState();
49 |
50 | ///
51 | /// The main evaluate entry point for expression trees
52 | ///
53 | ///
54 | ///
55 | internal bool Evaluate(IConditionEvaluationState state)
56 | {
57 | return BoolEvaluate(state);
58 | }
59 |
60 | ///
61 | /// Get display string for this node for use in the debugger.
62 | ///
63 | internal virtual string DebuggerDisplay { get; }
64 |
65 |
66 | #region REMOVE_COMPAT_WARNING
67 | internal virtual bool PossibleAndCollision
68 | {
69 | set { /* do nothing */ }
70 | get { return false; }
71 | }
72 |
73 | internal virtual bool PossibleOrCollision
74 | {
75 | set { /* do nothing */ }
76 | get { return false; }
77 | }
78 |
79 | internal abstract bool DetectOr();
80 | internal abstract bool DetectAnd();
81 | #endregion
82 |
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/NuGetPackageTransformationTest.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Linq;
3 | using Microsoft.VisualStudio.TestTools.UnitTesting;
4 | using Project2015To2017.Definition;
5 | using Project2015To2017.Reading;
6 | using Project2015To2017.Transforms;
7 |
8 | namespace Project2015To2017.Tests
9 | {
10 | [TestClass]
11 | public class NuGetPackageTransformationTest
12 | {
13 | [TestMethod]
14 | public void ConvertsNuspec()
15 | {
16 | var project = new ProjectReader().Read(Path.Combine("TestFiles", "OtherTestProjects", "net46console.testcsproj"));
17 |
18 | project.AssemblyAttributes =
19 | new AssemblyAttributes {
20 | InformationalVersion = "7.0",
21 | Version = "8.0",
22 | FileVersion = "9.0",
23 | Copyright = "copyright from assembly",
24 | Description = "description from assembly",
25 | Company = "assembly author"
26 | };
27 |
28 | new NuGetPackageTransformation().Transform(project);
29 |
30 | var transformedPackageConfig = project.PackageConfiguration;
31 |
32 | Assert.IsNull(transformedPackageConfig.Id);
33 | Assert.AreEqual("7.0", transformedPackageConfig.Version);
34 | Assert.AreEqual("some author", transformedPackageConfig.Authors);
35 | Assert.AreEqual("copyright from assembly", transformedPackageConfig.Copyright);
36 | Assert.IsTrue(transformedPackageConfig.RequiresLicenseAcceptance);
37 | Assert.AreEqual("a nice description.", transformedPackageConfig.Description);
38 | Assert.AreEqual("some tags API", transformedPackageConfig.Tags);
39 | Assert.AreEqual("someurl", transformedPackageConfig.LicenseUrl);
40 | Assert.AreEqual("Some long\n text\n with newlines", transformedPackageConfig.ReleaseNotes.Trim());
41 | }
42 |
43 | [TestMethod]
44 | public void ConvertsNuspecWithNoInformationalVersion()
45 | {
46 | var project = new ProjectReader().Read(Path.Combine("TestFiles", "OtherTestProjects", "net46console.testcsproj"));
47 |
48 | project.AssemblyAttributes =
49 | new AssemblyAttributes {
50 | Version = "8.0",
51 | FileVersion = "9.0",
52 | Copyright = "copyright from assembly",
53 | Description = "description from assembly",
54 | Company = "assembly author"
55 | };
56 |
57 | new NuGetPackageTransformation().Transform(project);
58 |
59 | var transformedPackageConfig = project.PackageConfiguration;
60 |
61 | Assert.AreEqual("8.0", transformedPackageConfig.Version);
62 | }
63 |
64 | [TestMethod]
65 | public void ConvertsDependencies()
66 | {
67 | var project = new ProjectReader().Read(Path.Combine("TestFiles", "OtherTestProjects", "net46console.testcsproj"));
68 |
69 | project.PackageReferences = new[]
70 | {
71 | new PackageReference
72 | {
73 | Id = "Newtonsoft.Json",
74 | Version = "10.0.2"
75 | },
76 | new PackageReference
77 | {
78 | Id = "Other.Package",
79 | Version = "1.0.2"
80 | }
81 | };
82 |
83 | new NuGetPackageTransformation().Transform(project);
84 |
85 | Assert.AreEqual("[10.0.2,11)", project.PackageReferences.Single(x => x.Id == "Newtonsoft.Json").Version);
86 | Assert.AreEqual("1.0.2", project.PackageReferences.Single(x => x.Id == "Other.Package").Version);
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestFiles/FileInclusion/fileExclusion.testcsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {62CAD5A7-4966-4289-9203-E6843CDEE4C6}
8 | Exe
9 | Properties
10 | Net46Console
11 | Net46Console
12 | v4.6
13 | 512
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 | ..\packages\Microsoft.Owin.3.1.0\lib\net45\Microsoft.Owin.dll
38 | True
39 |
40 |
41 | ..\packages\Microsoft.Owin.Host.SystemWeb.3.1.0\lib\net45\Microsoft.Owin.Host.SystemWeb.dll
42 | True
43 |
44 |
45 | ..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll
46 | True
47 |
48 |
49 | ..\packages\Owin.1.0\lib\net40\Owin.dll
50 | True
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Transforms/NuGetPackageTransformation.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Microsoft.Extensions.Logging;
5 | using Project2015To2017.Definition;
6 |
7 | namespace Project2015To2017.Transforms
8 | {
9 | public sealed class NuGetPackageTransformation
10 | : ITransformationWithTargetMoment, ILegacyOnlyProjectTransformation
11 | {
12 | public void Transform(Project definition)
13 | {
14 | if (definition.PackageConfiguration == null)
15 | {
16 | return;
17 | }
18 |
19 | var packageConfig = PopulatePlaceHolders(definition);
20 |
21 | ConstrainPackageReferences(definition.PackageReferences, packageConfig);
22 |
23 | definition.PackageConfiguration = packageConfig;
24 | }
25 |
26 | private static void ConstrainPackageReferences(IReadOnlyList rawPackageReferences, PackageConfiguration packageConfig)
27 | {
28 | var dependencies = packageConfig.Dependencies;
29 | if (dependencies == null || rawPackageReferences == null)
30 | {
31 | return;
32 | }
33 |
34 | var packageIdConstraints = dependencies.Select(dependency => new
35 | {
36 | PackageId = dependency.Attribute("id").Value,
37 | Version = dependency.Attribute("version").Value
38 | }).ToArray();
39 |
40 | foreach (var packageReference in rawPackageReferences)
41 | {
42 | var matchingPackage = packageIdConstraints.SingleOrDefault(dependency => packageReference.Id.Equals(dependency.PackageId, StringComparison.OrdinalIgnoreCase));
43 |
44 | if (matchingPackage != null)
45 | {
46 | packageReference.Version = matchingPackage.Version;
47 | }
48 | }
49 | }
50 |
51 | private PackageConfiguration PopulatePlaceHolders(Project project)
52 | {
53 | var rawPackageConfig = project.PackageConfiguration;
54 | var assemblyAttributes = project.AssemblyAttributes;
55 |
56 | return new PackageConfiguration
57 | {
58 | //Id does not need to be specified in new project format if it is just the same as the assembly name
59 | Id = rawPackageConfig.Id == "$id$" ? null : rawPackageConfig.Id,
60 | Version = PopulatePlaceHolder("version", rawPackageConfig.Version, assemblyAttributes.InformationalVersion ?? assemblyAttributes.Version),
61 | Authors = PopulatePlaceHolder("author", rawPackageConfig.Authors, assemblyAttributes.Company),
62 | Description = PopulatePlaceHolder("description", rawPackageConfig.Description, assemblyAttributes.Description),
63 | Copyright = PopulatePlaceHolder("copyright", rawPackageConfig.Copyright, assemblyAttributes.Copyright),
64 | LicenseUrl = rawPackageConfig.LicenseUrl,
65 | ProjectUrl = rawPackageConfig.ProjectUrl,
66 | IconUrl = rawPackageConfig.IconUrl,
67 | Tags = rawPackageConfig.Tags,
68 | ReleaseNotes = rawPackageConfig.ReleaseNotes,
69 | RequiresLicenseAcceptance = rawPackageConfig.RequiresLicenseAcceptance,
70 | Dependencies = rawPackageConfig.Dependencies,
71 | NuspecFile = rawPackageConfig.NuspecFile
72 | };
73 | }
74 |
75 | private string PopulatePlaceHolder(string placeHolder, string nuSpecValue, string assemblyAttributeValue)
76 | {
77 | if (nuSpecValue == $"${placeHolder}$")
78 | {
79 | return assemblyAttributeValue ?? nuSpecValue;
80 | }
81 |
82 | return nuSpecValue;
83 | }
84 |
85 | public TargetTransformationExecutionMoment ExecutionMoment =>
86 | TargetTransformationExecutionMoment.Early;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/UnsupportedProjectTypesTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Xml.Linq;
4 | using Microsoft.VisualStudio.TestTools.UnitTesting;
5 | using Project2015To2017.Definition;
6 |
7 | namespace Project2015To2017.Tests
8 | {
9 | [TestClass]
10 | public class UnsupportedProjectTypesTest
11 | {
12 | ///
13 | /// Run test cases
14 | ///
15 | ///
16 | ///
17 | ///
18 | [DataRow("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}", "ASP.NET 5", true)]
19 | [DataRow("{349C5851-65DF-11DA-9384-00065B846F21};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}", "ASP.NET MVC5", true)]
20 | [DataRow("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}", "C# only", false)]
21 | [DataRow("", "No ProjectTypeGuids tag", false)]
22 | [TestMethod]
23 | public void CheckAnUnsupportedProjectTypeReturnsCorrectResult(string guidTypes, string testCase, bool expected)
24 | {
25 | var xmlDocument = CreateTestProject("ProjectTypeGuids", guidTypes);
26 |
27 | var actual = UnsupportedProjectTypes.IsUnsupportedProjectType(xmlDocument);
28 |
29 | Assert.AreEqual(expected, actual, $"Failed for {testCase}: expected {expected} but returned {actual}");
30 | }
31 |
32 | ///
33 | /// Run test cases
34 | ///
35 | ///
36 | ///
37 | ///
38 | [DataRow("WindowsForms", "WinForms", false)]
39 | [DataRow("", "Other", false)]
40 | [TestMethod]
41 | public void CheckAnUnsupportedProjectOutputReturnsCorrectResult(string outputType, string testCase, bool expected)
42 | {
43 | var xmlDocument = CreateTestProject("MyType", outputType);
44 |
45 | var actual = UnsupportedProjectTypes.IsUnsupportedProjectType(xmlDocument);
46 |
47 | Assert.AreEqual(expected, actual, $"Failed for {testCase}: expected {expected} but returned {actual}");
48 | }
49 |
50 | [TestMethod]
51 | public void IsUnsupportedProjectType_ThrowsExceptionIfXDocumentIsNull()
52 | {
53 | Assert.ThrowsException(() =>
54 | {
55 | UnsupportedProjectTypes.IsUnsupportedProjectType(null);
56 | });
57 | }
58 |
59 | ///
60 | /// Create a test case using the given element name + value
61 | ///
62 | /// element name to add to PropertyGroup
63 | /// value to set
64 | ///
65 | private static Project CreateTestProject(string elementName, string value)
66 | {
67 | // parse empty template
68 | var xmlDocument = XDocument.Parse(template);
69 | if (!string.IsNullOrWhiteSpace(value))
70 | {
71 | var propertyGroup = xmlDocument.Descendants(Project.XmlLegacyNamespace + "PropertyGroup").First();
72 | propertyGroup.Add(new XElement(Project.XmlLegacyNamespace + elementName, value));
73 | }
74 | return new Project { ProjectDocument = xmlDocument };
75 | }
76 |
77 | ///
78 | /// Very basic proj template to create XDocument
79 | ///
80 | const string template = "" +
81 | "" +
82 | " " +
83 | " {104B8196-D5BC-4901-B00C-FA065F5CEAD1}" +
84 | " " +
85 | "";
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Definition/Project.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Xml.Linq;
6 | using NuGet.Configuration;
7 |
8 | namespace Project2015To2017.Definition
9 | {
10 | public sealed class Project
11 | {
12 | public static readonly XNamespace XmlLegacyNamespace = "http://schemas.microsoft.com/developer/msbuild/2003";
13 | public XNamespace XmlNamespace => this.IsModernProject ? XNamespace.None : XmlLegacyNamespace;
14 | public bool IsModernProject { get; set; }
15 |
16 | public IReadOnlyList AssemblyReferences { get; set; }
17 | public IReadOnlyList ProjectReferences { get; set; }
18 | public IReadOnlyList PackageReferences { get; set; }
19 | public PackageConfiguration PackageConfiguration { get; set; }
20 | public AssemblyAttributes AssemblyAttributes { get; set; }
21 | public IReadOnlyList PropertyGroups { get; set; }
22 | public IReadOnlyList Imports { get; set; }
23 | public IReadOnlyList Targets { get; set; }
24 | public IReadOnlyList BuildEvents { get; set; }
25 | public IReadOnlyList Configurations { get; set; }
26 | public IReadOnlyList Platforms { get; set; }
27 | public IList ItemGroups { get; set; }
28 |
29 | public XDocument ProjectDocument { get; set; }
30 | public string ProjectName { get; set; }
31 |
32 | public IList TargetFrameworks { get; } = new List();
33 | public bool AppendTargetFrameworkToOutputPath { get; set; } = true;
34 | public ApplicationType Type { get; set; }
35 | public FileInfo FilePath { get; set; }
36 | public string CodeFileExtension { get; set; }
37 | public DirectoryInfo ProjectFolder => this.FilePath.Directory;
38 | public Guid? ProjectGuid { get; set; }
39 |
40 | public bool HasMultipleAssemblyInfoFiles { get; set; }
41 |
42 | ///
43 | /// Files or folders that should be deleted as part of the conversion
44 | ///
45 | public IReadOnlyList Deletions { get; set; }
46 |
47 | ///
48 | /// The directory where NuGet stores its extracted packages for the project.
49 | /// In general this is the 'packages' folder within the parent solution, but
50 | /// it can be overridden, which is accounted for here.
51 | ///
52 | public DirectoryInfo NuGetPackagesPath
53 | {
54 | get
55 | {
56 | var projectFolder = this.ProjectFolder.FullName;
57 |
58 | var nuGetSettings = Settings.LoadDefaultSettings(projectFolder);
59 | var repositoryPathSetting = SettingsUtility.GetRepositoryPath(nuGetSettings);
60 |
61 | //return the explicitly set path, or if there isn't one, then use the solution's path if one was provided.
62 | //Otherwise assume a solution is one level above the project and therefore so is the 'packages' folder
63 | if (repositoryPathSetting != null)
64 | {
65 | return new DirectoryInfo(repositoryPathSetting);
66 | }
67 |
68 | if (this.Solution != null)
69 | {
70 | return this.Solution.NuGetPackagesPath;
71 | }
72 |
73 | var path = Path.GetFullPath(Path.Combine(projectFolder, "..", "packages"));
74 |
75 | return new DirectoryInfo(path);
76 | }
77 | }
78 |
79 | public FileInfo PackagesConfigFile { get; set; }
80 |
81 | ///
82 | /// The solution in which this project was found, if any.
83 | ///
84 | public Solution Solution { get; set; }
85 |
86 | public IReadOnlyList AssemblyAttributeProperties { get; set; } = Array.Empty();
87 |
88 | public bool IsWindowsFormsProject { get; set; }
89 | public bool IsWindowsPresentationFoundationProject { get; set; }
90 |
91 | public IReadOnlyList IntermediateOutputPaths { get; set; }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Reading/NuSpecReader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Linq;
4 | using System.Xml.Linq;
5 | using Microsoft.Extensions.Logging;
6 | using Project2015To2017.Definition;
7 |
8 | namespace Project2015To2017.Reading
9 | {
10 | public sealed class NuSpecReader
11 | {
12 | private readonly ILogger logger;
13 |
14 | public NuSpecReader(ILogger logger)
15 | {
16 | this.logger = logger;
17 | }
18 |
19 | public PackageConfiguration Read(FileInfo projectFile)
20 | {
21 | var nuspecFiles = projectFile.Directory
22 | .EnumerateFiles("*.nuspec", SearchOption.TopDirectoryOnly)
23 | .ToArray();
24 |
25 | if (nuspecFiles.Length == 0)
26 | {
27 | this.logger.LogDebug("No nuspec found, skipping package configuration.");
28 | return null;
29 | }
30 |
31 | if (nuspecFiles.Length > 1)
32 | {
33 | this.logger.LogError(@"Could not read from nuspec, multiple nuspecs found.");
34 | foreach (var item in nuspecFiles.Select(x => x.FullName))
35 | {
36 | this.logger.LogInformation(item);
37 | }
38 | return null;
39 | }
40 |
41 | var nuspecFile = nuspecFiles[0];
42 |
43 | this.logger.LogInformation($"Reading package info from nuspec {nuspecFile.FullName}.");
44 |
45 | XDocument nuspec;
46 | using (var filestream = File.Open(nuspecFile.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
47 | {
48 | nuspec = XDocument.Load(filestream);
49 | }
50 |
51 | var namespaces = new XNamespace[]
52 | {
53 | "",
54 | "http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd",
55 | "http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd",
56 | "http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd"
57 | };
58 |
59 | var packageConfig = namespaces
60 | .Select(ns => ExtractPackageConfiguration(nuspec, ns))
61 | .SingleOrDefault(config => config != null);
62 |
63 | if (packageConfig == null)
64 | {
65 | this.logger.LogError("Error reading package info from nuspec.");
66 | return null;
67 | }
68 |
69 | packageConfig.NuspecFile = nuspecFile;
70 |
71 | return packageConfig;
72 | }
73 |
74 | private PackageConfiguration ExtractPackageConfiguration(
75 | XDocument nuspec, XNamespace ns
76 | )
77 | {
78 | var metadata = nuspec?.Element(ns + "package")?.Element(ns + "metadata");
79 |
80 | if (metadata == null)
81 | {
82 | return null;
83 | }
84 |
85 | var id = metadata.Element(ns + "id")?.Value;
86 |
87 | var version = metadata.Element(ns + "version")?.Value;
88 |
89 | var dependencies = metadata.Element(ns + "dependencies")
90 | ?.Elements(ns + "dependency")
91 | .ToList();
92 |
93 | var packageConfig = new PackageConfiguration {
94 | Id = id,
95 | Version = version,
96 | Authors = GetElement(metadata, ns + "authors"),
97 | Description = GetElement(metadata, ns + "description"),
98 | Copyright = GetElement(metadata, ns + "copyright"),
99 | LicenseUrl = GetElement(metadata, ns + "licenseUrl"),
100 | ProjectUrl = GetElement(metadata, ns + "projectUrl"),
101 | IconUrl = GetElement(metadata, ns + "iconUrl"),
102 | Tags = GetElement(metadata, ns + "tags"),
103 | ReleaseNotes = GetElement(metadata, ns + "releaseNotes"),
104 | RequiresLicenseAcceptance = metadata.Element(ns + "requireLicenseAcceptance")?.Value != null && bool.Parse(metadata.Element(ns + "requireLicenseAcceptance")?.Value),
105 | Dependencies = dependencies
106 | };
107 |
108 | return packageConfig;
109 | }
110 |
111 | private static string GetElement(XElement metadata, XName elementName)
112 | {
113 | var value = metadata.Element(elementName)?.Value;
114 |
115 | return value;
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://ci.appveyor.com/project/hvanbakel/csprojtovs2017)
2 | [](https://www.nuget.org/packages/Project2015To2017)
3 | [](https://www.nuget.org/packages/Project2015To2017)
4 | [](https://www.nuget.org/packages/Project2015To2017.Cli)
5 | [](https://www.nuget.org/packages/Project2015To2017.Cli)
6 |
7 | # Convert your old project files to the new 2017 format
8 | With the introduction of Visual Studio 2017, Microsoft added some optimizations to how a project file can be set up. However, no tooling was made available that performed this conversion as it was not necessary to do since Visual Studio 2017 would work with the old format too.
9 |
10 | This project converts an existing csproj to the new format, shortening the project file and using all the nice new features that are part of Visual Studio 2017.
11 |
12 | ## What does it fix?
13 | There are a number of things [that VS2017 handles differently](http://www.natemcmaster.com/blog/2017/03/09/vs2015-to-vs2017-upgrade/) that are performed by this tool:
14 | 1. Include files using a wildcard as opposed to specifying every single file
15 | 2. A more succinct way of defining project references
16 | 3. A more succinct way of handling NuGet package references
17 | 4. Moving some of the attributes that used to be defined in AssemblyInfo.cs into the project file
18 | 5. Defining the NuGet package definition as part of the project file
19 |
20 | ## How it works
21 | ### As a Net Core Global Tool
22 | Assuming you have net core 2.1 installed you can run this on the command line:
23 | `dotnet tool install Project2015To2017.Migrate2017.Tool --global`
24 | This will install the tool for you to use it anywhere you would like. You can then call the tool as shown in the examples below.
25 |
26 | ### As a normal file download
27 | Using the tool is simple, it is a simple command line utility that has a single argument being the project file, solution file or folder you would like to convert.
28 | When you give it a directory path, the tool will discover all csproj files nested in it.
29 |
30 | ### Examples
31 | Below examples are for the global tool, for the normal file just replace `dotnet migrate-2017 migrate` with your executable.
32 |
33 | `dotnet migrate-2017 migrate "D:\Path\To\My\TestProject.csproj"`
34 |
35 | Or
36 |
37 | `dotnet migrate-2017 migrate "D:\Path\To\My\TestProject.sln"`
38 |
39 | Or
40 |
41 | `dotnet migrate-2017 migrate "D:\Path\To\My\Directory"`
42 |
43 | After confirming this is an old style project file, it will start performing the conversion. When it has gathered all the data it needs it first creates a backup of the old files and puts them into a backup folder and then generates a new project file in the new format.
44 |
45 | ## Commands
46 | As a sub command `migrate` being shown above there are 2 more options:
47 | * `evaluate` will run evaluation of projects found given the path specified
48 | * `analyze` will run analyzers to signal issues in the project files without converting
49 |
50 | ## Flags
51 | * `target-frameworks` will set the target framework on the outputted project file
52 | * `force-transformations` allows specifying individual transforms to be run on the projects
53 | * `force` ignores checks like project type being supported and will attempt a conversion regardless
54 | * `keep-assembly-info` instructs the migrate logic to keep the assembly info file
55 | * `old-output-path` will set `AppendTargetFrameworkToOutputPath` in converted project file
56 | * `no-backup` do not create a backup folder (e.g. when your solution is under source control)
57 |
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TargetFrameworkReplaceTransformationTest.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Microsoft.VisualStudio.TestTools.UnitTesting;
3 | using Project2015To2017.Definition;
4 | using Project2015To2017.Transforms;
5 |
6 | namespace Project2015To2017.Tests
7 | {
8 | [TestClass]
9 | public class TargetFrameworkReplaceTransformationTest
10 | {
11 | [TestMethod]
12 | public void HandlesProjectNull()
13 | {
14 | Project project = null;
15 | var targetFrameworks = new List { "netstandard2.0" };
16 |
17 | var transformation = new TargetFrameworkReplaceTransformation(targetFrameworks);
18 | transformation.Transform(project);
19 |
20 | Assert.IsNull(project);
21 | }
22 |
23 | [TestMethod]
24 | public void HandlesProjectTargetFrameworksEmpty()
25 | {
26 | var project = new Project();
27 | var targetFrameworks = new List { "netstandard2.0" };
28 |
29 | var transformation = new TargetFrameworkReplaceTransformation(targetFrameworks);
30 | transformation.Transform(project);
31 |
32 | Assert.AreEqual(1, project.TargetFrameworks.Count);
33 | Assert.AreEqual("netstandard2.0", project.TargetFrameworks[0]);
34 | }
35 |
36 | [TestMethod]
37 | public void HandlesOptionTargetFrameworksNull()
38 | {
39 | var project = new Project
40 | {
41 | TargetFrameworks = { "net46" }
42 | };
43 |
44 | var transformation = new TargetFrameworkReplaceTransformation(null);
45 | transformation.Transform(project);
46 |
47 | Assert.AreEqual(1, project.TargetFrameworks.Count);
48 | Assert.AreEqual("net46", project.TargetFrameworks[0]);
49 | }
50 |
51 | [TestMethod]
52 | public void HandlesOptionTargetFrameworksEmpty()
53 | {
54 | var project = new Project
55 | {
56 | TargetFrameworks = { "net46" }
57 | };
58 |
59 | var transformation = new TargetFrameworkReplaceTransformation(new List());
60 | transformation.Transform(project);
61 |
62 | Assert.AreEqual(1, project.TargetFrameworks.Count);
63 | Assert.AreEqual("net46", project.TargetFrameworks[0]);
64 | }
65 |
66 | [TestMethod]
67 | public void HandlesOptionTargetFrameworks()
68 | {
69 | var project = new Project
70 | {
71 | TargetFrameworks = { "net46" }
72 | };
73 | var targetFrameworks = new List { "netstandard2.0" };
74 |
75 | var transformation = new TargetFrameworkReplaceTransformation(targetFrameworks);
76 | transformation.Transform(project);
77 |
78 | Assert.AreEqual(1, project.TargetFrameworks.Count);
79 | Assert.AreEqual("netstandard2.0", project.TargetFrameworks[0]);
80 | }
81 |
82 | [TestMethod]
83 | public void HandlesOptionTargetFrameworksMulti()
84 | {
85 | var project = new Project
86 | {
87 | TargetFrameworks = { "net46" }
88 | };
89 | var targetFrameworks = new List { "netstandard2.0", "net47" };
90 |
91 | var transformation = new TargetFrameworkReplaceTransformation(targetFrameworks);
92 | transformation.Transform(project);
93 |
94 | Assert.AreEqual(2, project.TargetFrameworks.Count);
95 | Assert.AreEqual("netstandard2.0", project.TargetFrameworks[0]);
96 | Assert.AreEqual("net47", project.TargetFrameworks[1]);
97 | }
98 |
99 | [TestMethod]
100 | public void HandlesOptionAppendTargetFrameworkToOutputPathTrue()
101 | {
102 | var project = new Project();
103 |
104 | var transformation = new TargetFrameworkReplaceTransformation(null, true);
105 | transformation.Transform(project);
106 |
107 | Assert.AreEqual(true, project.AppendTargetFrameworkToOutputPath);
108 | }
109 |
110 | [TestMethod]
111 | public void HandlesOptionAppendTargetFrameworkToOutputPathFalse()
112 | {
113 | var project = new Project();
114 |
115 | var transformation = new TargetFrameworkReplaceTransformation(null, false);
116 | transformation.Transform(project);
117 |
118 | Assert.AreEqual(false, project.AppendTargetFrameworkToOutputPath);
119 | }
120 | }
121 | }
--------------------------------------------------------------------------------
/Project2015To2017.Tests/TestProjectPackageReferenceTransformationTest.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Linq;
3 | using Microsoft.VisualStudio.TestTools.UnitTesting;
4 | using Project2015To2017.Definition;
5 | using Project2015To2017.Migrate2017.Transforms;
6 | using Project2015To2017.Reading;
7 |
8 | namespace Project2015To2017.Tests
9 | {
10 | [TestClass]
11 | public class TestProjectPackageReferenceTransformationTest
12 | {
13 | [TestMethod]
14 | public void AddsTestPackages()
15 | {
16 | var project = new ProjectReader().Read(Path.Combine("TestFiles", "OtherTestProjects", "net46console.testcsproj"));
17 |
18 | project.Type = ApplicationType.TestProject;
19 | project.TargetFrameworks.Add("net45");
20 |
21 | var transformation = new TestProjectPackageReferenceTransformation();
22 |
23 | transformation.Transform(project);
24 |
25 | Assert.AreEqual(10, project.PackageReferences.Count);
26 | Assert.AreEqual(1, project.PackageReferences.Count(x => x.Id == "Microsoft.Owin.Host.HttpListener" && x.Version == "3.1.0"));
27 | Assert.AreEqual(1, project.PackageReferences.Count(x => x.Id == "Microsoft.NET.Test.Sdk" && x.Version == "15.0.0"));
28 | Assert.AreEqual(1, project.PackageReferences.Count(x => x.Id == "AutoMapper" && x.Version == "6.1.1" && x.IsDevelopmentDependency));
29 | }
30 |
31 | [TestMethod]
32 | public void AcceptsNetStandardFramework()
33 | {
34 | var project = new ProjectReader().Read(Path.Combine("TestFiles", "OtherTestProjects", "net46console.testcsproj"));
35 |
36 | project.Type = ApplicationType.TestProject;
37 | project.TargetFrameworks.Add("netstandard2.0");
38 |
39 | var transformation = new TestProjectPackageReferenceTransformation();
40 |
41 | transformation.Transform(project);
42 |
43 | Assert.AreEqual(10, project.PackageReferences.Count);
44 | Assert.AreEqual(1, project.PackageReferences.Count(x => x.Id == "Microsoft.Owin.Host.HttpListener" && x.Version == "3.1.0"));
45 | Assert.AreEqual(1, project.PackageReferences.Count(x => x.Id == "Microsoft.NET.Test.Sdk" && x.Version == "15.0.0"));
46 | Assert.AreEqual(1, project.PackageReferences.Count(x => x.Id == "AutoMapper" && x.Version == "6.1.1" && x.IsDevelopmentDependency));
47 | }
48 |
49 | [TestMethod]
50 | public void DoesNotAddTestPackagesIfExists()
51 | {
52 | var transformation = new TestProjectPackageReferenceTransformation();
53 |
54 | var project = new ProjectReader().Read(Path.Combine("TestFiles", "OtherTestProjects", "containsTestSDK.testcsproj"));
55 |
56 | project.TargetFrameworks.Add("net45");
57 |
58 | transformation.Transform(project);
59 |
60 | Assert.AreEqual(6, project.PackageReferences.Count);
61 | Assert.AreEqual(0, project.PackageReferences.Count(x => x.Id == "MSTest.TestAdapter"));
62 | }
63 |
64 | [TestMethod]
65 | public void TransformsPackages()
66 | {
67 | var project = new ProjectReader().Read(Path.Combine("TestFiles", "OtherTestProjects", "net46console.testcsproj"));
68 |
69 | var transformation = new TestProjectPackageReferenceTransformation();
70 |
71 | transformation.Transform(project);
72 |
73 | Assert.AreEqual(7, project.PackageReferences.Count);
74 | Assert.AreEqual(2, project.PackageReferences.Count(x => x.IsDevelopmentDependency));
75 | Assert.AreEqual(1, project.PackageReferences.Count(x => x.Id == "Microsoft.Owin" && x.Version == "3.1.0"));
76 | }
77 |
78 | [TestMethod]
79 | public void HandlesNonXml()
80 | {
81 | var project = new ProjectReader().Read(Path.Combine("TestFiles", "OtherTestProjects", "net46console.testcsproj"));
82 | var transformation = new TestProjectPackageReferenceTransformation();
83 |
84 | transformation.Transform(project);
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Reading/Conditionals/NumericExpressionNode.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 |
4 | using System;
5 | using System.Diagnostics;
6 |
7 | namespace Project2015To2017.Reading.Conditionals
8 | {
9 | ///
10 | /// Represents a number - evaluates as numeric.
11 | ///
12 | [DebuggerDisplay("{DebuggerDisplay,nq}")]
13 | internal sealed class NumericExpressionNode : OperandExpressionNode
14 | {
15 | private string _value;
16 |
17 | private NumericExpressionNode() { }
18 |
19 | internal NumericExpressionNode(string value)
20 | {
21 | this._value = value;
22 | }
23 |
24 | ///
25 | /// Evaluate as boolean
26 | ///
27 | internal override bool BoolEvaluate(IConditionEvaluationState state)
28 | {
29 | // Should be unreachable: all calls check CanBoolEvaluate() first
30 | ErrorUtilities.VerifyThrow(false, "Can't evaluate a numeric expression as boolean.");
31 | return false;
32 | }
33 |
34 | ///
35 | /// Evaluate as numeric
36 | ///
37 | internal override double NumericEvaluate(IConditionEvaluationState state)
38 | {
39 | return ConversionUtilities.ConvertDecimalOrHexToDouble(this._value);
40 | }
41 |
42 | ///
43 | /// Evaluate as a Version
44 | ///
45 | internal override Version VersionEvaluate(IConditionEvaluationState state)
46 | {
47 | return Version.Parse(this._value);
48 | }
49 |
50 | ///
51 | /// Whether it can be evaluated as a boolean: never allowed for numerics
52 | ///
53 | internal override bool CanBoolEvaluate(IConditionEvaluationState state)
54 | {
55 | // Numeric expressions are never allowed to be treated as booleans.
56 | return false;
57 | }
58 |
59 | ///
60 | /// Whether it can be evaluated as numeric
61 | ///
62 | internal override bool CanNumericEvaluate(IConditionEvaluationState state)
63 | {
64 | // It is not always possible to numerically evaluate even a numerical expression -
65 | // for example, it may overflow a double. So check here.
66 | return ConversionUtilities.ValidDecimalOrHexNumber(this._value);
67 | }
68 |
69 | ///
70 | /// Whether it can be evaluated as a Version
71 | ///
72 | internal override bool CanVersionEvaluate(IConditionEvaluationState state)
73 | {
74 | // Check if the value can be formatted as a Version number
75 | // This is needed for nodes that identify as Numeric but can't be parsed as numbers (e.g. 8.1.1.0 vs 8.1)
76 | Version unused;
77 | return Version.TryParse(this._value, out unused);
78 | }
79 |
80 | ///
81 | /// Get the unexpanded value
82 | ///
83 | internal override string GetUnexpandedValue(IConditionEvaluationState state)
84 | {
85 | return this._value;
86 | }
87 |
88 | ///
89 | /// Get the expanded value
90 | ///
91 | internal override string GetExpandedValue(IConditionEvaluationState state)
92 | {
93 | return this._value;
94 | }
95 |
96 | ///
97 | /// If any expression nodes cache any state for the duration of evaluation,
98 | /// now's the time to clean it up
99 | ///
100 | internal override void ResetState()
101 | {
102 | }
103 |
104 | internal override string DebuggerDisplay => $"#\"{this._value}\")";
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/Project2015To2017.Core/Transforms/PrimaryProjectPropertiesUpdateTransformation.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Xml.Linq;
5 | using Project2015To2017.Definition;
6 |
7 | namespace Project2015To2017.Transforms
8 | {
9 | public sealed class PrimaryProjectPropertiesUpdateTransformation : ITransformationWithTargetMoment
10 | {
11 | public void Transform(Project project)
12 | {
13 | AddTargetFrameworks(project, project.TargetFrameworks);
14 |
15 | var configurations = project.Configurations ?? Array.Empty();
16 | if (configurations.Count != 0)
17 | // ignore default "Debug;Release" configuration set
18 | if (configurations.Count != 2 || !configurations.Contains("Debug") ||
19 | !configurations.Contains("Release"))
20 | project.SetProperty("Configurations", string.Join(";", configurations));
21 |
22 | var platforms = project.Platforms ?? Array.Empty();
23 | if (platforms.Count != 0)
24 | // ignore default "AnyCPU" platform set
25 | if (platforms.Count != 1 || !platforms.Contains("AnyCPU"))
26 | project.SetProperty("Platforms", string.Join(";", platforms));
27 |
28 | project.SetProperty("AppendTargetFrameworkToOutputPath",
29 | project.AppendTargetFrameworkToOutputPath ? null : "false");
30 |
31 | project.SetProperty("ExtrasEnableWpfProjectSetup",
32 | project.IsWindowsPresentationFoundationProject ? "true" : null);
33 | project.SetProperty("ExtrasEnableWinFormsProjectSetup",
34 | project.IsWindowsFormsProject ? "true" : null);
35 |
36 | string outputType;
37 | switch (project.Type)
38 | {
39 | case ApplicationType.ConsoleApplication:
40 | outputType = "Exe";
41 | break;
42 | case ApplicationType.WindowsApplication:
43 | outputType = "WinExe";
44 | break;
45 | case ApplicationType.AppContainerExe:
46 | outputType = "AppContainerExe";
47 | break;
48 | default:
49 | outputType = null;
50 | break;
51 | }
52 |
53 | project.SetProperty("OutputType", outputType);
54 |
55 | AddPackageNodes(project);
56 |
57 | var propertyGroup = project.PrimaryPropertyGroup();
58 |
59 | if (project.BuildEvents != null && project.BuildEvents.Any())
60 | {
61 | foreach (var buildEvent in project.BuildEvents.Select(Extensions.RemoveAllNamespaces))
62 | {
63 | propertyGroup.Add(buildEvent);
64 | }
65 | }
66 | }
67 |
68 | private void AddPackageNodes(Project project)
69 | {
70 | var packageConfiguration = project.PackageConfiguration;
71 | if (packageConfiguration == null)
72 | {
73 | return;
74 | }
75 |
76 | //Add those properties not already covered by the project properties
77 |
78 | project.SetProperty("Authors", packageConfiguration.Authors);
79 | project.SetProperty("PackageIconUrl", packageConfiguration.IconUrl);
80 | project.SetProperty("PackageId", packageConfiguration.Id);
81 | project.SetProperty("PackageLicenseUrl", packageConfiguration.LicenseUrl);
82 | project.SetProperty("PackageProjectUrl", packageConfiguration.ProjectUrl);
83 | project.SetProperty("PackageReleaseNotes", packageConfiguration.ReleaseNotes);
84 | project.SetProperty("PackageTags", packageConfiguration.Tags);
85 |
86 | if (packageConfiguration.Id != null && packageConfiguration.Tags == null) project.SetProperty("PackageTags", "Library");
87 |
88 | if (packageConfiguration.RequiresLicenseAcceptance)
89 | {
90 | project.SetProperty("PackageRequireLicenseAcceptance", "true");
91 | }
92 | }
93 |
94 | private void AddTargetFrameworks(Project project, IList targetFrameworks)
95 | {
96 | if (targetFrameworks == null || targetFrameworks.Count == 0)
97 | {
98 | return;
99 | }
100 |
101 | var newElement = targetFrameworks.Count > 1
102 | ? new XElement("TargetFrameworks", string.Join(";", targetFrameworks))
103 | : new XElement("TargetFramework", targetFrameworks[0]);
104 |
105 | project.ReplacePropertiesWith(newElement,
106 | "TargetFrameworks", "TargetFramework", "TargetFrameworkVersion");
107 | }
108 |
109 | public TargetTransformationExecutionMoment ExecutionMoment =>
110 | TargetTransformationExecutionMoment.Late;
111 | }
112 | }
--------------------------------------------------------------------------------
/Project2015To2017.Migrate2017.Library/Transforms/XamlPagesTransformation.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Immutable;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Xml.Linq;
6 | using Microsoft.Extensions.Logging;
7 | using Project2015To2017.Definition;
8 | using Project2015To2017.Transforms;
9 |
10 | namespace Project2015To2017.Migrate2017.Transforms
11 | {
12 | public sealed class XamlPagesTransformation : ILegacyOnlyProjectTransformation
13 | {
14 | public XamlPagesTransformation(ILogger logger = null)
15 | {
16 | this.logger = logger ?? NoopLogger.Instance;
17 | }
18 |
19 | ///
20 | public void Transform(Project definition)
21 | {
22 | if (!definition.IsWindowsPresentationFoundationProject)
23 | {
24 | return;
25 | }
26 |
27 | var removeQueue = definition.ItemGroups
28 | .SelectMany(x => x.Elements())
29 | .Where(x => XamlPageFilter(x, definition))
30 | .ToImmutableArray();
31 |
32 | var count = 0u;
33 |
34 | foreach (var x in removeQueue)
35 | {
36 | x.Remove();
37 | count++;
38 | }
39 |
40 | if (count == 0)
41 | {
42 | return;
43 | }
44 |
45 | logger.LogDebug($"Removed {count} XAML items thanks to MSBuild.Sdk.Extras defaults");
46 | }
47 |
48 | private static readonly string[] FilteredTags = {"Page", "ApplicationDefinition", "Compile", "None"};
49 | private readonly ILogger logger;
50 |
51 | private static bool XamlPageFilter(XElement x, Project definition)
52 | {
53 | var tagLocalName = x.Name.LocalName;
54 | if (!FilteredTags.Contains(tagLocalName))
55 | {
56 | return false;
57 | }
58 |
59 | var include = x.Attribute("Include")?.Value;
60 | var update = x.Attribute("Update")?.Value;
61 | var link = include ?? update;
62 |
63 | if (link == null)
64 | {
65 | return false;
66 | }
67 |
68 | var projectFolder = definition.ProjectFolder.FullName;
69 | var inProject = Path.GetFullPath(Path.Combine(projectFolder, link)).StartsWith(projectFolder);
70 | if (!inProject)
71 | {
72 | return false;
73 | }
74 |
75 | var fileName = Path.GetFileName(link);
76 |
77 | if (tagLocalName == "Compile")
78 | {
79 | if (fileName.EndsWith(".Designer.cs", StringComparison.OrdinalIgnoreCase))
80 | {
81 | var autoGen = x.Element("AutoGen")?.Value ?? "true";
82 | if (!string.Equals(autoGen, "true", StringComparison.OrdinalIgnoreCase))
83 | {
84 | return false;
85 | }
86 |
87 | var designTime = x.Element("DesignTime")?.Value ?? "true";
88 | if (!string.Equals(designTime, "true", StringComparison.OrdinalIgnoreCase))
89 | {
90 | return false;
91 | }
92 |
93 | var designTimeSharedInput = x.Element("DesignTimeSharedInput")?.Value ?? "true";
94 | if (!string.Equals(designTimeSharedInput, "true", StringComparison.OrdinalIgnoreCase))
95 | {
96 | return false;
97 | }
98 |
99 | return true;
100 | }
101 |
102 | return link.EndsWith(".xaml.cs", StringComparison.OrdinalIgnoreCase)
103 | && x.Descendants().All(VerifyDefaultXamlCompileItem);
104 | }
105 |
106 | if (update != null)
107 | {
108 | return false;
109 | }
110 |
111 | // from now on (include != null) is invariant
112 |
113 | if (tagLocalName == "None" && fileName.EndsWith(".settings", StringComparison.OrdinalIgnoreCase))
114 | {
115 | var generator = x.Element("Generator")?.Value ?? "SettingsSingleFileGenerator";
116 | var lastGenOutput = x.Element("LastGenOutput")?.Value ?? ".cs";
117 |
118 | return string.Equals(generator, "SettingsSingleFileGenerator")
119 | && lastGenOutput.EndsWith(".cs", StringComparison.OrdinalIgnoreCase);
120 | }
121 |
122 | return include.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase);
123 |
124 | bool VerifyDefaultXamlCompileItem(XElement child)
125 | {
126 | var name = child.Name.LocalName;
127 |
128 | if (child.HasElements)
129 | {
130 | return false;
131 | }
132 |
133 | if (name == "DependentUpon")
134 | {
135 | return true;
136 | }
137 |
138 | return name == "SubType" && string.Equals(child.Value, "Code", StringComparison.OrdinalIgnoreCase);
139 | }
140 | }
141 | }
142 | }
--------------------------------------------------------------------------------
/Project2015To2017.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27130.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Project2015To2017.Tests", "Project2015To2017.Tests\Project2015To2017.Tests.csproj", "{1AF63B37-E57F-41F1-B34F-177DBC927D97}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5897C612-2D68-4B30-875D-4D10D387219F}"
9 | ProjectSection(SolutionItems) = preProject
10 | .editorconfig = .editorconfig
11 | appveyor.yml = appveyor.yml
12 | Directory.Build.props = Directory.Build.props
13 | README.md = README.md
14 | EndProjectSection
15 | EndProject
16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Project2015To2017", "Project2015To2017\Project2015To2017.csproj", "{88CBADAB-10F5-4C15-A0E9-F0042DD8BC0C}"
17 | EndProject
18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Project2015To2017.Core", "Project2015To2017.Core\Project2015To2017.Core.csproj", "{D9D7DB74-7503-47B3-ABFB-E6B991082E47}"
19 | EndProject
20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Project2015To2017.Console", "Project2015To2017.Console\Project2015To2017.Console.csproj", "{89BDCF6C-0B08-4545-9423-CBBBD826226B}"
21 | EndProject
22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Project2015To2017.Migrate2017.Tool", "Project2015To2017.Migrate2017.Tool\Project2015To2017.Migrate2017.Tool.csproj", "{449F6C91-0C98-40CE-8819-D24BD6FFE2C5}"
23 | EndProject
24 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Project2015To2017.Migrate2017.Library", "Project2015To2017.Migrate2017.Library\Project2015To2017.Migrate2017.Library.csproj", "{EBB75840-2B0E-4F56-B702-E256E34A40C1}"
25 | EndProject
26 | Global
27 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
28 | Debug|Any CPU = Debug|Any CPU
29 | Release|Any CPU = Release|Any CPU
30 | EndGlobalSection
31 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
32 | {1AF63B37-E57F-41F1-B34F-177DBC927D97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {1AF63B37-E57F-41F1-B34F-177DBC927D97}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {1AF63B37-E57F-41F1-B34F-177DBC927D97}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {1AF63B37-E57F-41F1-B34F-177DBC927D97}.Release|Any CPU.Build.0 = Release|Any CPU
36 | {88CBADAB-10F5-4C15-A0E9-F0042DD8BC0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37 | {88CBADAB-10F5-4C15-A0E9-F0042DD8BC0C}.Debug|Any CPU.Build.0 = Debug|Any CPU
38 | {88CBADAB-10F5-4C15-A0E9-F0042DD8BC0C}.Release|Any CPU.ActiveCfg = Release|Any CPU
39 | {88CBADAB-10F5-4C15-A0E9-F0042DD8BC0C}.Release|Any CPU.Build.0 = Release|Any CPU
40 | {D9D7DB74-7503-47B3-ABFB-E6B991082E47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
41 | {D9D7DB74-7503-47B3-ABFB-E6B991082E47}.Debug|Any CPU.Build.0 = Debug|Any CPU
42 | {D9D7DB74-7503-47B3-ABFB-E6B991082E47}.Release|Any CPU.ActiveCfg = Release|Any CPU
43 | {D9D7DB74-7503-47B3-ABFB-E6B991082E47}.Release|Any CPU.Build.0 = Release|Any CPU
44 | {89BDCF6C-0B08-4545-9423-CBBBD826226B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
45 | {89BDCF6C-0B08-4545-9423-CBBBD826226B}.Debug|Any CPU.Build.0 = Debug|Any CPU
46 | {89BDCF6C-0B08-4545-9423-CBBBD826226B}.Release|Any CPU.ActiveCfg = Release|Any CPU
47 | {89BDCF6C-0B08-4545-9423-CBBBD826226B}.Release|Any CPU.Build.0 = Release|Any CPU
48 | {449F6C91-0C98-40CE-8819-D24BD6FFE2C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
49 | {449F6C91-0C98-40CE-8819-D24BD6FFE2C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
50 | {449F6C91-0C98-40CE-8819-D24BD6FFE2C5}.Release|Any CPU.ActiveCfg = Release|Any CPU
51 | {449F6C91-0C98-40CE-8819-D24BD6FFE2C5}.Release|Any CPU.Build.0 = Release|Any CPU
52 | {EBB75840-2B0E-4F56-B702-E256E34A40C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
53 | {EBB75840-2B0E-4F56-B702-E256E34A40C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
54 | {EBB75840-2B0E-4F56-B702-E256E34A40C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
55 | {EBB75840-2B0E-4F56-B702-E256E34A40C1}.Release|Any CPU.Build.0 = Release|Any CPU
56 | EndGlobalSection
57 | GlobalSection(SolutionProperties) = preSolution
58 | HideSolutionNode = FALSE
59 | EndGlobalSection
60 | GlobalSection(ExtensibilityGlobals) = postSolution
61 | SolutionGuid = {23B4DBC6-9379-43AE-BC12-3BD4BEA6B4F6}
62 | EndGlobalSection
63 | EndGlobal
64 |
--------------------------------------------------------------------------------