@@ -25,8 +25,7 @@ public class AssemblyGenerator
2525 readonly string _outputRoot ;
2626 readonly IFileSystem _fileSystem ;
2727
28- public AssemblyGenerator
29- (
28+ public AssemblyGenerator (
3029 string outputRoot ,
3130 IFileSystem fileSystem = null
3231 )
@@ -102,6 +101,7 @@ void Save(string packageOutputRoot, ISymbolMap symbols, Assembly assembly, strin
102101
103102 SaveProjectFile ( ) ;
104103 SaveAssemblyInfo ( assembly . Name , assembly . Version , tarballFileName ) ;
104+ SaveDependencyAnchorFile ( ) ;
105105
106106 foreach ( Type type in assembly . Types ? . Values ?? Enumerable . Empty < Type > ( ) )
107107 {
@@ -185,6 +185,64 @@ IEnumerable<KeyValuePair<string, PackageVersion>> GetDependenciesCore(Dependency
185185 }
186186 }
187187
188+ void SaveDependencyAnchorFile ( )
189+ {
190+ string anchorNamespace = $ "{ assembly . GetNativeNamespace ( ) } .Internal.DependencyResolution";
191+ var syntaxTree = SF . SyntaxTree (
192+ SF . CompilationUnit (
193+ SF . List < ExternAliasDirectiveSyntax > ( ) ,
194+ SF . List < UsingDirectiveSyntax > ( ) ,
195+ SF . List < AttributeListSyntax > ( ) ,
196+ SF . List < MemberDeclarationSyntax > ( new [ ] {
197+ SF . NamespaceDeclaration (
198+ SF . IdentifierName ( anchorNamespace ) ,
199+ SF . List < ExternAliasDirectiveSyntax > ( ) ,
200+ SF . List < UsingDirectiveSyntax > ( ) ,
201+ SF . List ( new MemberDeclarationSyntax [ ] { GenerateDependencyAnchor ( ) } )
202+ )
203+ } )
204+ ) . NormalizeWhitespace ( elasticTrivia : true )
205+ ) ;
206+
207+ string directory = GetNamespaceDirectory ( packageOutputRoot , @anchorNamespace ) ;
208+ SaveSyntaxTree ( directory , "Anchor.cs" , syntaxTree ) ;
209+
210+ ClassDeclarationSyntax GenerateDependencyAnchor ( ) {
211+ return SF . ClassDeclaration (
212+ SF . List < AttributeListSyntax > ( ) ,
213+ SF . TokenList ( SF . Token ( SyntaxKind . PublicKeyword ) ) ,
214+ SF . Identifier ( "Anchor" ) ,
215+ null ,
216+ null ,
217+ SF . List < TypeParameterConstraintClauseSyntax > ( ) ,
218+ SF . List ( new MemberDeclarationSyntax [ ] {
219+ SF . ConstructorDeclaration (
220+ SF . List < AttributeListSyntax > ( ) ,
221+ SF . TokenList ( SF . Token ( SyntaxKind . PublicKeyword ) ) ,
222+ SF . Identifier ( "Anchor" ) ,
223+ SF . ParameterList ( SF . SeparatedList < ParameterSyntax > ( ) ) ,
224+ null ,
225+ SF . Block ( SF . List ( GenerateAnchorReferences ( ) ) ) ,
226+ null
227+ )
228+ } )
229+ ) ;
230+
231+ IEnumerable < StatementSyntax > GenerateAnchorReferences ( )
232+ {
233+ return assembly . Dependencies ? . Keys
234+ . Select ( k => assembly . GetNativeNamespace ( k ) )
235+ . Select ( n => $ "{ n } .Internal.DependencyResolution.Anchor")
236+ . Select ( t => SF . ExpressionStatement ( SF . ObjectCreationExpression (
237+ SF . Token ( SyntaxKind . NewKeyword ) ,
238+ SF . ParseTypeName ( t ) ,
239+ SF . ArgumentList ( SF . SeparatedList < ArgumentSyntax > ( ) ) ,
240+ null
241+ ) ) ) ?? Enumerable . Empty < StatementSyntax > ( ) ;
242+ }
243+ }
244+ }
245+
188246 void SaveAssemblyInfo ( string name , string version , string tarball )
189247 {
190248 SyntaxTree assemblyInfo = SF . SyntaxTree (
@@ -217,14 +275,8 @@ void SaveAssemblyInfo(string name, string version, string tarball)
217275
218276 void SaveType ( Type type )
219277 {
220- string packageName = Path . GetFileName ( packageOutputRoot ) ;
221278 string @namespace = symbols . GetNamespace ( type ) ;
222- if ( @namespace . StartsWith ( packageName ) )
223- {
224- @namespace = @namespace . Substring ( packageName . Length ) . TrimStart ( '.' ) ;
225- }
226-
227- string directory = Path . Combine ( packageOutputRoot , Path . Combine ( @namespace . Split ( '.' ) ) ) ;
279+ string directory = GetNamespaceDirectory ( packageOutputRoot , @namespace ) ;
228280
229281 switch ( type . Kind )
230282 {
@@ -252,17 +304,37 @@ void SaveType(Type type)
252304
253305 void SaveTypeFile ( string filename , SyntaxTree syntaxTree )
254306 {
255- if ( ! _fileSystem . Directory . Exists ( directory ) )
256- {
257- _fileSystem . Directory . CreateDirectory ( directory ) ;
258- }
307+ SaveSyntaxTree ( directory , filename , syntaxTree ) ;
308+ }
309+ }
259310
260- _fileSystem . File . WriteAllText (
261- Path . Combine ( directory , filename ) ,
262- syntaxTree . ToString ( )
263- ) ;
311+ void SaveSyntaxTree ( string directory , string filename , SyntaxTree syntaxTree )
312+ {
313+ if ( ! _fileSystem . Directory . Exists ( directory ) )
314+ {
315+ _fileSystem . Directory . CreateDirectory ( directory ) ;
264316 }
317+
318+ _fileSystem . File . WriteAllText (
319+ Path . Combine ( directory , filename ) ,
320+ syntaxTree . ToString ( )
321+ ) ;
322+ }
323+ }
324+
325+ string GetNamespaceDirectory ( string packageOutputRoot , string @namespace )
326+ {
327+ string packageName = Path . GetFileName ( packageOutputRoot ) ;
328+
329+ // packageOutputRoot is typically a directory like My.Root.Namespace/
330+ // We don't want to create redundant directories, i.e.
331+ // My.Root.Namespace/My/Root/Namespace/Sub/Namespace. We just
332+ // want My.Root.Namespace/Sub/Namespace.
333+ if ( @namespace . StartsWith ( packageName ) ) {
334+ @namespace = @namespace . Substring ( packageName . Length ) . TrimStart ( '.' ) ;
265335 }
336+
337+ return Path . Combine ( packageOutputRoot , Path . Combine ( @namespace . Split ( '.' ) ) ) ;
266338 }
267339 }
268340}
0 commit comments