Skip to content

Commit

Permalink
Multi target type reuse (dotnet#5300)
Browse files Browse the repository at this point in the history
* Support referenced assembly type reuse for multi-target project

* Resolve reabse conflict.

* rebase and fix test issue.

* Apply workaround for reusing WindowsAppSDK pkg types in maui 7.0-xxx project

* Rebase and resolve conflicts.
  • Loading branch information
imcarolwang committed Apr 30, 2024
1 parent 1a47276 commit 90240d0
Show file tree
Hide file tree
Showing 14 changed files with 448 additions and 47 deletions.
25 changes: 10 additions & 15 deletions src/dotnet-svcutil/lib/src/Bootstrapper/SvcutilBootstrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,22 +123,17 @@ internal async Task GenerateProjectAsync(bool keepBootstrapperDir, ILogger logge
this.MSBuildProj = await MSBuildProj.DotNetNewAsync(projectFullPath, logger, cancellationToken).ConfigureAwait(false);
this.MSBuildProj.AddDependency(svcutilPkgRef, true);

// Comment out code below for reasons: 1. it never used for .net core later than V2.1 since when the approach is always use TF from the generated project.
// 2. with below code applied when target framework is netstandard2.0 client machine require netcoreapp2.0 (obsolete) for bootstrapper to work
// 3. keep it here for future reference in case when we need definite bootstrapper TF version

// NOTE: If post v2.0 NetStandard ships a different version from NetCore the table below needs to be updated!
//var targetFramework = frameworkInfo.FullName;
//if (isSupportedTFM && frameworkInfo.IsKnownDnx)
//{
// if (frameworkInfo.Name == FrameworkInfo.Netstandard)
// {
// targetFramework = FrameworkInfo.Netcoreapp + TargetFrameworkHelper.NetStandardToNetCoreVersionMap[frameworkInfo.Version];
// }
// this.MSBuildProj.TargetFramework = targetFramework;
//}
var targetFramework = frameworkInfo.FullName;
if (isSupportedTFM && frameworkInfo.Name != FrameworkInfo.Netstandard && frameworkInfo.Version.CompareTo(new Version(6, 0)) >= 0)
{
this.MSBuildProj.TargetFramework = targetFramework;
if(targetFramework.ToLowerInvariant().Contains("net7.0-windows10"))
{
this.MSBuildProj.SetEnableMsixTooling();
}
}
// else
// The TFM is unknown: either, it was not provided or it is a version not yet known to the tool,
// The TFM is Netstandard or version lower than 6.0 or unknown: either, it was not provided or it is a version not yet known to the tool,
// we will use the default TF from the generated project.
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public void WsdlImported(WsdlImporter importer, Collection<ServiceEndpoint> endp

foreach (var binding in bindings)
{
if (binding is NetNamedPipeBinding && _options.Project != null && !_options.Project.TargetFrameworks.FirstOrDefault().ToLower().Contains("windows"))
if (binding is NetNamedPipeBinding && _options.Project != null && !_options.Project.TargetFrameworks.Any(t => t.ToLower().Contains("windows")))
{
MetadataConversionError error = new MetadataConversionError(SR.WrnTargetFrameworkNotSupported_NetNamedPipe, isWarning: true);
if (!importer.Errors.Contains(error))
Expand Down
21 changes: 15 additions & 6 deletions src/dotnet-svcutil/lib/src/Shared/FrameworkInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public static FrameworkInfo Parse(string fullFrameworkName)
// framework spec form: 'net5.0'
// framework spec form: '.NETCoreApp,Version=v6.0'
// framework spec form: '.NETFramework,Version=v4.8'
// framework spec form: 'net7.0-windows10.0.19041.0', 'net7.0-windows'
for (int i = 0; i < fullFrameworkName.Length; i++)
{
char c = fullFrameworkName[i];
Expand All @@ -49,17 +50,25 @@ public static FrameworkInfo Parse(string fullFrameworkName)

// Version ctr requires at least Major and Minor parts
string versionString = fullFrameworkName.Substring(i);
if ((name == Netfx) && !versionString.Contains("."))
if ((name == Netfx))
{
// net452
StringBuilder sb = new StringBuilder(versionString);
for (int j = 1; j < sb.Length; j += 2)
if(!versionString.Contains("."))
{
sb.Insert(j, '.');
StringBuilder sb = new StringBuilder(versionString);
for (int j = 1; j < sb.Length; j += 2)
{
sb.Insert(j, '.');
}
versionString = sb.ToString();
}
versionString = sb.ToString();
// net7.0-windows10.0.19041.0
if (versionString.Contains("-"))
{
versionString = versionString.Substring(0, versionString.IndexOf("-"));
}
}

version = new Version(versionString);
break;
}
Expand Down
31 changes: 18 additions & 13 deletions src/dotnet-svcutil/lib/src/Shared/MSBuildProj.cs
Original file line number Diff line number Diff line change
Expand Up @@ -221,14 +221,9 @@ public static async Task<MSBuildProj> ParseAsync(string projectText, string proj
if (targetFrameworksElements.Count() > 0)
{
var targetFrameworks = targetFrameworksElements.Last().Value;
if (targetFrameworks.ToString().StartsWith("$"))
{
targetFrameworks = GetValueFromDirBuildProps(targetFrameworks, msbuildProj.DirectoryPath);
}

foreach (var targetFx in targetFrameworks.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries).Select(p => p.Trim()))
{
if (!string.IsNullOrWhiteSpace(targetFx))
if (!string.IsNullOrEmpty(targetFx) && !targetFx.ToString().StartsWith("$"))
{
msbuildProj._targetFrameworks.Add(targetFx);
}
Expand Down Expand Up @@ -591,6 +586,14 @@ public bool AddDependency(ProjectDependency dependency, bool copyInternalAssets
return addDependency;
}

public void SetEnableMsixTooling()
{
// workaround for https://github.com/microsoft/WindowsAppSDK/issues/3548: dotnet build fails when WindowsAppSDK is referenced in console application.
// affects MAUI project targeting net7.0-windows10.0xxx, not reproduce in net8.0-window10.0xxx
// ref: https://github.com/dotnet/maui/issues/5886
SetPropertyValue("EnableMsixTooling", "true");
}

// Sets the property value in a PropertyGroup. Returns true if the value was changed, and false if it was already set to that value.
private bool SetPropertyValue(string propertyName, string value)
{
Expand Down Expand Up @@ -759,7 +762,7 @@ public async Task<IEnumerable<ProjectDependency>> ResolveProjectReferencesAsync(

using (var safeLogger = await SafeLogger.WriteStartOperationAsync(logger, "Resolving project references ...").ConfigureAwait(false))
{
if (_targetFrameworks.Count == 1 && TargetFrameworkHelper.IsSupportedFramework(this.TargetFramework, out var frameworkInfo) && frameworkInfo.IsDnx)
if (_targetFrameworks.Count >= 1 && TargetFrameworkHelper.IsSupportedFramework(this.TargetFramework, out var frameworkInfo) && frameworkInfo.IsDnx)
{
await this.RestoreAsync(logger, cancellationToken).ConfigureAwait(false);

Expand Down Expand Up @@ -794,15 +797,17 @@ await AsyncHelper.RunAsync(async () =>
try
{
var assetsFile = new FileInfo(Path.Combine(this.DirectoryPath, "obj", "project.assets.json")).FullName;
if (File.Exists(assetsFile))
if (File.Exists(assetsFile) && !(this.TargetFramework.Contains("-") && !this.TargetFramework.ToLower().Contains("windows")))
{
LockFile lockFile = LockFileUtilities.GetLockFile(assetsFile, logger as NuGet.Common.ILogger);

if (lockFile != null)
{
if (lockFile.Targets.Count == 1)
LockFileTarget target = lockFile.Targets.Count == 1 ? lockFile.Targets[0] : lockFile.Targets.FirstOrDefault(t =>
t.Name.StartsWith(this.TargetFramework, StringComparison.InvariantCultureIgnoreCase) //this.TargetFramework:net7.0-windows, targets:net7.0-windows7.0
|| this.TargetFramework.StartsWith(t.Name, StringComparison.InvariantCultureIgnoreCase));//this.TargetFramework:net7.0-windows10.0.19041.0, targets:net7.0-windows10.0.19041
if (target != null)
{
foreach (var lib in lockFile.Targets[0].Libraries)
foreach (var lib in target.Libraries)
{
bool isPackage = StringComparer.OrdinalIgnoreCase.Compare(lib.Type, "package") == 0;

Expand Down Expand Up @@ -970,7 +975,7 @@ public async Task<IEnumerable<KeyValuePair<string, string>>> ResolveProperyValue

if (propertyTable.Count() != propertyNames.Count())
{
propertyTable = await _propertyResolver.EvaluateProjectPropertiesAsync(this.FullPath, this.TargetFrameworks.FirstOrDefault(), propertyNames, this.GlobalProperties, logger, cancellationToken).ConfigureAwait(false);
propertyTable = await _propertyResolver.EvaluateProjectPropertiesAsync(this.FullPath, this.TargetFramework, propertyNames, this.GlobalProperties, logger, cancellationToken).ConfigureAwait(false);

foreach (var entry in propertyTable)
{
Expand Down Expand Up @@ -1000,7 +1005,7 @@ private async Task<string> ResolveDepsFilePathFromBuildConfigAsync(string output
{
var depsFiles = Directory.GetFiles(binFolder, "*", SearchOption.AllDirectories)
.Where(d => Path.GetFileName(d).Equals(fileName, RuntimeEnvironmentHelper.FileStringComparison))
.Where(f => PathHelper.GetFolderName(Path.GetDirectoryName(f)) == this.TargetFrameworks.FirstOrDefault())
.Where(f => PathHelper.GetFolderName(Path.GetDirectoryName(f)) == this.TargetFramework || Directory.GetParent(Directory.GetParent(f).FullName).Name == this.TargetFramework)
.Select(f => new FileInfo(f))
.OrderByDescending(f => f.CreationTimeUtc);

Expand Down
11 changes: 0 additions & 11 deletions src/dotnet-svcutil/lib/src/Shared/TargetFrameworkHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,6 @@ public static string GetBestFitTargetFramework(IEnumerable<string> targetFramewo
}
}

if (fxInfo != null)
{
return fxInfo.FullName;
}

return targetFramework;
}

Expand Down Expand Up @@ -212,12 +207,6 @@ public static bool IsSupportedFramework(string fullFrameworkName, out FrameworkI
{
bool isSupported = false;

var tfx = fullFrameworkName.Split('-');
if (tfx.Length > 1)
{
fullFrameworkName = tfx[0];
}

if (FrameworkInfo.TryParse(fullFrameworkName, out frameworkInfo))
{
isSupported = (frameworkInfo.Name == FrameworkInfo.Netstandard && frameworkInfo.Version >= MinSupportedNetStandardVersion) ||
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace ServiceReference
{
using System.Runtime.Serialization;


[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "99.99.99")]
[System.Runtime.Serialization.DataContractAttribute(Name="TypeReuseCompositeType", Namespace="http://schemas.datacontract.org/2004/07/TypesLib")]
public partial class TypeReuseCompositeType : object
{

private bool BoolValueField;

private string StringValueField;

[System.Runtime.Serialization.DataMemberAttribute()]
public bool BoolValue
{
get
{
return this.BoolValueField;
}
set
{
this.BoolValueField = value;
}
}

[System.Runtime.Serialization.DataMemberAttribute()]
public string StringValue
{
get
{
return this.StringValueField;
}
set
{
this.StringValueField = value;
}
}
}

[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "99.99.99")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="ServiceReference.ITypeReuseSvc")]
public interface ITypeReuseSvc
{

[System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/ITypeReuseSvc/GetData", ReplyAction="http://tempuri.org/ITypeReuseSvc/GetDataResponse")]
System.Threading.Tasks.Task<BinLib.BinLibrary> GetDataAsync(int value);

[System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/ITypeReuseSvc/GetDataUsingDataContract", ReplyAction="http://tempuri.org/ITypeReuseSvc/GetDataUsingDataContractResponse")]
System.Threading.Tasks.Task<ServiceReference.TypeReuseCompositeType> GetDataUsingDataContractAsync(ServiceReference.TypeReuseCompositeType composite);
}

[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "99.99.99")]
public interface ITypeReuseSvcChannel : ServiceReference.ITypeReuseSvc, System.ServiceModel.IClientChannel
{
}

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "99.99.99")]
public partial class TypeReuseSvcClient : System.ServiceModel.ClientBase<ServiceReference.ITypeReuseSvc>, ServiceReference.ITypeReuseSvc
{

/// <summary>
/// Implement this partial method to configure the service endpoint.
/// </summary>
/// <param name="serviceEndpoint">The endpoint to configure</param>
/// <param name="clientCredentials">The client credentials</param>
static partial void ConfigureEndpoint(System.ServiceModel.Description.ServiceEndpoint serviceEndpoint, System.ServiceModel.Description.ClientCredentials clientCredentials);

public TypeReuseSvcClient() :
base(TypeReuseSvcClient.GetDefaultBinding(), TypeReuseSvcClient.GetDefaultEndpointAddress())
{
this.Endpoint.Name = EndpointConfiguration.BasicHttpBinding_ITypeReuseSvc.ToString();
ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
}

public TypeReuseSvcClient(EndpointConfiguration endpointConfiguration) :
base(TypeReuseSvcClient.GetBindingForEndpoint(endpointConfiguration), TypeReuseSvcClient.GetEndpointAddress(endpointConfiguration))
{
this.Endpoint.Name = endpointConfiguration.ToString();
ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
}

public TypeReuseSvcClient(EndpointConfiguration endpointConfiguration, string remoteAddress) :
base(TypeReuseSvcClient.GetBindingForEndpoint(endpointConfiguration), new System.ServiceModel.EndpointAddress(remoteAddress))
{
this.Endpoint.Name = endpointConfiguration.ToString();
ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
}

public TypeReuseSvcClient(EndpointConfiguration endpointConfiguration, System.ServiceModel.EndpointAddress remoteAddress) :
base(TypeReuseSvcClient.GetBindingForEndpoint(endpointConfiguration), remoteAddress)
{
this.Endpoint.Name = endpointConfiguration.ToString();
ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
}

public TypeReuseSvcClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
base(binding, remoteAddress)
{
}

public System.Threading.Tasks.Task<BinLib.BinLibrary> GetDataAsync(int value)
{
return base.Channel.GetDataAsync(value);
}

public System.Threading.Tasks.Task<ServiceReference.TypeReuseCompositeType> GetDataUsingDataContractAsync(ServiceReference.TypeReuseCompositeType composite)
{
return base.Channel.GetDataUsingDataContractAsync(composite);
}

public virtual System.Threading.Tasks.Task OpenAsync()
{
return System.Threading.Tasks.Task.Factory.FromAsync(((System.ServiceModel.ICommunicationObject)(this)).BeginOpen(null, null), new System.Action<System.IAsyncResult>(((System.ServiceModel.ICommunicationObject)(this)).EndOpen));
}

private static System.ServiceModel.Channels.Binding GetBindingForEndpoint(EndpointConfiguration endpointConfiguration)
{
if ((endpointConfiguration == EndpointConfiguration.BasicHttpBinding_ITypeReuseSvc))
{
System.ServiceModel.BasicHttpBinding result = new System.ServiceModel.BasicHttpBinding();
result.MaxBufferSize = int.MaxValue;
result.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max;
result.MaxReceivedMessageSize = int.MaxValue;
result.AllowCookies = true;
return result;
}
throw new System.InvalidOperationException(string.Format("Could not find endpoint with name \'{0}\'.", endpointConfiguration));
}

private static System.ServiceModel.EndpointAddress GetEndpointAddress(EndpointConfiguration endpointConfiguration)
{
if ((endpointConfiguration == EndpointConfiguration.BasicHttpBinding_ITypeReuseSvc))
{
return new System.ServiceModel.EndpointAddress("http://localhost:51074/TypeReuseSvc.svc");
}
throw new System.InvalidOperationException(string.Format("Could not find endpoint with name \'{0}\'.", endpointConfiguration));
}

private static System.ServiceModel.Channels.Binding GetDefaultBinding()
{
return TypeReuseSvcClient.GetBindingForEndpoint(EndpointConfiguration.BasicHttpBinding_ITypeReuseSvc);
}

private static System.ServiceModel.EndpointAddress GetDefaultEndpointAddress()
{
return TypeReuseSvcClient.GetEndpointAddress(EndpointConfiguration.BasicHttpBinding_ITypeReuseSvc);
}

public enum EndpointConfiguration
{

BasicHttpBinding_ITypeReuseSvc,
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"providerId": "Microsoft.Tools.ServiceModel.Svcutil",
"version": "99.99.99",
"options": {
"inputs": [
"$testCasesPath$/wsdl/TypeReuseSvc.wsdl"
],
"namespaceMappings": [
"*, ServiceReference"
],
"outputFile": "Reference.cs",
"references": [
"$TEMP$MultiTargetTypeReuse//TypeReuseClient//bin//Debug//net6.0//BinLib.dll"
],
"targetFramework": "N.N",
"typeReuseMode": "All"
}
}
Loading

0 comments on commit 90240d0

Please sign in to comment.