简体   繁体   中英

Buck - building android app with multiple Gradle flavors/build types and manifests

I'm working on a project where we have several modules, each one of these with a number of dependencies to other modules, and so on and so forth. We migrated our project to Gradle (and also changed the structure to accommodate it to Gradle's defaults) some time ago because we needed versatility when building different versions of the app: free vs paid, debug (no proguard) vs release (proguard), etc. We were really thrilled when we completed the migration, but this happiness quickly became diluted in a puddle of mud when the build time started to become a pain. Making a rather simple change in the code and deploy the app on the phone takes anywhere 90-120 seconds, and that is simply unacceptable.

So we decided to give Buck a try, as we have heard nothing but good words from other developers. After very little time (way less than with Gradle) we managed to build our app successfully (which, unfortunately, doesn't mean "generating an APK correctly", but at least it built). The thing is that, as a general rule, we have one manifest file per flavor, and unless I have overlooked something fundamental, Buck only lets you specify manifest files when using the rule android_binary . The result of this is that the generated APK's manifest file contains only the boilerplate code of the first level module from which Gradle's build chain starts:

main -> debug -> free/paid -> common

That is, the manifest file only contains main's manifest. This rule is located in the top level BUCK file, which is the file that contains the alias specified in .buckconfig.

I'm pretty sure I'm doing something wrong. It wouldn't make sense Buck doesn't allow you to not have multiple manifests.

Any ideas?

Francis Toth ( https://stackoverflow.com/users/1873643/francis-toth ) came up with a very comprehensive explanation in the Buck discussion group:

Just to give you the context, I have an application composed of different library modules, each having an Android-Manifest, some resources, assets etc... and a BUCK file which pretty much look like this :

android_library(
  name = 'src',
  srcs = glob(['src/main/java/**/*.java']),
  deps = DEPENDENCIES + [':res'],
  visibility = [ 'PUBLIC' ],
  exported_deps = DEPENDENCIES
)

android_resource(
  name = 'manifest',
  manifest = 'src/main/AndroidManifest.xml',
  deps = ['//othermodule:manifest'],
  visibility = [ 'PUBLIC' ],
)

android_resource(
  name = 'assets',
  assets = 'src/main/assets',
  visibility = [ 'PUBLIC' ],
)

android_resource(
  name = 'res',
  res = 'src/main/res',
  package = 'com.sherpa.android',
  visibility = [ 'PUBLIC' ],
)

project_config(
  src_target = ':src',
  src_roots = ['src/main/java']
)

My app BUCK file looks like this :

android_binary (
  name = 'bin',
  manifest = ':manifest', # Here I make a reference on the android_manifest build rule described below
  target = 'Google Inc.:Google APIs:19',
  keystore = ':debug_keystore',
  deps = BINARY_DEPENDENCIES
)

MANIFEST_DEPENDENCIES = ['//module1:manifest', '//module2:manifest']
android_manifest (
  name = 'manifest',
  skeleton = 'AndroidManifest.xml', # The app's base manifest
  deps = MANIFEST_DEPENDENCIES # App's manifest will be merged with all its dependencies (module1 and module2's manifest)
)

Your problem, as far as I understand seems to be pretty similar. In order to merge the manifest, you have to create a android_resource build rule which tells where is your module library along with the manifests it has to be merged with.

And that solved my problem :-)

In buck the manifest merger will never override anything in the manifest tag. That includes the package attribute, the debuggable attribute, etc.

There is a feature of manifest_entries in android_binary that isn't documented but it's how OKBuck does build flavors. Before packaging the manifest, any placeholders in manifest_entries get substituted for any expressions in AndroidManifest.xml

So if your manifest tag looks something like this:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package = "com.whatever${flavor}"
   android:debuggable = "${debuggable}"
>

And your manifest_entries in your android_binary looks like this:

  manifest_entries = {
    'placeholders': {
      'flavor': '.test',
      'debuggable': 'true',
    },
  },

The resulting manifest will look like this:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package = "com.whatever.test"
   android:debuggable = "true"
>

Then you can build up your manifest_entries placeholders with dictionary manipulation and flatten_dicts()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM