Skip to content

Commit aa90e21

Browse files
authored
GenAPI: Optionally wrap types in conditional compilation (#10003)
This allows passing in a list of types to GenAPI that will be wrapped in #if conditional compilation blocks.
1 parent 2e42f0b commit aa90e21

File tree

5 files changed

+87
-6
lines changed

5 files changed

+87
-6
lines changed

src/Microsoft.Cci.Extensions/Filters/DocIdExcludeListFilter.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,12 @@ public DocIdExcludeListFilter(string excludeListFilePath, bool excludeMembers)
2828
_excludeMembers = excludeMembers;
2929
}
3030

31+
public bool IncludeForwardedTypes { get; set; }
32+
3133
public bool Include(INamespaceDefinition ns)
3234
{
3335
// Only include non-empty namespaces
34-
if (!ns.GetTypes().Any(Include))
36+
if (!ns.GetTypes(IncludeForwardedTypes).Any(Include))
3537
return false;
3638

3739
string namespaceId = ns.DocId();

src/Microsoft.Cci.Extensions/Filters/DocIdIncludeListFilter.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@ public DocIdIncludeListFilter(string includeListFilePath)
2626

2727
public bool AlwaysIncludeNonEmptyTypes { get; set; }
2828

29+
public bool IncludeForwardedTypes { get; set; }
30+
2931
public bool Include(INamespaceDefinition ns)
3032
{
3133
// Only include non-empty namespaces
32-
if (!ns.GetTypes().Any(Include))
34+
if (!ns.GetTypes(IncludeForwardedTypes).Any(Include))
3335
return false;
3436

3537
string namespaceId = ns.DocId();

src/Microsoft.Cci.Extensions/Writers/CSharp/CSharpWriter.cs

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public class CSharpWriter : SimpleTypeMemberTraverser, ICciWriter, IDisposable
2222
private readonly bool _apiOnly;
2323
private readonly ICciFilter _cciFilter;
2424
private bool _firstMemberGroup;
25+
private ICciFilter _currentTypeListFilter;
2526

2627
public CSharpWriter(ISyntaxWriter writer, ICciFilter filter, bool apiOnly, bool writeAssemblyAttributes = false)
2728
: base(filter)
@@ -34,6 +35,12 @@ public CSharpWriter(ISyntaxWriter writer, ICciFilter filter, bool apiOnly, bool
3435
_writeAssemblyAttributes = writeAssemblyAttributes;
3536
}
3637

38+
public class ConditionalTypeList
39+
{
40+
public ICciFilter Filter { get; set; }
41+
public string Symbol { get; set; }
42+
}
43+
3744
public ISyntaxWriter SyntaxWriter { get { return _syntaxWriter; } }
3845

3946
public ICciDeclarationWriter DeclarationWriter { get { return _declarationWriter; } }
@@ -48,6 +55,10 @@ public CSharpWriter(ISyntaxWriter writer, ICciFilter filter, bool apiOnly, bool
4855

4956
public bool PutBraceOnNewLine { get; set; }
5057

58+
public IEnumerable<ConditionalTypeList> ConditionalTypeLists { get; set; }
59+
60+
public string DefaultCondition { get; set; }
61+
5162
public bool IncludeGlobalPrefixForCompilation
5263
{
5364
get { return _declarationWriter.ForCompilationIncludeGlobalPrefix; }
@@ -87,7 +98,23 @@ public override void Visit(IAssembly assembly)
8798
_declarationWriter.WriteDeclaration(assembly);
8899
}
89100

90-
base.Visit(assembly);
101+
var namespaces = assembly.GetAllNamespaces();
102+
_currentTypeListFilter = null;
103+
104+
// first pass, visit types *not* mentioned in ConditionalTypeLists
105+
WriteConditionStart(DefaultCondition);
106+
Visit(namespaces);
107+
WriteConditionEnd(DefaultCondition);
108+
109+
// second pass, visit types mentioned in ConditionalTypeLists
110+
foreach (var typeList in ConditionalTypeLists ?? Enumerable.Empty<ConditionalTypeList>())
111+
{
112+
_currentTypeListFilter = typeList.Filter;
113+
114+
WriteConditionStart(typeList.Symbol);
115+
Visit(namespaces.Where(_currentTypeListFilter.Include));
116+
WriteConditionEnd(typeList.Symbol);
117+
}
91118
}
92119

93120
public override void Visit(INamespaceDefinition ns)
@@ -102,7 +129,16 @@ public override void Visit(INamespaceDefinition ns)
102129

103130
using (_syntaxWriter.StartBraceBlock(PutBraceOnNewLine))
104131
{
105-
base.Visit(ns);
132+
var types = ns.GetTypes(this.IncludeForwardedTypes);
133+
if (ConditionalTypeLists != null)
134+
{
135+
// in the first pass we want all types *except* the ones in ConditionalTypeLists filters
136+
// in the second pass we want *only* the types in ConditionalTypeLists filters
137+
var firstPass = _currentTypeListFilter == null;
138+
types = types.Where(t => ConditionalTypeLists.Any(c => c.Filter.Include(t) == !firstPass));
139+
}
140+
141+
Visit(types);
106142
}
107143
}
108144

@@ -296,6 +332,24 @@ private void WriteMemberGroupHeader(ITypeDefinitionMember member)
296332
}
297333
}
298334

335+
private void WriteConditionStart(string condition)
336+
{
337+
if (!string.IsNullOrEmpty(condition))
338+
{
339+
_syntaxWriter.Write($"#if {condition}");
340+
_syntaxWriter.WriteLine();
341+
}
342+
}
343+
344+
private void WriteConditionEnd(string condition)
345+
{
346+
if (!string.IsNullOrEmpty(condition))
347+
{
348+
_syntaxWriter.Write($"#endif // {condition}");
349+
_syntaxWriter.WriteLine();
350+
}
351+
}
352+
299353
public static string MemberGroupHeading(ITypeDefinitionMember member)
300354
{
301355
if (member == null)

src/Microsoft.DotNet.GenAPI/GenAPITask.cs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,13 +155,24 @@ public string DocIdKinds
155155
/// </summary>
156156
public bool AlwaysIncludeBase { get; set; }
157157

158+
/// <summary>
159+
/// [CSDecl] Specify one or more namespace+type list(s) in the DocId format of types that should be wrapped inside an #if with the symbol specified in the <c>Symbol</c> metadata item.
160+
/// If the <c>Symbol</c> metadata item is empty the types won't be wrapped but will still be output after all other types, this can be used in combination with <see cref="DefaultCondition" /> to wrap all other types.
161+
/// </summary>
162+
public ITaskItem[] ConditionalTypeLists { get; set; }
163+
164+
/// <summary>
165+
/// [CSDecl] #if condition to apply to all types not included in <see cref="ConditionalTypeLists" />.
166+
/// </summary>
167+
public string DefaultCondition { get; set; }
168+
158169
/// <summary>
159170
/// Exclude members when return value or parameter types are excluded.
160171
/// </summary>
161172
public bool ExcludeMembers { get; set; }
162173

163174
/// <summary>
164-
/// Language Version to target.
175+
/// [CSDecl] Language Version to target.
165176
/// </summary>
166177
public string LangVersion { get; set; }
167178

@@ -333,7 +344,7 @@ private static ICciFilter GetFilter(
333344

334345
if (!string.IsNullOrWhiteSpace(excludeApiList))
335346
{
336-
includeFilter = new IntersectionFilter(includeFilter, new DocIdExcludeListFilter(excludeApiList, excludeMembers));
347+
includeFilter = new IntersectionFilter(includeFilter, new DocIdExcludeListFilter(excludeApiList, excludeMembers) { IncludeForwardedTypes = includeForwardedTypes });
337348
}
338349

339350
if (!string.IsNullOrWhiteSpace(excludeAttributesList))
@@ -394,6 +405,16 @@ private ICciWriter GetWriter(TextWriter output, ISyntaxWriter syntaxWriter, bool
394405
writer.AlwaysIncludeBase = AlwaysIncludeBase;
395406
writer.LangVersion = GetLangVersion(LangVersion);
396407
writer.IncludeForwardedTypes = FollowTypeForwards;
408+
writer.DefaultCondition = DefaultCondition;
409+
if (ConditionalTypeLists != null)
410+
{
411+
writer.ConditionalTypeLists = ConditionalTypeLists.Select(c =>
412+
new CSharpWriter.ConditionalTypeList
413+
{
414+
Symbol = c.GetMetadata("Symbol"),
415+
Filter = new DocIdIncludeListFilter(c.ItemSpec) { IncludeForwardedTypes = FollowTypeForwards }
416+
});
417+
}
397418
return writer;
398419
}
399420
}

src/Microsoft.DotNet.GenAPI/build/Microsoft.DotNet.GenAPI.targets

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949
Assembly="$(GenAPIInputAssembly)"
5050
LibPath="$(GenAPILibPath)"
5151
ApiList="$(GenAPIApiList)"
52+
ConditionalTypeLists="@(GenAPIConditionalTypeLists)"
53+
DefaultCondition="$(GenAPIDefaultCondition)"
5254
OutputPath="$(GenAPITargetPath)"
5355
HeaderFile="$(GenAPIHeaderFile)"
5456
WriterType="$(GenAPIWriterType)"

0 commit comments

Comments
 (0)