Search code examples
androidbuild.gradlegradle-kotlin-dsl

How to add a library/module to a project using a gradle task?


The idea is to be able to add libraries to a project without actually modifying the code. This is mainly to be able to generate builds as per requirements. While I could achieve this with buildFlavors, I wanted the flexibility of being able to do this for any number of libraries.

Basically its something like this,

  • I have a base app
  • One team developed a library A
  • I have another library B
  • One client needs only library A, another needs both A and B. Down the line other clients could need different libraries.

What I need is to add these libraries to the Base App and generate builds as per requirements.

For this, I created the following task. For the moment I am just trying with local modules already registered in settings.gradle file, but not added to the project

val myArg: String by project
//use task in this way - gradlew addDependencyAndGenerateBuilds -PmyArg dependency name
tasks.register("addDependencyAndGenerateBuilds"){
  doLast {
    if(project.hasProperty("myArg")){
      dependencies.implementation(dependencies.project(":$myArg"))
    }
    finalizedBy("clean", "build")
  }
}

Now if I run this task - ./gradlew addDependencyAndGenerateBuilds -PmyArg libraryA, I get the following error,

Task 'libraryA' not found in root project

What I need is for this libraryA to be added to the base app and new builds generated accordingly.


Solution

  • Turns out there was an issue with run task command. There needs to be an = between myArg and libraryA.

    The full command is like this,

    ./gradlew addDependencyAndGenerateBuilds -PmyArg=libraryA
    

    While this fixed the issue mentioned (i.e. Task libraryA not found..), it failed to build with some errors.

    I then scrapped the whole idea and just added an extension method in the app gradle.

    fun DependencyHandlerScope.addExtension(extensionsFilePath: String) {
      val extensionsFile = File("${project.rootDir}", extensionsFilePath)
      if (extensionsFile.exists()) {
        val extensions = extensionsFile.readBytes().decodeToString()
        val extensionsJson = Gson().fromJson(extensions, JsonObject::class.java)
        if (extensionsJson.has("modules")) {
          println("Adding module dependencies")
          extensionsJson.getAsJsonArray("modules").forEach {
            implementation(project(it.asString))
          }
        }
      }
    }
    

    This is used in the dependencies block like this,

    dependencies {
      ........
      ........
      addExtension("extensions.json")
    }
    

    This method takes in a Json file containing a list of dependencies and then just adds them to the project. All one had to do is then edit this json file before generating a build.

    To further simplify this, I just added a task that edits the json file,

    val myArg: String by project
    tasks.register("addNewExtension") {
      doLast {
        val extensionsFile = File(
          "${project.rootDir}",
          "extensions.json"
        )
        val extensionsJson = if (extensionsFile.exists()) {
          val extensionsData = extensionsFile.readBytes().decodeToString()
          Gson().fromJson(extensionsData, JsonObject::class.java)
        } else {
          JsonObject()
        }
    
        if (project.hasProperty("myArg")) {
          val modules = extensionsJson.getAsJsonArray("modules") ?: JsonArray()
          if (!modules.contains(JsonPrimitive(":$myArg"))) {
            modules.add(":$myArg")
          }
          extensionsJson.add("modules", modules)
        }
    
        extensionsFile.delete()
        extensionsFile.createNewFile()
        extensionsFile.writeBytes(extensionsJson.toString().toByteArray())
      }
    }
    

    You could then run - ./gradlew addNewExtension -PmyArg=libraryA, followed by a ./gradlew build. This will then update the json, and then generate a new build with the library included.