private static void RunCodeGen() { var directoryName = @"latest"; var generatedDirectory = "GeneratedSourceFiles"; if (!Directory.Exists(generatedDirectory)) { Directory.CreateDirectory(generatedDirectory); // let any exceptions bleed through } var projectDirectory = Path.Combine(generatedDirectory, _outputAssemblyName); if (!Directory.Exists(projectDirectory)) { Directory.CreateDirectory(projectDirectory); // let any exceptions bleed through } var directoryPath = Path.Combine(projectDirectory, directoryName); if (Directory.Exists(directoryPath)) { var oldDirectoryPath = Path.Combine(projectDirectory, Path.GetFileNameWithoutExtension(Path.GetRandomFileName())); Directory.Move(directoryPath, oldDirectoryPath); } Directory.CreateDirectory(directoryPath); // let any exceptions bleed through // Add references for assemblies referenced by the input assembly var sourceFiles = new List <SourceFile>(); var generatedProxyNames = new List <string>(); var generatedProxyNamespaces = new List <string>(); foreach (var assemblyName in _assemblyNames) { var assembly = Assembly.LoadFrom(assemblyName); foreach (var t in assembly.DefinedTypes) { if (t.IsInterface) { var internalTypeRepresentation = Utilities.GetTypeDefinitionInformation(t); var proxyInterfacesSource = new ProxyInterfaceGenerator(internalTypeRepresentation).TransformText(); sourceFiles.Add(new SourceFile() { FileName = $"ProxyInterfaces_{t.Name}.cs", SourceCode = proxyInterfacesSource, }); var immortalSource = new DispatcherGenerator(internalTypeRepresentation).TransformText(); sourceFiles.Add(new SourceFile() { FileName = $"Dispatcher_{t.Name}.cs", SourceCode = immortalSource, }); var instanceProxySource = new ProxyGenerator(internalTypeRepresentation, internalTypeRepresentation.Name + "Proxy").TransformText(); sourceFiles.Add(new SourceFile() { FileName = $"Proxy_{t.Name}.cs", SourceCode = instanceProxySource, }); generatedProxyNames.Add($"{internalTypeRepresentation.Name}Proxy_Implementation"); generatedProxyNamespaces.Add(internalTypeRepresentation.Namespace); } else if (t.IsValueType) { // This code creates a generated source file holding the definition of any // struct that was defined in the input assembly. // // The short-term reason for this code is to support cases like // PerformanceTestInterruptible's IJob.cs where the IDL interface definition // relies on custom structs. // // The long-term reason for this code is that structs are supported by many // different languages and serialization formats, so once we have a true // cross-language IDL, it makes sense to have structs be a part of that IDL. // very silly hack! var sb = new StringBuilder(); sb.AppendLine($"using System;"); sb.AppendLine($"using System.Runtime.Serialization;"); sb.AppendLine($"namespace {t.Namespace} {{"); foreach (var customAttribute in t.CustomAttributes) { sb.AppendLine($"[{customAttribute.AttributeType.Name}]"); } sb.AppendLine($"public struct {t.Name}"); sb.AppendLine($"{{"); foreach (var field in t.GetFields()) { if (field.CustomAttributes.Count() > 0) { foreach (var ca in field.CustomAttributes) { sb.AppendLine($" [{ca.AttributeType.Name}]"); } } sb.AppendLine($" public {field.FieldType.Name} {field.Name};"); } sb.AppendLine($"}}"); sb.AppendLine($"}}"); sourceFiles.Add(new SourceFile() { FileName = $"{t.Name}.cs", SourceCode = sb.ToString(), }); } } } var immortalSerializerSource = new ImmortalSerializerGenerator(generatedProxyNames, generatedProxyNamespaces).TransformText(); sourceFiles.Add(new SourceFile { FileName = $"ImmortalSerializer.cs", SourceCode = immortalSerializerSource, }); var conditionToPackageInfo = new Dictionary <string, Dictionary <string, HashSet <PackageReferenceInfo> > >(); var conditionToProjectReference = new Dictionary <string, HashSet <string> >(); var execAssembly = Assembly.GetExecutingAssembly(); var projFile = Path.Combine(Path.GetDirectoryName(execAssembly.Location), $@"{execAssembly.GetName().Name}.csproj"); _projectFiles.Add(projFile); var defaultConditionString = string.Empty; foreach (var projectFile in _projectFiles) { var doc = XDocument.Load(projectFile); foreach (var itemGroup in doc.Descendants("ItemGroup")) { var itemGroupCondition = itemGroup.Attributes().FirstOrDefault(a => a.Name == "Condition"); var condition = itemGroupCondition == null ? defaultConditionString : itemGroupCondition.Value; foreach (var packageReference in itemGroup.Descendants("PackageReference")) { var elements = packageReference.Elements(); var attributes = packageReference.Attributes().ToList(); var packageIncludeAttribute = attributes.FirstOrDefault(a => a.Name == "Include"); var packageUpdateAttribute = attributes.FirstOrDefault(a => a.Name == "Update"); if (packageIncludeAttribute == null && packageUpdateAttribute == null) { continue; } var packageNameAttribute = packageIncludeAttribute ?? packageUpdateAttribute; var packageName = packageNameAttribute.Value; var versionAttribute = attributes.FirstOrDefault(a => a.Name == "Version"); string packageVersion; if (versionAttribute == null) { var packageVersionElement = elements.FirstOrDefault(e => e.Name == "Version"); if (packageVersionElement == null) { continue; } packageVersion = packageVersionElement.Value; } else { packageVersion = versionAttribute.Value; } if (!conditionToPackageInfo.ContainsKey(condition)) { conditionToPackageInfo.Add(condition, new Dictionary <string, HashSet <PackageReferenceInfo> >()); } var packageReferenceInfo = new PackageReferenceInfo(packageName, packageVersion, packageReference.ToString()); if (!conditionToPackageInfo[condition].ContainsKey(packageName)) { conditionToPackageInfo[condition].Add(packageName, new HashSet <PackageReferenceInfo>()); } conditionToPackageInfo[condition][packageName].Add(packageReferenceInfo); } foreach (var projectReference in itemGroup.Descendants("ProjectReference")) { var attributes = projectReference.Attributes().ToList(); var projectIncludeAttribute = attributes.FirstOrDefault(a => a.Name == "Include"); var projectPath = projectIncludeAttribute.Value; var formerBasePath = new Uri(new FileInfo(projectFile).Directory.FullName + Path.DirectorySeparatorChar); var currentBasePath = new Uri(new DirectoryInfo(directoryPath).FullName + Path.DirectorySeparatorChar); var projectPathUri = new Uri(formerBasePath, projectPath); var newRelativePath = currentBasePath.MakeRelativeUri(projectPathUri); if (!conditionToProjectReference.ContainsKey(condition)) { conditionToProjectReference.Add(condition, new HashSet <string>()); } conditionToProjectReference[condition].Add(projectReference.ToString().Replace(projectPath.ToString(), newRelativePath.ToString())); } } } var defaultConditionInfo = conditionToPackageInfo.ContainsKey(defaultConditionString) ? conditionToPackageInfo[defaultConditionString] : null; foreach (var cp in conditionToPackageInfo) { foreach (var nameToInfo in cp.Value) { var packageInfos = new HashSet <PackageReferenceInfo>(nameToInfo.Value.Union( defaultConditionInfo == null || !defaultConditionInfo.ContainsKey(nameToInfo.Key) ? new List <PackageReferenceInfo>() : defaultConditionInfo[nameToInfo.Key].ToList())); if (packageInfos.Count > 1) { Console.WriteLine($"WARNING: Detected multiple versions of package {nameToInfo.Key} : {string.Join(",", packageInfos.Select(pi => pi.PackageVersion))}"); } } } var conditionalPackageReferences = new List <string>(); foreach (var cpi in conditionToPackageInfo) { conditionalPackageReferences.Add($"<ItemGroup{(cpi.Key != string.Empty ? $" Condition=\"{cpi.Key}\"" : string.Empty)}>{string.Join("\n", cpi.Value.SelectMany(v => v.Value).Select(pri => pri.ReferenceString))}</ItemGroup>"); } var conditionalProjectReferences = new List <string>(); foreach (var cpi in conditionToProjectReference) { conditionalProjectReferences.Add($"<ItemGroup{(cpi.Key != string.Empty ? $" Condition=\"{cpi.Key}\"" : string.Empty)}>{string.Join("\n", cpi.Value)}</ItemGroup>"); } var projectFileSource = $@" <Project Sdk=""Microsoft.NET.Sdk""> <PropertyGroup> <TargetFrameworks>{string.Join(";", _targetFrameworks)}</TargetFrameworks> <Platforms>x64</Platforms> </PropertyGroup> {string.Join(string.Empty, conditionalPackageReferences)} {string.Join(string.Empty, conditionalProjectReferences)} </Project>"; var projectFileXml = XDocument.Parse(projectFileSource); var projectSourceFile = new SourceFile { FileName = $"{_outputAssemblyName}.csproj", SourceCode = projectFileXml.ToString() }; sourceFiles.Add(projectSourceFile); var trees = sourceFiles .Select(s => CSharpSyntaxTree.ParseText( s.SourceCode, path: Path.Combine(directoryPath, s.FileName), encoding: Encoding.GetEncoding(0) )); string directory = null; foreach (var tree in trees) { if (directory == null) { directory = Path.GetDirectoryName(tree.FilePath); } var sourceFile = tree.FilePath; File.WriteAllText(sourceFile, tree.GetRoot().ToFullString()); } }
private static void RunCodeGen() { var directoryName = @"latest"; var generatedDirectory = "GeneratedSourceFiles"; if (!Directory.Exists(generatedDirectory)) { Directory.CreateDirectory(generatedDirectory); // let any exceptions bleed through } var projectDirectory = Path.Combine(generatedDirectory, _outputAssemblyName); if (!Directory.Exists(projectDirectory)) { Directory.CreateDirectory(projectDirectory); // let any exceptions bleed through } var directoryPath = Path.Combine(projectDirectory, directoryName); if (Directory.Exists(directoryPath)) { var oldDirectoryPath = Path.Combine(projectDirectory, Path.GetFileNameWithoutExtension(Path.GetRandomFileName())); Directory.Move(directoryPath, oldDirectoryPath); } Directory.CreateDirectory(directoryPath); // let any exceptions bleed through // Add references for assemblies referenced by the input assembly var sourceFiles = new List <SourceFile>(); var generatedProxyNames = new List <string>(); var generatedProxyNamespaces = new List <string>(); foreach (var assemblyName in _assemblyNames) { var assembly = Assembly.LoadFrom(assemblyName); foreach (var t in assembly.DefinedTypes) { if (t.IsInterface) { var internalTypeRepresentation = Utilities.GetTypeDefinitionInformation(t); var proxyInterfacesSource = new ProxyInterfaceGenerator(internalTypeRepresentation).TransformText(); sourceFiles.Add(new SourceFile() { FileName = $"ProxyInterfaces_{t.Name}.cs", SourceCode = proxyInterfacesSource, }); var immortalSource = new DispatcherGenerator(internalTypeRepresentation).TransformText(); sourceFiles.Add(new SourceFile() { FileName = $"Dispatcher_{t.Name}.cs", SourceCode = immortalSource, }); var instanceProxySource = new ProxyGenerator(internalTypeRepresentation, internalTypeRepresentation.Name + "Proxy").TransformText(); sourceFiles.Add(new SourceFile() { FileName = $"Proxy_{t.Name}.cs", SourceCode = instanceProxySource, }); generatedProxyNames.Add($"{internalTypeRepresentation.Name}Proxy_Implementation"); generatedProxyNamespaces.Add(internalTypeRepresentation.Namespace); } else if (t.IsValueType) { // This code creates a generated source file holding the definition of any // struct that was defined in the input assembly. // // The short-term reason for this code is to support cases like // PerformanceTestInterruptible's IJob.cs where the IDL interface definition // relies on custom structs. // // The long-term reason for this code is that structs are supported by many // different languages and serialization formats, so once we have a true // cross-language IDL, it makes sense to have structs be a part of that IDL. // very silly hack! var sb = new StringBuilder(); sb.AppendLine($"using System;"); sb.AppendLine($"using System.Runtime.Serialization;"); sb.AppendLine($"namespace {t.Namespace} {{"); foreach (var customAttribute in t.CustomAttributes) { sb.AppendLine($"[{customAttribute.AttributeType.Name}]"); } sb.AppendLine($"public struct {t.Name}"); sb.AppendLine($"{{"); foreach (var field in t.GetFields()) { if (field.CustomAttributes.Count() > 0) { foreach (var ca in field.CustomAttributes) { sb.AppendLine($" [{ca.AttributeType.Name}]"); } } sb.AppendLine($" public {field.FieldType.Name} {field.Name};"); } sb.AppendLine($"}}"); sb.AppendLine($"}}"); sourceFiles.Add(new SourceFile() { FileName = $"{t.Name}.cs", SourceCode = sb.ToString(), }); } } } var immortalSerializerSource = new ImmortalSerializerGenerator(generatedProxyNames, generatedProxyNamespaces).TransformText(); sourceFiles.Add(new SourceFile { FileName = $"ImmortalSerializer.cs", SourceCode = immortalSerializerSource, }); var referenceLocations = new Dictionary <string, string>(); var assemblyFileNames = _assemblyNames.Select(Path.GetFileName).ToList(); foreach (var fileName in Directory.GetFiles(_binPath, "*.dll", SearchOption.TopDirectoryOnly) .Union(Directory.GetFiles(_binPath, "*.exe", SearchOption.TopDirectoryOnly))) { var assemblyPath = Path.GetFullPath(fileName); if (assemblyFileNames.Contains(Path.GetFileName(assemblyPath))) { continue; } Assembly assembly; try { assembly = Assembly.LoadFile(assemblyPath); } catch (Exception) { continue; } var assemblyName = assembly.GetName().Name; var assemblyLocation = assembly.Location; var assemblyLocationUri = new Uri(assemblyLocation); var assemblyLocationRelativePath = new Uri(Path.GetFullPath(directoryPath)).MakeRelativeUri(assemblyLocationUri).ToString(); referenceLocations.Add(assemblyName, assemblyLocationRelativePath); } var conditionToPackageInfo = new Dictionary <string, List <Tuple <string, string, string> > >(); var execAssembly = Assembly.GetExecutingAssembly(); var projFile = Path.Combine(Path.GetDirectoryName(execAssembly.Location), $@"{execAssembly.GetName().Name}.csproj"); var doc = XDocument.Load(projFile); foreach (var itemGroup in doc.Descendants("ItemGroup")) { var itemGroupCondition = itemGroup.Attributes().FirstOrDefault(a => a.Name == "Condition"); var condition = itemGroupCondition == null ? string.Empty : itemGroupCondition.Value; foreach (var packageReference in itemGroup.Descendants("PackageReference")) { var elements = packageReference.Elements(); var attributes = packageReference.Attributes().ToList(); var packageIncludeAttribute = attributes.FirstOrDefault(a => a.Name == "Include"); var packageUpdateAttribute = attributes.FirstOrDefault(a => a.Name == "Update"); if (packageIncludeAttribute == null && packageUpdateAttribute == null) { continue; } var packageNameAttribute = packageIncludeAttribute ?? packageUpdateAttribute; var packageName = packageNameAttribute.Value; var packageMode = packageNameAttribute.Name.ToString(); var versionAttribute = attributes.FirstOrDefault(a => a.Name == "Version"); string packageVersion; if (versionAttribute == null) { var packageVersionElement = elements.FirstOrDefault(e => e.Name == "Version"); if (packageVersionElement == null) { continue; } packageVersion = packageVersionElement.Value; } else { packageVersion = versionAttribute.Value; } if (!conditionToPackageInfo.ContainsKey(condition)) { conditionToPackageInfo.Add(condition, new List <Tuple <string, string, string> >()); } conditionToPackageInfo[condition].Add(new Tuple <string, string, string>(packageMode, packageName, packageVersion)); } } var conditionalPackageReferences = new List <string>(); foreach (var cpi in conditionToPackageInfo) { var packageReferences = new List <string>(); foreach (var pi in cpi.Value) { packageReferences.Add( $@" <PackageReference {pi.Item1}=""{pi.Item2}"" Version=""{pi.Item3}"" />"); } if (cpi.Key == String.Empty || cpi.Key == _targetFramework) { conditionalPackageReferences.Add( $@" <ItemGroup> {string.Join("\n", packageReferences)} </ItemGroup> "); } } var references = new List <string>(); foreach (var rl in referenceLocations) { references.Add( $@" <Reference Include=""{rl.Key}""> <HintPath>{rl.Value}</HintPath> </Reference>"); } var referencesItemGroup = $@" <ItemGroup> {string.Join("\n", references)} </ItemGroup> "; var projectFileSource = $@" <Project Sdk=""Microsoft.NET.Sdk""> <PropertyGroup> <TargetFramework>{_targetFramework}</TargetFramework> </PropertyGroup> {referencesItemGroup}{string.Join(string.Empty, conditionalPackageReferences)}</Project>"; var projectSourceFile = new SourceFile() { FileName = $"{_outputAssemblyName}.csproj", SourceCode = projectFileSource }; sourceFiles.Add(projectSourceFile); var trees = sourceFiles .Select(s => CSharpSyntaxTree.ParseText( s.SourceCode, path: Path.Combine(directoryPath, s.FileName), encoding: Encoding.GetEncoding(0) )); string directory = null; foreach (var tree in trees) { if (directory == null) { directory = Path.GetDirectoryName(tree.FilePath); } var sourceFile = tree.FilePath; File.WriteAllText(sourceFile, tree.GetRoot().ToFullString()); } }