Xamarin.Forms problems with Embedded Resources and Proguard - xamarin.forms

I have two different applications, both using files that I've included in my Xamarin.Forms project's root. The files are set as Embedded Resources under "Build Action" and then fetched using the code below.
var assembly = Assembly.GetExecutingAssembly();
Stream stream = assembly.GetManifestResourceStream("test.text.json");
This approach works just fine with all compilation options such as AoT and LLVM, except for when Proguard is involved. If the application is compiled using Proguard it will just instantly force close without being able to read the files.
So far I've tried making exceptions for the classes in the project using a custom proguard.cfg-file that is set as my "ProguardConfiguration" in VS 2017.
-dontobfuscate
-keep class com.companyname.test** { *; }
-keeppackagenames com.companyname.test**
This has not yielded any results. The application still crashes instantly. Every other application using Proguard is working just fine as long as it's not loading any files locally. Should I use a different way of loading files or can I exclude anything else in my Proguard configuration to make this work?
EDIT: Just updating this post to draw attention to the solution. It turns out I have solved this problem before but for some reason it seems to be back. Here is the log output:
android.view.InflateException: Binary XML file line #20: Binary XML file
line #20: Error inflating class android.support.v7.widget.FitWindowsFrameLayout
Which is something that I have already added to my general Proguard rules without me having to add a special Proguard configuration to my project. As a solution this time I simply added these two lines to my proguard.cfg file:
-keep public class android.support.v7.widget.** { *; }
-dontwarn android.support.**
Thanks Alen.Toma for pointing me in the right direction.

Related

Vaadin Flow 14, Jetty embedded and static files

I'm trying to create app based on Jetty 9.4.20 (embedded) and Vaadin Flow 14.0.12.
It based on very nice project vaadin14-embedded-jetty.
I want to package app with one main-jar and all dependency libs must be in folder 'libs' near main-jar.
I remove maven-assembly-plugin, instead use maven-dependency-plugin and maven-jar-plugin. In maven-dependency-plugin i add section <execution>get-dependencies</execution> where i unpack directories META-INF/resources/,META-INF/services/ from Vaadin Flow libs to the result JAR.
In this case app work fine. But if i comment section <execution>get-dependencies</execution> then result package didn't contain that directories and app didn't work.
It just cannot give some static files from Vaadin Flow libs.
This error occurs only if i launch packaged app with ...
$ java -jar vaadin14-embedded-jetty-1.0-SNAPSHOT.jar
... but from Intellij Idea it launch correctly.
There was an opinion that is Jetty staring with wrong ClassLoader and cannot maintain requests to static files in Jar-libs.
The META-INF/services/ files MUST be maintained from the Jetty libs.
That's important for Jetty to use java.util.ServiceLoader.
If you are merging contents of JAR files into a single JAR file, that's called a "uber jar".
There are many techniques to do this, but if you are using maven-assembly-plugin or maven-dependency-plugin to build this "uber jar" then you will not be merging critical files that have the same name across multiple JAR files.
Consider using maven-shade-plugin and it's associated Resource Transformers to properly merge these files.
http://maven.apache.org/plugins/maven-shade-plugin/
http://maven.apache.org/plugins/maven-shade-plugin/examples/resource-transformers.html
The ServicesResourceTransformer is the one that merges META-INF/services/ files, use it.
As for static content, that works fine, but you have to setup your Base Resource properly.
Looking at your source, you do the following ...
final URI webRootUri = ManualJetty.class.getResource("/webapp/").toURI();
final WebAppContext context = new WebAppContext();
context.setBaseResource(Resource.newResource(webRootUri));
That won't work reliably in 100% of cases (as you have noticed when running in the IDE vs command line).
The Class.getResource(String) is only reliable if you lookup a file (not a directory).
Consider that the Jetty Project Embedded Cookbook recipes have techniques for this.
See:
WebAppContextFromClasspath.java
ResourceHandlerFromClasspath.java
DefaultServletFileServer.java
DefaultServletMultipleBases.java
XmlEnhancedServer.java
MultipartMimeUploadExample.java
Example:
// Figure out what path to serve content from
ClassLoader cl = ManualJetty.class.getClassLoader();
// We look for a file, as ClassLoader.getResource() is not
// designed to look for directories (we resolve the directory later)
URL f = cl.getResource("webapp/index.html");
if (f == null)
{
throw new RuntimeException("Unable to find resource directory");
}
// Resolve file to directory
URI webRootUri = f.toURI().resolve("./").normalize();
System.err.println("WebRoot is " + webRootUri);
WebAppContext context = new WebAppContext();
context.setBaseResource(Resource.newResource(webRootUri));

How to resolve Xamarin Android linking problem

My Xamarin Forms Android app works fine in Debug. But in Release, when deployed to either the emulator or to a device, the app is "stopped" after showing the splash screen. The debug log shows these errors, indicating that it can't find class FitWindowsLinearLayout...
10-23 22:24:59.270 14008 14008 E AndroidRuntime: Caused by:
android.view.InflateException: Binary XML file line #14: Binary XML
file line #14: Error inflating class
android.support.v7.widget.FitWindowsLinearLayout
10-23 22:24:59.270 14008 14008 E AndroidRuntime: Caused by:
android.view.InflateException: Binary XML file line #14: Error
inflating class android.support.v7.widget.FitWindowsLinearLayout
10-23 22:24:59.270 14008 14008 E AndroidRuntime: Caused by:
java.lang.ClassNotFoundException: Didn't find class
"android.support.v7.widget.FitWindowsLinearLayout" on path:
DexPathList[[zip file
"/data/app/zeemerix.NflCalcXF-1/base.apk"],nativeLibraryDirectories=[/data/app/zeemerix.NflCalcXF-1/lib/arm,
/data/app/zeemerix.NflCalcXF-1/base.apk!/lib/armeabi-v7a, /system/lib,
/vendor/lib]]
Changing Linking to "None" resolves the problem, but then the APK is very large.
Should I use "Skip Linking Assemblies"? If so, what to enter?
I tried using a Proguard profile, but when I do, then the MainPage appears as a blank screen, and the debug log says it can't find Help_ObClick(), which is an event handler in the MainPage code behind. The profile I used is this...
-dontobfuscate
-keep class android.support.v7.widget.** { *; }
-dontwarn android.support.v7.widget.**
-keep class android.support.v4.widget.Space { *; }
-dontwarn android.support.v4.widget.Space
My Android Options:
Off: Use shared runtime
On: Enable Proguard
On: Enable developer instrumentation
Linking: Sdk and User Assemblies [Setting it to None resolves problem]
Compile using Android version: 8.1
Min Android version: 7.0
Target Android version: 8.1
My setup:
VS Community 2017, Version 15.7.3
Xamarin 4.10.10.1
Xamarin Android SDK 8.3.3.2
Xamarin Forms 3.3.0.912540
Xamarin support libraries v27.0.2.1
As you know linking 'None' works, try then linking 'Sdk Assemblies Only'
If that doesn't work, then you can go into your build folder, copy all the names of all the assemblies minus extension and add them to the "Skip linking assemblies" field
Then set linking to 'Sdk and User Assemblies' and try again.
It will take forever but then remove each of these files from the list until you find the one(s) that cause the problem.
For me it was much easier as Link 'Sdk Assemblies Only' worked so i could eliminate all the Sdk assemblies found here:
https://learn.microsoft.com/en-us/xamarin/cross-platform/internals/available-assemblies
The user list was much smaller and thus process of elimination much quicker...
Fwiw, libraries that use reflection are often a problem as they cant be easily identified as used before compile and get marked for delete.
In my case it was Json.Net, RestSharp and the like....
My published app size is only a third what it would have been otherwise...
if you want to link SDK and User Assemblies you have to skip linking your .net standard and any projects you use, like customControls in my project.
There are many other ways to do this but this is easier.

Can't deploy precompiled, merged webapp to Azure

I'm trying to deploy an ASP.NET web application to Azure. It's hybrid Web Forms, MVC, and WebAPI, and there are a TON of aspx/ascx files, such that they really need to be precompiled or every deploy will render the site sluggish for awhile.
I am trying to deploy via SCM integration with GitHub via kudu, with precompiled views, all merged to a single assembly.
Note that:
Deploy works fine with precompilation disabled.
Deploy works fine from Visual Studio
Build works fine if I copy the msbuild command from the Azure log, replace the relevant paths, and run it locally on my Windows 8.1 machine.
I've set up the Advanced Precompile settings as:
Don't allow precompiled site to be udpatable
Don't emit debug information
Merge all pages and control outputs to a single assembly = AppViews.dll
Here's the .deployment file for Azure
[config]
project = WebSite/WebSite.csproj
SCM_BUILD_ARGS=/p:Configuration=Release;PublishProfile=azure-prod /v:n
You notice I'm sending the verbosity /v to "normal" for extra diagnostic information.
Here is info I get toward the tail of the deployment log:
AspNetPreCompile:
D:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_compiler.exe -v \ -p D:\home\site\repository\WebSite\obj\Release\AspnetCompileMerge\Source -c D:\home\site\repository\WebSite\obj\Release\AspnetCompileMerge\TempBuildDir
GenerateAssemblyInfoFromExistingAssembleInfo:
Creating directory "obj\Release\AssemblyInfo".
D:\Windows\Microsoft.NET\Framework\v4.0.30319\Csc.exe /out:obj\Release\AssemblyInfo\AssemblyInfo.dll /target:library Properties\AssemblyInfo.cs
AspNetMerge:
Running aspnet_merge.exe.
D:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools\aspnet_merge.exe D:\home\site\repository\WebSite\obj\Release\AspnetCompileMerge\TempBuildDir -w AppViews.dll -copyattrs obj\Release\AssemblyInfo\AssemblyInfo.dll -a
aspnet_merge : error 1003: The directory 'D:\home\site\repository\WebSite\obj\Release\AspnetCompileMerge\TempBuildDir' does not exist. [D:\home\site\repository\WebSite\WebSite.csproj]
Done Building Project "D:\home\site\repository\WebSite\WebSite.csproj" (Build;pipelinePreDeployCopyAllFilesToOneFolder target(s)) -- FAILED.
Build FAILED.
It looks like aspnet_compiler.exe runs, but doesn't do what it's supposed to, which is why the TempBuildDir directory (supposed to be the output of the compiler) does not exist in time for the AspNetMerge target. Contrast that with my system, where that directory DOES in fact exist, containing the marker aspx/ascx/etc. files, static content, a PrecompiledApp.config file, and a whole mess of stuff in the bin directory.
aspnet_compiler.exe has an -errorstack flag but it's not clear to me how I could get MSBuild to add this just via the .deployment file, or even if that app is really even throwing an error.
I could just deploy via Visual Studio, but I would really like to take advantage of the SCM integration so I can just push to my prod branch and let it go. Any suggestions?
I replied on https://github.com/projectkudu/kudu/issues/1341, but copying my answer here in case someone lands here...
Way back, we had found that aspnet_compiler.exe was not working within Azure Websites due to how it dealt with the profile folder. We made a change at the time that's a bit of a hack but got us going: we turned it into a no-op, by pointing HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\aspnet_compiler.exe to our own dummy exe (D:\Program Files (x86)\aspnet_compiler\KuduAspNetCompiler.exe).
But trying it now, it appears to work correctly today, likely thanks to improvements in the Azure Websites hosting environment. So we will try getting rid of this hack and doing a full test pass to make sure it doesn't cause any major regressions. If all goes well, we can get that into production, which should enable those scenarios.
In the short term, you may be able to work around this by having your build script:
copy aspnet_compiler.exe from D:\Windows\Microsoft.NET\Framework\v4.0.30319 into your own site files, but under a different name (e.g. aspnet_compiler2.exe)
convince msbuild to use that one
Note: This GitHub issue on projectkudu will eventually make this solution obsolete, but for the meantime, that issue is filed as Backlog, and this works right now.
Thank you thank you David Ebbo. With this information, I was able to bootstrap my build to work for the short term.
First, I downloaded the aspnet_compiler.exe from the Azure instance using the Diagnostic Console available at https://{WEBSITE_NAME}.scm.azurewebsites.net/DebugConsole and added that to my own repository. This way there's no question about any difference between 32/64-bit, etc. I renamed it to azure_aspnet_compiler.exe in my repository.
Second, the AspNetCompiler task doesn't give you the option to change the tool name. It's hardcoded, but as a virtual property so it's overrideable. So I had to create my own task class, and package it in its own assembly, which I built in Release mode and also included in my repository.
public class AzureAspNetCompiler : Microsoft.Build.Tasks.AspNetCompiler
{
private string _toolName = "aspnet_compiler.exe";
protected override string ToolName
{
get { return _toolName; }
}
public string CustomToolName // Because ToolName cannot have a setter
{
get { return _toolName; }
set { _toolName = value; }
}
}
Next I needed to replace the AspNetPreCompile task in MSBuild, but I couldn't figure out how to do that directly. But that task wasn't doing anything anyway, so why not just run right after it?
I added this to the top of my Website.csproj file to import the DLL containing the AzureAspNetCompiler class. Note that the path is relative to the Website.csproj file I'm editing.
<UsingTask TaskName="AzureBuildTargets.AzureAspNetCompiler"
AssemblyFile="..\DeploymentTools\AzureBuildTargets.dll" />
Then I added this right below it, which is basically stealing the MSBuild target definition of AspNetPreCompile from C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v11.0\Web\Transform\Microsoft.Web.Publishing.AspNetCompileMerge.targets, with some of the property setting stuff near the top of it left out (because the original task will do that for us anyway.) Just take note of the ToolPath and CustomToolName values at the bottom of the (renamed) AzureAspNetCompiler element.
<PropertyGroup>
<!--Relative to solution root apparently-->
<LocalRepoDeploymentTools>.\DeploymentTools</LocalRepoDeploymentTools>
<AzureAspnetCompilerPath>$([System.IO.Path]::GetFullPath($(LocalRepoDeploymentTools)))</AzureAspnetCompilerPath>
</PropertyGroup>
<Target Name="NoReallyAspNetPreCompile" AfterTargets="AspNetPreCompile">
<AzureAspNetCompiler
PhysicalPath="$(_PreAspnetCompileMergeSingleTargetFolderFullPath)"
TargetPath="$(_PostAspnetCompileMergeSingleTargetFolderFullPath)"
VirtualPath="$(_AspNetCompilerVirtualPath)"
Force="$(_AspNetCompilerForce)"
Debug="$(DebugSymbols)"
Updateable="$(EnableUpdateable)"
KeyFile="$(_AspNetCompileMergeKeyFile)"
KeyContainer="$(_AspNetCompileMergeKeyContainer)"
DelaySign="$(DelaySign)"
AllowPartiallyTrustedCallers="$(AllowPartiallyTrustedCallers)"
FixedNames="$(_AspNetCompilerFixedNames)"
Clean="$(Clean)"
MetabasePath="$(_AspNetCompilerMetabasePath)"
ToolPath="$(AzureAspnetCompilerPath)"
CustomToolName="azure_aspnet_compiler.exe"
/>
<!--
Removing APP_DATA is done here so that the output groups reflect the fact that App_data is
not present
-->
<RemoveDir Condition="'$(DeleteAppDataFolder)' == 'true' And Exists('$(_PostAspnetCompileMergeSingleTargetFolderFullPath)\App_Data')"
Directories="$(_PostAspnetCompileMergeSingleTargetFolderFullPath)\App_Data" />
<CollectFilesinFolder Condition="'$(UseMerge)' != 'true'"
RootPath="$(_PostAspnetCompileMergeSingleTargetFolderFullPath)" >
<Output TaskParameter="Result" ItemName="_AspnetCompileMergePrecompiledOutputNoMetadata" />
</CollectFilesinFolder>
<ItemGroup Condition="'$(UseMerge)' != 'true'">
<FileWrites Include="$(_PostAspnetCompileMergeSingleTargetFolderFullPath)\**"/>
</ItemGroup>
With this in place, everything works as I would expect it to.

Flex Localization: Could not find compiled resource bundle

I tried every solution i found in the internet.
Im using flex 4.5, This is what im doing:
created directory locale/en_US in my src directory
add resources.properties file to that directory with some mappings.
add -locale en_US -source-path=./locale/{locale} -allow-source-path-overlap=true to the compile args.
checked in the framework that the en_US locale directory appear.
add metadata:
<fx:Metadata>
[ResourceBundle("resources")]
</fx:Metadata>
starting the app gives me the exception:
Error: Could not find compiled resource bundle 'resources' for locale 'en_US'.
This is some of the main solutions i tried:
uncheck "Remove unused RSLs" from the build path.
add the directory as a source path.
using the argument -include-resource-bundles and give my directory here (with using the argument -resource-bundle-list to get all bundles).
Any idea what else can i do?
Here is my structure for a mobile app (Android and iOS):
In src/locale I have 3 subdirs: de_DE, en_US, ru_RU
And in compiler options: -locale=ru_RU,en_US,de_DE -source-path=locale/{locale}
For another mobile app I have:
In src/locale 4 subdirs: en_US, hr_HR, sr_RS, sl_SI.
I had to add the latter 3 dirs with copylocale command.
And in compiler options: -locale hr_HR sr_RS sl_SI en_US -allow-source-path-overlap=true
Both apps work well for me with the latest Apache Flex SDK.
Here is the contents of a src/locale/hr_HR/recources.properties file:
# resources.properties file for locale hr_HR
navbar.tables=Stolovi za igranje:
navbar.all=Svi
navbar.vacant_long=Slobodni
navbar.vacant_short=Slb.
navbar.full_long=Su puni
navbar.full_short=Su puni
comments.good_long=dobri
comments.good_short=Dbr.
comments.bad_long=loši
comments.bad_short=loši
comments.without_long=neutralni
comments.without_short=ntr.
help.title=Pomoć
OK i found a solution here:
http://www.nbilyk.com/flex-localization-example
im really not sure why it should be that difficult.
anyway, if someone ever need a help with that. after you successfully compile the file using ant (like described in the link), if you want to load it dynamcally like i needed just use (for example):
resourceManager.localeChain = ["en_US", "es_ES"];
resourceManager.loadResourceModule("Resources_en_US.swf");
resourceManager.loadResourceModule("Resources_es_ES.swf");
worked well for me, no need to add anything to the compiler args for that solution.
Try using the fully qualified directory path name. If you're using ant you can use ${basedir}/src/locale/{locale}

Setting Jetty resourcebase to static file embedded in the same jar file

I am trying to access static resource (eg. first.html) packed inside the same .jar file (testJetty.jar), which also has a class which starts the jetty (v.8) server (MainTest.java). I am unable to set the resource base correctly.
The structure of my jar file (testJetty.jar):
testJetty.jar
first.html
MainTest.java
==
Works fine on local machine, but when I wrap it in jar file and then run it, it doesn't work, giving "404: File not found" error.
I tried to set the resourcebase with the following values, all of which failed:
a) Tried setting it to .
resource_handler.setResourceBase("."); // Results in directory containing the jar file, D:\Work\eclipseworkspace\testJettyResult
b) Tried getting it from getResource
ClassLoader loader = this.getClass().getClassLoader();
File indexLoc = new File(loader.getResource("first.html").getFile());
String htmlLoc = indexLoc.getAbsolutePath();
resource_handler.setResourceBase(htmloc); // Results in D:\Work\eclipseworkspace\testJettyResult\file:\D:\Work\eclipseworkspace\testJettyResult\testJetty1.jar!\first.html
c) Tried getting the webdir
String webDir = this.getClass().getProtectionDomain()
.getCodeSource().getLocation().toExternalForm();
resource_handler.setResourceBase(webdir); // Results in D:/Work/eclipseworkspace/testJettyResult/testJetty1.jar
None of these 3 approaches worked.
Any help or alternative would be appreciated
Thanks
abbas
The solutions provided in this thread work but I think some clarity to the solution could be useful.
If you are building a fat jar and use the ProtectionDomain way you may hit some issues because you are loading the whole jar!
class.getProtectionDomain().getCodeSource().getLocation().toExternalForm();
So the better solution is the other provided solution
contextHandler.setResourceBase(
YourClass.class
.getClassLoader()
.getResource("WEB-INF")
.toExternalForm());
The problem here is if you are building a fat jar you are not really dumping your webapp resources into WEB-INF but are probably going into the root of the jar, so a simple workaround is to create a folder XXX and use the second approach as follows:
contextHandler.setResourceBase(
YourClass.class
.getClassLoader()
.getResource("XXX")
.toExternalForm());
Or change your build tool to export the webapp files into that given directory. Maybe Maven does this on a Jar for you but gradle does not.
Not unusually, I found a solution to my problem. The 3rd approach mentioned by Stephen in Embedded Jetty : how to use a .war that is included in the .jar from which Jetty starts? worked!
So, I changed from Resource_handler to WebAppContext, where WebAppContext is pointing to the same jar (testJetty.jar) and it worked!
String webDir = MainTest.class.getProtectionDomain()
.getCodeSource().getLocation().toExternalForm(); ; // Results in D:/Work/eclipseworkspace/testJettyResult/testJetty.jar
WebAppContext webappContext = new WebAppContext(webDir, "/");
It looks like ClassLoader.getResource does not understand an empty string or . or / as an argument. In my jar file I had to move all stuf to WEB-INF(any other wrapping dir will do). So the code looks like
contextHandler.setResourceBase(EmbeddedJetty.class.getClassLoader().getResource("WEB-INF").toExternalForm());
so the context looks like this then:
ContextHandler:744 - Started o.e.j.w.WebAppContext#48b3806{/,jar:file:/Users/xxx/projects/dropbox/ui/target/ui-1.0-SNAPSHOT.jar!/WEB-INF,AVAILABLE}

Resources