Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/linker.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSven Boemer <sbomer@gmail.com>2020-04-24 01:15:06 +0300
committerGitHub <noreply@github.com>2020-04-24 01:15:06 +0300
commitd60f4eab2982dfef19d71dfcf2d5ab2286950af2 (patch)
treecdeabea66ef73ddee0693d93675d3ebe7d863e0d
parentf5a987566eef13ea8f06e1fcfd3680ce307ae51f (diff)
Add feature removal based on XML substitutions (#1132)
* Add feature removal based on XML substitutions * Don't use Dictionary.TryAdd on mono * Add doc comments to the task * Change feature argument and description * Use High importance for invalid features And for invalid feature settings * Look for embedded ILLink.Substitutions.xml * Undo indentation change * Tweak description slighty * Respond to feedback - Don't check for multiple feature settings of same feature - Put substitution step after BlacklistStep - Require a location in ctors that take an XPathDocument * Remove --exclude-feature from ILLink build * Add steps in a consistent order * Only allow passing boolean feature values * Use new logging APIs
-rw-r--r--src/ILLink.Tasks/LinkTask.cs18
-rw-r--r--src/linker/Linker.Steps/BlacklistStep.cs42
-rw-r--r--src/linker/Linker.Steps/BodySubstituterStep.cs87
-rw-r--r--src/linker/Linker.Steps/MarkStep.cs22
-rw-r--r--src/linker/Linker.Steps/ResolveFromXmlStep.cs21
-rw-r--r--src/linker/Linker/Driver.cs113
-rw-r--r--src/linker/Linker/LinkContext.cs19
-rw-r--r--src/linker/Mono.Linker.csproj1
-rw-r--r--src/linker/README.md8
-rw-r--r--test/ILLink.Tasks.Tests/ILLink.Tasks.Tests.cs46
-rw-r--r--test/Mono.Linker.Tests.Cases/Attributes/OnlyKeepUsed/ComAttributesAreRemovedWhenFeatureExcluded.cs3
-rw-r--r--test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/BaseRemovedEventSource.cs3
-rw-r--r--test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/BaseRemovedEventSourceEmptyBody.cs3
-rw-r--r--test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/Excluded.cs3
-rw-r--r--test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/LocalsOfModifiedMethodAreRemoved.cs3
-rw-r--r--test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/NonEventWithLog.cs3
-rw-r--r--test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/StubbedMethodWithExceptionHandlers.cs3
-rw-r--r--test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/UnusedComInterfaceIsRemovedWhenComFeatureExcluded.cs3
-rw-r--r--test/Mono.Linker.Tests.Cases/LinkXml/CanPreserveExcludedFeatureCom.cs3
-rw-r--r--test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnAssembly.cs3
-rw-r--r--test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnEvent.cs3
-rw-r--r--test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnField.cs3
-rw-r--r--test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnMethod.cs3
-rw-r--r--test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnProperty.cs3
-rw-r--r--test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnType.cs3
-rw-r--r--test/Mono.Linker.Tests.Cases/Substitutions/EmbeddedSubstitutions.cs25
-rw-r--r--test/Mono.Linker.Tests.Cases/Substitutions/EmbeddedSubstitutions.xml7
-rw-r--r--test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutions.cs51
-rw-r--r--test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutions.xml8
-rw-r--r--test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutionsGlobalFalse.xml9
-rw-r--r--test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutionsGlobalTrue.xml9
-rw-r--r--test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutionsInvalid.cs33
-rw-r--r--test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutionsInvalid.xml9
-rw-r--r--test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutionsNested.cs75
-rw-r--r--test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutionsNested.xml32
-rw-r--r--test/Mono.Linker.Tests.Cases/TestFramework/VerifyExpectModifiedAttributesWork.cs3
36 files changed, 601 insertions, 82 deletions
diff --git a/src/ILLink.Tasks/LinkTask.cs b/src/ILLink.Tasks/LinkTask.cs
index d5932913e..4f0193af5 100644
--- a/src/ILLink.Tasks/LinkTask.cs
+++ b/src/ILLink.Tasks/LinkTask.cs
@@ -107,6 +107,14 @@ namespace ILLink.Tasks
bool? _iPConstProp;
/// <summary>
+ /// A list of feature names used by the body substitution logic.
+ /// Each Item requires "Value" boolean metadata with the value of
+ /// the feature setting.
+ /// Maps to '--feature'.
+ /// </summary>
+ public ITaskItem [] FeatureSettings { get; set; }
+
+ /// <summary>
/// Boolean specifying whether to enable sealer optimization globally.
/// Maps to '--enable-opt sealer' or '--disable-opt sealer'.
/// </summary>
@@ -329,6 +337,16 @@ namespace ILLink.Tasks
}
}
+ if (FeatureSettings != null) {
+ foreach (var featureSetting in FeatureSettings) {
+ var feature = featureSetting.ItemSpec;
+ var featureValue = featureSetting.GetMetadata ("Value");
+ if (String.IsNullOrEmpty(featureValue))
+ throw new ArgumentException ("feature settings require \"Value\" metadata");
+ args.Append ("--feature ").Append (feature).Append(" ").AppendLine (featureValue);
+ }
+ }
+
if (LinkSymbols)
args.AppendLine ("-b");
diff --git a/src/linker/Linker.Steps/BlacklistStep.cs b/src/linker/Linker.Steps/BlacklistStep.cs
index c1187ce72..daa3d81c2 100644
--- a/src/linker/Linker.Steps/BlacklistStep.cs
+++ b/src/linker/Linker.Steps/BlacklistStep.cs
@@ -27,6 +27,7 @@
//
using System;
+using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Reflection;
@@ -41,13 +42,15 @@ namespace Mono.Linker.Steps {
protected override void Process ()
{
+ var steps_to_add = new Stack<IStep> ();
+
foreach (string name in Assembly.GetExecutingAssembly ().GetManifestResourceNames ()) {
- if (!name.EndsWith (".xml", StringComparison.OrdinalIgnoreCase) || !ShouldProcessAssemblyResource (GetAssemblyName (name)))
+ if (!name.EndsWith (".xml", StringComparison.OrdinalIgnoreCase) || !ShouldProcessRootDescriptorResource (GetAssemblyName (name)))
continue;
try {
Context.LogMessage ($"Processing resource linker descriptor: {name}");
- AddToPipeline (GetResolveStep (name));
+ steps_to_add.Push (GetResolveStep (name));
} catch (XmlException ex) {
/* This could happen if some broken XML file is included. */
Context.LogMessage ($"Error processing {name}: {ex}");
@@ -55,22 +58,36 @@ namespace Mono.Linker.Steps {
}
foreach (var asm in Context.GetAssemblies ()) {
- foreach (var rsc in asm.Modules
- .SelectMany (mod => mod.Resources)
- .Where (res => res.ResourceType == ResourceType.Embedded)
- .Where (res => res.Name.EndsWith (".xml", StringComparison.OrdinalIgnoreCase))
- .Where (res => ShouldProcessAssemblyResource (GetAssemblyName (res.Name)))
+ var embeddedXml = asm.Modules
+ .SelectMany (mod => mod.Resources)
+ .Where (res => res.ResourceType == ResourceType.Embedded)
+ .Where (res => res.Name.EndsWith (".xml", StringComparison.OrdinalIgnoreCase));
+ foreach (var rsc in embeddedXml
+ .Where (res => ShouldProcessRootDescriptorResource (GetAssemblyName (res.Name)))
.Cast<EmbeddedResource> ()) {
try {
Context.LogMessage ($"Processing embedded resource linker descriptor: {rsc.Name}");
-
- AddToPipeline (GetExternalResolveStep (rsc, asm));
+ steps_to_add.Push (GetExternalResolveStep (rsc, asm));
} catch (XmlException ex) {
/* This could happen if some broken XML file is embedded. */
Context.LogMessage ($"Error processing {rsc.Name}: {ex}");
}
}
+
+ foreach (var rsc in embeddedXml
+ .Where (res => res.Name.Equals ("ILLink.Substitutions.xml", StringComparison.OrdinalIgnoreCase))
+ .Cast<EmbeddedResource> ()) {
+ try {
+ Context.LogMessage ($"Processing embedded {rsc.Name} from {asm.Name}");
+ steps_to_add.Push (GetExternalSubstitutionStep (rsc, asm));
+ } catch (XmlException ex) {
+ Context.LogMessage ($"Error processing {rsc.Name}: {ex}");
+ }
+ }
}
+
+ foreach (var step in steps_to_add)
+ AddToPipeline (step);
}
static string GetAssemblyName (string descriptor)
@@ -82,7 +99,7 @@ namespace Mono.Linker.Steps {
return descriptor.Substring (0, pos);
}
- bool ShouldProcessAssemblyResource (string name)
+ bool ShouldProcessRootDescriptorResource (string name)
{
AssemblyDefinition assembly = Context.GetLoadedAssembly (name);
@@ -110,6 +127,11 @@ namespace Mono.Linker.Steps {
return new ResolveFromXmlStep (GetExternalDescriptor (resource), resource.Name, assembly, "resource " + resource.Name + " in " + assembly.FullName);
}
+ IStep GetExternalSubstitutionStep (EmbeddedResource resource, AssemblyDefinition assembly)
+ {
+ return new BodySubstituterStep (GetExternalDescriptor (resource), resource.Name, assembly, "resource " + resource.Name + " in " + assembly.FullName);
+ }
+
static ResolveFromXmlStep GetResolveStep (string descriptor)
{
return new ResolveFromXmlStep (GetDescriptor (descriptor), "descriptor " + descriptor + " from " + Assembly.GetExecutingAssembly ().FullName);
diff --git a/src/linker/Linker.Steps/BodySubstituterStep.cs b/src/linker/Linker.Steps/BodySubstituterStep.cs
index 9ea44b55b..4f00fc177 100644
--- a/src/linker/Linker.Steps/BodySubstituterStep.cs
+++ b/src/linker/Linker.Steps/BodySubstituterStep.cs
@@ -1,5 +1,4 @@
using System;
-using System.IO;
using System.Linq;
using System.Globalization;
using System.Xml.XPath;
@@ -9,37 +8,63 @@ namespace Mono.Linker.Steps
{
public class BodySubstituterStep : BaseStep
{
- protected override void Process ()
+ readonly XPathDocument _document;
+ readonly string _xmlDocumentLocation;
+ readonly string _resourceName;
+ readonly AssemblyDefinition _resourceAssembly;
+
+ public BodySubstituterStep (XPathDocument document, string xmlDocumentLocation)
{
- var files = Context.Substitutions;
- if (files == null)
- return;
+ _document = document;
+ _xmlDocumentLocation = xmlDocumentLocation;
+ }
- foreach (var file in files) {
- try {
- ReadSubstitutionFile (GetSubstitutions (file));
- } catch (Exception ex) when (!(ex is XmlResolutionException)) {
- throw new XmlResolutionException ($"Failed to process XML substitution '{file}'", ex);
- }
+ public BodySubstituterStep (XPathDocument document, string resourceName, AssemblyDefinition resourceAssembly, string xmlDocumentLocation = "")
+ : this (document, xmlDocumentLocation)
+ {
+ if (string.IsNullOrEmpty (resourceName))
+ throw new ArgumentNullException (nameof (resourceName));
- }
+ _resourceName = resourceName;
+ _resourceAssembly = resourceAssembly ?? throw new ArgumentNullException (nameof (resourceAssembly));
}
- static XPathDocument GetSubstitutions (string substitutionsFile)
+ protected override void Process ()
{
- using (FileStream fs = File.OpenRead (substitutionsFile)) {
- return GetSubstitutions (fs);
+ try {
+ ReadSubstitutions (_document);
+
+ if (!string.IsNullOrEmpty (_resourceName) && Context.StripResources)
+ Context.Annotations.AddResourceToRemove (_resourceAssembly, _resourceName);
+ } catch (Exception ex) when (!(ex is XmlResolutionException)) {
+ throw new XmlResolutionException ($"Failed to process XML substitution: '{_xmlDocumentLocation}'", ex);
}
}
- static XPathDocument GetSubstitutions (Stream substitutions)
+ bool ShouldProcessSubstitutions (XPathNavigator nav)
{
- using (StreamReader sr = new StreamReader (substitutions)) {
- return new XPathDocument (sr);
+ var feature = GetAttribute (nav, "feature");
+ if (string.IsNullOrEmpty (feature))
+ return true;
+
+ var value = GetAttribute (nav, "featurevalue");
+ if (string.IsNullOrEmpty (value)) {
+ Context.LogMessage (MessageContainer.CreateErrorMessage ($"Feature {feature} does not specify a \"featurevalue\" attribute", 1001));
+ return false;
}
+
+ if (!bool.TryParse (value, out bool bValue)) {
+ Context.LogMessage(MessageContainer.CreateErrorMessage ($"Unsupported non-boolean feature definition {feature}", 1002));
+ return false;
+ }
+
+ if (Context.FeatureSettings == null || !Context.FeatureSettings.TryGetValue (feature, out bool featureSetting))
+ return false;
+
+ return bValue == featureSetting;
}
- void ReadSubstitutionFile (XPathDocument document)
+ void ReadSubstitutions (XPathDocument document)
{
XPathNavigator nav = document.CreateNavigator ();
@@ -47,7 +72,8 @@ namespace Mono.Linker.Steps
if (!nav.MoveToChild ("linker", ""))
return;
- // TODO: Add handling for feature
+ if (!ShouldProcessSubstitutions (nav))
+ return;
ProcessAssemblies (nav.SelectChildren ("assembly", ""));
}
@@ -57,6 +83,9 @@ namespace Mono.Linker.Steps
while (iterator.MoveNext ()) {
var name = GetAssemblyName (iterator.Current);
+ if (!ShouldProcessSubstitutions (iterator.Current))
+ continue;
+
AssemblyDefinition assembly = Context.GetLoadedAssembly (name.Name);
if (assembly == null) {
@@ -78,6 +107,9 @@ namespace Mono.Linker.Steps
while (iterator.MoveNext ()) {
XPathNavigator nav = iterator.Current;
+ if (!ShouldProcessSubstitutions (nav))
+ continue;
+
string fullname = GetAttribute (nav, "fullname");
TypeDefinition type = assembly.MainModule.GetType (fullname);
@@ -96,21 +128,32 @@ namespace Mono.Linker.Steps
if (!nav.HasChildren)
return;
+ if (!ShouldProcessSubstitutions (nav))
+ return;
+
XPathNodeIterator methods = nav.SelectChildren ("method", "");
if (methods.Count > 0)
ProcessMethods (type, methods);
var fields = nav.SelectChildren ("field", "");
if (fields.Count > 0) {
- while (fields.MoveNext ())
+ while (fields.MoveNext ()) {
+ if (!ShouldProcessSubstitutions (fields.Current))
+ continue;
+
ProcessField (type, fields);
+ }
}
}
void ProcessMethods (TypeDefinition type, XPathNodeIterator iterator)
{
- while (iterator.MoveNext ())
+ while (iterator.MoveNext ()) {
+ if (!ShouldProcessSubstitutions (iterator.Current))
+ continue;
+
ProcessMethod (type, iterator);
+ }
}
void ProcessMethod (TypeDefinition type, XPathNodeIterator iterator)
diff --git a/src/linker/Linker.Steps/MarkStep.cs b/src/linker/Linker.Steps/MarkStep.cs
index ecd44c47c..3caf171ce 100644
--- a/src/linker/Linker.Steps/MarkStep.cs
+++ b/src/linker/Linker.Steps/MarkStep.cs
@@ -1178,8 +1178,11 @@ namespace Mono.Linker.Steps {
{
// Keep default ctor for XmlSerializer support. See https://github.com/mono/linker/issues/957
MarkDefaultConstructor (type, new DependencyInfo (DependencyKind.SerializationMethodForType, type));
- if (!_context.IsFeatureExcluded ("deserialization"))
- MarkMethodsIf (type.Methods, IsSpecialSerializationConstructor, new DependencyInfo (DependencyKind.SerializationMethodForType, type));
+#if !FEATURE_ILLINK
+ if (_context.IsFeatureExcluded ("deserialization"))
+ return;
+#endif
+ MarkMethodsIf (type.Methods, IsSpecialSerializationConstructor, new DependencyInfo (DependencyKind.SerializationMethodForType, type));
}
internal protected virtual TypeDefinition MarkType (TypeReference reference, DependencyInfo reason)
@@ -1241,7 +1244,12 @@ namespace Mono.Linker.Steps {
// TODO: This needs work to ensure we handle EventSource appropriately.
// This marks static fields of KeyWords/OpCodes/Tasks subclasses of an EventSource type.
- if (!_context.IsFeatureExcluded ("etw") && BCL.EventTracingForWindows.IsEventSourceImplementation (type, _context)) {
+ if (
+#if !FEATURE_ILLINK
+ !_context.IsFeatureExcluded ("etw") &&
+#endif
+ BCL.EventTracingForWindows.IsEventSourceImplementation (type, _context)
+ ) {
MarkEventSourceProviders (type);
}
@@ -1288,10 +1296,14 @@ namespace Mono.Linker.Steps {
if (ShouldMarkTypeStaticConstructor (type) && reason.Kind != DependencyKind.TriggersCctorForCalledMethod)
MarkStaticConstructor (type, new DependencyInfo (DependencyKind.CctorForType, type));
- if (_context.IsFeatureExcluded ("deserialization"))
+#if !FEATURE_ILLINK
+ if (_context.IsFeatureExcluded ("deserialization")) {
MarkMethodsIf (type.Methods, HasOnSerializeAttribute, new DependencyInfo (DependencyKind.SerializationMethodForType, type));
- else
+ } else
+#endif
+ {
MarkMethodsIf (type.Methods, HasOnSerializeOrDeserializeAttribute, new DependencyInfo (DependencyKind.SerializationMethodForType, type));
+ }
}
DoAdditionalTypeProcessing (type);
diff --git a/src/linker/Linker.Steps/ResolveFromXmlStep.cs b/src/linker/Linker.Steps/ResolveFromXmlStep.cs
index b070b07db..529d48f72 100644
--- a/src/linker/Linker.Steps/ResolveFromXmlStep.cs
+++ b/src/linker/Linker.Steps/ResolveFromXmlStep.cs
@@ -62,7 +62,7 @@ namespace Mono.Linker.Steps {
readonly string _resourceName;
readonly AssemblyDefinition _resourceAssembly;
- public ResolveFromXmlStep (XPathDocument document, string xmlDocumentLocation = "<unspecified>")
+ public ResolveFromXmlStep (XPathDocument document, string xmlDocumentLocation)
{
_document = document;
_xmlDocumentLocation = xmlDocumentLocation;
@@ -108,8 +108,10 @@ namespace Mono.Linker.Steps {
protected virtual void ProcessAssembly (AssemblyDefinition assembly, XPathNodeIterator iterator)
{
+#if !FEATURE_ILLINK
if (IsExcluded (iterator.Current))
return;
+#endif
if (GetTypePreserve (iterator.Current) == TypePreserve.All) {
foreach (var type in assembly.MainModule.Types)
@@ -232,8 +234,10 @@ namespace Mono.Linker.Steps {
protected virtual void ProcessType (TypeDefinition type, XPathNavigator nav)
{
+#if !FEATURE_ILLINK
if (IsExcluded (nav))
return;
+#endif
TypePreserve preserve = GetTypePreserve (nav);
if (preserve != TypePreserve.Nothing)
@@ -328,8 +332,10 @@ namespace Mono.Linker.Steps {
protected virtual void ProcessField (TypeDefinition type, XPathNodeIterator iterator)
{
+#if !FEATURE_ILLINK
if (IsExcluded (iterator.Current))
return;
+#endif
string value = GetSignature (iterator.Current);
if (!String.IsNullOrEmpty (value))
@@ -394,8 +400,10 @@ namespace Mono.Linker.Steps {
protected virtual void ProcessMethod (TypeDefinition type, XPathNodeIterator iterator, bool required)
{
+#if !FEATURE_ILLINK
if (IsExcluded (iterator.Current))
return;
+#endif
string value = GetSignature (iterator.Current);
if (!String.IsNullOrEmpty (value))
@@ -490,9 +498,11 @@ namespace Mono.Linker.Steps {
protected virtual void ProcessEvent (TypeDefinition type, XPathNodeIterator iterator, bool required)
{
+#if !FEATURE_ILLINK
if (IsExcluded (iterator.Current))
return;
-
+#endif
+
string value = GetSignature (iterator.Current);
if (!String.IsNullOrEmpty (value))
ProcessEventSignature (type, value, required);
@@ -560,9 +570,11 @@ namespace Mono.Linker.Steps {
protected virtual void ProcessProperty (TypeDefinition type, XPathNodeIterator iterator, bool required)
{
+#if !FEATURE_ILLINK
if (IsExcluded (iterator.Current))
return;
-
+#endif
+
string value = GetSignature (iterator.Current);
if (!String.IsNullOrEmpty (value))
ProcessPropertySignature (type, value, GetAccessors (iterator.Current), required);
@@ -698,6 +710,7 @@ namespace Mono.Linker.Steps {
return nav.GetAttribute (attribute, _ns);
}
+#if !FEATURE_ILLINK
protected virtual bool IsExcluded (XPathNavigator nav)
{
var value = GetAttribute (nav, "feature");
@@ -706,7 +719,7 @@ namespace Mono.Linker.Steps {
return Context.IsFeatureExcluded (value);
}
-
+#endif
public override string ToString ()
{
diff --git a/src/linker/Linker/Driver.cs b/src/linker/Linker/Driver.cs
index 767e9c73f..880275471 100644
--- a/src/linker/Linker/Driver.cs
+++ b/src/linker/Linker/Driver.cs
@@ -178,9 +178,13 @@ namespace Mono.Linker {
#if !FEATURE_ILLINK
I18nAssemblies assemblies = I18nAssemblies.All;
-#endif
- var custom_steps = new List<string> ();
var excluded_features = new HashSet<string> (StringComparer.Ordinal);
+ var resolve_from_xapi_steps = new Stack<string> ();
+#endif
+ var resolve_from_assembly_steps = new Stack<(string, ResolveFromAssemblyStep.RootVisibility)> ();
+ var resolve_from_xml_steps = new Stack<string> ();
+ var body_substituter_steps = new Stack<string> ();
+ var custom_steps = new Stack<string> ();
var set_optimizations = new List<(CodeOptimizations, string, bool)> ();
bool dumpDependencies = false;
string dependenciesFileName = null;
@@ -252,11 +256,11 @@ namespace Mono.Linker {
return -1;
}
- if (!GetStringParam (token, l => context.AddSubstitutionFile (l)))
+ if (!GetStringParam (token, l => body_substituter_steps.Push (l)))
return -1;
continue;
-
+#if !FEATURE_ILLINK
case "--exclude-feature":
if (arguments.Count < 1) {
ErrorMissingArgument (token);
@@ -272,7 +276,7 @@ namespace Mono.Linker {
return -1;
continue;
-
+#endif
case "--explicit-reflection":
if (!GetBoolParam (token, l => context.AddReflectionAnnotations = l))
return -1;
@@ -280,7 +284,7 @@ namespace Mono.Linker {
continue;
case "--custom-step":
- if (!GetStringParam (token, l => custom_steps.Add (l)))
+ if (!GetStringParam (token, l => custom_steps.Push (l)))
return -1;
continue;
@@ -320,29 +324,46 @@ namespace Mono.Linker {
continue;
case "--disable-opt":
- if (!GetStringParam (token, l => {
- if (!GetOptimizationName (l, out var opt))
- return;
+ {
+ string optName = null;
+ if (!GetStringParam (token, l => optName = l))
+ return -1;
- string assemblyName = GetNextStringValue ();
- set_optimizations.Add ((opt, assemblyName, false));
- }))
+ if (!GetOptimizationName (optName, out var opt))
return -1;
- continue;
+ string assemblyName = GetNextStringValue ();
+ set_optimizations.Add ((opt, assemblyName, false));
+ continue;
+ }
case "--enable-opt":
- if (!GetStringParam (token, l => {
- if (!GetOptimizationName (l, out var opt))
- return;
-
- string assemblyName = GetNextStringValue ();
- set_optimizations.Add ((opt, assemblyName, true));
- }))
+ {
+ string optName = null;
+ if (!GetStringParam (token, l => optName = l))
+ return -1;
+
+ if (!GetOptimizationName (optName, out var opt))
return -1;
+ string assemblyName = GetNextStringValue ();
+ set_optimizations.Add ((opt, assemblyName, true));
+
continue;
+ }
+ case "--feature":
+ {
+ string featureName = null;
+ if (!GetStringParam (token, l => featureName = l))
+ return -1;
+
+ if (!GetBoolParam (token, value => {
+ context.SetFeatureValue (featureName, value);
+ }))
+ return -1;
+ continue;
+ }
case "--new-mvid":
//
// This is not same as --deterministic which calculates MVID
@@ -440,7 +461,7 @@ namespace Mono.Linker {
case "x":
if (!GetStringParam (token, l => {
foreach (string file in GetFiles (l))
- AddResolveFromXmlStep (p, file);
+ resolve_from_xml_steps.Push (file);
}))
return -1;
@@ -454,7 +475,7 @@ namespace Mono.Linker {
? ResolveFromAssemblyStep.RootVisibility.PublicAndFamily
: ResolveFromAssemblyStep.RootVisibility.Any;
foreach (string file in GetFiles (l))
- p.PrependStep (new ResolveFromAssemblyStep (file, rootVisibility));
+ resolve_from_assembly_steps.Push ((file, rootVisibility));
}))
return -1;
@@ -464,7 +485,7 @@ namespace Mono.Linker {
case "i":
if (!GetStringParam (token, l => {
foreach (string file in GetFiles (l))
- p.PrependStep (new ResolveFromXApiStep (new XPathDocument (file)));
+ resolve_from_xapi_steps.Push (file);
}))
return -1;
@@ -538,6 +559,21 @@ namespace Mono.Linker {
//
// Modify the default pipeline
//
+
+#if !FEATURE_ILLINK
+ foreach (var file in resolve_from_xapi_steps)
+ p.PrependStep (new ResolveFromXApiStep (new XPathDocument (file)));
+#endif
+
+ foreach (var file in resolve_from_xml_steps)
+ AddResolveFromXmlStep (p, file);
+
+ foreach (var (file, rootVisibility) in resolve_from_assembly_steps)
+ p.PrependStep (new ResolveFromAssemblyStep (file, rootVisibility));
+
+ foreach (var file in body_substituter_steps)
+ AddBodySubstituterStep (p, file);
+
if (ignoreDescriptors)
p.RemoveStep (typeof (BlacklistStep));
@@ -550,20 +586,17 @@ namespace Mono.Linker {
#if !FEATURE_ILLINK
p.AddStepAfter (typeof (LoadReferencesStep), new LoadI18nAssemblies (assemblies));
- if (assemblies != I18nAssemblies.None) {
+ if (assemblies != I18nAssemblies.None)
p.AddStepAfter (typeof (PreserveDependencyLookupStep), new PreserveCalendarsStep (assemblies));
- }
#endif
- if (_needAddBypassNGenStep) {
+ if (_needAddBypassNGenStep)
p.AddStepAfter (typeof (SweepStep), new AddBypassNGenStep ());
- }
-
- p.AddStepBefore (typeof (MarkStep), new BodySubstituterStep ());
if (removeCAS)
p.AddStepBefore (typeof (MarkStep), new RemoveSecurityStep ());
+#if !FEATURE_ILLINK
if (excluded_features.Count > 0) {
p.AddStepBefore (typeof (MarkStep), new RemoveFeaturesStep () {
FeatureCOM = excluded_features.Contains ("com"),
@@ -576,6 +609,7 @@ namespace Mono.Linker {
excluded_features.CopyTo (excluded);
context.ExcludedFeatures = excluded;
}
+#endif
p.AddStepBefore (typeof (MarkStep), new RemoveUnreachableBlocksStep ());
p.AddStepBefore (typeof (OutputStep), new ClearInitLocalsStep ());
@@ -584,13 +618,21 @@ namespace Mono.Linker {
//
// Pipeline setup with all steps enabled
//
+ // ResolveFromAssemblyStep [optional, possibly many]
+ // ResolveFromXmlStep [optional, possibly many]
+ // [mono only] ResolveFromXApiStep [optional, possibly many]
// LoadReferencesStep
+ // [mono only] LoadI18nAssemblies
// BlacklistStep [optional]
+ // dynamically adds steps:
+ // ResolveFromXmlStep [optional, possibly many]
+ // BodySubstituterStep [optional, possibly many]
// PreserveDependencyLookupStep
+ // [mono only] PreselveCalendarsStep [optional]
// TypeMapStep
// BodySubstituterStep [optional]
// RemoveSecurityStep [optional]
- // RemoveFeaturesStep [optional]
+ // [mono only] RemoveFeaturesStep [optional]
// RemoveUnreachableBlocksStep [optional]
// MarkStep
// ReflectionBlockedStep [optional]
@@ -600,6 +642,7 @@ namespace Mono.Linker {
// CleanStep
// RegenerateGuidStep [optional]
// ClearInitLocalsStep
+ // SealerStep
// OutputStep
//
@@ -651,6 +694,11 @@ namespace Mono.Linker {
pipeline.PrependStep (new ResolveFromXmlStep (new XPathDocument (file), file));
}
+ void AddBodySubstituterStep (Pipeline pipeline, string file)
+ {
+ pipeline.AddStepBefore (typeof (MarkStep), new BodySubstituterStep (new XPathDocument (file), file));
+ }
+
protected virtual void AddXmlDependencyRecorder (LinkContext context, string fileName)
{
context.Tracer.AddRecorder (new XmlDependencyRecorder (context, fileName));
@@ -952,16 +1000,17 @@ namespace Mono.Linker {
Console.WriteLine (" --enable-opt NAME [ASM] Enable one of the additional optimizations globaly or for a specific assembly name");
Console.WriteLine (" clearinitlocals: Remove initlocals");
Console.WriteLine (" sealer: Any method or type which does not have override is marked as sealed");
+#if !FEATURE_ILLINK
Console.WriteLine (" --exclude-feature NAME Any code which has a feature <name> in linked assemblies will be removed");
Console.WriteLine (" com: Support for COM Interop");
Console.WriteLine (" etw: Event Tracing for Windows");
-#if !FEATURE_ILLINK
Console.WriteLine (" remoting: .NET Remoting dependencies");
-#endif
Console.WriteLine (" sre: System.Reflection.Emit namespace");
Console.WriteLine (" globalization: Globalization data and globalization behavior");
+#endif
Console.WriteLine (" --explicit-reflection Adds to members never used through reflection DisablePrivateReflection attribute. Defaults to false");
Console.WriteLine (" --keep-dep-attributes Keep attributes used for manual dependency tracking. Defaults to false");
+ Console.WriteLine (" --feature FEATURE VALUE Apply any optimizations defined when this feature setting is a constant known at link time");
Console.WriteLine (" --new-mvid Generate a new guid for each linked assembly (short -g). Defaults to true");
Console.WriteLine (" --strip-resources Remove XML descriptor resources for linked assemblies. Defaults to true");
Console.WriteLine (" --strip-security Remove metadata and code related to Code Access Security. Defaults to true");
diff --git a/src/linker/Linker/LinkContext.cs b/src/linker/Linker/LinkContext.cs
index d4bf02463..79c21ba32 100644
--- a/src/linker/Linker/LinkContext.cs
+++ b/src/linker/Linker/LinkContext.cs
@@ -118,7 +118,7 @@ namespace Mono.Linker {
public bool StripResources { get; set; }
- public List<string> Substitutions { get; private set; }
+ public Dictionary<string, bool> FeatureSettings { get; private set; }
public List<string> AttributeDefinitions { get; private set; }
@@ -161,8 +161,9 @@ namespace Mono.Linker {
public IReflectionPatternRecorder ReflectionPatternRecorder { get; set; }
+#if !FEATURE_ILLINK
public string [] ExcludedFeatures { get; set; }
-
+#endif
public CodeOptimizationsSettings Optimizations { get; set; }
public bool AddReflectionAnnotations { get; set; }
@@ -214,17 +215,15 @@ namespace Mono.Linker {
Optimizations = new CodeOptimizationsSettings (defaultOptimizations);
}
- public void AddSubstitutionFile (string file)
+ public void SetFeatureValue (string feature, bool value)
{
- if (Substitutions == null) {
- Substitutions = new List<string> { file };
+ Debug.Assert (!String.IsNullOrEmpty (feature));
+ if (FeatureSettings == null) {
+ FeatureSettings = new Dictionary<string, bool> { { feature, value } };
return;
}
- if (Substitutions.Contains (file))
- return;
-
- Substitutions.Add (file);
+ FeatureSettings [feature] = value;
}
public void AddAttributeDefinitionFile (string file)
@@ -437,10 +436,12 @@ namespace Mono.Linker {
_resolver.Dispose ();
}
+#if !FEATURE_ILLINK
public bool IsFeatureExcluded (string featureName)
{
return ExcludedFeatures != null && Array.IndexOf (ExcludedFeatures, featureName) >= 0;
}
+#endif
public bool IsOptimizationEnabled (CodeOptimizations optimization, MemberReference context)
{
diff --git a/src/linker/Mono.Linker.csproj b/src/linker/Mono.Linker.csproj
index c7951ee91..947c270af 100644
--- a/src/linker/Mono.Linker.csproj
+++ b/src/linker/Mono.Linker.csproj
@@ -29,6 +29,7 @@
<Compile Remove="Linker\XApiReader.cs" />
<Compile Remove="Linker.Steps\LoadI18nAssemblies.cs" />
<Compile Remove="Linker.Steps\PreserveCalendarsStep.cs" />
+ <Compile Remove="Linker.Steps\RemoveFeaturesStep.cs" />
<Compile Remove="Linker.Steps\ResolveFromXApiStep.cs" />
</ItemGroup>
diff --git a/src/linker/README.md b/src/linker/README.md
index 318a19946..21ad02581 100644
--- a/src/linker/README.md
+++ b/src/linker/README.md
@@ -116,6 +116,9 @@ An example of a substitution XML file
<field name="MyNumericField" value="5" initialize="true">
</field>
</type>
+ <type fullname="UserCode.Substitutions.Playground" feature="EnableOptionalFeature" featurevalue="false">
+ <method signature="System.String UseOptionalFeature()" body="remove" />
+ </type>
</assembly>
</linker>
```
@@ -132,6 +135,11 @@ value and override the existing behaviour. The rule can also apply to static fie
if set to default value without explicit `initialize` setting could help to elide whole
explicit static constructor.
+The `feature` and `featurevalue` attributes are optional, but must be used together if they are used.
+They can be applied to any other element to specify conditions under which the contained substitutions
+are applied. For example, the above substitution for `UseOptionalFeature` will be applied only if
+`--feature EnableOptionalFeature false` is passed to the linker.
+
### Adding custom steps to the linker.
You can write custom steps for the linker and tell the linker to use them.
diff --git a/test/ILLink.Tasks.Tests/ILLink.Tasks.Tests.cs b/test/ILLink.Tasks.Tests/ILLink.Tasks.Tests.cs
index 44266efb7..bbae3a027 100644
--- a/test/ILLink.Tasks.Tests/ILLink.Tasks.Tests.cs
+++ b/test/ILLink.Tasks.Tests/ILLink.Tasks.Tests.cs
@@ -304,6 +304,52 @@ namespace ILLink.Tasks.Tests
}
}
+ public static IEnumerable<object []> FeatureSettingsCases => new List<object []> {
+ new object [] {
+ new ITaskItem [] {
+ new TaskItem ("FeatureName", new Dictionary<string, string> { { "Value", "true" } })
+ },
+ },
+ new object [] {
+ new ITaskItem [] {
+ new TaskItem ("FeatureName", new Dictionary<string, string> { { "Value", "true" } }),
+ new TaskItem ("FeatureName", new Dictionary<string, string> { { "Value", "false" } })
+ },
+ },
+ new object [] {
+ new ITaskItem [] {
+ new TaskItem ("FeatureName1", new Dictionary<string, string> { { "value", "true" } }),
+ new TaskItem ("FeatureName2", new Dictionary<string, string> { { "value", "false" } }),
+ },
+ },
+ };
+
+ [Theory]
+ [MemberData (nameof (FeatureSettingsCases))]
+ public void TestFeatureSettings (ITaskItem [] featureSettings)
+ {
+ var task = new MockTask () {
+ FeatureSettings = featureSettings
+ };
+ using (var driver = task.CreateDriver ()) {
+ var expectedSettings = featureSettings.Select (f => new { Feature = f.ItemSpec, Value = f.GetMetadata ("Value") })
+ .GroupBy (f => f.Feature)
+ .Select (f => f.Last())
+ .ToDictionary (f => f.Feature, f => bool.Parse(f.Value));
+ var actualSettings = driver.Context.FeatureSettings;
+ Assert.Equal (expectedSettings, actualSettings);
+ }
+ }
+
+ [Fact]
+ public void TestInvalidFeatureSettings ()
+ {
+ var task = new MockTask () {
+ FeatureSettings = new ITaskItem [] { new TaskItem ("FeatureName") }
+ };
+ Assert.Throws <ArgumentException> (() => task.CreateDriver ());
+ }
+
[Fact]
public void TestExtraArgs ()
{
diff --git a/test/Mono.Linker.Tests.Cases/Attributes/OnlyKeepUsed/ComAttributesAreRemovedWhenFeatureExcluded.cs b/test/Mono.Linker.Tests.Cases/Attributes/OnlyKeepUsed/ComAttributesAreRemovedWhenFeatureExcluded.cs
index ca83503d4..0fd0317f7 100644
--- a/test/Mono.Linker.Tests.Cases/Attributes/OnlyKeepUsed/ComAttributesAreRemovedWhenFeatureExcluded.cs
+++ b/test/Mono.Linker.Tests.Cases/Attributes/OnlyKeepUsed/ComAttributesAreRemovedWhenFeatureExcluded.cs
@@ -3,6 +3,9 @@ using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Metadata;
namespace Mono.Linker.Tests.Cases.Attributes.OnlyKeepUsed {
+#if NETCOREAPP
+ [IgnoreTestCase ("--exclude-feature is not supported on .NET Core")]
+#endif
[SetupLinkerArgument ("--used-attrs-only", "true")]
[SetupLinkerArgument ("--exclude-feature", "com")]
public class ComAttributesAreRemovedWhenFeatureExcluded {
diff --git a/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/BaseRemovedEventSource.cs b/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/BaseRemovedEventSource.cs
index 38cf2da9d..868ac6d35 100644
--- a/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/BaseRemovedEventSource.cs
+++ b/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/BaseRemovedEventSource.cs
@@ -4,6 +4,9 @@ using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Metadata;
namespace Mono.Linker.Tests.Cases.BCLFeatures.ETW {
+#if NETCOREAPP
+ [IgnoreTestCase ("--exclude-feature is not supported on .NET Core")]
+#endif
[SetupLinkerArgument ("--exclude-feature", "etw")]
// Keep framework code that calls EventSource methods like OnEventCommand
[SetupLinkerCoreAction ("skip")]
diff --git a/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/BaseRemovedEventSourceEmptyBody.cs b/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/BaseRemovedEventSourceEmptyBody.cs
index c36fa18ab..05fc018c6 100644
--- a/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/BaseRemovedEventSourceEmptyBody.cs
+++ b/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/BaseRemovedEventSourceEmptyBody.cs
@@ -3,6 +3,9 @@ using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Metadata;
namespace Mono.Linker.Tests.Cases.BCLFeatures.ETW {
+#if NETCOREAPP
+ [IgnoreTestCase ("--exclude-feature is not supported on .NET Core")]
+#endif
[SetupLinkerArgument ("--exclude-feature", "etw")]
// Keep framework code that calls EventSource methods like OnEventCommand
[SetupLinkerCoreAction ("skip")]
diff --git a/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/Excluded.cs b/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/Excluded.cs
index 0568f7a3d..59be7a22d 100644
--- a/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/Excluded.cs
+++ b/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/Excluded.cs
@@ -4,6 +4,9 @@ using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Metadata;
namespace Mono.Linker.Tests.Cases.BCLFeatures.ETW {
+#if NETCOREAPP
+ [IgnoreTestCase ("--exclude-feature is not supported on .NET Core")]
+#endif
[SetupLinkerArgument ("--exclude-feature", "etw")]
// Keep framework code that calls EventSource methods like OnEventCommand
[SetupLinkerCoreAction ("skip")]
diff --git a/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/LocalsOfModifiedMethodAreRemoved.cs b/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/LocalsOfModifiedMethodAreRemoved.cs
index 97af4deaf..06ba2b79f 100644
--- a/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/LocalsOfModifiedMethodAreRemoved.cs
+++ b/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/LocalsOfModifiedMethodAreRemoved.cs
@@ -3,6 +3,9 @@ using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Metadata;
namespace Mono.Linker.Tests.Cases.BCLFeatures.ETW {
+#if NETCOREAPP
+ [IgnoreTestCase ("--exclude-feature is not supported on .NET Core")]
+#endif
[SetupLinkerArgument ("--exclude-feature", "etw")]
// Keep framework code that calls EventSource methods like OnEventCommand
[SetupLinkerCoreAction ("skip")]
diff --git a/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/NonEventWithLog.cs b/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/NonEventWithLog.cs
index 5b0b82f18..e6f76bf95 100644
--- a/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/NonEventWithLog.cs
+++ b/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/NonEventWithLog.cs
@@ -4,6 +4,9 @@ using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Metadata;
namespace Mono.Linker.Tests.Cases.BCLFeatures.ETW {
+#if NETCOREAPP
+ [IgnoreTestCase ("--exclude-feature is not supported on .NET Core")]
+#endif
[SetupLinkerArgument ("--exclude-feature", "etw")]
// Used to avoid different compilers generating different IL which can mess up the instruction asserts
[SetupCompileArgument ("/optimize+")]
diff --git a/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/StubbedMethodWithExceptionHandlers.cs b/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/StubbedMethodWithExceptionHandlers.cs
index 7d6cb22c8..68ca8f37c 100644
--- a/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/StubbedMethodWithExceptionHandlers.cs
+++ b/test/Mono.Linker.Tests.Cases/BCLFeatures/ETW/StubbedMethodWithExceptionHandlers.cs
@@ -4,6 +4,9 @@ using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Metadata;
namespace Mono.Linker.Tests.Cases.BCLFeatures.ETW {
+#if NETCOREAPP
+ [IgnoreTestCase ("--exclude-feature is not supported on .NET Core")]
+#endif
[SetupLinkerArgument ("--exclude-feature", "etw")]
// Keep framework code that calls EventSource methods like OnEventCommand
[SetupLinkerCoreAction ("skip")]
diff --git a/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/UnusedComInterfaceIsRemovedWhenComFeatureExcluded.cs b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/UnusedComInterfaceIsRemovedWhenComFeatureExcluded.cs
index de8908678..597da0c7b 100644
--- a/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/UnusedComInterfaceIsRemovedWhenComFeatureExcluded.cs
+++ b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/UnusedComInterfaceIsRemovedWhenComFeatureExcluded.cs
@@ -3,6 +3,9 @@ using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Metadata;
namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.OnReferenceType {
+#if NETCOREAPP
+ [IgnoreTestCase ("--exclude-feature is not supported on .NET Core")]
+#endif
[SetupLinkerArgument ("--exclude-feature", "com")]
public class UnusedComInterfaceIsRemovedWhenComFeatureExcluded {
public static void Main ()
diff --git a/test/Mono.Linker.Tests.Cases/LinkXml/CanPreserveExcludedFeatureCom.cs b/test/Mono.Linker.Tests.Cases/LinkXml/CanPreserveExcludedFeatureCom.cs
index 0016d7272..df2aeb29f 100644
--- a/test/Mono.Linker.Tests.Cases/LinkXml/CanPreserveExcludedFeatureCom.cs
+++ b/test/Mono.Linker.Tests.Cases/LinkXml/CanPreserveExcludedFeatureCom.cs
@@ -4,6 +4,9 @@ using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Metadata;
namespace Mono.Linker.Tests.Cases.LinkXml {
+#if NETCOREAPP
+ [IgnoreTestCase ("--exclude-feature is not supported on .NET Core")]
+#endif
[SetupLinkerArgument ("--exclude-feature", "com")]
public class CanPreserveExcludedFeatureCom {
public static void Main()
diff --git a/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnAssembly.cs b/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnAssembly.cs
index cbdfb5509..59d546a15 100644
--- a/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnAssembly.cs
+++ b/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnAssembly.cs
@@ -3,6 +3,9 @@ using Mono.Linker.Tests.Cases.Expectations.Metadata;
using Mono.Linker.Tests.Cases.LinkXml.FeatureExclude.Dependencies;
namespace Mono.Linker.Tests.Cases.LinkXml.FeatureExclude {
+#if NETCOREAPP
+ [IgnoreTestCase ("--exclude-feature is not supported on .NET Core")]
+#endif
[SetupLinkerArgument ("--exclude-feature", "one")]
[SetupCompileBefore ("library1.dll", new[] {typeof (OnAssembly_Lib1)})]
[SetupCompileBefore ("library2.dll", new[] {typeof (OnAssembly_Lib2)})]
diff --git a/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnEvent.cs b/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnEvent.cs
index e733ab2d1..2cf664c5e 100644
--- a/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnEvent.cs
+++ b/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnEvent.cs
@@ -3,6 +3,9 @@ using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Metadata;
namespace Mono.Linker.Tests.Cases.LinkXml.FeatureExclude {
+#if NETCOREAPP
+ [IgnoreTestCase ("--exclude-feature is not supported on .NET Core")]
+#endif
[SetupLinkerArgument ("--exclude-feature", "one")]
public class OnEvent {
public static void Main ()
diff --git a/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnField.cs b/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnField.cs
index 93a61f0ab..466be6199 100644
--- a/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnField.cs
+++ b/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnField.cs
@@ -2,6 +2,9 @@ using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Metadata;
namespace Mono.Linker.Tests.Cases.LinkXml.FeatureExclude {
+#if NETCOREAPP
+ [IgnoreTestCase ("--exclude-feature is not supported on .NET Core")]
+#endif
[SetupLinkerArgument ("--exclude-feature", "one")]
public class OnField {
public static void Main ()
diff --git a/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnMethod.cs b/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnMethod.cs
index 11feb7a6a..30741f2a2 100644
--- a/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnMethod.cs
+++ b/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnMethod.cs
@@ -2,6 +2,9 @@ using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Metadata;
namespace Mono.Linker.Tests.Cases.LinkXml.FeatureExclude {
+#if NETCOREAPP
+ [IgnoreTestCase ("--exclude-feature is not supported on .NET Core")]
+#endif
[SetupLinkerArgument ("--exclude-feature", "one")]
public class OnMethod {
public static void Main ()
diff --git a/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnProperty.cs b/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnProperty.cs
index eb043fb01..c550c605c 100644
--- a/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnProperty.cs
+++ b/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnProperty.cs
@@ -2,6 +2,9 @@ using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Metadata;
namespace Mono.Linker.Tests.Cases.LinkXml.FeatureExclude {
+#if NETCOREAPP
+ [IgnoreTestCase ("--exclude-feature is not supported on .NET Core")]
+#endif
[SetupLinkerArgument ("--exclude-feature", "one")]
public class OnProperty {
public static void Main ()
diff --git a/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnType.cs b/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnType.cs
index 368812fcc..731f54080 100644
--- a/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnType.cs
+++ b/test/Mono.Linker.Tests.Cases/LinkXml/FeatureExclude/OnType.cs
@@ -2,6 +2,9 @@ using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Metadata;
namespace Mono.Linker.Tests.Cases.LinkXml.FeatureExclude {
+#if NETCOREAPP
+ [IgnoreTestCase ("--exclude-feature is not supported on .NET Core")]
+#endif
[SetupLinkerArgument ("--exclude-feature", "one")]
public class OnType {
public static void Main ()
diff --git a/test/Mono.Linker.Tests.Cases/Substitutions/EmbeddedSubstitutions.cs b/test/Mono.Linker.Tests.Cases/Substitutions/EmbeddedSubstitutions.cs
new file mode 100644
index 000000000..479be2d6f
--- /dev/null
+++ b/test/Mono.Linker.Tests.Cases/Substitutions/EmbeddedSubstitutions.cs
@@ -0,0 +1,25 @@
+using Mono.Linker.Tests.Cases.Expectations.Assertions;
+using Mono.Linker.Tests.Cases.Expectations.Metadata;
+
+namespace Mono.Linker.Tests.Cases.Substitutions
+{
+ [SetupCompileResource ("EmbeddedSubstitutions.xml", "ILLink.Substitutions.xml")]
+ [IncludeBlacklistStep (true)]
+ public class EmbeddedSubstitutions
+ {
+ public static void Main ()
+ {
+ ConvertToThrowMethod ();
+ }
+
+ [Kept]
+ [ExpectedInstructionSequence (new [] {
+ "ldstr",
+ "newobj",
+ "throw"
+ })]
+ public static void ConvertToThrowMethod ()
+ {
+ }
+ }
+}
diff --git a/test/Mono.Linker.Tests.Cases/Substitutions/EmbeddedSubstitutions.xml b/test/Mono.Linker.Tests.Cases/Substitutions/EmbeddedSubstitutions.xml
new file mode 100644
index 000000000..3bd0175a6
--- /dev/null
+++ b/test/Mono.Linker.Tests.Cases/Substitutions/EmbeddedSubstitutions.xml
@@ -0,0 +1,7 @@
+<linker>
+ <assembly fullname="test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
+ <type fullname="Mono.Linker.Tests.Cases.Substitutions.EmbeddedSubstitutions">
+ <method signature="System.Void ConvertToThrowMethod()" body="remove" />
+ </type>
+ </assembly>
+</linker> \ No newline at end of file
diff --git a/test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutions.cs b/test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutions.cs
new file mode 100644
index 000000000..980451da4
--- /dev/null
+++ b/test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutions.cs
@@ -0,0 +1,51 @@
+using Mono.Linker.Tests.Cases.Expectations.Assertions;
+using Mono.Linker.Tests.Cases.Expectations.Metadata;
+
+namespace Mono.Linker.Tests.Cases.Substitutions
+{
+ [SetupLinkerSubstitutionFile ("FeatureSubstitutions.xml")]
+ [SetupLinkerArgument ("--feature", "OptionalFeature", "false")]
+ public class FeatureSubstitutions
+ {
+ [Kept]
+ static bool IsOptionalFeatureEnabled {
+ [Kept]
+ [ExpectedInstructionSequence (new [] {
+ "ldc.i4.0",
+ "ret",
+ })]
+ get;
+ }
+
+ public static void Main ()
+ {
+ TestOptionalFeature ();
+ }
+
+ [Kept]
+ [ExpectBodyModified]
+ [ExpectedInstructionSequence (new [] {
+ "call",
+ "brfalse",
+ "call",
+ "ret",
+ })]
+ static void TestOptionalFeature ()
+ {
+ if (IsOptionalFeatureEnabled) {
+ UseOptionalFeature ();
+ } else {
+ UseFallback ();
+ }
+ }
+
+ static void UseOptionalFeature ()
+ {
+ }
+
+ [Kept]
+ static void UseFallback ()
+ {
+ }
+ }
+}
diff --git a/test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutions.xml b/test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutions.xml
new file mode 100644
index 000000000..44d332d75
--- /dev/null
+++ b/test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutions.xml
@@ -0,0 +1,8 @@
+<linker feature="OptionalFeature" featurevalue="false">
+ <assembly fullname="test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
+ <type fullname="Mono.Linker.Tests.Cases.Substitutions.FeatureSubstitutions">
+ <method signature="System.Boolean get_IsOptionalFeatureEnabled()" body="stub" value="false">
+ </method>
+ </type>
+ </assembly>
+</linker> \ No newline at end of file
diff --git a/test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutionsGlobalFalse.xml b/test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutionsGlobalFalse.xml
new file mode 100644
index 000000000..a9452db38
--- /dev/null
+++ b/test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutionsGlobalFalse.xml
@@ -0,0 +1,9 @@
+<!-- Check that the feature attribute can be used on the linker element. -->
+<linker feature="GlobalCondition" featurevalue="false">
+ <assembly fullname="test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
+ <type fullname="Mono.Linker.Tests.Cases.Substitutions.FeatureInAssembly">
+ <method signature="System.Boolean GlobalConditionMethod()" body="stub" value="false">
+ </method>
+ </type>
+ </assembly>
+</linker> \ No newline at end of file
diff --git a/test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutionsGlobalTrue.xml b/test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutionsGlobalTrue.xml
new file mode 100644
index 000000000..3b8d006f9
--- /dev/null
+++ b/test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutionsGlobalTrue.xml
@@ -0,0 +1,9 @@
+<!-- Check that the feature attribute can be used on the linker element. -->
+<linker feature="GlobalCondition" featurevalue="true">
+ <assembly fullname="test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
+ <type fullname="Mono.Linker.Tests.Cases.Substitutions.FeatureSubstitutionsNested">
+ <method signature="System.Boolean GlobalConditionMethod()" body="stub" value="true">
+ </method>
+ </type>
+ </assembly>
+</linker> \ No newline at end of file
diff --git a/test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutionsInvalid.cs b/test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutionsInvalid.cs
new file mode 100644
index 000000000..73ba6acbc
--- /dev/null
+++ b/test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutionsInvalid.cs
@@ -0,0 +1,33 @@
+using Mono.Linker.Tests.Cases.Expectations.Assertions;
+using Mono.Linker.Tests.Cases.Expectations.Metadata;
+
+namespace Mono.Linker.Tests.Cases.Substitutions
+{
+ [SetupLinkerSubstitutionFile ("FeatureSubstitutionsInvalid.xml")]
+ [SetupLinkerArgument ("--feature", "NoValueFeature", "true")]
+ [LogContains ("Feature NoValueFeature does not specify a \"featurevalue\" attribute")]
+ public class FeatureSubstitutionsInvalid
+ {
+ public static void Main ()
+ {
+ NoValueFeatureMethod ();
+ NonBooleanFeatureMethod ();
+ BooleanFeatureMethod ();
+ }
+
+ [Kept]
+ static void NoValueFeatureMethod ()
+ {
+ }
+
+ [Kept]
+ static void NonBooleanFeatureMethod ()
+ {
+ }
+
+ [Kept]
+ static void BooleanFeatureMethod ()
+ {
+ }
+ }
+}
diff --git a/test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutionsInvalid.xml b/test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutionsInvalid.xml
new file mode 100644
index 000000000..9a0ed8a39
--- /dev/null
+++ b/test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutionsInvalid.xml
@@ -0,0 +1,9 @@
+<linker>
+ <assembly fullname="test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
+ <type fullname="Mono.Linker.Tests.Cases.Substitutions.FeatureSubstitutionsInvalid">
+ <method signature="System.Boolean NoValueFeatureMethod()" body="stub" value="true" feature="NoValueFeature" />
+ <method signature="System.Boolean NonBooleanFeatureMethod()" body="stub" value="false" feature="NonBooleanFeature" featurevalue="nonboolean" />
+ <method signature="System.Boolean BooleanFeatureMethod()" body="stub" value="false" feature="BooleanFeature" featurevalue="true" />
+ </type>
+ </assembly>
+</linker> \ No newline at end of file
diff --git a/test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutionsNested.cs b/test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutionsNested.cs
new file mode 100644
index 000000000..5e124ee48
--- /dev/null
+++ b/test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutionsNested.cs
@@ -0,0 +1,75 @@
+using System;
+using Mono.Linker.Tests.Cases.Expectations.Assertions;
+using Mono.Linker.Tests.Cases.Expectations.Metadata;
+
+namespace Mono.Linker.Tests.Cases.Substitutions
+{
+ [SetupLinkerSubstitutionFile ("FeatureSubstitutionsGlobalTrue.xml")]
+ [SetupLinkerSubstitutionFile ("FeatureSubstitutionsGlobalFalse.xml")]
+ [SetupLinkerSubstitutionFile ("FeatureSubstitutionsNested.xml")]
+ [SetupLinkerArgument ("--feature", "GlobalCondition", "true")]
+ [SetupLinkerArgument ("--feature", "AssemblyCondition", "false")]
+ [SetupLinkerArgument ("--feature", "TypeCondition", "true")]
+ [SetupLinkerArgument ("--feature", "MethodCondition", "false")]
+ [SetupLinkerArgument ("--feature", "FieldCondition", "true")]
+ public class FeatureSubstitutionsNested
+ {
+ public static void Main () {
+ GlobalConditionMethod ();
+ AssemblyConditionMethod ();
+ TypeConditionMethod ();
+ MethodConditionMethod ();
+ _ = FieldConditionField;
+ }
+
+ [Kept]
+ [ExpectedInstructionSequence (new [] {
+ "ldc.i4.1",
+ "ret",
+ })]
+ static bool GlobalConditionMethod () {
+ throw new NotImplementedException ();
+ }
+
+ [Kept]
+ [ExpectedInstructionSequence (new [] {
+ "ldc.i4.0",
+ "ret",
+ })]
+ static bool AssemblyConditionMethod () {
+ throw new NotImplementedException ();
+ }
+
+ [Kept]
+ [ExpectedInstructionSequence (new [] {
+ "ldc.i4.1",
+ "ret",
+ })]
+ static bool TypeConditionMethod () {
+ throw new NotImplementedException ();
+ }
+
+ [Kept]
+ [ExpectedInstructionSequence (new [] {
+ "ldc.i4.0",
+ "ret",
+ })]
+ static bool MethodConditionMethod () {
+ throw new NotImplementedException ();
+ }
+
+ [Kept]
+ static readonly bool FieldConditionField;
+
+ [Kept]
+ [ExpectedInstructionSequence (new [] {
+ "nop",
+ "ldc.i4.1",
+ "stsfld",
+ "ret"
+ })]
+ static FeatureSubstitutionsNested ()
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutionsNested.xml b/test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutionsNested.xml
new file mode 100644
index 000000000..122bea08d
--- /dev/null
+++ b/test/Mono.Linker.Tests.Cases/Substitutions/FeatureSubstitutionsNested.xml
@@ -0,0 +1,32 @@
+<linker>
+ <!-- Check that the feature attribute can be used on the assembly element. -->
+ <assembly fullname="test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" feature="AssemblyCondition" featurevalue="false">
+ <type fullname="Mono.Linker.Tests.Cases.Substitutions.FeatureSubstitutionsNested">
+ <method signature="System.Boolean AssemblyConditionMethod()" body="stub" value="false" />
+ </type>
+ <!-- Or on the type element. -->
+ <type fullname="Mono.Linker.Tests.Cases.Substitutions.FeatureSubstitutionsNested" feature="TypeCondition" featurevalue="true">
+ <method signature="System.Boolean TypeConditionMethod()" body="stub" value="true">
+ </method>
+ <!-- Or on the method element. -->
+ <method signature="System.Boolean MethodConditionMethod()" body="stub" value="false" feature="MethodCondition" featurevalue="false" />
+ <!-- Else case -->
+ <method signature="System.Boolean MethodConditionMethod()" body="stub" value="true" feature="MethodCondition" featurevalue="true" />
+ <!-- Or on the field element. -->
+ <field name="FieldConditionField" value="true" initialize="true" feature="FieldCondition" featurevalue="true" />
+ <!-- Else case -->
+ <field name="FieldConditionField" value="false" initialize="true" feature="FieldCondition" featurevalue="false" />
+ </type>
+ <!-- Else case for the type feature attribute -->
+ <type fullname="Mono.Linker.Tests.Cases.Substitutions.FeatureSubstitutionsNested" feature="TypeCondition" featurevalue="false">
+ <method signature="System.Boolean TypeConditionMethod()" body="stub" value="false">
+ </method>
+ </type>
+ </assembly>
+ <!-- Else case for the assembly feature attribute -->
+ <assembly fullname="test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" feature="AssemblyCondition" featurevalue="true">
+ <type fullname="Mono.Linker.Tests.Cases.Substitutions.FeatureSubstitutionsNested">
+ <method signature="System.Boolean AssemblyConditionMethod()" body="stub" value="true" />
+ </type>
+ </assembly>
+</linker> \ No newline at end of file
diff --git a/test/Mono.Linker.Tests.Cases/TestFramework/VerifyExpectModifiedAttributesWork.cs b/test/Mono.Linker.Tests.Cases/TestFramework/VerifyExpectModifiedAttributesWork.cs
index 3b3a7f015..787c5179e 100644
--- a/test/Mono.Linker.Tests.Cases/TestFramework/VerifyExpectModifiedAttributesWork.cs
+++ b/test/Mono.Linker.Tests.Cases/TestFramework/VerifyExpectModifiedAttributesWork.cs
@@ -8,6 +8,9 @@ namespace Mono.Linker.Tests.Cases.TestFramework {
/// This test is here to give some coverage to the attribute to ensure it doesn't break. We need to leverage the ETW feature since it is the only
/// one that modifies bodies currently
/// </summary>
+#if NETCOREAPP
+ [IgnoreTestCase ("--exclude-feature is not supported on .NET Core")]
+#endif
[SetupLinkerArgument ("--exclude-feature", "etw")]
// Keep framework code that calls EventSource methods like OnEventCommand
[SetupLinkerCoreAction ("skip")]