# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- # vim: set filetype=python: # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. # If --with-gradle is specified, build mobile/android with Gradle. If no # Gradle binary is specified use the in tree Gradle wrapper. The wrapper # downloads and installs Gradle, which is good for local developers but not # good in automation. option( "--without-gradle", nargs="?", help="Disable building mobile/android with Gradle " "(argument: location of binary or wrapper (gradle/gradlew))", ) @depends("--with-gradle") def with_gradle(value): if not value: die( "Building --without-gradle is no longer supported: " "see https://bugzilla.mozilla.org/show_bug.cgi?id=1414415." ) if value: return True @depends(host, "--with-gradle", build_environment) @imports(_from="os.path", _import="isfile") def gradle(host, value, build_env): if len(value): gradle = value[0] else: gradle = os.path.join(build_env.topsrcdir, "gradlew") if host.os == "WINNT": gradle = gradle + ".bat" # TODO: verify that $GRADLE is executable. if not isfile(gradle): die("GRADLE must be executable: %s", gradle) return gradle set_config("GRADLE", gradle) @dependable @imports(_from="itertools", _import="chain") def gradle_android_build_config(): def capitalize(s): # str.capitalize lower cases trailing letters. if s: return s[0].upper() + s[1:] else: return s def variant(productFlavors, buildType): return namespace( productFlavors=productFlavors, buildType=buildType, # Like 'WithoutGeckoBinariesDebug' name="".join(capitalize(t) for t in chain(productFlavors, (buildType,))), ) return namespace( geckoview=namespace( variant=variant(("withGeckoBinaries",), "debug"), ), geckoview_example=namespace( variant=variant(("withGeckoBinaries",), "debug"), ), ) @depends(gradle_android_build_config) def gradle_android_intermediates_folder(build_config): """Path to intermediates classes folder.""" def uncapitalize(s): if s: return s[0].lower() + s[1:] else: return s def capitalize(s): # str.capitalize lower cases trailing letters. if s: return s[0].upper() + s[1:] else: return s productFlavor = uncapitalize( "".join(capitalize(f) for f in build_config.geckoview.variant.productFlavors) ) buildType = uncapitalize(build_config.geckoview.variant.buildType) return ( "gradle/build/mobile/android/geckoview/intermediates/javac/{}{}/classes".format( productFlavor, capitalize(buildType), ) ) set_config( "GRADLE_ANDROID_GECKOVIEW_APILINT_FOLDER", gradle_android_intermediates_folder ) @depends(gradle_android_build_config) def gradle_android_geckoview_test_runner_bundle(build_config): """Path to intermediates classes folder.""" def uncapitalize(s): if s: return s[0].lower() + s[1:] else: return s def capitalize(s): # str.capitalize lower cases trailing letters. if s: return s[0].upper() + s[1:] else: return s productFlavor = uncapitalize( "".join(capitalize(f) for f in build_config.geckoview.variant.productFlavors) ) buildType = uncapitalize(build_config.geckoview.variant.buildType) variant = uncapitalize(build_config.geckoview.variant.name) return "gradle/build/mobile/android/test_runner/outputs/bundle/{}/test_runner-{}-{}.aab".format( variant, productFlavor, buildType, ) set_config( "GRADLE_ANDROID_GECKOVIEW_TEST_RUNNER_BUNDLE", gradle_android_geckoview_test_runner_bundle, ) @depends(gradle_android_build_config) def gradle_android_geckoview_example_bundle(build_config): """Path to intermediates classes folder.""" def uncapitalize(s): if s: return s[0].lower() + s[1:] else: return s def capitalize(s): # str.capitalize lower cases trailing letters. if s: return s[0].upper() + s[1:] else: return s productFlavor = uncapitalize( "".join(capitalize(f) for f in build_config.geckoview.variant.productFlavors) ) buildType = uncapitalize(build_config.geckoview.variant.buildType) variant = uncapitalize(build_config.geckoview.variant.name) return "gradle/build/mobile/android/geckoview_example/outputs/bundle/{}/geckoview_example-{}-{}.aab".format( variant, productFlavor, buildType, ) set_config( "GRADLE_ANDROID_GECKOVIEW_EXAMPLE_BUNDLE", gradle_android_geckoview_example_bundle ) @depends(gradle_android_build_config) def gradle_android_variant_name(build_config): """Like "withoutGeckoBinariesDebug".""" def uncapitalize(s): if s: return s[0].lower() + s[1:] else: return s return namespace( geckoview=uncapitalize(build_config.geckoview.variant.name), ) set_config( "GRADLE_ANDROID_GECKOVIEW_VARIANT_NAME", gradle_android_variant_name.geckoview ) @depends(gradle_android_build_config) def gradle_android_app_tasks(build_config): """Gradle tasks run by |mach android assemble-app|.""" return [ "geckoview:generateJNIWrappersForGenerated{geckoview.variant.name}".format( geckoview=build_config.geckoview ), ] set_config("GRADLE_ANDROID_APP_TASKS", gradle_android_app_tasks) @dependable def gradle_android_generate_sdk_bindings_tasks(): """Gradle tasks run by |mach android generate-sdk-bindings|.""" return [ "geckoview:generateSDKBindings", ] set_config( "GRADLE_ANDROID_GENERATE_SDK_BINDINGS_TASKS", gradle_android_generate_sdk_bindings_tasks, ) @depends(gradle_android_build_config) def gradle_android_generate_generated_jni_wrappers_tasks(build_config): """Gradle tasks run by |mach android generate-generated-jni-wrappers|.""" return [ "geckoview:generateJNIWrappersForGenerated{geckoview.variant.name}".format( geckoview=build_config.geckoview ), ] set_config( "GRADLE_ANDROID_GENERATE_GENERATED_JNI_WRAPPERS_TASKS", gradle_android_generate_generated_jni_wrappers_tasks, ) @depends(gradle_android_build_config) def gradle_android_test_tasks(build_config): """Gradle tasks run by |mach android test|.""" return [ "geckoview:test{geckoview.variant.name}UnitTest".format( geckoview=build_config.geckoview ), ] set_config("GRADLE_ANDROID_TEST_TASKS", gradle_android_test_tasks) @depends(gradle_android_build_config) def gradle_android_lint_tasks(build_config): """Gradle tasks run by |mach android lint|.""" return [ "geckoview:lint{geckoview.variant.name}".format( geckoview=build_config.geckoview ), ] set_config("GRADLE_ANDROID_LINT_TASKS", gradle_android_lint_tasks) @depends(gradle_android_build_config) def gradle_android_api_lint_tasks(build_config): """Gradle tasks run by |mach android api-lint|.""" return [ "geckoview:apiLint{geckoview.variant.name}".format( geckoview=build_config.geckoview ), ] set_config("GRADLE_ANDROID_API_LINT_TASKS", gradle_android_api_lint_tasks) set_config( "GRADLE_ANDROID_FORMAT_LINT_FIX_TASKS", ["spotlessJavaApply", "spotlessKotlinApply"] ) @dependable def gradle_android_format_lint_check_tasks(): return ["spotlessJavaCheck", "spotlessKotlinCheck"] set_config( "GRADLE_ANDROID_FORMAT_LINT_CHECK_TASKS", gradle_android_format_lint_check_tasks ) set_config( "GRADLE_ANDROID_FORMAT_LINT_FOLDERS", [ "mobile/android/annotations", "mobile/android/geckoview", "mobile/android/geckoview_example", "mobile/android/test_runner", "mobile/android/examples/messaging_example", "mobile/android/examples/port_messaging_example", ], ) @depends(gradle_android_build_config) def gradle_android_checkstyle_tasks(build_config): """Gradle tasks run by |mach android checkstyle|.""" return [ "geckoview:checkstyle{geckoview.variant.name}".format( geckoview=build_config.geckoview ), ] set_config("GRADLE_ANDROID_CHECKSTYLE_TASKS", gradle_android_checkstyle_tasks) @depends(gradle_android_build_config) def gradle_android_checkstyle_output_files(build_config): def uncapitalize(s): if s: return s[0].lower() + s[1:] else: return s variant = uncapitalize(build_config.geckoview.variant.name) """Output folder for checkstyle""" return [ "gradle/build/mobile/android/geckoview/reports/checkstyle/{}.xml".format( variant ), ] set_config( "GRADLE_ANDROID_CHECKSTYLE_OUTPUT_FILES", gradle_android_checkstyle_output_files ) option( "--disable-android-bundle", help="{Enable|Disable} AAB build.", ) imply_option("--disable-android-bundle", False, when="--enable-address-sanitizer") @depends(gradle_android_build_config, "--disable-android-bundle") def gradle_android_archive_geckoview_tasks(build_config, aab_enabled): """Gradle tasks run by |mach android archive-geckoview|.""" tasks = [ "geckoview:assemble{geckoview.variant.name}".format( geckoview=build_config.geckoview ), "geckoview:assemble{geckoview.variant.name}AndroidTest".format( geckoview=build_config.geckoview ), "test_runner:assemble{geckoview_example.variant.name}".format( geckoview_example=build_config.geckoview_example ), "geckoview_example:assemble{geckoview_example.variant.name}".format( geckoview_example=build_config.geckoview_example ), "messaging_example:assemble{geckoview_example.variant.name}".format( geckoview_example=build_config.geckoview_example ), "port_messaging_example:assemble{geckoview_example.variant.name}".format( geckoview_example=build_config.geckoview_example ), "geckoview:publish{geckoview.variant.name}PublicationToMavenRepository".format( geckoview=build_config.geckoview ), "exoplayer2:publishDebugPublicationToMavenRepository", ] if aab_enabled: tasks += [ "test_runner:bundle{geckoview_example.variant.name}".format( geckoview_example=build_config.geckoview_example ), "geckoview_example:bundle{geckoview_example.variant.name}".format( geckoview_example=build_config.geckoview_example ), ] return tasks set_config( "GRADLE_ANDROID_ARCHIVE_GECKOVIEW_TASKS", gradle_android_archive_geckoview_tasks ) @depends(gradle_android_build_config) def gradle_android_geckoview_docs_tasks(build_config): """Gradle tasks run by |mach android geckoview-docs|.""" return [ "geckoview:javadoc{geckoview.variant.name}".format( geckoview=build_config.geckoview ), ] set_config("GRADLE_ANDROID_GECKOVIEW_DOCS_TASKS", gradle_android_geckoview_docs_tasks) @depends(gradle_android_build_config) def gradle_android_geckoview_docs_archive_tasks(build_config): """Gradle tasks run by |mach android geckoview-docs --archive| or |... --upload.""" return [ "geckoview:javadocCopyJar{geckoview.variant.name}".format( geckoview=build_config.geckoview ), ] set_config( "GRADLE_ANDROID_GECKOVIEW_DOCS_ARCHIVE_TASKS", gradle_android_geckoview_docs_archive_tasks, ) @depends(gradle_android_build_config) def gradle_android_geckoview_docs_output_files(build_config): """Output files for GeckoView javadoc.""" def uncapitalize(s): if s: return s[0].lower() + s[1:] else: return s variant = uncapitalize(build_config.geckoview.variant.name) return [ "gradle/build/mobile/android/geckoview/reports/javadoc-results-{}.json".format( variant ), ] set_config( "GRADLE_ANDROID_GECKOVIEW_DOCS_OUTPUT_FILES", gradle_android_geckoview_docs_output_files, ) @depends(gradle_android_build_config) def gradle_android_archive_coverage_artifacts_tasks(build_config): """Gradle tasks run by |mach android archive-coverage-artifacts|.""" return [ "geckoview:archiveClassfiles{geckoview.variant.name}".format( geckoview=build_config.geckoview ), "geckoview:copyCoverageDependencies", ] set_config( "GRADLE_ANDROID_ARCHIVE_COVERAGE_ARTIFACTS_TASKS", gradle_android_archive_coverage_artifacts_tasks, ) @depends(gradle_android_build_config) def gradle_android_build_geckoview_example_tasks(build_config): """Gradle tasks run by |mach android build-geckoview_example|.""" return [ "geckoview_example:assemble{geckoview_example.variant.name}".format( geckoview_example=build_config.geckoview_example ), "geckoview_example:bundle{geckoview_example.variant.name}".format( geckoview_example=build_config.geckoview_example ), "geckoview:assemble{geckoview.variant.name}AndroidTest".format( geckoview=build_config.geckoview ), "test_runner:assemble{geckoview.variant.name}".format( geckoview=build_config.geckoview ), "test_runner:bundle{geckoview.variant.name}".format( geckoview=build_config.geckoview ), ] set_config( "GRADLE_ANDROID_BUILD_GECKOVIEW_EXAMPLE_TASKS", gradle_android_build_geckoview_example_tasks, ) @depends(gradle_android_build_config) def gradle_android_install_geckoview_test_runner_tasks(build_config): """Gradle tasks run by |mach android install-geckoview-test_runner|.""" return [ "test_runner:install{geckoview.variant.name}".format( geckoview=build_config.geckoview ), ] set_config( "GRADLE_ANDROID_INSTALL_GECKOVIEW_TEST_RUNNER_TASKS", gradle_android_install_geckoview_test_runner_tasks, ) @depends(gradle_android_build_config) def gradle_android_install_geckoview_test_tasks(build_config): """Gradle tasks run by |mach android install-geckoview-test|.""" return [ "geckoview:install{geckoview.variant.name}AndroidTest".format( geckoview=build_config.geckoview ), ] set_config( "GRADLE_ANDROID_INSTALL_GECKOVIEW_TEST_TASKS", gradle_android_install_geckoview_test_tasks, ) @depends(gradle_android_build_config) def gradle_android_install_geckoview_example_tasks(build_config): """Gradle tasks run by |mach android install-geckoview_example|.""" return [ "geckoview_example:install{geckoview_example.variant.name}".format( geckoview_example=build_config.geckoview_example ), ] set_config( "GRADLE_ANDROID_INSTALL_GECKOVIEW_EXAMPLE_TASKS", gradle_android_install_geckoview_example_tasks, ) @depends( gradle_android_api_lint_tasks, gradle_android_format_lint_check_tasks, gradle_android_checkstyle_tasks, ) @imports(_from="itertools", _import="chain") def gradle_android_dependencies_tasks(*tasks): """Gradle tasks run by |mach android dependencies|.""" # The union, plus a bit more, of all of the Gradle tasks # invoked by the android-* automation jobs. def withoutGeckoBinaries(task): return task.replace("withGeckoBinaries", "withoutGeckoBinaries") return list(withoutGeckoBinaries(t) for t in chain(*tasks)) set_config("GRADLE_ANDROID_DEPENDENCIES_TASKS", gradle_android_dependencies_tasks) # Automation uses this to change log levels, not use the daemon, and use # offline mode. option(env="GRADLE_FLAGS", default="", help="Flags to pass to Gradle.") @depends("GRADLE_FLAGS") def gradle_flags(value): return value[0] if value else "" set_config("GRADLE_FLAGS", gradle_flags) # Automation will set this to (file:///path/to/local, ...) via the mozconfig. # Local developer default is (maven.google.com). option( env="GRADLE_MAVEN_REPOSITORIES", nargs="+", default=( "https://maven.mozilla.org/maven2/", "https://maven.google.com/", "https://repo.maven.apache.org/maven2/", "https://plugins.gradle.org/m2/", ), help="Comma-separated URLs of Maven repositories containing Gradle dependencies.", ) option( "--allow-insecure-gradle-repositories", help="Gradle is allowed to connect to insecure Maven repositories.", ) set_config( "ALLOW_INSECURE_GRADLE_REPOSITORIES", True, when="--allow-insecure-gradle-repositories", ) @depends("GRADLE_MAVEN_REPOSITORIES") @imports(_from="os.path", _import="isdir") def gradle_maven_repositories(values): if not values: die("GRADLE_MAVEN_REPOSITORIES must not be empty") if not all(values): die("GRADLE_MAVEN_REPOSITORIES entries must not be empty") return values set_config("GRADLE_MAVEN_REPOSITORIES", gradle_maven_repositories)