Skip to content
This repository was archived by the owner on Nov 20, 2018. It is now read-only.

Create replacement API for Microsoft.DotNet.ProjectModel #180

Merged
merged 1 commit into from
Oct 4, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions DotNetTools.sln
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{59E02BDF
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "NuGetPackager", "tools\NuGetPackager\NuGetPackager.xproj", "{8B781D87-1FC3-4A34-9089-2BDF6B562B85}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Extensions.ProjectModel.Sources", "src\Microsoft.Extensions.ProjectModel.Sources\Microsoft.Extensions.ProjectModel.Sources.xproj", "{99D6CE89-7302-4C3A-83EB-D534C24644D2}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Extensions.ProjectModel.Tests", "test\Microsoft.Extensions.ProjectModel.Tests\Microsoft.Extensions.ProjectModel.Tests.xproj", "{1A66A831-4F06-46D9-B483-70A4E75A2F7F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -88,6 +92,14 @@ Global
{8B781D87-1FC3-4A34-9089-2BDF6B562B85}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8B781D87-1FC3-4A34-9089-2BDF6B562B85}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8B781D87-1FC3-4A34-9089-2BDF6B562B85}.Release|Any CPU.Build.0 = Release|Any CPU
{99D6CE89-7302-4C3A-83EB-D534C24644D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{99D6CE89-7302-4C3A-83EB-D534C24644D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{99D6CE89-7302-4C3A-83EB-D534C24644D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{99D6CE89-7302-4C3A-83EB-D534C24644D2}.Release|Any CPU.Build.0 = Release|Any CPU
{1A66A831-4F06-46D9-B483-70A4E75A2F7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1A66A831-4F06-46D9-B483-70A4E75A2F7F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1A66A831-4F06-46D9-B483-70A4E75A2F7F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1A66A831-4F06-46D9-B483-70A4E75A2F7F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -105,5 +117,7 @@ Global
{8A2E6961-6B12-4A8E-8215-3E7301D52EAC} = {F5B382BC-258F-46E1-AC3D-10E5CCD55134}
{53F3B53D-303A-4DAA-9C38-4F55195FA5B9} = {66517987-2A5A-4330-B130-207039378FD4}
{8B781D87-1FC3-4A34-9089-2BDF6B562B85} = {59E02BDF-98DE-4D64-B576-2D0299D5E052}
{99D6CE89-7302-4C3A-83EB-D534C24644D2} = {66517987-2A5A-4330-B130-207039378FD4}
{1A66A831-4F06-46D9-B483-70A4E75A2F7F} = {F5B382BC-258F-46E1-AC3D-10E5CCD55134}
EndGlobalSection
EndGlobal
1 change: 1 addition & 0 deletions NuGet.config
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="msbuild" value="https://dotnet.myget.org/F/msbuild/api/v3/index.json" />
<add key="AspNetCore" value="https://dotnet.myget.org/F/aspnetcore-ci-dev/api/v3/index.json" />
<add key="NuGet" value="https://api.nuget.org/v3/index.json" />
</packageSources>
Expand Down
6 changes: 6 additions & 0 deletions NuGetPackageVerifier.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@
}
}
},
"adx-nonshipping": {
"rules": [],
"packages": {
"Microsoft.Extensions.ProjectModel.Sources": {}
}
},
"Default": {
"rules": [
"DefaultCompositeRule"
Expand Down
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"projects": [ "src"]
"projects": [ "src", "test"]
}
6 changes: 6 additions & 0 deletions makefile.shade
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,11 @@ k-standard-goals
"-n src/Microsoft.Extensions.SecretManager.Tools/Microsoft.Extensions.SecretManager.Tools.nuspec " +
"-n src/Microsoft.Extensions.Caching.SqlConfig.Tools/Microsoft.Extensions.Caching.SqlConfig.Tools.nuspec " +
"-n src/Microsoft.DotNet.Watcher.Tools/Microsoft.DotNet.Watcher.Tools.nuspec ");

DotnetPack("src/Microsoft.Extensions.ProjectModel.Sources/project.json", BUILD_DIR_LOCAL, E("Configuration"), E("KOREBUILD_DOTNET_PACK_OPTIONS") + " --no-build");
foreach (var nupkg in Files.Include(Path.Combine(BUILD_DIR_LOCAL, "*/" + E("Configuration") + "/*.nupkg")))
{
File.Copy(nupkg, Path.Combine(BUILD_DIR_LOCAL, Path.GetFileName(nupkg)), true);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.DotNet.ProjectModel;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NuGet.Frameworks;

namespace Microsoft.Extensions.ProjectModel
{
internal class DotNetProjectContext : IProjectContext
{
private readonly ProjectContext _projectContext;
private readonly OutputPaths _paths;
private readonly Lazy<JObject> _rawProject;
private readonly CommonCompilerOptions _compilerOptions;

public DotNetProjectContext(ProjectContext projectContext, string configuration, string outputPath)
{
if (projectContext == null)
{
throw new ArgumentNullException(nameof(projectContext));
}

if (string.IsNullOrEmpty(configuration))
{
throw new ArgumentNullException(nameof(configuration));
}

_rawProject = new Lazy<JObject>(() =>
{
using (var stream = new FileStream(projectContext.ProjectFile.ProjectFilePath, FileMode.Open, FileAccess.Read))
using (var streamReader = new StreamReader(stream))
using (var jsonReader = new JsonTextReader(streamReader))
{
return JObject.Load(jsonReader);
}
});

Configuration = configuration;
_projectContext = projectContext;

_paths = projectContext.GetOutputPaths(configuration, buidBasePath: null, outputPath: outputPath);
_compilerOptions = _projectContext.ProjectFile.GetCompilerOptions(TargetFramework, Configuration);

// Workaround https://github.com/dotnet/cli/issues/3164
IsClassLibrary = !(_compilerOptions.EmitEntryPoint
?? projectContext.ProjectFile.GetCompilerOptions(null, configuration).EmitEntryPoint.GetValueOrDefault());
}

public bool IsClassLibrary { get; }

public NuGetFramework TargetFramework => _projectContext.TargetFramework;
public string Config => _paths.RuntimeFiles.Config;
public string DepsJson => _paths.RuntimeFiles.DepsJson;
public string RuntimeConfigJson => _paths.RuntimeFiles.RuntimeConfigJson;
public string PackagesDirectory => _projectContext.PackagesDirectory;

public string AssemblyFullPath =>
!IsClassLibrary && (_projectContext.IsPortable || TargetFramework.IsDesktop())
? _paths.RuntimeFiles.Executable
: _paths.RuntimeFiles.Assembly;

public string Configuration { get; }
public string ProjectFullPath => _projectContext.ProjectFile.ProjectFilePath;
public string ProjectName => _projectContext.ProjectFile.Name;
// TODO read from xproj if available
public string RootNamespace => _projectContext.ProjectFile.Name;
public string TargetDirectory => _paths.RuntimeOutputPath;
public string Platform => _compilerOptions.Platform;

public IEnumerable<string> CompilationItems
=> _compilerOptions.CompileInclude.ResolveFiles();

public IEnumerable<string> EmbededItems
=> _compilerOptions.EmbedInclude.ResolveFiles();

/// <summary>
/// Returns string values of top-level keys in the project.json file
/// </summary>
/// <param name="propertyName"></param>
/// <param name="propertyNameComparer"></param>
/// <returns></returns>
public string FindProperty(string propertyName) => FindProperty<string>(propertyName);

public TProperty FindProperty<TProperty>(string propertyName)
{
foreach (var item in _rawProject.Value)
{
if (item.Key.Equals(propertyName, StringComparison.OrdinalIgnoreCase))
{
return item.Value.Value<TProperty>();
}
}

return default(TProperty);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.DotNet.ProjectModel.Files;

namespace Microsoft.Extensions.ProjectModel
{
internal static class IncludeContextExtensions
{
public static IEnumerable<string> ResolveFiles(this IncludeContext context)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure this warrants its own extension method/class. Might as well inline this as a private method.

{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}

return IncludeFilesResolver
.GetIncludeFiles(context, "/", diagnostics: null)
.Select(f => f.SourcePath);
}
}
}
28 changes: 28 additions & 0 deletions src/Microsoft.Extensions.ProjectModel.Sources/IProjectContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;
using NuGet.Frameworks;

namespace Microsoft.Extensions.ProjectModel
{
public interface IProjectContext
{
string ProjectName { get; }
string Configuration { get; }
string Platform { get; }
string ProjectFullPath { get; }
string RootNamespace { get; }
bool IsClassLibrary { get; }
NuGetFramework TargetFramework { get; }
string Config { get; }
string DepsJson { get; }
string RuntimeConfigJson { get; }
string PackagesDirectory { get; }
string TargetDirectory { get; }
string AssemblyFullPath { get; }
IEnumerable<string> CompilationItems { get; }
IEnumerable<string> EmbededItems { get; }
string FindProperty(string propertyName);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

namespace Microsoft.Extensions.ProjectModel.Internal
{
internal class DotNetCoreSdk
{
public string BasePath { get; set; }
public string Version { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.DotNet.Cli.Utils;
using Newtonsoft.Json;
using NuGet.Versioning;

namespace Microsoft.Extensions.ProjectModel.Internal
{
internal class DotNetCoreSdkResolver
{
private readonly string _installationDir;

/// <summary>
/// Represents a resolver that uses the currently executing <see cref="Muxer"/> to find the .NET Core SDK installation
/// </summary>
public static readonly DotNetCoreSdkResolver DefaultResolver = new DotNetCoreSdkResolver(Path.GetDirectoryName(new Muxer().MuxerPath));

/// <summary>
/// Instantiates a resolver that locates the SDK
/// </summary>
/// <param name="installationDir">The directory containing dotnet muxer, aka DOTNET_HOME</param>
public DotNetCoreSdkResolver(string installationDir)
{
_installationDir = installationDir;
}

/// <summary>
/// Find the latest SDK installation (uses SemVer 1.0 to determine what is "latest")
/// </summary>
/// <returns>Path to SDK root directory</returns>
public DotNetCoreSdk ResolveLatest()
{
var latest = FindInstalled()
.Select(d => new { path = d, version = SemanticVersion.Parse(Path.GetFileName(d)) })
.OrderByDescending(sdk => sdk.version)
.FirstOrDefault();

if (latest == null)
{
throw CreateSdkNotInstalledException();
}

return new DotNetCoreSdk
{
BasePath = latest.path,
Version = latest.version.ToFullString()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any value in ToStringing here? Why not just let the DotNetCoreSdk class have Version has a semantic version?

Seems that we don't use the version directly so it's wasteful/less informative to do this bit.

Copy link
Contributor

@NTaylorMullen NTaylorMullen Oct 3, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would also let you not use an anonymous object in the logic you use to determine "latest" here and then in the "sdk" logic in the next method

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NuGet.Versioning.SematicVersion is an implementation detail of how we figure out which version is "latest". In theory, SDK versions may not be valid SemanticVersions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If they end up not being valid semantic versioning then doesn't our resolution logic fall apart? Reason being we order the versions based on how semantic versions are ordered.

};
}

public DotNetCoreSdk ResolveProjectSdk(string projectDir)
{
var sdkVersion = ResolveGlobalJsonSdkVersion(projectDir);
if (string.IsNullOrEmpty(sdkVersion))
{
return ResolveLatest();
}

var sdk = FindInstalled()
.Where(p => Path.GetFileName(p).Equals(sdkVersion, StringComparison.OrdinalIgnoreCase))
.Select(d => new { path = d, version = SemanticVersion.Parse(Path.GetFileName(d)) })
.FirstOrDefault();

if (sdk == null)
{
throw CreateSdkNotInstalledException();
}

return new DotNetCoreSdk
{
BasePath = sdk.path,
Version = sdk.version.ToFullString()
};
}

private Exception CreateSdkNotInstalledException()
{
return new DirectoryNotFoundException($"Could not find an installation of the .NET Core SDK in '{_installationDir}'");
}

private IEnumerable<string> FindInstalled()
=> Directory.EnumerateDirectories(Path.Combine(_installationDir, "sdk"));

private string ResolveGlobalJsonSdkVersion(string start)
{
var dir = new DirectoryInfo(start);
FileInfo fileInfo = null;
while (dir != null)
{
var candidate = Path.Combine(dir.FullName, "global.json");
if (File.Exists(candidate))
{
fileInfo = new FileInfo(candidate);
break;
}
dir = dir.Parent;
}
if (fileInfo == null)
{
return null;
}
try
{
var contents = File.ReadAllText(fileInfo.FullName);
var globalJson = JsonConvert.DeserializeObject<GlobalJsonStub>(contents);
return globalJson?.sdk?.version;
}
catch (JsonException)
{
// TODO log
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be helpful at least the exception message is logged.

return null;
}
}

private class GlobalJsonStub
{
public GlobalJsonSdkStub sdk { get; set; }

public class GlobalJsonSdkStub
{
public string version { get; set; }
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0.25420" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0.25420</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>99d6ce89-7302-4c3a-83eb-d534c24644d2</ProjectGuid>
<RootNamespace>Microsoft.Extensions.ProjectModel</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
Loading