Freemarker template for gradle build files - android

Hi I'm trying to make a build.gradle file from a freemarker template and I'm running in to small problem both Freemarker and de gradle DSL support ${someString}
So somewhere in the gradle script I have field/variable definition.
For instance I define a String like below:
def gitSha = 'git rev-parse --short HEAD'.execute([], project.rootDir).text.trim()
And would use that somewhere else in the gradle file like so:
filename = "new-archive-${gitSha}.jar"
But then when I generate the gradle file with Freemarker I get the error gitSha is null or not defined in the data-model. Is there a way to make a distinction between ${gitSha} which is for the gradle DSL and the other ${someStrings} which are part of the Freemarker template?

In principle the correct solution would be to use something else instead of ${} in some of the two languages (let's say, #{}). But at least in Freemarker that's not configurable. But it could be emulated (kind of...), if you write a custom TempalateLoader that just wraps another TemplateLoader, but filters the Reader so that #{-s are replaced with ${-s, and ${ are replaced with #{. And then, the same transformation has to be applied to the Writer where Freemarker writes its output into, so the #{ in the output are replaced with ${-s. (I hope BTW that sooner or later Freemarker will have a configurable interpolation syntax, because this issue with ${ comes up quite often, but that's the future...)

Related

Delphi 11: Find correct toolsversion for MSBuild programmatically

We're upgrading to Delphi 11.1 from 10.4.
We have a few scripts which build and deploy Android projects. They assemble an msbuild command that looked like this:
msbuild someproject.dproj /v:q /p:Platform=Android /t:Build;Deploy /p:Config=Release /p:BT_BuildType=AppStore
With 11.1, this throws an error message:
C:\Program Files (x86)\Embarcadero\Studio\22.0\bin\CodeGear.Common.Targets(940,7): error MSB4036: The "XmlPeek" task was not found. Check the following: 1.) The name of the task in the project file is the same as the name of the task class. 2.) The task class is "public" and implements the Microsoft.Build.Framework.ITask interface. 3.) The task is correctly declared with <UsingTask> in the project file, or in the *.tasks files located in the "C:\Windows\Microsoft.NET\Framework\v2.0.50727" directory. [someproject.dproj]
Now, C:\Program Files (x86)\Embarcadero\Studio\22.0\bin\rsvars.bat, which is used by all of our build scripts, explicitly sets the .NET framework as below:
#SET FrameworkDir=C:\Windows\Microsoft.NET\Framework\v4.0.30319
#SET FrameworkVersion=v4.5
After some research, I hit on the idea of adding a toolsversion parameter to the msbuild command as below, and this worked:
msbuild someproject.dproj /v:q /p:Platform=Android /t:Build;Deploy /p:Config=Release /p:BT_BuildType=AppStore /toolsversion:4.0
This is all well and good, but I would prefer not to hard-code the toolsversion number in the script(s).
Is there a way I can programmatically obtain the correct value of the toolsversion that Delphi itself is using when it generates builds, etc.?
I assume that just finding the highest .NET version installed will not suffice (and even then, one has to "translate" that to a toolsversion). It has to marry up with whatever Delphi is doing (e.g., in the CodeGear.Common.Targets file referenced in the original error message).
This is a bug in Delphi 11.1 that has at least 3 bug reports: RSP-37855, RSP-38466, and RSP-38467.
You can add /tv:4.0 to your MSBuild command line, or modify the first line in the .dproj file to:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0" DefaultTargets="Build">
Assuming that rsvars.bat has been run,
FOR %%v IN ("%frameworkdir%") DO SET "toolsversion=%%~nv"
ECHO msbuild ...blah... /toolsversion:%toolsversion:~1%
The variable frameworkdir will be set by rsvars.bat. The for command parses its value (eg. C:\Windows\Microsoft.NET\Framework\v4.0.30319 as though it is a filename, and picks v4.0 as the "filename" (~n modifier [filename] of the metavariable %%v)
Then use the value assigned to toolsversion, starting at character 1 (where the first character is "character 0")
--- Given more info in comment
FOR %%v IN ("%frameworkdir%") DO ECHO %%~nxv|FINDSTR /R ".*\..*\.">nul&IF ERRORLEVEL 1 (SET "toolsversion=%%~nxv") ELSE SET "toolsversion=%%~nv"
Oh ye of little faith :)

Is it possible to use business code for Android and iOS written in Kotlin in a multiplatform Xamarin application?

I have some code that is written in Java and Kotlin for Android; the Java part can be translated into Kotlin using the Android Studio. Most of this code is business; that means, independent on any hardware or platform specifics; some Android specific classes (like "Bitmap") can be replaced by abstract or general self-defined classes.
As already known, Kotlin business code can be used in multiplatform applications for Android and iOS. Description here : https://kotlinlang.org/docs/multiplatform-mobile-integrate-in-existing-app.html .
Xamarin is used for multiplatform apps, too.
On the other hand, there is a way to include Kotlin code in Xamarin projects. For this purpose, the Xamarin.Kotlin.StdLib is used : https://libraries.io/nuget/Xamarin.Kotlin.StdLib .
My question: Is it possible to develop a Xamarin project (maybe with Xamarin Forms) that includes the Kotlin business code and will work in both Android and iOS environments?
Here are some instructions.
ANDROID STUDIO
Create a new Project: "File -> New -> New Module -> Kotlin
Multiplatform Shared Module".
Follow these instructions:
https://kotlinlang.org/docs/multiplatform-mobile-integrate-in-existing-app.html
until "Run your cross-platform application on Android".
For Android we're finished. Following steps are only for iOS.
We now assume that there is a module with the name "shared". If this module has another name, please replace it in the following instructions.
XCODE:
The following instructions are similar to that in https://kotlinlang.org/docs/multiplatform-mobile-integrate-in-existing-app.html#make-your-cross-platform-application-work-on-ios . The difference is that we don't want to build the app now, but a framework.
File New - Project.
Select the template for "Framework" and click "Next".
Choose a product name (for example, "KmmExample"). Language:
Objective C
Build Phases - New Run Script Phase:
cd "$SRCROOT/.."
chmod +x gradlew
./gradlew :shared:embedAndSignAppleFrameworkForXcode
Move the Run Script phase up so that it is located after the
"Dependencies" item.
On the Build Settings tab, switch to "All" build settings.
In the Search Paths paragraph, specify the Framework Search Path for both Debug and Release:
$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)
In the Linking paragraph:
Specify the "Mach-O Type" as "Static Library".
Specify the Other Linker Flags as
$(inherited) -framework shared
In the Architectures paragraph, "Architectures" may be unchanged or changed to "$(ARCHS_STANDARD_INCLUDING_64_BIT)".
Build the project.
If successful, there will be a folder structure inside "shared/build". There will be a subfolder "xcode-frameworks". In the "Debug" resp. "Release" directory, there will be subfolder(s) with the name(s) of the iOS device(s) or simulator(s). For example, "iphonesimulator15.5". It contains another subfolder: "shared.framework". In the "Headers" you find a "shared.h", and there is the library itself: "shared". (In the "shared/build" folder there will be also a "bin" directory with device and simulator names containing "debugFramework" and similar structures inside.)
OBJECTIVE SHARPIE
Download the "Objective Sharpie" tool
(https://learn.microsoft.com/en-us/xamarin/cross-platform/macios/binding/objective-sharpie/)
In order to create "ApiDefinitions.cs" from shared.h using
"sharpie" follow these steps:
Open a Termimal (Command-line) session.
Change to the directory where "shared" is located.
Type:
sharpie bind --output=./SharpieOutput --namespace=Shared --sdk iphoseos15.5 -scope ./shared ./shared/build/xcode-frameworks/Debug/iphonesimulator15.5/shared.framework/Headers/shared.h
(please replace "iphoneos15.5" by the correct SDK and "Debug/iphonesimulator15.5" by the correct folder name)
You also can choose another namespace instead of "Shared". It will be specified in the ApiDefinitions.h.
If successful, a "ApiDefinitions.cs" will be in a new subfolder
"SharpieOutput".
VISUAL STUDIO / XAMARIN
Use Visual Studio 2019 or 2022 or higher.
Create a new Solution, say, "MyApp".
"MyApp" should contain four projects: "MyApp", "MyApp.Android",
"MyApp.iOS".
Here, we don't talk about "MyApp.Android".
Right-click on the solution name and add a "New Project". Choose "iOS - Library - Bindings Library". Its name may be "MyApp.iOS.Binding".
Replace the ApiDefinitions.cs by that that has been created in the precding step.
Add the "shared" library (created by XCode in one of the steps above) as a "Native Library".
Right-click on ApiDefinitions.cs and change "Build action" =
"ObjcBindingApiDefinition".
When you now open ApiDefinitions.cs, you'll probably see a lot of errors and marked lines. They may be handled as follows:
For the "[Verify]" attribute, please check here: https://learn.microsoft.com/en-us/xamarin/ios/platform/binding-swift/walkthrough#build-a-binding-library , section 5.
If "NativeHandle" creates a compiler error, please add at the top:
#if !NET
using NativeHandle=System.IntPtr;
#endif
You may remove "using" with the shared module ("using shared")
If you encounter errors like "Cannot declare instance members in a static class (CS0708) and "static classes cannot implement interfaces" (CS0714): try to comment out or remove the attribute "[Category]".
"[Unavailable (PlatformName.Swift)]" may be removed if yielding an error.
Handle typed classes resp. Interfaces with "<T>". For example, the "<T>" attribute should be added to some interfaces representing typed classes. In some cases special handling is necessary.
CS0246: In some cases, when an element cannot be found, an attribute "[BaseType(typeof(SharedBase))]" may help (assuming that the interface SharedBase is defined at the beginning of this file).
If, during a build run, there are warnings CS8767 ("… hides inherited member"), add the attribute [Override] above these members. For "New()", however, add "[New]" instead.
In case of linker errors MT5211 "Native linking failed, undefined Objective-C class …", add the attribute "[Protocol]" in front of the interface definition:
[BaseTye (typeof (NSObject))]
[Protocol]
public interface MyInterface { … }
Other compiler / linker errors (e.g. " … was built for newer iOS version (…) than being linked …" possibly can result in warnings after that.
The "MyApp" project contains all platform independent code. Those classes which contain platform dependent parts should be defined as interfaces.
"MyApp.Android" and "MyApp.iOS" should implement these interfaces. For "MyApp.iOS", "MyApp" and "MyApp.iOS.Binding" should be added as References. The classes implementing those interfaces now can use the interfaces defined in ApiDefinitions.h.
End of instructions.
I have posted the same question in another forum - https://learn.microsoft.com/en-us/answers/questions/875957/use-business-code-for-android-and-ios-written-in-k.html - and I have posted a possible answer, see the answer of Jul 19, 2022. Spoiler: yes, it is possible, but not directly, and it is complicated.

android gradle mergeDebugAssets

android project with files in assets, these file need encrypt before generator apk
every times i changed some file in assets,
i need copy out these file , encrypt ,then copy into assets
what i want is:
keep file in assets not encypt (can edit it conveniently) ,
but file in .apk encrypted
encrypt work do automatically by gradle.build
my basic idea is thad add some task before mergeDebugAssets (or mergeReleaseAssets)
before mergeDebugAssets, i replace all file in file://$MODULE_DIR$/build/assets
code like below
task processAssetFile {
// code : replace file in build/assets
}
mergeDebugAssets.dependsOn processAssetFile
the problem is
mergeDebugAssets is not available in gradle.build
error log below:
Could not find property 'mergeDebugAssets' on project ':gradle'.
so is there some idea can achieve my goals ?
android studio ver :0.52
You can always find the task by its name.
I would also suggest that you want to perform encryption for other build variants (makes sense for release even more than for debug).
This requires a little bit of coding since you need to create a task for each variant, too.
It is best to create those tasks dynamically so you don't forget a variant.
Say you're encrypting with some command-line tool, the code you need to add could look like this:
android.applicationVariants.all { variant ->
String suffix = variant.variantData.name.capitalize()
Task mergeAssetsTask = tasks.findByName("merge${suffix}Assets")
Task processAssetFileTask = tasks.create(name: "process${suffix}AssetFile", type: Exec)
processAssetFileTask.commandLine "path/to/your/encryption/tool",
"--input-file=${inputFilePath}",
"--output-file=${outputFilePath}"
mergeAssetsTask.dependsOn processAssetFileTask
}

Generate Javadoc error Android Studio

For some reason I cannot generate a javadoc with Android Studio, after like 96 warnings it gives me this:
95 warnings
java.lang.NullPointerException
at com.sun.tools.javadoc.TypeMaker.getType(TypeMaker.java:83)
at com.sun.tools.javadoc.TypeMaker.getType(TypeMaker.java:44)
at com.sun.tools.javadoc.ClassDocImpl.superclassType(ClassDocImpl.java:496)
at com.sun.tools.doclets.internal.toolkit.util.Util.getAllInterfaces(Util.java:453)
at com.sun.tools.doclets.internal.toolkit.util.Util.getAllInterfaces(Util.java:491)
at com.sun.tools.doclets.internal.toolkit.util.ClassTree.processType(ClassTree.java:194)
at com.sun.tools.doclets.internal.toolkit.util.ClassTree.buildTree(ClassTree.java:146)
at com.sun.tools.doclets.internal.toolkit.util.ClassTree.<init>(ClassTree.java:91)
at com.sun.tools.doclets.internal.toolkit.AbstractDoclet.startGeneration(AbstractDoclet.java:123)
at com.sun.tools.doclets.internal.toolkit.AbstractDoclet.start(AbstractDoclet.java:83)
at com.sun.tools.doclets.formats.html.HtmlDoclet.start(HtmlDoclet.java:63)
at com.sun.tools.doclets.standard.Standard.start(Standard.java:39)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at com.sun.tools.javadoc.DocletInvoker.invoke(DocletInvoker.java:280)
at com.sun.tools.javadoc.DocletInvoker.start(DocletInvoker.java:160)
at com.sun.tools.javadoc.Start.parseAndExecute(Start.java:397)
at com.sun.tools.javadoc.Start.begin(Start.java:167)
at com.sun.tools.javadoc.Main.execute(Main.java:59)
at com.sun.tools.javadoc.Main.main(Main.java:49)
javadoc exited with exit code 1
Is there a way to create the javadoc in android studio? If not, how could i create one, I need to give it with my project.
A combination of the two answers given here worked well for me. Though the docs generate fine, I still get 900 warnings and 140 errors BUT they're all related to the android packages. This is just the given solutions combined and explained a bit for people who aren't familiar with Android Studio or command line interfaces.
HOW TO GENERATE JAVADOC IN ANDROID STUDIO
Open Android Studio > Tools > Generate Javadoc
Select the scope, usually the whole project
Check include jdk and library sources
Specify the output directory. A load of files will be dumped here, so I recommend creating a new folder.
Select which methods you want to expose. Usually protected is desirable unless it has to look impressive for school.
The "tricky" part, "Other command line arguments". Under the pretty GUI, there's a direct call to the javadoc command, which runs the javadoc generator. The command line uses a structure like this: program_name [-flag] argument [-flag] argument, which as you can guess calls the program with certain optional behaviours, passing in what those should be.
So by specifying the following under "Other command line arguments"
-encoding utf-8 -bootclasspath /path/to/sdk/platforms/android-##/android.jar followed by clicking OK
you're really calling the javadoc program with these flags and arguments. These two flags allow the javadoc program to ignore unicode characters and find the android.jar though it seems that everything marked with # is read as a number and the android javadocs are filled with them.
I urge you to read through the warnings for your classes despite the seemingly large amount of spam, as javadoc will tell you when you've forgotten things like empty #return statements.
Adding the following line on "other command line arguments" fixed the errors:
-bootclasspath /path/to/sdk/platforms/android-##/android.jar
I don't think this issue is specific to Android Studio. I'm guessing it will happen anytime you've got Unicode characters in your JavaDoc comments.
Try using the following command:
javadoc -encoding utf-8
Alternatively, you can just use Unicode escapes (e.g. \u0000) instead of including Unicode characters directly.
In Eclipse, you can add extras to the JavaDoc command:
Project -> Generate Javadoc -> Next -> on the last page, in Extra Javadoc options write:
-encoding UTF-8
If you have a static final string variable containing the escaped unicode, you can try referencing the value of the string in the doc. Android Studio was able to resolve the unicode in the sidebar documentation for me. I don't know if this will work if you're trying to generate the doc from the command line though.
private static final String UNICODE_VALUE = "\u251c";//or whatever string
/**
* {#value #UNICODE_VALUE}
*/
//whatever you want to document
I know this may be late, but it's worth the effort. you may add this to the gradle.build file
tasks.withType(Javadoc) {
options.addStringOption('Xdoclint:none', '-quiet')
options.addStringOption('encoding', 'UTF-8')
options.addStringOption('charSet', 'UTF-8')
}

How to pass a parameter to the Java code in run/debug configuration from Android Studio

My android app does some http requests to my server. However sometimes I am debugging the new api code that runs on my development machine. I would like to be able to pass something (like an environment variable) so in my code, if it's present I would be able to use that as the hostname for the api requests from the android emulator.
So I'm looking for a way to pass something like:
API_SERVER=http://10.0.2.2/myapp/
and in my code I would use it somehow, for example:
final static String API_SERVER_REAL = "http://example.com/";
final String apiServerOverride = System.getenv("API_SERVER");
final String API_SERVER = (null != apiServerOverride && !apiServerOverride.isEmpty() ? apiServerOverride : API_SERVER_REAL);
I know this thread is quite old, but in my opinion none of provided answers actually solves the problem. Flavors are ill-suited for parametrizing your build with things like API URLs, and even worse for things like API keys etc.
Firstly, build.gradle which defines flavors is part of project source, therefore it must not contain such information in order to be safely committed into source control systems.
Secondly, a need may arise to test different flavors against different API endpoints/keys. What if you just want to hit some debug http server you just created to solve a bug? Would you create a flavor for that? Probably not... Flavors are good for things like "free flavor" and "premium flavor".
This problem is easily solved using gradles -P flag. You can access gradle properties that are passed this way as regular variables inside your gradle.build, and you can vary it's behavior accordingly.
If you want to push this flags further into your application you can use Scott's solution that was posted here, combined with the provided flag.
The build command would then probably look like:
$ gradle build -Papiroot=http://www.example.com
And in your build.gradle you would define the writeValue task like this:
task writeValue(type:Exec) {
commandLine '/usr/local/bin/adb', 'shell', "echo 'API_SERVER=${apiroot}' > /data/data/values.properties"
}
FYI the -P flag can be easily configured in Android Studio by navigating from the menu:
Run -> Run/Debug Configurations -> Defaults -> Gradle -> Script Parameters
Probably the simplest thing is to write the data you want to pass to a file on the device in /data/data; your Android app can read the device trivially (perhaps make it a .properties file and use java.util.Properties to read it in). To write it out, use this kind of task in your build.gradle file (and use the correct path to the adb command for your setup):
task writeValue(type:Exec) {
commandLine '/usr/local/bin/adb', 'shell', 'echo \'API_SERVER=http://10.0.2.2/myapp/\' > /data/data/values.properties'
}
There's documentation on Gradle exec tasks at http://www.gradle.org/docs/current/dsl/org.gradle.api.tasks.Exec.html
You can execute this task manually from Android Studio by using the Gradle tasks view:
Due to a bug in Android Studio, you cannot pass vm or script parameters from a gradle configuration. The issue is here.
As a workaround in Linux envs (probably Mac too), you can create a bash configuration where you will be able to add all desired parameters.
I suggest using productFlavors. Each flavor can contain environment specific settings. I simply have a class called 'Environment' which contains all the public static final Strings that I need and each product flavor includes an different version of this class with the values set for the environment.

Categories

Resources