diff --git a/tools/crashlytics/defs.bzl b/tools/crashlytics/defs.bzl index 482acb6..1ac1e1b 100644 --- a/tools/crashlytics/defs.bzl +++ b/tools/crashlytics/defs.bzl @@ -1,73 +1,154 @@ +load("//tools/googleservices:defs.bzl", "google_services_xml") load("@rules_android//android:rules.bzl", "android_library") -def crashlytics_android_library(name, package_name, build_id, resource_files): - _CRASHLYTICS_PROP_TEMPLATE = \ - """build_id={build_id} +_CRASHLYTICS_PROP_TEMPLATE = """build_id={build_id} package_name={package_name}""" - crashlytics_properties_file = "_%s_crashlytics/assets/crashlytics-build.properties" % name - crashlytics_properties_file_content = _CRASHLYTICS_PROP_TEMPLATE.format( + +_CRASHLYTICS_RES_TEMPLATE = """ + + {build_id} +""" + +_CRASHLYTICS_KEEP_CONTENT = """ +""" + +_CRASHLYTICS_MANIFEST_TEMPLATE = """ + + +""" + +def _crashlytics_android_impl(ctx): + package_name = ctx.attr.package_name + # Expand make variables in build_id + build_id = ctx.expand_make_variables("build_id", ctx.attr.build_id, {}) + + properties_file = ctx.outputs.properties + res_values_file = ctx.outputs.res_values + res_keep_file = ctx.outputs.res_keep + manifest_file = ctx.outputs.manifest + + # Generate crashlytics-build.properties + properties_content = _CRASHLYTICS_PROP_TEMPLATE.format( build_id = build_id, package_name = package_name, ) - - native.genrule( - name = "%s_crashlytics_setup_properties" % name, - outs = [crashlytics_properties_file], - tools = ["@tools_android//tools/crashlytics"], - cmd = "$(location @tools_android//tools/crashlytics) \"%s\" $@" % crashlytics_properties_file_content, + ctx.actions.write( + output = properties_file, + content = properties_content, + is_executable = False, ) - # Generate the unique identifier for Fabric backend to identify builds. - # https://docs.fabric.io/android/crashlytics/build-tools.html#optimize-builds-when-you-re-not-proguarding-or-using-beta-by-crashlytics - _CRASHLYTICS_RES_TEMPLATE = \ - """ - - {build_id} -""" - crashlytics_res_values_file = "_%s_crashlytics/res/values/com_crashlytics_build_id.xml" % name - crashlytics_res_values_file_content = _CRASHLYTICS_RES_TEMPLATE.format(build_id = build_id) - - native.genrule( - name = "%s_crashlytics_setup_res" % name, - outs = [crashlytics_res_values_file], - tools = ["@tools_android//tools/crashlytics"], - cmd = "$(location @tools_android//tools/crashlytics) \"%s\" $@" % crashlytics_res_values_file_content, + # Generate build ID resource XML + res_values_content = _CRASHLYTICS_RES_TEMPLATE.format(build_id = build_id) + ctx.actions.write( + output = res_values_file, + content = res_values_content, + is_executable = False, ) - _CRASHLYTICS_KEEP_CONTENT = \ - """ -""" - crashlytics_res_keep_file = "_%s_crashlytics/res/raw/%s_crashlytics.keep.xml" % (name, package_name) - - native.genrule( - name = "%s_crashlytics_setup_res_keep" % name, - outs = [crashlytics_res_keep_file], - tools = ["@tools_android//tools/crashlytics"], - cmd = "$(location @tools_android//tools/crashlytics) \"%s\" $@" % _CRASHLYTICS_KEEP_CONTENT, + # Generate keep XML + ctx.actions.write( + output = res_keep_file, + content = _CRASHLYTICS_KEEP_CONTENT, + is_executable = False, ) - _CRASHLYTICS_MANIFEST_TEMPLATE = \ -""" - - -""" - crashlytics_manifest_file = "_%s_crashlytics/CrashlyticsManifest.xml" % name - crashlytics_manifest_file_content = _CRASHLYTICS_MANIFEST_TEMPLATE.format(package_name = package_name) - - native.genrule( - name = "%s_crashlytics_setup_manifest" % name, - outs = [crashlytics_manifest_file], - tools = ["@tools_android//tools/crashlytics"], - cmd = "$(location @tools_android//tools/crashlytics) \"%s\" $@" % crashlytics_manifest_file_content, + # Generate manifest + manifest_content = _CRASHLYTICS_MANIFEST_TEMPLATE.format(package_name = package_name) + ctx.actions.write( + output = manifest_file, + content = manifest_content, + is_executable = False, ) - android_library( - name = name, - assets = [crashlytics_properties_file], - assets_dir = "_%s_crashlytics/assets" % name, - custom_package = package_name, - manifest = crashlytics_manifest_file, - resource_files = [crashlytics_res_values_file, crashlytics_res_keep_file] + resource_files, - ) + # Collect all resource files + all_resource_files = [res_values_file, res_keep_file] + ctx.files.resource_files + + return [ + DefaultInfo(files = depset([ + properties_file, + manifest_file, + ] + all_resource_files)), + OutputGroupInfo( + assets = depset([properties_file]), + manifest = depset([manifest_file]), + resources = depset(all_resource_files), + ), + ] + +crashlytics_android = rule( + implementation = _crashlytics_android_impl, + attrs = { + "package_name": attr.string( + mandatory = True, + doc = "The package name (or application ID) of the Android app.", + ), + "build_id": attr.string( + mandatory = True, + doc = "The build ID for Crashlytics. Supports make variable expansion like $(crashlyticsBuildID).", + ), + "resource_files": attr.label_list( + allow_files = True, + doc = "Additional resource files to include.", + ), + "_generator": attr.label( + default = "@tools_android//tools/crashlytics", + executable = True, + cfg = "exec", + ), + }, + outputs = { + "properties": "%{name}_crashlytics/assets/crashlytics-build.properties", + "res_values": "%{name}_crashlytics/res/values/com_crashlytics_build_id.xml", + "res_keep": "%{name}_crashlytics/res/raw/%{name}_crashlytics_keep.xml", + "manifest": "%{name}_crashlytics/CrashlyticsManifest.xml", + }, + doc = """Generates Crashlytics configuration files. + + Generates the unique identifier for Fabric backend to identify builds. + See: https://docs.fabric.io/android/crashlytics/build-tools.html + """, +) + +def crashlytics_android_library(name, package_name, build_id, google_services_json, **kwargs): + """Creates an Android library with Crashlytics and Google Services configuration. + + Args: + name: Name of the android_library target. + package_name: The package name (or application ID) of the Android app. + Supports select() statements. + build_id: The build ID for Crashlytics. + google_services_json: The google-services.json file. + **kwargs: Additional arguments to pass to android_library. + """ + gsx_name = name + "_google_services_xml" + gen_name = name + "_gen" + + google_services_xml( + name = gsx_name, + package_name = package_name, + google_services_json = google_services_json, + ) + + crashlytics_android( + name = gen_name, + package_name = package_name, + build_id = build_id, + resource_files = [":%s" % gsx_name], + ) + + android_library( + name = name, + assets = [":%s_crashlytics/assets/crashlytics-build.properties" % gen_name], + assets_dir = "%s_crashlytics/assets" % gen_name, + custom_package = package_name, + manifest = ":%s_crashlytics/CrashlyticsManifest.xml" % gen_name, + resource_files = [ + ":%s_crashlytics/res/values/com_crashlytics_build_id.xml" % gen_name, + ":%s_crashlytics/res/raw/%s_crashlytics_keep.xml" % (gen_name, gen_name), + ":%s" % gsx_name, + ], + **kwargs + ) diff --git a/tools/googleservices/defs.bzl b/tools/googleservices/defs.bzl index df96456..c5d071a 100644 --- a/tools/googleservices/defs.bzl +++ b/tools/googleservices/defs.bzl @@ -1,39 +1,50 @@ -"""Macros to support Google services, e.g. Firebase Cloud Messaging.""" +"""Rules to support Google services, e.g. Firebase Cloud Messaging.""" load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") load("@bazel_tools//tools/build_defs/repo:jvm.bzl", "jvm_maven_import_external") +def _google_services_xml_impl(ctx): + package_name = ctx.attr.package_name + google_services_json = ctx.file.google_services_json -def google_services_xml(package_name, google_services_json): - """Creates Android resource XML for Google services. - - The XML is based on a google-services.json file. - - This macro assumes that the Android tools repository is named "tools_android" - in the top-level project's WORKSPACE file. - - Args: - package_name: The package name (or application ID) of the Android app. - google_services_json: The google-services.json file. - - Returns: - A list of the generated resource files which can be used with - android_binary.resource_files or android_library.resource_files. - """ - # Adding the package name and google-services.json file to the outs and name - # of the rule is necessary in case there are multiple calls to - # google_services_xml() with different package names or different json files. - outs = ["google_services_xml/%s/%s/res/values/values.xml" % - (package_name, google_services_json.replace("/", "_"))] - name = "gen_google_services_xml_%s_%s" % ( - package_name.replace(".", "_"), - google_services_json.replace(".", "_").replace("/", "_")) - if not native.existing_rule(name): - native.genrule( - name = name, - srcs = [google_services_json], - outs = outs, - tools = ["@tools_android//third_party/googleservices:GenerateGoogleServicesXml"], - cmd = "$(location @tools_android//third_party/googleservices:GenerateGoogleServicesXml) %s $< $@" % package_name, + output = ctx.actions.declare_file( + "google_services_xml/%s/%s/res/values/values.xml" % ( + package_name, + google_services_json.path.replace("/", "_"))) + + ctx.actions.run( + outputs = [output], + inputs = [google_services_json], + executable = ctx.executable._generator, + arguments = [package_name, google_services_json.path, output.path], + mnemonic = "GenerateGoogleServicesXml", ) - return outs + + return [DefaultInfo(files = depset([output]))] + +google_services_xml = rule( + implementation = _google_services_xml_impl, + attrs = { + "package_name": attr.string( + mandatory = True, + doc = "The package name (or application ID) of the Android app.", + ), + "google_services_json": attr.label( + mandatory = True, + allow_single_file = [".json"], + doc = "The google-services.json file.", + ), + "_generator": attr.label( + default = "@tools_android//third_party/googleservices:GenerateGoogleServicesXml", + executable = True, + cfg = "exec", + ), + }, + doc = """Creates Android resource XML for Google services. + + The XML is based on a google-services.json file. + + This rule assumes that the Android tools repository is named "tools_android" + in the top-level project's WORKSPACE file. + """, +)