8000 Enable Refit on iOS via code generation by anaisbetts · Pull Request #38 · reactiveui/refit · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Enable Refit on iOS via code generation #38

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 66 commits into from
Oct 12, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
1cbd070
Update README
anaisbetts Oct 11, 2014
e04b5a4
Add a bunch of NuGet packages
anaisbetts May 4, 2014
1307a46
Write some code to find uses of RestService.For<T>
anaisbetts May 4, 2014
e40b727
First hack at writing a mustache template to generate the code we want
anaisbetts May 4, 2014
3a2930d
Generate a full template
anaisbetts May 4, 2014
7fea67a
Rename some things
anaisbetts May 4, 2014
ce48c38
Split GitHubApi into its own file to make tests more compelling
anaisbetts May 4, 2014
d3158ec
Write the top-level method to generate templates
anaisbetts May 4, 2014
9ae13aa
Actually add asserts
anaisbetts May 4, 2014
674ac38
Not actually using this
anaisbetts May 4, 2014
69a6d03
Update The Plan
anaisbetts May 4, 2014
2af2fb6
Move InterfaceStubGenerator to its own EXE
anaisbetts May 5, 2014
31d6775
Rename the template because 'cs' is being detected as a culture name
anaisbetts May 6, 2014
c5f7da0
Use flat file, fall back to resource
anaisbetts May 6, 2014
f410f38
Fix up the tests
anaisbetts May 6, 2014
654c8e1
Write a comment hint, and make the built class partial so people can …
anaisbetts May 6, 2014
52ca23d
Finish main() to generate the interface file
anaisbetts May 7, 2014
338ebca
Make it easier to modify / subclass generated templates
anaisbetts May 7, 2014
76005c8
Rewrite to use pre-built class
anaisbetts May 19, 2014
653b0a8
Add a Monotouch project
anaisbetts May 19, 2014
b3d1d36
Add a Monotouch unit test runner
anaisbetts May 19, 2014
5e3b135
Project file crap
anaisbetts May 19, 2014
5151095
Fix a dumb bug in the interface generator
anaisbetts May 19, 2014
8bb9620
Inner Interface?
anaisbetts May 19, 2014
e42598e
Nuke more Castling
anaisbetts May 19, 2014
6e30133
We have Liftoff
anaisbetts May 19, 2014
d020573
Don't stub the stub file
anaisbetts May 19, 2014
b2925df
Girllllllll you got this
anaisbetts May 19, 2014
95514e2
Nuke old Refit-Tests
anaisbetts May 19, 2014
0877061
Xamarin.iOS is not havin' anything but a fully-qualified name
anaisbetts May 19, 2014
c61df04
MonoAndroid build doesn't define in Xam Studio
anaisbetts May 19, 2014
4cd48cf
Move the planet to get iOS working on OS X
anaisbetts May 19, 2014
cd721f4
Project nonsense
anaisbetts May 19, 2014
cab80f0
Ignore RefitStubs
anaisbetts May 19, 2014
eee88cc
Make sure Refit stubs don't get linked away
anaisbetts May 19, 2014
057de11
Double down on our dumb hack
anaisbetts May 19, 2014
611599c
Rewrite integration tests to be async
anaisbetts Jun 10, 2014
07c8ca3
Mono doesn't like user agents with spaces
anaisbetts Jun 10, 2014
6e5efbb
Update csproj stuff on VS
anaisbetts Jun 10, 2014
1291ba0
RIP Castle.Core
anaisbetts Jun 10, 2014
6289c91
Move to package restore
anaisbetts Jun 10, 2014
e7abc4c
Update the nuspec file
anaisbetts Jun 10, 2014
2871ed7
Enable package restore in makefile
anaisbetts Jun 10, 2014
3594b79
Add a content file for the NuGet package
anaisbetts Jun 10, 2014
60d1049
Start committing RefitStubs
anaisbetts Jun 10, 2014
6dbd2af
First hack at a targets file
anaisbetts Jun 10, 2014
d01d9e4
Add the Tools too
anaisbetts Jun 10, 2014
12da78a
Gotta wrap in a Project node
anaisbetts Jun 10, 2014
d5c5c60
Version bump to 2.0.0
anaisbetts Jun 10, 2014
abbdccf
Omg Windows
anaisbetts Jun 10, 2014
0956323
Fix the package name
anaisbetts Jun 10, 2014
422fd89
Ignore folders that have been given to us on the @Compile line
anaisbetts Jun 10, 2014
cc22d61
Fix the giant mess that epic rebase caused
anaisbetts Oct 11, 2014
f179f5a
No need for iOS Hax
anaisbetts Oct 11, 2014
e01cff3
Resurrect Refit-Tests-Android
anaisbetts Oct 11, 2014
9b3e11e
Refit is now Xamarin.iOS 64-bit only
anaisbetts Oct 11, 2014
96df08f
Attempt to port iOS tests to 64-bit, realize there's no 64-bit runner
anaisbetts Oct 11, 2014
8e0c49b
xbuild only, remove mdtool hax
anaisbetts Oct 11, 2014
93eea69
Update the NuSpec with Xamarin.iOS Unified
anaisbetts Oct 11, 2014
922aee1
Remove iOS-64 until it works
anaisbetts Oct 11, 2014
4357984
Pin Refit to 4.0.3
anaisbetts Oct 11, 2014
7a6bde5
Computers.
anaisbetts Oct 11, 2014
129bbc3
RIP SL5
anaisbetts Oct 11, 2014
410fb38
None of this type loading stuff, just use Bait and Switch
anaisbetts Oct 12, 2014
1e9f80e
We need to go at the end of the csproj
anaisbetts Oct 12, 2014
8f8bd82
ProjectDir actually works as a targets file
anaisbetts Oct 12, 2014
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ Generated_Code #added for RIA/Silverlight projects

# packages
Release/
packages/

*.userprefs
Refit-Tests/test-results
packages
Binary file modified .nuget/NuGet.exe
Binary file not shown.
6 changes: 6 additions & 0 deletions InterfaceStubGenerator/App.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>
55 changes: 55 additions & 0 deletions InterfaceStubGenerator/GeneratedInterfaceStubTemplate.mustache
8000
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using System;
using System.Net.Http;
{{#UsingList}}
using {{Item}};
{{/UsingList}}

/* ******** Hey You! *********
*
* This is a generated file, and gets rewritten every time you build the
* project. If you want to edit it, you need to edit the mustache template
* in the Refit package */

namespace RefitInternalGenerated
{
[AttributeUsage (AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate)]
sealed class PreserveAttribute : Attribute
{
//
// Fields
//
public bool AllMembers;

public bool Conditional;
}
}

{{#ClassList}}
namespace {{Namespace}}
{
using RefitInternalGenerated;

[Preserve]
public partial class AutoGenerated{{InterfaceName}} : {{InterfaceName}}
{
public HttpClient Client { get; protected set; }
readonly Dictionary<string, Func<HttpClient, object[], object>> methodImpls;

public AutoGenerated{{InterfaceName}}(HttpClient client, IRequestBuilder requestBuilder)
{
methodImpls = requestBuilder.InterfaceHttpMethods.ToDictionary(k => k, v => requestBuilder.BuildRestResultFuncForMethod(v));
Client = client;
}

{{#MethodList}}
public virtual {{ReturnType}} {{Name}}({{ArgumentListWithTypes}})
{
var arguments = new object[] { {{ArgumentList}} };
return ({{ReturnType}}) methodImpls["{{Name}}"](Client, arguments);
}

{{/MethodList}}
}
}

{{/ClassList}}
152 changes: 152 additions & 0 deletions InterfaceStubGenerator/InterfaceStubGenerator.cs
5D32
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Nustache;
using Nustache.Core;

namespace Refit.Generator
{
// * Find all calls to RestService.For<T>, extract all T's
// * Search for all Interfaces, find the method definitions
// * Generate the data we need for the template based on interface method
// defn's
// * Get this into an EXE in tools, write a targets file to beforeBuild execute it
// * Get a props file that adds a dummy file to the project
// * Write an implementation of RestService that just takes the interface name to
// guess the class name based on our template
//
// What if the Interface is in another module? (since we copy usings, should be fine)
// What if the Interface itself is Generic? (fuck 'em)
public class InterfaceStubGenerator
{
public string GenerateInterfaceStubs(string[] paths)
{
var trees = paths.Select(x => CSharpSyntaxTree.ParseFile(x)).ToList();
var interfaceNamesToFind = trees.SelectMany(FindInterfacesToGenerate).Distinct().ToList();

var interfacesToGenerate = trees.SelectMany(x => x.GetRoot().DescendantNodes().OfType<InterfaceDeclarationSyntax>())
.Where(x => interfaceNamesToFind.Contains(x.Identifier.ValueText))
.ToList();

var templateInfo = GenerateTemplateInfoForInterfaceList(interfacesToGenerate);

Encoders.HtmlEncode = (s) => s;
var text = Render.StringToString(ExtractTemplateSource(), templateInfo);
return text;
}

public List<string> FindInterfacesToGenerate(SyntaxTree tree)
{
var restServiceCalls = tree.GetRoot().DescendantNodes()
.OfType<MemberAccessExpressionSyntax>()
.Where(x => x.Expression is IdentifierNameSyntax &&
((IdentifierNameSyntax)x.Expression).Identifier.ValueText == "RestService" &&
x.Name.Identifier.ValueText == "For");

return restServiceCalls
.SelectMany(x => ((GenericNameSyntax)x.Name).TypeArgumentList.Arguments)
.Select(x => ((IdentifierNameSyntax)x).Identifier.ValueText)
.Distinct()
.ToList();
}

public TemplateInformation GenerateTemplateInfoForInterfaceList(List<InterfaceDeclarationSyntax> interfaceList)
{
var usings = interfaceList
.SelectMany(interfaceTree => {
var rootNode = interfaceTree.Parent;
while (rootNode.Parent != null) rootNode = rootNode.Parent;

return rootNode.DescendantNodes()
.OfType<UsingDirectiveSyntax>()
.Select(x => x.Name.ToString());
})
.Distinct()
.Where(x => x != "System" && x != "System.Net.Http")
.Select(x => new UsingDeclaration() { Item = x });

var ret = new TemplateInformation() {
ClassList = interfaceList.Select(x => GenerateClassInfoForInterface(x)).ToList(),
UsingList = usings.ToList(),
};

return ret;
}

public ClassTemplateInfo GenerateClassInfoForInterface(InterfaceDeclarationSyntax interfaceTree)
{
var ret = new ClassTemplateInfo();
var parent = interfaceTree.Parent;
while (parent != null && !(parent is NamespaceDeclarationSyntax)) parent = parent.Parent;

var ns = parent as NamespaceDeclarationSyntax;
ret.Namespace = ns.Name.ToString();
ret.InterfaceName = interfaceTree.Identifier.ValueText;

ret.MethodList = interfaceTree.Members
.OfType<MethodDeclarationSyntax>()
.Select(x => new MethodTemplateInfo() {
Name = x.Identifier.ValueText,
ReturnType = x.ReturnType.ToString(),
ArgumentList = String.Join(",", x.ParameterList.Parameters
.Select(y => y.Identifier.ValueText)),
ArgumentListWithTypes = String.Join(",", x.ParameterList.Parameters
.Select(y => String.Format("{0} {1}", y.Type.ToString(), y.Identifier.ValueText))),
})
.ToList();

return ret;
}

public static string ExtractTemplateSource()
{
var ourPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

// Try to return a flat file from the same directory, if it doesn't
// exist, use the built-in resource version
if (File.Exists(ourPath)) {
return File.ReadAllText(Path.Combine(ourPath, "GeneratedInterfaceStubTemplate.cs.mustache"), Encoding.UTF8);
}

using (var src = typeof(InterfaceStubGenerator).Assembly.GetManifestResourceStream("Refit.Generator.GeneratedInterfaceStubTemplate.mustache")) {
var ms = new MemoryStream();
src.CopyTo(ms);
return Encoding.UTF8.GetString(ms.ToArray());
}
}
}

public class UsingDeclaration
{
public string Item { get; set; }
}

public class ClassTemplateInfo
{
public string Namespace { get; set; }
public string InterfaceName { get; set; }
public List<MethodTemplateInfo> MethodList { get; set; }
}

public class MethodTemplateInfo
{
public string ReturnType { get; set; }
public string Name { get; set; }
public string ArgumentListWithTypes { get; set; }
public string ArgumentList { get; set; }
}

public class TemplateInformation
{
public List<UsingDeclaration> UsingList { get; set; }
public List<ClassTemplateInfo> ClassList;
}
}
115 changes: 115 additions & 0 deletions InterfaceStubGenerator/InterfaceStubGenerator.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Pr F438 oject="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{5694F8AD-7A15-4717-B649-1749A311300B}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Refit.Generator</RootNamespace>
<AssemblyName>InterfaceStubGenerator</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Nustache.Core">
<HintPath>..\packages\Nustache.1.13.8.22\lib\net20\Nustache.Core.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="System.Collections.Immutable">
<HintPath>..\packages\Microsoft.Bcl.Immutable.1.1.20-beta\lib\portable-net45+win8\System.Collections.Immutable.dll</HintPath>
</Reference>
<Reference Include="System.Reflection.Metadata">
<HintPath>..\packages\Microsoft.Bcl.Metadata.1.0.11-alpha\lib\portable-net45+win8\System.Reflection.Metadata.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CodeAnalysis">
<HintPath>..\packages\Microsoft.CodeAnalysis.Common.0.7.4052301-beta\lib\net45\Microsoft.CodeAnalysis.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CodeAnalysis.CSharp">
<HintPath>..\packages\Microsoft.CodeAnalysis.CSharp.0.7.4052301-beta\lib\net45\Microsoft.CodeAnalysis.CSharp.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="InterfaceStubGenerator.cs" />
<Compile Include="Mono.Options\Options.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<EmbeddedResource Include="GeneratedInterfaceStubTemplate.mustache">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.5">
<Visible>False</Visible>
<ProductName>Microsoft .NET Framework 4.5 %28x86 and x64%29</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>false</Install>
</BootstrapperPackage>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
Loading
0