Added a tool to merge the assemblies and generate the map
This commit is contained in:
Родитель
6aa085069d
Коммит
ecb34ce1a5
|
@ -9,6 +9,7 @@ docs/
|
|||
generated/
|
||||
**/scriptcs_packages/
|
||||
tmp/
|
||||
util/binderator/
|
||||
|
||||
*.xam
|
||||
*.DS_Store
|
||||
|
@ -33,5 +34,3 @@ Details.md
|
|||
*.commit
|
||||
cake.sh
|
||||
**/.mfractor/**
|
||||
|
||||
util/
|
55
build.cake
55
build.cake
|
@ -1,7 +1,5 @@
|
|||
// Tools needed by cake addins
|
||||
#tool nuget:?package=ILRepack&version=2.0.13
|
||||
#tool nuget:?package=Cake.MonoApiTools&version=3.0.1
|
||||
//#tool nuget:?package=Microsoft.DotNet.BuildTools.GenAPI&version=1.0.0-beta-00081
|
||||
#tool nuget:?package=vswhere
|
||||
|
||||
// Cake Addins
|
||||
|
@ -46,6 +44,8 @@ if (IsRunningOnWindows ()) {
|
|||
}
|
||||
var MONODROID_PATH = MONODROID_BASE_PATH.Combine(ANDROID_SDK_VERSION);
|
||||
|
||||
var ANDROIDX_MAPPER_EXE = $"util/AndroidXMapper/AndroidXMapper/bin/{BUILD_CONFIG}/net47/AndroidXMapper.exe";
|
||||
|
||||
Information ("MONODROID_BASE_PATH: {0}", MONODROID_BASE_PATH);
|
||||
Information ("MONODROID_PATH: {0}", MONODROID_PATH);
|
||||
|
||||
|
@ -160,8 +160,6 @@ Task("samples")
|
|||
});
|
||||
});
|
||||
|
||||
Task("nuget-fat");
|
||||
|
||||
Task("nuget-validation")
|
||||
.Does(() =>
|
||||
{
|
||||
|
@ -202,6 +200,18 @@ Task("nuget-validation")
|
|||
|
||||
});
|
||||
|
||||
Task ("androidxmapper")
|
||||
.Does (() =>
|
||||
{
|
||||
MSBuild (
|
||||
"./util/AndroidXMapper/AndroidXMapper.sln", c => {
|
||||
c.Configuration = BUILD_CONFIG;
|
||||
c.MaxCpuCount = 0;
|
||||
c.Verbosity = VERBOSITY;
|
||||
c.Restore = true;
|
||||
});
|
||||
});
|
||||
|
||||
Task ("diff")
|
||||
.IsDependentOn ("merge")
|
||||
.Does (() =>
|
||||
|
@ -225,15 +235,29 @@ Task ("diff")
|
|||
MonoApiMarkdown ("./output/api-info.previous.xml", "./output/api-info.xml", "./output/api-diff.md");
|
||||
});
|
||||
|
||||
Task ("generate-mapping")
|
||||
.IsDependentOn ("androidxmapper")
|
||||
.IsDependentOn ("merge")
|
||||
.Does (() =>
|
||||
{
|
||||
DownloadFile (BASE_API_INFO_URL, "./output/api-info.previous.xml");
|
||||
|
||||
StartProcess(ANDROIDX_MAPPER_EXE,
|
||||
$"generate -v " +
|
||||
$" -s output/api-info.previous.xml " +
|
||||
$" -x output/api-info.xml " +
|
||||
$" -j util/AndroidXMapper/Resources/androidx-class-mapping.csv " +
|
||||
$" -m util/AndroidXMapper/Resources/override-mapping.csv " +
|
||||
$" -o output/androidx-mapping.csv");
|
||||
});
|
||||
|
||||
Task ("merge")
|
||||
.IsDependentOn ("androidxmapper")
|
||||
.IsDependentOn ("libs")
|
||||
.Does (() =>
|
||||
{
|
||||
EnsureDirectoryExists("./output/");
|
||||
|
||||
if (FileExists ("./output/AndroidX.Merged.dll"))
|
||||
DeleteFile ("./output/AndroidX.Merged.dll");
|
||||
|
||||
var allDlls = GetFiles ($"./generated/*/bin/{BUILD_CONFIG}/{TF_MONIKER}/Xamarin.*.dll");
|
||||
|
||||
var mergeDlls = allDlls
|
||||
|
@ -241,16 +265,12 @@ Task ("merge")
|
|||
.Select(g => g.FirstOrDefault())
|
||||
.ToList();
|
||||
|
||||
Information("Merging: \n - {0}", string.Join("\n - ", mergeDlls));
|
||||
|
||||
ILRepack ("./output/AndroidX.Merged.dll", mergeDlls.First(), mergeDlls.Skip(1), new ILRepackSettings {
|
||||
CopyAttrs = true,
|
||||
AllowMultiple = true,
|
||||
//TargetKind = ILRepack.TargetKind.Dll,
|
||||
Libs = new List<DirectoryPath> {
|
||||
MONODROID_PATH
|
||||
},
|
||||
});
|
||||
StartProcess(ANDROIDX_MAPPER_EXE,
|
||||
$"merge" +
|
||||
$" -a {string.Join(" -a ", mergeDlls)} " +
|
||||
$" -o output/AndroidX.Merged.dll " +
|
||||
$" -s \"{MONODROID_PATH}\" " +
|
||||
$" --inject-assemblyname");
|
||||
});
|
||||
|
||||
Task ("ci-setup")
|
||||
|
@ -301,6 +321,7 @@ Task ("ci")
|
|||
.IsDependentOn ("binderate")
|
||||
.IsDependentOn ("nuget")
|
||||
.IsDependentOn ("nuget-validation")
|
||||
.IsDependentOn ("generate-mapping")
|
||||
.IsDependentOn ("diff")
|
||||
.IsDependentOn ("samples");
|
||||
|
||||
|
|
|
@ -0,0 +1,288 @@
|
|||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
|
||||
# Visual Studio 2015 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUNIT
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# DNX
|
||||
project.lock.json
|
||||
artifacts/
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding add-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/packages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/packages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/packages/repositories.config
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Microsoft Azure ApplicationInsights config file
|
||||
ApplicationInsights.config
|
||||
|
||||
# Windows Store app package directory
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
node_modules/
|
||||
orleans.codegen.cs
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# Xcode
|
||||
#
|
||||
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
|
||||
|
||||
## Build generated
|
||||
build/
|
||||
DerivedData
|
||||
|
||||
## Various settings
|
||||
*.pbxuser
|
||||
!default.pbxuser
|
||||
*.mode1v3
|
||||
!default.mode1v3
|
||||
*.mode2v3
|
||||
!default.mode2v3
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
xcuserdata
|
||||
|
||||
## Other
|
||||
*.xccheckout
|
||||
*.moved-aside
|
||||
*.xcuserstate
|
||||
*.xcscmblueprint
|
||||
|
||||
## Obj-C/Swift specific
|
||||
*.hmap
|
||||
*.ipa
|
||||
|
||||
# CocoaPods
|
||||
#
|
||||
# We recommend against adding the Pods directory to your .gitignore. However
|
||||
# you should judge for yourself, the pros and cons are mentioned at:
|
||||
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
||||
#
|
||||
# Pods/
|
||||
|
||||
# Carthage
|
||||
#
|
||||
# Add this line if you want to avoid checking in source code from Carthage dependencies.
|
||||
# Carthage/Checkouts
|
||||
|
||||
Carthage/Build
|
||||
|
||||
# fastlane
|
||||
#
|
||||
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
|
||||
# screenshots whenever they are needed.
|
||||
# For more information about the recommended setup visit:
|
||||
# https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md
|
||||
|
||||
fastlane/report.xml
|
||||
fastlane/screenshots
|
||||
.vscode/
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.28606.126
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AndroidXMapper", "AndroidXMapper\AndroidXMapper.csproj", "{FC5A597E-B645-4F7A-994B-639787F92FA0}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{FC5A597E-B645-4F7A-994B-639787F92FA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FC5A597E-B645-4F7A-994B-639787F92FA0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FC5A597E-B645-4F7A-994B-639787F92FA0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FC5A597E-B645-4F7A-994B-639787F92FA0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {6B1166B4-0102-495F-B720-E755FA917EC8}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,14 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net47</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ILRepack.Lib" Version="2.0.16" />
|
||||
<PackageReference Include="Mono.Cecil" Version="0.10.3" />
|
||||
<PackageReference Include="Mono.Options" Version="5.3.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,198 @@
|
|||
using ILRepacking;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace AndroidXMapper
|
||||
{
|
||||
public class TestAttribute : Attribute
|
||||
{
|
||||
private readonly string assemblyName;
|
||||
|
||||
public TestAttribute(string assemblyName)
|
||||
{
|
||||
this.assemblyName = assemblyName;
|
||||
}
|
||||
|
||||
public string AssemblyName => assemblyName;
|
||||
}
|
||||
|
||||
[TestAttribute("the anme")]
|
||||
public class AssemblyMerger
|
||||
{
|
||||
private const string InjectedAttributeNamespace = "Xamarin.AndroidX.Internal";
|
||||
private const string InjectedAttributeTypeName = "InjectedAssemblyNameAttribute";
|
||||
|
||||
public AssemblyMerger(List<string> assemblies, List<string> searchDirectories, bool injectAssemblyNames)
|
||||
{
|
||||
Assemblies = assemblies ?? throw new ArgumentNullException(nameof(assemblies));
|
||||
SearchDirectories = searchDirectories ?? new List<string>();
|
||||
InjectAssemblyNames = injectAssemblyNames;
|
||||
}
|
||||
|
||||
public List<string> Assemblies { get; }
|
||||
|
||||
public List<string> SearchDirectories { get; }
|
||||
|
||||
public bool InjectAssemblyNames { get; }
|
||||
|
||||
public void Merge(string outputPath)
|
||||
{
|
||||
var assemblies = Assemblies;
|
||||
|
||||
if (Program.Verbose)
|
||||
{
|
||||
Console.WriteLine("Merging:");
|
||||
foreach (var include in assemblies)
|
||||
Console.WriteLine($" - {include}");
|
||||
}
|
||||
|
||||
var tempRoot = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
if (InjectAssemblyNames)
|
||||
{
|
||||
assemblies = assemblies.ToList();
|
||||
if (!Directory.Exists(tempRoot))
|
||||
Directory.CreateDirectory(tempRoot);
|
||||
|
||||
for (int i = 0; i < assemblies.Count; i++)
|
||||
{
|
||||
var ass = assemblies[i];
|
||||
var temp = Path.Combine(tempRoot, Guid.NewGuid().ToString() + ".dll");
|
||||
InjectAssemblyName(ass, temp);
|
||||
assemblies[i] = temp;
|
||||
}
|
||||
|
||||
if (Program.Verbose)
|
||||
{
|
||||
Console.WriteLine("Temporary assemblies:");
|
||||
foreach (var include in assemblies)
|
||||
Console.WriteLine($" - {include}");
|
||||
}
|
||||
}
|
||||
|
||||
var options = new RepackOptions
|
||||
{
|
||||
InputAssemblies = assemblies.ToArray(),
|
||||
OutputFile = outputPath,
|
||||
SearchDirectories = SearchDirectories.ToArray(),
|
||||
CopyAttributes = true,
|
||||
AllowMultipleAssemblyLevelAttributes = true,
|
||||
LogVerbose = Program.Verbose
|
||||
};
|
||||
var repacker = new ILRepack(options);
|
||||
repacker.Repack();
|
||||
|
||||
if (InjectAssemblyNames)
|
||||
{
|
||||
MergeAssemblyNameAttributes(outputPath);
|
||||
|
||||
if (Directory.Exists(tempRoot))
|
||||
Directory.Delete(tempRoot, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void InjectAssemblyName(string assemblyPath, string outputPath)
|
||||
{
|
||||
var assemblyName = Path.GetFileNameWithoutExtension(assemblyPath);
|
||||
|
||||
using (var assembly = AssemblyDefinition.ReadAssembly(assemblyPath))
|
||||
{
|
||||
var module = assembly.MainModule;
|
||||
|
||||
var mscorlibName = module.AssemblyReferences.FirstOrDefault(a => a.Name == "mscorlib");
|
||||
var mscorlib = module.AssemblyResolver.Resolve(mscorlibName);
|
||||
var attributeType = mscorlib.MainModule.GetType("System.Attribute");
|
||||
var baseCtor = attributeType.Methods.FirstOrDefault(m => m.Name == ".ctor");
|
||||
|
||||
var iana = new TypeDefinition(InjectedAttributeNamespace, InjectedAttributeTypeName, TypeAttributes.Class);
|
||||
iana.BaseType = module.ImportReference(attributeType);
|
||||
|
||||
var field = new FieldDefinition("assemblyName", FieldAttributes.Private | FieldAttributes.InitOnly, module.TypeSystem.String);
|
||||
|
||||
var getterAttributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName;
|
||||
var getter = new MethodDefinition("get_AssemblyName", getterAttributes, module.TypeSystem.String);
|
||||
getter.DeclaringType = iana;
|
||||
getter.HasThis = true;
|
||||
getter.IsGetter = true;
|
||||
getter.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
|
||||
getter.Body.Instructions.Add(Instruction.Create(OpCodes.Ldfld, field));
|
||||
getter.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
|
||||
|
||||
var property = new PropertyDefinition("AssemblyName", PropertyAttributes.None, module.TypeSystem.String);
|
||||
property.HasThis = true;
|
||||
property.GetMethod = getter;
|
||||
|
||||
var methodAttributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName;
|
||||
var ctor = new MethodDefinition(".ctor", methodAttributes, module.TypeSystem.Void);
|
||||
var param = new ParameterDefinition("assemblyName", ParameterAttributes.None, module.TypeSystem.String);
|
||||
ctor.Parameters.Add(param);
|
||||
|
||||
ctor.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
|
||||
ctor.Body.Instructions.Add(Instruction.Create(OpCodes.Call, module.ImportReference(baseCtor)));
|
||||
ctor.Body.Instructions.Add(Instruction.Create(OpCodes.Nop));
|
||||
ctor.Body.Instructions.Add(Instruction.Create(OpCodes.Nop));
|
||||
ctor.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
|
||||
ctor.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_1));
|
||||
ctor.Body.Instructions.Add(Instruction.Create(OpCodes.Stfld, field));
|
||||
ctor.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
|
||||
|
||||
iana.Fields.Add(field);
|
||||
iana.Properties.Add(property);
|
||||
iana.Methods.Add(ctor);
|
||||
iana.Methods.Add(getter);
|
||||
module.Types.Add(iana);
|
||||
|
||||
foreach (var type in module.Types)
|
||||
{
|
||||
type.CustomAttributes.Add(new CustomAttribute(ctor)
|
||||
{
|
||||
ConstructorArguments = { new CustomAttributeArgument(module.TypeSystem.String, assemblyName) }
|
||||
});
|
||||
}
|
||||
|
||||
assembly.Write(outputPath);
|
||||
}
|
||||
}
|
||||
|
||||
private void MergeAssemblyNameAttributes(string assemblyPath)
|
||||
{
|
||||
using (var assembly = AssemblyDefinition.ReadAssembly(assemblyPath, new ReaderParameters { ReadWrite = true }))
|
||||
{
|
||||
var correct = assembly.MainModule.GetType(InjectedAttributeNamespace + "." + InjectedAttributeTypeName);
|
||||
correct.Attributes |= TypeAttributes.Public;
|
||||
correct.CustomAttributes.Clear();
|
||||
|
||||
foreach (var type in assembly.MainModule.Types.ToArray())
|
||||
{
|
||||
var attribute = type.CustomAttributes.FirstOrDefault(a => IsRandomType(a.AttributeType));
|
||||
if (attribute != null && attribute.Constructor.DeclaringType != correct)
|
||||
{
|
||||
type.CustomAttributes.Add(new CustomAttribute(correct.Methods[0])
|
||||
{
|
||||
ConstructorArguments = { attribute.ConstructorArguments[0] }
|
||||
});
|
||||
type.CustomAttributes.Remove(attribute);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var type in assembly.MainModule.Types.Where(IsRandomType).ToArray())
|
||||
{
|
||||
assembly.MainModule.Types.Remove(type);
|
||||
}
|
||||
|
||||
assembly.Write();
|
||||
}
|
||||
|
||||
bool IsRandomType(TypeReference attr)
|
||||
{
|
||||
return
|
||||
attr.Namespace == InjectedAttributeNamespace &&
|
||||
attr.Name.EndsWith(InjectedAttributeTypeName) &&
|
||||
attr.Name != InjectedAttributeTypeName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace AndroidXMapper
|
||||
{
|
||||
public struct BindingType
|
||||
{
|
||||
public static BindingType Empty = new BindingType(FullType.Empty, FullType.Empty);
|
||||
|
||||
public FullType NetType;
|
||||
public FullType JavaType;
|
||||
|
||||
public BindingType(FullType netType, FullType javaType)
|
||||
{
|
||||
NetType = netType;
|
||||
JavaType = javaType;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
obj is BindingType other &&
|
||||
EqualityComparer<FullType>.Default.Equals(NetType, other.NetType) &&
|
||||
EqualityComparer<FullType>.Default.Equals(JavaType, other.JavaType);
|
||||
|
||||
public override int GetHashCode() =>
|
||||
(NetType, JavaType).GetHashCode();
|
||||
|
||||
public override string ToString() =>
|
||||
$"{NetType} ({JavaType})";
|
||||
|
||||
public void Deconstruct(out FullType netType, out FullType javaType)
|
||||
{
|
||||
netType = NetType;
|
||||
javaType = JavaType;
|
||||
}
|
||||
|
||||
public static implicit operator (FullType NetType, FullType JavaType) (BindingType value) =>
|
||||
(value.NetType, value.JavaType);
|
||||
|
||||
public static implicit operator BindingType((FullType NetType, FullType JavaType) value) =>
|
||||
new BindingType(value.NetType, value.JavaType);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
namespace AndroidXMapper
|
||||
{
|
||||
public struct FullType
|
||||
{
|
||||
public static FullType Empty = new FullType(string.Empty, string.Empty, string.Empty);
|
||||
|
||||
public string Container;
|
||||
public string Namespace;
|
||||
public string Name;
|
||||
|
||||
public FullType(string ns, string n)
|
||||
{
|
||||
Container = string.Empty;
|
||||
Namespace = ns;
|
||||
Name = n;
|
||||
}
|
||||
|
||||
public FullType(string container, string ns, string n)
|
||||
{
|
||||
Container = container;
|
||||
Namespace = ns;
|
||||
Name = n;
|
||||
}
|
||||
|
||||
public string FullName =>
|
||||
$"{Namespace}.{Name}";
|
||||
|
||||
public bool IsEmpty =>
|
||||
Namespace == string.Empty || Name == string.Empty;
|
||||
|
||||
public override bool Equals(object obj) =>
|
||||
obj is FullType other && Container == other.Container && Namespace == other.Namespace && Name == other.Name;
|
||||
|
||||
public override int GetHashCode() =>
|
||||
(Container, Namespace, Name).GetHashCode();
|
||||
|
||||
public override string ToString() =>
|
||||
FullName;
|
||||
|
||||
public void Deconstruct(out string c, out string ns, out string n)
|
||||
{
|
||||
c = Container;
|
||||
ns = Namespace;
|
||||
n = Name;
|
||||
}
|
||||
|
||||
public static implicit operator (string Container, string Namespace, string Name) (FullType value) =>
|
||||
(value.Container, value.Namespace, value.Name);
|
||||
|
||||
public static implicit operator FullType((string Container, string Namespace, string Name) value) =>
|
||||
new FullType(value.Container, value.Namespace, value.Name);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
using Mono.Options;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace AndroidXMapper
|
||||
{
|
||||
public class GenerateCommand : Command
|
||||
{
|
||||
public GenerateCommand()
|
||||
: base("generate", "Generates the mapping file.")
|
||||
{
|
||||
Options = new OptionSet
|
||||
{
|
||||
$"usage: {Program.Name} {Name} [OPTIONS]",
|
||||
"",
|
||||
Help,
|
||||
"",
|
||||
{ "s|support=", "The path to the merged Android.Support api-info.xml", v => AndroidSupportApi = v },
|
||||
{ "x|androidx=", "The path to the merged AndroidX api-info.xml", v => AndroidXApi = v },
|
||||
{ "j|java=", "The path to the Java mapping csv", v => JavaTypeMapping = v },
|
||||
{ "m|override=", "The path to the mapping overides csv", v => OverrideMapping = v },
|
||||
{ "o|output=", "The output path to use for the generated mapping", v => SetOutputPath(v) },
|
||||
{ "exclude-warnings", "Include warnings in the output", v => ExcludeWarnings = v != null },
|
||||
{ "?|h|help", "Show this message and exit", _ => ShowHelp = true },
|
||||
};
|
||||
}
|
||||
|
||||
private void SetOutputPath(string path)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
return;
|
||||
|
||||
if (path.EndsWith("/") || path.EndsWith("\\"))
|
||||
path += "androidx-mapping.csv";
|
||||
|
||||
var dir = Path.GetDirectoryName(path);
|
||||
if (!string.IsNullOrWhiteSpace(dir) && !Directory.Exists(dir))
|
||||
Directory.CreateDirectory(dir);
|
||||
|
||||
OutputPath = path;
|
||||
}
|
||||
|
||||
public bool ShowHelp { get; private set; }
|
||||
|
||||
public bool ExcludeWarnings { get; private set; }
|
||||
|
||||
public string AndroidSupportApi { get; private set; }
|
||||
|
||||
public string AndroidXApi { get; private set; }
|
||||
|
||||
public string JavaTypeMapping { get; private set; }
|
||||
|
||||
public string OverrideMapping { get; private set; }
|
||||
|
||||
public string OutputPath { get; private set; }
|
||||
|
||||
public override int Invoke(IEnumerable<string> args)
|
||||
{
|
||||
StreamWriter writer = null;
|
||||
|
||||
try
|
||||
{
|
||||
var extra = Options.Parse(args);
|
||||
|
||||
if (ShowHelp)
|
||||
{
|
||||
Options.WriteOptionDescriptions(CommandSet.Out);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ValidateArguments())
|
||||
return 1;
|
||||
|
||||
var outputWriter = string.IsNullOrWhiteSpace(OutputPath)
|
||||
? Console.Out
|
||||
: (writer = new StreamWriter(OutputPath));
|
||||
|
||||
var generator = new MappingGenerator(AndroidSupportApi, AndroidXApi, OverrideMapping, JavaTypeMapping);
|
||||
generator.Generate(outputWriter, !ExcludeWarnings);
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.Error.WriteLine($"{Program.Name}: An error occurred: `{ex.Message}`.");
|
||||
if (Program.Verbose)
|
||||
Console.Error.WriteLine(ex);
|
||||
return 1;
|
||||
}
|
||||
finally
|
||||
{
|
||||
writer?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private bool ValidateArguments()
|
||||
{
|
||||
var hasError = false;
|
||||
|
||||
if (string.IsNullOrEmpty(AndroidSupportApi))
|
||||
{
|
||||
Console.Error.WriteLine($"{Program.Name}: Missing required argument `--support=PATH`.");
|
||||
hasError = true;
|
||||
}
|
||||
else if (!File.Exists(AndroidSupportApi))
|
||||
{
|
||||
Console.Error.WriteLine($"{Program.Name}: File does not exist: `{AndroidSupportApi}`.");
|
||||
hasError = true;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(AndroidXApi))
|
||||
{
|
||||
Console.Error.WriteLine($"{Program.Name}: Missing required argument `--androidx=PATH`.");
|
||||
hasError = true;
|
||||
}
|
||||
else if (!File.Exists(AndroidXApi))
|
||||
{
|
||||
Console.Error.WriteLine($"{Program.Name}: File does not exist: `{AndroidXApi}`.");
|
||||
hasError = true;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(JavaTypeMapping) && !File.Exists(JavaTypeMapping))
|
||||
{
|
||||
Console.Error.WriteLine($"{Program.Name}: File does not exist: `{JavaTypeMapping}`.");
|
||||
hasError = true;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(OverrideMapping) && !File.Exists(OverrideMapping))
|
||||
{
|
||||
Console.Error.WriteLine($"{Program.Name}: File does not exist: `{OverrideMapping}`.");
|
||||
hasError = true;
|
||||
}
|
||||
|
||||
if (hasError)
|
||||
Console.Error.WriteLine($"{Program.Name}: Use `{Program.Name} help {Name}` for details.");
|
||||
|
||||
return !hasError;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,266 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace AndroidXMapper
|
||||
{
|
||||
public class MappingGenerator
|
||||
{
|
||||
private readonly Dictionary<BindingType, BindingType> typeMappings = new Dictionary<BindingType, BindingType>();
|
||||
|
||||
public string SupportApiPath { get; }
|
||||
|
||||
public string AndroidXApiPath { get; }
|
||||
|
||||
public string OverridesPath { get; }
|
||||
|
||||
public string JavaMappingPath { get; }
|
||||
|
||||
public MappingGenerator(string supportApiPath, string androidXApiPath, string overridesPath, string javaMappingPath)
|
||||
{
|
||||
SupportApiPath = supportApiPath ?? throw new ArgumentNullException(nameof(supportApiPath));
|
||||
AndroidXApiPath = androidXApiPath ?? throw new ArgumentNullException(nameof(androidXApiPath));
|
||||
OverridesPath = overridesPath;
|
||||
JavaMappingPath = javaMappingPath;
|
||||
}
|
||||
|
||||
public void Generate(TextWriter writer, bool includeWarnings)
|
||||
{
|
||||
var supportTypes = GetAllTypes(SupportApiPath);
|
||||
var xTypes = GetAllTypes(AndroidXApiPath);
|
||||
var overrideMappings = LoadMapping(OverridesPath);
|
||||
var javaMappings = LoadMapping(JavaMappingPath);
|
||||
|
||||
const string Prefix = ",,,,,,,,,";
|
||||
|
||||
writer.WriteLine(
|
||||
"Support .NET namespace," +
|
||||
"Support .NET type name," +
|
||||
"AndroidX .NET namespace," +
|
||||
"AndroidX .NET type name," +
|
||||
"AndroidX .NET assembly," +
|
||||
"Support Java package," +
|
||||
"Support Java class," +
|
||||
"AndroidX Java package," +
|
||||
"AndroidX Java class," +
|
||||
"Messages");
|
||||
|
||||
foreach (var supportType in supportTypes)
|
||||
{
|
||||
var useJavaType = !supportType.JavaType.IsEmpty && javaMappings.Count > 0;
|
||||
|
||||
if (TryGetMapping(overrideMappings, supportType.NetType, out var overrideType))
|
||||
{
|
||||
var matched = xTypes.Where(t => t.NetType.FullName == overrideType).ToList();
|
||||
if (matched.Count == 1)
|
||||
{
|
||||
typeMappings[supportType] = matched[0];
|
||||
}
|
||||
else if (matched.Count > 1)
|
||||
{
|
||||
if (includeWarnings)
|
||||
writer.WriteLine($"{Prefix}WARNING: Too many override types found for type {overrideType}: {string.Join(", ", matched)}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (includeWarnings)
|
||||
writer.WriteLine($"{Prefix}WARNING: Unable to find override type for type {overrideType}.");
|
||||
}
|
||||
}
|
||||
else if (TryGetExactMatch(xTypes, supportType, out var exactMatch))
|
||||
{
|
||||
typeMappings[supportType] = exactMatch;
|
||||
}
|
||||
else if (useJavaType && TryGetMapping(javaMappings, supportType.JavaType, out var androidx))
|
||||
{
|
||||
var matched = xTypes.Where(t => t.JavaType.FullName == androidx).ToList();
|
||||
|
||||
// a special case for the XxxConsts types
|
||||
const string ConstsSuffix = "Consts";
|
||||
if (matched.Count == 2 && matched.Any(m => m.NetType.Name.EndsWith(ConstsSuffix)))
|
||||
{
|
||||
matched.RemoveAll(m => m.NetType.Name.EndsWith(ConstsSuffix) != supportType.NetType.Name.EndsWith(ConstsSuffix));
|
||||
}
|
||||
|
||||
if (matched.Count == 1)
|
||||
{
|
||||
typeMappings[supportType] = matched[0];
|
||||
}
|
||||
else if (matched.Count > 1)
|
||||
{
|
||||
if (includeWarnings)
|
||||
writer.WriteLine($"{Prefix}WARNING: Too many AndroidX types found for Java type {androidx}: {string.Join(", ", matched)}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (includeWarnings)
|
||||
writer.WriteLine($"{Prefix}WARNING: Unable to find AndroidX type for Java type {androidx}.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (includeWarnings && useJavaType)
|
||||
writer.WriteLine($"{Prefix}WARNING: Unable to find a Java mapping for {supportType}. Trying managed mapping...");
|
||||
|
||||
var matched = xTypes.Where(xt => xt.NetType.Name == supportType.NetType.Name).ToList();
|
||||
if (matched.Count == 1)
|
||||
{
|
||||
typeMappings[supportType] = matched[0];
|
||||
|
||||
if (includeWarnings && useJavaType)
|
||||
writer.WriteLine($"{Prefix}WARNING: Found a type that appears to match {matched[0]}.");
|
||||
}
|
||||
else if (matched.Count > 1)
|
||||
{
|
||||
if (includeWarnings)
|
||||
writer.WriteLine($"{Prefix}WARNING: Too many AndroidX types found for .NET type {supportType.NetType}: {string.Join(", ", matched)}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (includeWarnings)
|
||||
writer.WriteLine($"{Prefix}WARNING: Unable to find AndroidX type for .NET type {supportType.NetType}.");
|
||||
}
|
||||
}
|
||||
|
||||
if (typeMappings.TryGetValue(supportType, out var androidXType))
|
||||
{
|
||||
writer.WriteLine(
|
||||
$"{supportType.NetType.Namespace}," +
|
||||
$"{supportType.NetType.Name}," +
|
||||
$"{androidXType.NetType.Namespace}," +
|
||||
$"{androidXType.NetType.Name}," +
|
||||
$"{androidXType.NetType.Container}," +
|
||||
$"{supportType.JavaType.Namespace}," +
|
||||
$"{supportType.JavaType.Name}," +
|
||||
$"{androidXType.JavaType.Namespace}," +
|
||||
$"{androidXType.JavaType.Name},");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryGetExactMatch(IEnumerable<BindingType> xTypes, BindingType supportType, out BindingType exactMatch)
|
||||
{
|
||||
var netMatches = xTypes.Where(t => t.NetType.FullName == supportType.NetType.FullName).ToList();
|
||||
if (netMatches.Count == 1)
|
||||
{
|
||||
exactMatch = netMatches[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
var javaMatches = xTypes.Where(t => t.JavaType.FullName == supportType.JavaType.FullName).ToList();
|
||||
if (javaMatches.Count == 1)
|
||||
{
|
||||
exactMatch = javaMatches[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
exactMatch = BindingType.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryGetMapping(Dictionary<string, string> mappings, FullType type, out string androidx)
|
||||
{
|
||||
if (mappings.TryGetValue(type.FullName, out androidx))
|
||||
return true;
|
||||
|
||||
string nested = "";
|
||||
while (type.Name.Contains("."))
|
||||
{
|
||||
nested = type.Name.Substring(type.Name.LastIndexOf(".")) + nested;
|
||||
type.Name = type.Name.Substring(0, type.Name.LastIndexOf("."));
|
||||
|
||||
if (mappings.TryGetValue(type.FullName, out androidx))
|
||||
{
|
||||
androidx += nested;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private Dictionary<string, string> LoadMapping(string mapping)
|
||||
{
|
||||
var dic = new Dictionary<string, string>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(mapping) && File.Exists(mapping))
|
||||
{
|
||||
var lines = File.ReadAllLines(mapping);
|
||||
foreach (var line in lines)
|
||||
{
|
||||
var parts = line.Split(',');
|
||||
dic.Add(parts[0], parts[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return dic;
|
||||
}
|
||||
|
||||
private List<BindingType> GetAllTypes(string api)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(api))
|
||||
throw new ArgumentException($"Invalid api.xml path: {api}", nameof(api));
|
||||
if (!File.Exists(api))
|
||||
throw new FileNotFoundException($"The api.xml does not exist: {api}");
|
||||
|
||||
var xdoc = XDocument.Load(api);
|
||||
var classes = xdoc.Descendants("class");
|
||||
return classes.Select(GetFullType).ToList();
|
||||
}
|
||||
|
||||
private BindingType GetFullType(XElement element)
|
||||
{
|
||||
var assembly = GetAttributeValue(element, "Xamarin.AndroidX.Internal.InjectedAssemblyNameAttribute", "AssemblyName");
|
||||
var ns = "";
|
||||
var name = element.Attribute("name").Value;
|
||||
var java = GetJavaType(element);
|
||||
|
||||
var parent = element.Parent?.Parent;
|
||||
while (parent != null)
|
||||
{
|
||||
if (parent.Name == "class")
|
||||
{
|
||||
name = parent.Attribute("name").Value + "." + name;
|
||||
assembly = GetAttributeValue(parent, "Xamarin.AndroidX.Internal.InjectedAssemblyNameAttribute", "AssemblyName");
|
||||
}
|
||||
else if (parent.Name == "namespace")
|
||||
{
|
||||
ns = parent.Attribute("name").Value;
|
||||
}
|
||||
|
||||
parent = parent.Parent?.Parent;
|
||||
}
|
||||
|
||||
return new BindingType(new FullType(assembly, ns, name), java);
|
||||
}
|
||||
|
||||
private static string GetAttributeValue(XElement element, string attributeType, string propertyName)
|
||||
{
|
||||
var attributeElement = element
|
||||
?.Element("attributes")
|
||||
?.Elements("attribute")
|
||||
?.FirstOrDefault(e => e.Attribute("name")?.Value == attributeType);
|
||||
|
||||
var value = attributeElement
|
||||
?.Element("properties")
|
||||
?.Elements("property")
|
||||
?.FirstOrDefault(e => e.Attribute("name")?.Value == propertyName)
|
||||
?.Attribute("value")?.Value ?? string.Empty;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private FullType GetJavaType(XElement element)
|
||||
{
|
||||
var javaType = GetAttributeValue(element, "Android.Runtime.RegisterAttribute", "Name");
|
||||
var parts = javaType?.Split('/');
|
||||
var type = parts?.LastOrDefault()?.Split('$');
|
||||
if (parts?.Length > 0 && type?.Length > 0)
|
||||
return new FullType(string.Join(".", parts.Take(parts.Length - 1)), string.Join(".", type));
|
||||
else
|
||||
return FullType.Empty;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
using Mono.Options;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace AndroidXMapper
|
||||
{
|
||||
public class MergeCommand : Command
|
||||
{
|
||||
public MergeCommand()
|
||||
: base("merge", "Merges the various AndroidX libraries.")
|
||||
{
|
||||
Options = new OptionSet
|
||||
{
|
||||
$"usage: {Program.Name} {Name} [OPTIONS]",
|
||||
"",
|
||||
Help,
|
||||
"",
|
||||
{ "a|assembly=", "One or more assemblies to merge", v => Assemblies.Add(v) },
|
||||
{ "o|output=", "The output path to use for the merged assembly", v => SetOutputPath(v) },
|
||||
{ "s|search=", "One or more search directories", v => SearchDirectories.Add(v) },
|
||||
{ "inject-assemblyname", "Add the assembly names to the types", _ => InjectAssemblyNames = true },
|
||||
{ "?|h|help", "Show this message and exit", _ => ShowHelp = true },
|
||||
};
|
||||
}
|
||||
|
||||
private void SetOutputPath(string path)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
return;
|
||||
|
||||
if (path.EndsWith("/") || path.EndsWith("\\"))
|
||||
path += "AndroidX.Merged.dll";
|
||||
|
||||
var dir = Path.GetDirectoryName(path);
|
||||
if (!string.IsNullOrWhiteSpace(dir) && !Directory.Exists(dir))
|
||||
Directory.CreateDirectory(dir);
|
||||
|
||||
OutputPath = path;
|
||||
}
|
||||
|
||||
public bool ShowHelp { get; private set; }
|
||||
|
||||
public List<string> Assemblies { get; } = new List<string>();
|
||||
|
||||
public string OutputPath { get; private set; }
|
||||
|
||||
public List<string> SearchDirectories { get; } = new List<string>();
|
||||
|
||||
public bool InjectAssemblyNames { get; private set; }
|
||||
|
||||
public override int Invoke(IEnumerable<string> args)
|
||||
{
|
||||
try
|
||||
{
|
||||
var extra = Options.Parse(args);
|
||||
|
||||
if (ShowHelp)
|
||||
{
|
||||
Options.WriteOptionDescriptions(CommandSet.Out);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ValidateArguments())
|
||||
return 1;
|
||||
|
||||
var merger = new AssemblyMerger(Assemblies, SearchDirectories, InjectAssemblyNames);
|
||||
merger.Merge(OutputPath);
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.Error.WriteLine($"{Program.Name}: An error occurred: `{ex.Message}`.");
|
||||
if (Program.Verbose)
|
||||
Console.Error.WriteLine(ex);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private bool ValidateArguments()
|
||||
{
|
||||
var hasError = false;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(OutputPath))
|
||||
{
|
||||
Console.Error.WriteLine($"{Program.Name}: An output path is required `--output=PATH`.");
|
||||
hasError = true;
|
||||
}
|
||||
|
||||
if (Assemblies.Count == 0)
|
||||
{
|
||||
Console.Error.WriteLine($"{Program.Name}: At least one assembly is required `--assembly=PATH`.");
|
||||
hasError = true;
|
||||
}
|
||||
|
||||
var missing = Assemblies.Where(i => !File.Exists(i));
|
||||
|
||||
if (missing.Any())
|
||||
{
|
||||
foreach (var file in missing)
|
||||
Console.Error.WriteLine($"{Program.Name}: File does not exist: `{file}`.");
|
||||
hasError = true;
|
||||
}
|
||||
|
||||
if (hasError)
|
||||
Console.Error.WriteLine($"{Program.Name}: Use `{Program.Name} help {Name}` for details.");
|
||||
|
||||
return !hasError;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
using Mono.Options;
|
||||
|
||||
namespace AndroidXMapper
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public const string Name = "androidxmapper";
|
||||
|
||||
public static bool Verbose;
|
||||
|
||||
static int Main(string[] args)
|
||||
{
|
||||
var commands = new CommandSet(Name)
|
||||
{
|
||||
$"usage: {Name} COMMAND [OPTIONS]",
|
||||
"",
|
||||
"A utility that helps create the Android.Support to AndroidX mapping file.",
|
||||
"",
|
||||
"Global options:",
|
||||
{ "v|verbose", "Use a more verbose output", _ => Verbose = true },
|
||||
"",
|
||||
"Available commands:",
|
||||
new GenerateCommand(),
|
||||
new MergeCommand(),
|
||||
};
|
||||
return commands.Run(args);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
Old build artifact,AndroidX build artifact
|
||||
android.arch.core:common,androidx.arch.core:core-common:2.0.0-rc01
|
||||
android.arch.core:core,androidx.arch.core:core:2.0.0-rc01
|
||||
android.arch.core:core-testing,androidx.arch.core:core-testing:2.0.0-rc01
|
||||
android.arch.core:runtime,androidx.arch.core:core-runtime:2.0.0-rc01
|
||||
android.arch.lifecycle:common,androidx.lifecycle:lifecycle-common:2.0.0-rc01
|
||||
android.arch.lifecycle:common-java8,androidx.lifecycle:lifecycle-common-java8:2.0.0-rc01
|
||||
android.arch.lifecycle:compiler,androidx.lifecycle:lifecycle-compiler:2.0.0-rc01
|
||||
android.arch.lifecycle:extensions,androidx.lifecycle:lifecycle-extensions:2.0.0-rc01
|
||||
android.arch.lifecycle:livedata,androidx.lifecycle:lifecycle-livedata:2.0.0-rc01
|
||||
android.arch.lifecycle:livedata-core,androidx.lifecycle:lifecycle-livedata-core:2.0.0-rc01
|
||||
android.arch.lifecycle:reactivestreams,androidx.lifecycle:lifecycle-reactivestreams:2.0.0-rc01
|
||||
android.arch.lifecycle:runtime,androidx.lifecycle:lifecycle-runtime:2.0.0-rc01
|
||||
android.arch.lifecycle:viewmodel,androidx.lifecycle:lifecycle-viewmodel:2.0.0-rc01
|
||||
android.arch.paging:common,androidx.paging:paging-common:2.0.0-rc01
|
||||
android.arch.paging:runtime,androidx.paging:paging-runtime:2.0.0-rc01
|
||||
android.arch.paging:rxjava2,androidx.paging:paging-rxjava2:2.0.0-rc01
|
||||
android.arch.persistence.room:common,androidx.room:room-common:2.0.0-rc01
|
||||
android.arch.persistence.room:compiler,androidx.room:room-compiler:2.0.0-rc01
|
||||
android.arch.persistence.room:guava,androidx.room:room-guava:2.0.0-rc01
|
||||
android.arch.persistence.room:migration,androidx.room:room-migration:2.0.0-rc01
|
||||
android.arch.persistence.room:runtime,androidx.room:room-runtime:2.0.0-rc01
|
||||
android.arch.persistence.room:rxjava2,androidx.room:room-rxjava2:2.0.0-rc01
|
||||
android.arch.persistence.room:testing,androidx.room:room-testing:2.0.0-rc01
|
||||
android.arch.persistence:db,androidx.sqlite:sqlite:2.0.0-rc01
|
||||
android.arch.persistence:db-framework,androidx.sqlite:sqlite-framework:2.0.0-rc01
|
||||
com.android.support.constraint:constraint-layout,androidx.constraintlayout:constraintlayout:1.1.2
|
||||
com.android.support.constraint:constraint-layout-solver,androidx.constraintlayout:constraintlayout-solver:1.1.2
|
||||
com.android.support.test.espresso.idling:idling-concurrent,androidx.test.espresso.idling:idling-concurrent:3.1.0
|
||||
com.android.support.test.espresso.idling:idling-net,androidx.test.espresso.idling:idling-net:3.1.0
|
||||
com.android.support.test.espresso:espresso-accessibility,androidx.test.espresso:espresso-accessibility:3.1.0
|
||||
com.android.support.test.espresso:espresso-contrib,androidx.test.espresso:espresso-contrib:3.1.0
|
||||
com.android.support.test.espresso:espresso-core,androidx.test.espresso:espresso-core:3.1.0
|
||||
com.android.support.test.espresso:espresso-idling-resource,androidx.test.espresso:espresso-idling-resource:3.1.0
|
||||
com.android.support.test.espresso:espresso-intents,androidx.test.espresso:espresso-intents:3.1.0
|
||||
com.android.support.test.espresso:espresso-remote,androidx.test.espresso:espresso-remote:3.1.0
|
||||
com.android.support.test.espresso:espresso-web,androidx.test.espresso:espresso-web:3.1.0
|
||||
com.android.support.test.janktesthelper:janktesthelper,androidx.test.jank:janktesthelper:1.0.1
|
||||
com.android.support.test.services:test-services,androidx.test:test-services:1.1.0
|
||||
com.android.support.test.uiautomator:uiautomator,androidx.test.uiautomator:uiautomator:2.2.0
|
||||
com.android.support.test:monitor,androidx.test:monitor:1.1.0
|
||||
com.android.support.test:orchestrator,androidx.test:orchestrator:1.1.0
|
||||
com.android.support.test:rules,androidx.test:rules:1.1.0
|
||||
com.android.support.test:runner,androidx.test:runner:1.1.0
|
||||
com.android.support:animated-vector-drawable,androidx.vectordrawable:vectordrawable-animated:1.0.0
|
||||
com.android.support:appcompat-v7,androidx.appcompat:appcompat:1.0.0
|
||||
com.android.support:asynclayoutinflater,androidx.asynclayoutinflater:asynclayoutinflater:1.0.0
|
||||
com.android.support:car,androidx.car:car:1.0.0-alpha5
|
||||
com.android.support:cardview-v7,androidx.cardview:cardview:1.0.0
|
||||
com.android.support:collections,androidx.collection:collection:1.0.0
|
||||
com.android.support:coordinatorlayout,androidx.coordinatorlayout:coordinatorlayout:1.0.0
|
||||
com.android.support:cursoradapter,androidx.cursoradapter:cursoradapter:1.0.0
|
||||
com.android.support:customtabs,androidx.browser:browser:1.0.0
|
||||
com.android.support:customview,androidx.customview:customview:1.0.0
|
||||
com.android.support:design,com.google.android.material:material:1.0.0-rc01
|
||||
com.android.support:documentfile,androidx.documentfile:documentfile:1.0.0
|
||||
com.android.support:drawerlayout,androidx.drawerlayout:drawerlayout:1.0.0
|
||||
com.android.support:exifinterface,androidx.exifinterface:exifinterface:1.0.0
|
||||
com.android.support:gridlayout-v7,androidx.gridlayout:gridlayout:1.0.0
|
||||
com.android.support:heifwriter,androidx.heifwriter:heifwriter:1.0.0
|
||||
com.android.support:interpolator,androidx.interpolator:interpolator:1.0.0
|
||||
com.android.support:leanback-v17,androidx.leanback:leanback:1.0.0
|
||||
com.android.support:loader,androidx.loader:loader:1.0.0
|
||||
com.android.support:localbroadcastmanager,androidx.localbroadcastmanager:localbroadcastmanager:1.0.0
|
||||
com.android.support:media2,androidx.media2:media2:1.0.0-alpha03
|
||||
com.android.support:media2-exoplayer,androidx.media2:media2-exoplayer:1.0.0-alpha01
|
||||
com.android.support:mediarouter-v7,androidx.mediarouter:mediarouter:1.0.0
|
||||
com.android.support:multidex,androidx.multidex:multidex:2.0.0
|
||||
com.android.support:multidex-instrumentation,androidx.multidex:multidex-instrumentation:2.0.0
|
||||
com.android.support:palette-v7,androidx.palette:palette:1.0.0
|
||||
com.android.support:percent,androidx.percentlayout:percentlayout:1.0.0
|
||||
com.android.support:preference-leanback-v17,androidx.leanback:leanback-preference:1.0.0
|
||||
com.android.support:preference-v14,androidx.legacy:legacy-preference-v14:1.0.0
|
||||
com.android.support:preference-v7,androidx.preference:preference:1.0.0
|
||||
com.android.support:print,androidx.print:print:1.0.0
|
||||
com.android.support:recommendation,androidx.recommendation:recommendation:1.0.0
|
||||
com.android.support:recyclerview-selection,androidx.recyclerview:recyclerview-selection:1.0.0
|
||||
com.android.support:recyclerview-v7,androidx.recyclerview:recyclerview:1.0.0
|
||||
com.android.support:slices-builders,androidx.slice:slice-builders:1.0.0
|
||||
com.android.support:slices-core,androidx.slice:slice-core:1.0.0
|
||||
com.android.support:slices-view,androidx.slice:slice-view:1.0.0
|
||||
com.android.support:slidingpanelayout,androidx.slidingpanelayout:slidingpanelayout:1.0.0
|
||||
com.android.support:support-annotations,androidx.annotation:annotation:1.0.0
|
||||
com.android.support:support-compat,androidx.core:core:1.0.0
|
||||
com.android.support:support-content,androidx.contentpager:contentpager:1.0.0
|
||||
com.android.support:support-core-ui,androidx.legacy:legacy-support-core-ui:1.0.0
|
||||
com.android.support:support-core-utils,androidx.legacy:legacy-support-core-utils:1.0.0
|
||||
com.android.support:support-dynamic-animation,androidx.dynamicanimation:dynamicanimation:1.0.0
|
||||
com.android.support:support-emoji,androidx.emoji:emoji:1.0.0
|
||||
com.android.support:support-emoji-appcompat,androidx.emoji:emoji-appcompat:1.0.0
|
||||
com.android.support:support-emoji-bundled,androidx.emoji:emoji-bundled:1.0.0
|
||||
com.android.support:support-fragment,androidx.fragment:fragment:1.0.0
|
||||
com.android.support:support-media-compat,androidx.media:media:1.0.0
|
||||
com.android.support:support-tv-provider,androidx.tvprovider:tvprovider:1.0.0
|
||||
com.android.support:support-v13,androidx.legacy:legacy-support-v13:1.0.0
|
||||
com.android.support:support-v4,androidx.legacy:legacy-support-v4:1.0.0
|
||||
com.android.support:support-vector-drawable,androidx.vectordrawable:vectordrawable:1.0.0
|
||||
com.android.support:swiperefreshlayout,androidx.swiperefreshlayout:swiperefreshlayout:1.0.0
|
||||
com.android.support:textclassifier,androidx.textclassifier:textclassifier:1.0.0
|
||||
com.android.support:transition,androidx.transition:transition:1.0.0
|
||||
com.android.support:versionedparcelable,androidx.versionedparcelable:versionedparcelable:1.0.0
|
||||
com.android.support:viewpager,androidx.viewpager:viewpager:1.0.0
|
||||
com.android.support:wear,androidx.wear:wear:1.0.0
|
||||
com.android.support:webkit,androidx.webkit:webkit:1.0.0
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,6 @@
|
|||
Old type,AndroidX type
|
||||
Android.Support.V4.Media.MediaSessionManager,AndroidX.Media.MediaSessionManager
|
||||
Android.Support.V4.App.ActionBarDrawerToggle.IDelegate,AndroidX.Legacy.App.ActionBarDrawerToggle.IDelegate
|
||||
Android.Support.V4.App.ActionBarDrawerToggle.IDelegateProvider,AndroidX.Legacy.App.ActionBarDrawerToggle.IDelegateProvider
|
||||
Android.Support.V7.App.ActionBarDrawerToggle.IDelegate,AndroidX.AppCompat.App.ActionBarDrawerToggle.IDelegate
|
||||
Android.Support.V7.App.ActionBarDrawerToggle.IDelegateProvider,AndroidX.AppCompat.App.ActionBarDrawerToggle.IDelegateProvider
|
|
Загрузка…
Ссылка в новой задаче