3

my situation is this: i have a project, which uses some generated code. In the generated code, a certain URI is hardcoded in pretty much all files.

So, at some point i got to two generated codebases: one targeting DEVELOPMENT, another targeting STAGING.

I want to solve that via Gradle just as I would via Makefiles.

So, at first i made a new Module in the root of my project named SOAP-DEV and SOAP-STAGE, defined two new variables in gradle.properties called

# Make sure only one of these is set to 'true' and *do* set the other one to 'false'. Please.
USE_STAGING_SOAP=
USE_DEVELOPMENT_SOAP=1

I ended up using 1 and blank, because true and false didn't seem to do the trick and both counted as true.

So, once the building issue was done with and only one of them seemed to be building (both actually, but I was getting only one using <dev/stage> soap pack line, i think that was actually ok and that the proper one was being built.

then I hit another roadblock - it seems my project's main module - app can't see files outside it's folder, which makes sense.

Now I'm trying with a different approach - to have SOAP-STAGE and SOAP-DEV be subfolders of ProjectName/app instead of ProjectName so the #import line can actually see the sources it needs to see to build properly.

Yet, that's not happening :/

What is the proper way of setting a configurable choice of which folder to build for only a part of the project's codebase, but so that they are seen by the project's #import in the classes?

I would gladly revert to the old idea of having both SOAP folders as modules in my project, if I could get the classes inside the project to see the related-module's classes, namely the soap classes.

To better illustrate what I'm trying to do, here's my main module's gradle.build file. I hope you'll understand what I'm trying to do with this.

import java.text.SimpleDateFormat

apply plugin: 'com.android.application'

android {
    signingConfigs {
        hockeyApp {
            keyAlias 'myKeyAlias'
            keyPassword 'myKeyAliasHA'
            storeFile file('../myKeystore.jks')
            storePassword 'myKeyAliasHA'
        }
    }
    compileSdkVersion 21
    buildToolsVersion "21.1.2"
    defaultConfig {
        applicationId "com.my.application"
        minSdkVersion 15
        targetSdkVersion 21
        versionCode 1
        versionName getVersion()
    }
    buildTypes {
        hockeyApp {
            signingConfig signingConfigs.hockeyApp
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    android.enforceUniquePackageName=false
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:22.0.0'
    compile 'com.facebook.android:facebook-android-sdk:4.0.0'
    compile 'com.google.android.gms:play-services:7.0.0'
    compile 'com.android.support:recyclerview-v7:21.0.0'
    compile 'com.daimajia.swipelayout:library:1.1.9@aar'
    compile files('libs/dpdsoaplib.jar')
    compile project (':androidNeoReaderSDK')

    if(USE_STAGING_SOAP == 1)
    {
        compile {
            source = 'SOAP-STAGE/src/main/java'
        }
    }
    if(USE_DEVELOPMENT_SOAP == 1)
    {
        compile {
            source = 'SOAP-DEV/src/main/java'
        }
    }
    compile "com.android.support:support-v4:21.0.+"
    compile 'com.google.maps.android:android-maps-utils:0.3+'
}

def getVersion() {
    def Calendar cal = Calendar.getInstance();
    def SimpleDateFormat dateFormat = new SimpleDateFormat("MMddHHmmss");

    def StringBuilder sb = new StringBuilder("v");
    sb.append("0.2");
    sb.append(".");
    sb.append(dateFormat.format(cal.getTimeInMillis()));
    sb.append("a");

    return sb.toString();

}

However, if I can actually get both of these folders (SOAP-DEV and SOAP-STAGE) to be added as source folders to the project/module, I think that would resolve the problem of not being able to import proper classes, which was only caused by the classes being moved from the ProjectName/app/src/java folder, to the ProjectName/SOAP-DEV/src/java and ProjectName/SOAP-STAGE/src/java folders.

The two folders do have identical packagenames and contain classes of the same name which only point to a different soap server.

So, people, any ideas? I thought this Gradle was supposed to introduce some build system / makefile functionalities to android studio, but so far it does not seem to be the case.

Right now i'm trying something in between - to have both folders as Modules in the root of the project but also have one or the other's code visible in my app module's import statements.

Shark
  • 6,513
  • 3
  • 28
  • 50

2 Answers2

1

Your initial idea of having 2 modules in your root directory with both version of your generated code is a good start. Now, using a variable to dynamically changing the content of your app is not supported by gradle.

The philosophy behind this limitation is that it MUST be possible to evaluate the need of executing a gradle task simply by looking at it's inputs and outputs. (i.e. input didn't change ==> no need to re-execute the task).

Fortunately, gradle offers alternative ways of writing flexible builds:

You can use flavors (i.e. variations of the same product).

Define 2 flavors :

android {
    ...
    productFlavors {
       dev{}
       staging{}
    }
}


dependencies {
    devCompile project(':SOAP-DEV')
    stagingCompile project(':SOAP-STAGE')
    ...
}

Note that the prefix in dependencies is exactly the name of the flavors.

This will produce 2 apks : MyApp-dev.apk and MyApp-staging.apk

ben75
  • 29,217
  • 10
  • 88
  • 134
  • I've been toying with the idea of having it done by different flavors. However - I am not quite sure how will the different flavors really solve my "cannot resolve symbol XXX" in the `import ...` statements. But I do appreciate the answer - so the product flavors go in the root build.gradle file, or the `app` module's build.gradle file? – Shark May 18 '15 at 15:35
  • The flavors must be defined in app module – ben75 May 18 '15 at 15:47
  • What I'm really trying to accomplish is this: - 1) move the generated code from the main app/module into separate folders - 2) have them share a same packagename, so the imports/includes don't get confused - 3) be able to somehow tell it to use STAGE or DEV folder as a source folder, so that there are no "same package name already defined" errors but also no "invalid symbol / i cannot see that package name" errors. – Shark May 18 '15 at 16:03
  • I realize that somehow i need to have two different source folders. I just don't know how to add another source folder to-be-used yet. I have no problem defining two different build flavors that are each hardcoded to use different source folders for the generated code part. But I also need the main module to see at least one version of that code to avoid the "cannot resolve symbol ..." error which I currently have with my two module approach. Even tho I got to the point where only the module i want actually builds. – Shark May 18 '15 at 16:07
  • So you have kind of circular dependency between your hand-written code and your generated-code ? – ben75 May 18 '15 at 16:09
  • Worst case scenario is - fork two versions of the project, put only DEV generated files in first, and use STAGING in second, build&deploy the one i need, when i need it. However i would like to have some better and more casual control of which one gets built than this. – Shark May 18 '15 at 16:09
  • nope, no circular dependancy. one set (soap-dev) of generated code is hardcoded to URI1, the other set (soap-stage) is hardcoded to URI2 (different servers). my hand-written code depends on the generated classes (which are the same between two sets, apart from the hardcoded uri difference) and i would like it to work with any one of those two sets provided, preferably having an option to pick which one I would want to use at build-time aka compile-time. but i'm slowly realizing i won't be able to just switch this set as easily as i imagined it with a normal (makefile) build system... – Shark May 18 '15 at 16:11
  • Maybe something like this http://stackoverflow.com/questions/16737006/using-build-flavors-structuring-source-folders-and-build-gradle-correctly – Shark May 18 '15 at 16:14
0

So, the answer (in the end) came down to this:

The structure was altered a bit, and two new subfolders were added at the "main module" (main module = app) level.

ProjectRoot/app/src/main/my/package/name/...
ProjectRoot/app/src/soap_dev/my/package/name/...
ProjectRoot/app/src/soap_stage/my/package/name/...

Then, the generated code got moved from

ProjectRoot/app/src/main/my/package/name/backend/soap/generated

to

ProjectRoot/app/src/SOAP_DEV_/my/package/name/backend/soap/generated
ProjectRoot/app/src/SOAP_STAGE_/my/package/name/backend/soap/generated

Two new build flavors were made in the module "app"

   productFlavors {
       SOAP_DEV_ {
       }
       SOAP_STAGE_ {
       }
    }

And that resulted in the flavors applying to all current build configurations available to the project.

Shark
  • 6,513
  • 3
  • 28
  • 50