Android studio | Dependency Management - android-gradle-plugin

Can anyone suggest, how can we add a dependency at build time in android gradle based on some condition like:
dependencies{
if(someCondition){
// add dependency
}
}
Thanks in advance!!

I found a solution for this:
Step1: Declare a boolean variable in gradle at root level.
like: def someDependencyEnabled = true //This could be dynamically set.
Step2: Using this boolean variable we can apply a check like:
if(someDependencyEnabled){
//Add some dependency
}
else
{
//Add some other dependency
}
Step3: Define Different source set for different situations:
android.sourceSets {
main {
java.srcDirs = ['src/main/java', someDependencyEnabled ? 'src/dependency_enabled_src' : 'src/dependency_disabled_src']
}
}
where:
'src/main/java' : is the common src file which contain common code.
'src/dependency_enabled_src': is the source folder that contain dependency specific code. which is further used by 'src/main/java'.
'src/dependency_disabled_src': is the source folder that contain alternate code when particular dependency is disabled.
In my case I wrote same name classes, methods & package name in both folders (dependency_enabled & dependency_disabled src) and wrote methods with desired implementation in dependency_enabled_src & empty methods for dependency_disabled_src.

Related

How to create a file in the resources folder in Kotlin, Gradle

In a completely fresh project, I want to create a single file myFile.json inside the src/main/resources/ folder at run time.
For reading a file, I need to do some config in the build.gradle.kts file, but I can't find anything on what to do for creating a file.
Assuming the directory src/main/resources/ exists:
val f = File("src/main/resources/myFile.json")
withContext(Dispatchers.IO) {
f.createNewFile() // This is the answer to the question
f.printWriter().use { out ->
out.println("{}")
}
}
#Endzeit has asked what have you tried so far. Please share the code.
Like #cyberbrain says - are you sure you want to write to resources folder?
Here is code that writes back to where the source resources folder is:
fun main(args: Array<String>) {
// Let's assume you want your project to be portable, so you don't
// want to use absolute file paths.
// Find out where your IDE will launch the project from. Normally this is
// the root folder of the whole project. Find out with this: the `canonicalPath` will help:
val workingFolder = File(".")
println("workingFolder=${workingFolder.canonicalPath}")
// Define the folder you want to write in to
// this will vary especially if you have a nested project structure
// IntelliJ under the Edit > Copy Path menu option will help you find the resources
// relative location
val parentFolder = File("src/main/resources")
println("parentFolder=${parentFolder.canonicalPath}")
require(parentFolder.exists())
val outFile = File(parentFolder, "test.txt")
outFile.printWriter(StandardCharsets.UTF_8).use {
it.println("Hello world")
}
println("Wrote to ${outFile.canonicalPath}")
}

Appy external file to the setting.gradle

I have a similar code like below. But since the logics are same for my other projects I wanted to keep this in a single file and "apply" in every project. So I tried using the "apply" command, but it won't work. Nothing complaining about the build but my logic is not executing in the build process.
Question - Why?
lateinit var projectConfigurer: (ProjectDescriptor, ProjectDescriptor) -> Unit
projectConfigurer = { _, project ->
project.name = project.name.replace("/", "-")
// Some logic to validate project naming convention
project.children.forEach { child ->
projectConfigurer(project, child)
}
}
rootProject.children.forEach { project ->
projectConfigurer(rootProject, project)
}
Here is what I wanted to do.
apply(from = "common/common.settings.gradle.kts")
And all the code I needed to move to "common.settings.gradle.kts", which was in a common folder reletive to "settings.gradle.kts"

IntelliJ - how to get the classpath for a module

I'm new to IntelliJ and working on a big project with hundreds of modules, I was just wondering how would I get the classpath for a specific module?
It might help you. Because each module has its own independent classpath. You can combine the classpaths of all modules in the project, and that's what that method did, but trying to use that classpath is unlikely to result in correct behavior in multi-module projects.
public static String getFullClassPath(Module m){
String cp = "";
cp += CompilerPaths.getModuleOutputPath(m,false);
for(VirtualFile vf : OrderEnumerator.orderEntries(m).recursively().getClassesRoots()){
String entry = new File(vf.getPath()).getAbsolutePath();
if(entry.endsWith("!/")){ //not sure why it happens in the returned paths
entry = entry.substring(0,entry.length()-2);
}
if(entry.endsWith("!")){
entry = entry.substring(0,entry.length()-1);
}
cp += File.pathSeparator + entry;
}
return cp;
}

Get project in Eclipse plugin without having an open editor

In a Eclipse plugin it's easy to get the current project(IProject) if there's an editor opened, you just need to use this snippet:
IEditorPart editor = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor();
IFileEditorInput input = (IFileEditorInput)editor.getEditorInput();
IFile file = input.getFile();
IProject project = file.getProject();
But, is there a way to get the project if I don't have any kind of file opened in the editor?, i.e: imagine that you have a plugin that adds an option when you right click a project, and if you click this option a dialog window is launched, how can I print the project name in this dialog?
For menu items and the like which use a 'command' with a 'handler' you can use code in the handler which is something like:
public class CommandHandler extends AbstractHandler
{
#Override
public Object execute(ExecutionEvent event) throws ExecutionException
{
ISelection sel = HandlerUtil.getCurrentSelection(event);
if (sel instanceof IStructuredSelection)
{
Object selected = ((IStructuredSelection)sel).getFirstElement();
IResource resource = (IResource)Platform.getAdapterManager().getAdapter(selected, IResource.class);
if (resource != null)
{
IProject project = resource.getProject();
...
}
}
return null;
}
}
What do you mean by "The current project"? Getting a specific project will always require some way of uniquely identifying that specific project.
If by current project you mean that the project is open, then that's not a good criterion for uniqueness (in the general case), since multiple projects can be open at the same time.
A guarantee of uniquely defining a project is by getting a reference to a resource contained by that project. For example, this can be done through the editor input, as you state, or trough a selection, as greg pointed out.
If you have the project's name, then you can use IWorkspaceRoot#getProject(String), but I assume that's not the case. Still, for completeness:
ResourcesPlugin.getWorkspace().getRoot().getProject("MyProject");
You could also get a list of all projects, and iterate over that list to check for a property that you know the project has (or the projects have). See the example below. Of course, this again doesn't guarantee uniqueness in the general case, since there can be multiple projects that satisfy the criteria. That's why I used Lists in the example.
IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
List<IProject> openProjects = new ArrayList<>();
List<IProject> myNatureProjects = new ArrayList<>();
for(IProject project : projects)
{
if(project.isOpen())
openProjects.add(project);
if(project.hasNature("MyNatureId")
myNatureProjects.add(project);
}

How to pass parameters or arguments into a Gradle task?

I have a Gradle build script into which I am trying to include Eric Wendelin's CSS plugin.
It's easy enough to implement, and because I only want minification (rather than combining and gzipping), I've got the pertinent parts of the build script looking like this:
minifyCss {
source = "src/main/webapp/css/brandA/styles.css"
dest = "${buildDir}/brandA/styles.css"
yuicompressor {
lineBreakPos = -1
}
}
war {
baseName = 'ex-ren'
}
war.doFirst {
tasks.myTask.minifyCss.execute()
}
This is perfect - when I run the gradle war task, it calls the minifyCss task, takes the source css file, and creates a minified version in the buildDir
However, I have a handful of css files which need minify-ing, but not combining into one file (hence I'm not using the combineCss task)
What I'd like to be able to do is make the source and dest properties (assuming that's the correct terminology?) of the minifyCss task reference variables of some sort - either variables passed into the task in the signature, or global variables, or something ...
Something like this I guess (which doesn't work):
minifyCss(sourceFile, destFile) {
source = sourceFile
dest = destFile
yuicompressor {
lineBreakPos = -1
}
}
war {
baseName = 'ex-ren'
}
war.doFirst {
tasks.myTask.minifyCss.execute("src/main/webapp/css/brandA/styles.css", "${buildDir}/brandA/styles.css")
tasks.myTask.minifyCss.execute("src/main/webapp/css/brandB/styles.css", "${buildDir}/brandB/styles.css")
tasks.myTask.minifyCss.execute("src/main/webapp/css/brandC/styles.css", "${buildDir}/brandC/styles.css")
}
This doesn't work either:
def sourceFile = null
def destFile = null
minifyCss {
source = sourceFile
dest = destFile
yuicompressor {
lineBreakPos = -1
}
}
war {
baseName = 'ex-ren'
}
war.doFirst {
sourceFile = "src/main/webapp/css/brandA/styles.css"
destFile = "${buildDir}/brandA/styles.css"
tasks.myTask.minifyCss.execute()
}
For the life of me I cannot work out how to call a task and pass variables in :(
Any help very much appreciated;
You should consider passing the -P argument in invoking Gradle.
From Gradle Documentation :
--project-prop
Sets a project property of the root project, for example -Pmyprop=myvalue. See Section 14.2, “Gradle properties and system properties”.
Considering this build.gradle
task printProp << {
println customProp
}
Invoking Gradle -PcustomProp=myProp will give this output :
$ gradle -PcustomProp=myProp printProp
:printProp
myProp
BUILD SUCCESSFUL
Total time: 3.722 secs
This is the way I found to pass parameters.
If the task you want to pass parameters to is of type JavaExec and you are using Gradle 5, for example the application plugin's run task, then you can pass your parameters through the --args=... command line option. For example gradle run --args="foo --bar=true".
Otherwise there is no convenient builtin way to do this, but there are 3 workarounds.
1. If few values, task creation function
If the possible values are few and are known in advance, you can programmatically create a task for each of them:
void createTask(String platform) {
String taskName = "myTask_" + platform;
task (taskName) {
... do what you want
}
}
String[] platforms = ["macosx", "linux32", "linux64"];
for(String platform : platforms) {
createTask(platform);
}
You would then call your tasks the following way:
./gradlew myTask_macosx
2. Standard input hack
A convenient hack is to pass the arguments through standard input, and have your task read from it:
./gradlew myTask <<<"arg1 arg2 arg\ in\ several\ parts"
with code below:
String[] splitIntoTokens(String commandLine) {
String regex = "(([\"']).*?\\2|(?:[^\\\\ ]+\\\\\\s+)+[^\\\\ ]+|\\S+)";
Matcher matcher = Pattern.compile(regex).matcher(commandLine);
ArrayList<String> result = new ArrayList<>();
while (matcher.find()) {
result.add(matcher.group());
}
return result.toArray();
}
task taskName, {
doFirst {
String typed = new Scanner(System.in).nextLine();
String[] parsed = splitIntoTokens(typed);
println ("Arguments received: " + parsed.join(" "))
... do what you want
}
}
You will also need to add the following lines at the top of your build script:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.Scanner;
3. -P parameters
The last option is to pass a -P parameter to Gradle:
./gradlew myTask -PmyArg=hello
You can then access it as myArg in your build script:
task myTask {
doFirst {
println myArg
... do what you want
}
}
Credit to #789 for his answer on splitting arguments into tokens
I would suggest the method presented on the Gradle forum:
def createMinifyCssTask(def brand, def sourceFile, def destFile) {
return tasks.create("minify${brand}Css", com.eriwen.gradle.css.tasks.MinifyCssTask) {
source = sourceFile
dest = destFile
}
}
I have used this method myself to create custom tasks, and it works very well.
task mathOnProperties << {
println Integer.parseInt(a)+Integer.parseInt(b)
println new Integer(a) * new Integer(b)
}
$ gradle -Pa=3 -Pb=4 mathOnProperties
:mathOnProperties
7
12
BUILD SUCCESSFUL
Its nothing more easy.
run command: ./gradlew clean -PjobId=9999
and
in gradle use: println(project.gradle.startParameter.projectProperties)
You will get clue.
I think you probably want to view the minification of each set of css as a separate task
task minifyBrandACss(type: com.eriwen.gradle.css.tasks.MinifyCssTask) {
source = "src/main/webapp/css/brandA/styles.css"
dest = "${buildDir}/brandA/styles.css"
}
etc etc
BTW executing your minify tasks in an action of the war task seems odd to me - wouldn't it make more sense to make them a dependency of the war task?
Here is a solution for Kotlin DSL (build.gradle.kts).
I first try to get the variable as a property and if it was null try to get it from OS environment variables (can be useful in CIs like GitHub Actions).
tasks.create("MyCustomTask") {
val songName = properties["songName"]
?: System.getenv("SONG_NAME")
?: error("""Property "songName" or environment variable "SONG_NAME" not found""")
// OR getting the property with 'by'. Did not work for me!
// For this approach, name of the variable should be the same as the property name
// val songName: String? by properties
println("The song name: $songName")
}
We can then pass a value for the property from command line:
./gradlew MyCustomTask -PsongName="Black Forest"
Or create a file named local.properties at the root of the project and set the property:
songName=Black Forest
We can also add an env variable named SONG_NAME with our desired value and then run the task:
./gradlew MyCustomTask