Skip to content

Commit

Permalink
new modern AddReference(.., AddReferenceOptions)
Browse files Browse the repository at this point in the history
older signatures has been marked as obsolete and scheduled to be removed in future versions
  • Loading branch information
3F committed Apr 28, 2024
1 parent 1f8b392 commit 5327937
Show file tree
Hide file tree
Showing 3 changed files with 270 additions and 45 deletions.
132 changes: 132 additions & 0 deletions MvsSln/Core/AddReferenceOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*!
* Copyright (c) 2013 Denis Kuzmin <[email protected]> github/3F
* Copyright (c) MvsSln contributors https://github.com/3F/MvsSln/graphs/contributors
* Licensed under the MIT License (MIT).
* See accompanying License.txt file or visit https://github.com/3F/MvsSln
*/

using System;

namespace net.r_eg.MvsSln.Core
{
[Flags]
public enum AddReferenceOptions
{
None,

Default = HideEmbedInteropTypes | HideSpecificVersion,

DefaultResolve = Default
| ResolveAssemblyName
| OmitArchitecture
| OmitCultureNeutral
| OmitPublicKeyTokenNull,

Mini = Default | HidePrivate,

MiniResolve = Mini | DefaultResolve | OmitCulture,

MicroResolve = MiniResolve | OmitPublicKeyToken,

NanoResolve = MiniResolve | OmitVersion,

PicoResolve = NanoResolve | MicroResolve,

DefaultAsm = Default | MakeRelativePath,

/// <summary>
/// Meta `Private` (n. Copy Local) = true.
/// </summary>
/// <remarks>If not, false. In addition, see <see cref="HidePrivate"/> to disable it.</remarks>
Private = 1,

/// <summary>
/// Meta `EmbedInteropTypes` = true.
/// </summary>
/// <remarks>If not, false. In addition, see <see cref="HideEmbedInteropTypes"/> to disable it.</remarks>
EmbedInteropTypes = 2,

/// <summary>
/// Meta 'SpecificVersion' = true.
/// </summary>
/// <remarks>If not, false. In addition, see <see cref="HideSpecificVersion"/> to disable it.</remarks>
SpecificVersion = 4,

/// <summary>
/// Do not generate `Private` meta at all.
/// </summary>
HidePrivate = 8,

/// <summary>
/// Do not generate `EmbedInteropTypes` meta at all.
/// </summary>
HideEmbedInteropTypes = 0x10,

/// <summary>
/// Do not generate `SpecificVersion` meta at all.
/// </summary>
HideSpecificVersion = 0x20,

/// <summary>
/// Do not generate `HintPath` meta at all.
/// </summary>
HideHintPath = 0x40,

/// <summary>
/// Evaluate properties from input paths.
/// e.g. `metacor\$(namespace)\$(libname)` as `metacor\net.r_eg.DllExport\DllExport.dll`
/// </summary>
EvaluatePath = 0x80,

/// <summary>
/// Save `HintPath` meta with evaluated value.
/// </summary>
/// <remarks>Activates <see cref="EvaluatePath"/></remarks>
EvaluatedHintPath = 0x100 | EvaluatePath,

/// <summary>
/// Resolve assembly name using full path to module.
/// </summary>
ResolveAssemblyName = 0x200,

/// <summary>
/// Do not specify `Version` in Include.
/// </summary>
/// <remarks>~ Version=1.5.1.35977</remarks>
OmitVersion = 0x400,

/// <summary>
/// Do not specify `PublicKeyToken=null` in Include.
/// </summary>
OmitPublicKeyTokenNull = 0x800,

/// <summary>
/// Do not specify `PublicKeyToken` in Include.
/// </summary>
/// <remarks>~ PublicKeyToken=4bbd2ef743db151e</remarks>
OmitPublicKeyToken = 0x1000,

/// <summary>
/// Do not specify `Culture=neutral` in Include.
/// </summary>
/// <remarks>Culture=neutral</remarks>
OmitCultureNeutral = 0x2000,

/// <summary>
/// Do not specify `Culture` in Include.
/// </summary>
/// <remarks>~ Culture=...</remarks>
OmitCulture = 0x4000 | OmitCultureNeutral,

/// <summary>
/// Do not specify `processorArchitecture` in Include.
/// </summary>
/// <remarks>~ processorArchitecture=...</remarks>
OmitArchitecture = 0x8000,

/// <summary>
/// Make relative path for `HintPath` meta when it is possible.
/// </summary>
MakeRelativePath = 0x10_000,
}
}
16 changes: 15 additions & 1 deletion MvsSln/Core/IXProject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -241,12 +241,23 @@ public interface IXProject
/// <returns></returns>
string GetFullPath(string relative);

/// <inheritdoc cref="AddReference(string, string, AddReferenceOptions)"/>
bool AddReference(string inc, AddReferenceOptions options = AddReferenceOptions.Default);

/// <summary>
/// Adds 'Reference' item using <see cref="Assembly"/> information.
/// </summary>
/// <inheritdoc cref="AddReference(string, string, AddReferenceOptions)"/>
bool AddReference(Assembly asm, AddReferenceOptions options = AddReferenceOptions.DefaultAsm);

/// <summary>
/// Adds 'Reference' item.
/// </summary>
/// <param name="inc">Include attribute.</param>
/// <param name="path">Path to module for meta 'HintPath' or related.</param>
/// <param name="options">Options to control Include or Meta values.</param>
/// <returns></returns>
bool AddReference(string inc);
bool AddReference(string inc, string path, AddReferenceOptions options = AddReferenceOptions.Default);

/// <summary>
/// Adds 'Reference' item.
Expand All @@ -256,6 +267,7 @@ public interface IXProject
/// <param name="embed">Meta 'EmbedInteropTypes'.</param>
/// <param name="spec">Meta 'SpecificVersion'.</param>
/// <returns></returns>
[Obsolete("Use modern AddReference() with AddReferenceOptions instead. Scheduled to be removed in future versions.")]
bool AddReference(Assembly asm, bool local, bool? embed = null, bool? spec = null);

/// <summary>
Expand All @@ -266,6 +278,7 @@ public interface IXProject
/// <param name="embed">Meta 'EmbedInteropTypes'.</param>
/// <param name="spec">Meta 'SpecificVersion'.</param>
/// <returns></returns>
[Obsolete("Use modern AddReference() with AddReferenceOptions instead. Scheduled to be removed in future versions.")]
bool AddReference(string fullpath, bool local, bool? embed = null, bool? spec = null);

/// <summary>
Expand All @@ -277,6 +290,7 @@ public interface IXProject
/// <param name="embed">Meta 'EmbedInteropTypes'.</param>
/// <param name="spec">Meta 'SpecificVersion'.</param>
/// <returns></returns>
[Obsolete("Use modern AddReference() with AddReferenceOptions instead. Scheduled to be removed in future versions.")]
bool AddReference(string inc, string path, bool local, bool? embed = null, bool? spec = null);

/// <summary>
Expand Down
167 changes: 123 additions & 44 deletions MvsSln/Core/XProject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -451,66 +451,85 @@ public IEnumerable<PropertyItem> GetProperties()
/// <returns></returns>
public string GetFullPath(string relative) => Path.GetFullPath(Path.Combine(RootPath, relative));

/// <summary>
/// Adds 'Reference' item.
/// </summary>
/// <param name="inc">Include attribute.</param>
/// <returns></returns>
public bool AddReference(string inc) => AddItem(ITEM_REF, inc);
public bool AddReference(string inc, AddReferenceOptions options = AddReferenceOptions.Default)
{
if(inc == null) throw new ArgumentNullException(nameof(inc));
return AddReference(inc, path: null, options);
}

/// <summary>
/// Adds 'Reference' item.
/// </summary>
/// <param name="asm">Assembly for adding.</param>
/// <param name="local">Meta 'Private' - i.e. Copy Local.</param>
/// <param name="embed">Meta 'EmbedInteropTypes'.</param>
/// <param name="spec">Meta 'SpecificVersion'.</param>
/// <returns></returns>
public bool AddReference(Assembly asm, AddReferenceOptions options = AddReferenceOptions.DefaultAsm)
{
if(asm == null) throw new ArgumentNullException(nameof(asm));
return AddReference(asm.FullName, asm.Location, options | AddReferenceOptions.ResolveAssemblyName);
}

public bool AddReference(string inc, string path, AddReferenceOptions options = AddReferenceOptions.Default)
{
Dictionary<string, string> meta = [];

if(!options.HasFlag(AddReferenceOptions.HidePrivate))
{
meta["Private"] = options.HasFlag(AddReferenceOptions.Private).ToString();
}

if(!options.HasFlag(AddReferenceOptions.HideEmbedInteropTypes))
{
meta["EmbedInteropTypes"] = options.HasFlag(AddReferenceOptions.EmbedInteropTypes).ToString();
}

if(!options.HasFlag(AddReferenceOptions.HideSpecificVersion))
{
meta["SpecificVersion"] = options.HasFlag(AddReferenceOptions.SpecificVersion).ToString();
}

if((options & (AddReferenceOptions.EvaluatePath | AddReferenceOptions.EvaluatedHintPath)) != 0)
{
throw new NotImplementedException
(
"Planned to review together with ~Evaluate() methods in future releases. Alternatively, see implementation from https://github.com/3F/vsSolutionBuildEvent"
);
}

if(path != null && !options.HasFlag(AddReferenceOptions.HideHintPath))
{
meta["HintPath"] = options.HasFlag(AddReferenceOptions.MakeRelativePath)
? GetRelativePath(path) : path;
}

if(options.HasFlag(AddReferenceOptions.ResolveAssemblyName))
{
inc = GetAssemblyDisplayName(AssemblyName.GetAssemblyName(path ?? inc), options);
}

return AddItem(ITEM_REF, inc, meta);
}

[Obsolete("Use modern AddReference() with AddReferenceOptions instead. Scheduled to be removed in future versions.")]
public bool AddReference(Assembly asm, bool local, bool? embed = null, bool? spec = null)
{
return AddReference(asm.ToString(), GetRelativePath(asm.Location), local, embed, spec);
return AddReference(asm?.ToString(), GetRelativePath(asm?.Location), local, embed, spec);
}

/// <summary>
/// Adds 'Reference' item.
/// </summary>
/// <param name="fullpath">Full path to binary file.</param>
/// <param name="local">Meta 'Private' - i.e. Copy Local.</param>
/// <param name="embed">Meta 'EmbedInteropTypes'.</param>
/// <param name="spec">Meta 'SpecificVersion'.</param>
/// <returns></returns>
[Obsolete("Use modern AddReference() with AddReferenceOptions instead. Scheduled to be removed in future versions.")]
public bool AddReference(string fullpath, bool local, bool? embed = null, bool? spec = null)
{
//TODO: fullpath may contain unevaluated properties, e.g.: metalib\$(namespace)\$(libname)
string inc = AssemblyName.GetAssemblyName(fullpath).FullName;
return AddReference(inc, GetRelativePath(fullpath), local, embed, spec);
}

/// <summary>
/// Adds 'Reference' item.
/// </summary>
/// <param name="inc">Include attribute.</param>
/// <param name="path">Meta 'HintPath'.</param>
/// <param name="local">Meta 'Private' - i.e. Copy Local.</param>
/// <param name="embed">Meta 'EmbedInteropTypes'.</param>
/// <param name="spec">Meta 'SpecificVersion'.</param>
/// <returns></returns>
[Obsolete("Use modern AddReference() with AddReferenceOptions instead. Scheduled to be removed in future versions.")]
public bool AddReference(string inc, string path, bool local, bool? embed = null, bool? spec = null)
{
var meta = new Dictionary<string, string>() {
{ "HintPath", path },
{ "Private", local.ToString() }
};
AddReferenceOptions options = AddReferenceOptions.None;
if(local) options |= AddReferenceOptions.Private;

if(embed != null) {
meta["EmbedInteropTypes"] = embed.ToString();
}
if(embed == true) options |= AddReferenceOptions.EmbedInteropTypes;
else if(embed == null) options |= AddReferenceOptions.HideEmbedInteropTypes;

if(spec != null) {
meta["SpecificVersion"] = spec.ToString();
}
if(spec == true) options |= AddReferenceOptions.SpecificVersion;
else if(spec == null) options |= AddReferenceOptions.HideSpecificVersion;

return AddItem(ITEM_REF, inc, meta);
return AddReference(inc, path, options);
}

/// <summary>
Expand Down Expand Up @@ -785,6 +804,66 @@ protected bool AddImport(ProjectImportElement element, string condition, string
return true;
}

protected string GetAssemblyDisplayName(AssemblyName asm, AddReferenceOptions options)
{
if(asm == null) throw new ArgumentNullException(nameof(asm));

bool _No(AddReferenceOptions input) => (options & input) == 0;

if(_No(AddReferenceOptions.ResolveAssemblyName)) throw new ArgumentOutOfRangeException(nameof(options));

// MvsSln, Version=2.6.1.0, Culture=neutral, PublicKeyToken=4bbd2ef743db151e
// DllExport, Version=1.5.1.35977, Culture=neutral, PublicKeyToken=8337224c9ad9e356, processorArchitecture=MSIL

options &= ~AddReferenceOptions.ResolveAssemblyName; // note, this is necessary because of the logic below

if(_No(AddReferenceOptions.OmitVersion
| AddReferenceOptions.OmitPublicKeyTokenNull
| AddReferenceOptions.OmitPublicKeyToken
| AddReferenceOptions.OmitCultureNeutral
| AddReferenceOptions.OmitCulture
| AddReferenceOptions.OmitArchitecture))
{
return asm.FullName;
}

StringBuilder sb = new(asm.Name);

if(_No(AddReferenceOptions.OmitVersion)) sb.Append($", Version={asm.Version}");

if(_No(AddReferenceOptions.OmitCulture))
{
if(!string.IsNullOrEmpty(asm.CultureInfo?.Name))
{
sb.Append($", Culture={asm.CultureInfo?.Name}");
}
else if(_No(AddReferenceOptions.OmitCultureNeutral))
{
sb.Append($", Culture=neutral");
}
}

if(_No(AddReferenceOptions.OmitPublicKeyToken))
{
byte[] token = asm.GetPublicKeyToken();
if(token.Length > 0)
{
sb.Append($", PublicKeyToken={token.ToHexString()}");
}
else if(_No(AddReferenceOptions.OmitPublicKeyTokenNull))
{
sb.Append($", PublicKeyToken=null");
}
}

if(_No(AddReferenceOptions.OmitArchitecture))
{
sb.Append($", processorArchitecture={asm.ProcessorArchitecture}");
}

return sb.ToString();
}

private string FindGuid(Project eProject) => eProject?.GetProjectGuid().NullIfEmpty() ?? ProjectItem.project.pGuid;

#region DebuggerDisplay
Expand Down

0 comments on commit 5327937

Please sign in to comment.