Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Begin declarative-izing NiA #5

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
e822817
Begin declarative-izing NiA - adapt the core/common subproject
tresat Mar 6, 2024
95a5d1b
Migrate hilt plugin from external project to internal build-logic
tresat Mar 7, 2024
70c51a6
Migrate android-hilt plugin to the NiA's internal build logic
tresat Mar 7, 2024
ffedca1
Building convention plugin for Android + Hilt + Jacoco
tresat Mar 8, 2024
4437cb7
Begin declarative-izing NiA - adapt the core/domain subproject
tresat Mar 14, 2024
ee630f0
Update gradle version to nightly so it can build without custom local…
tresat Mar 27, 2024
3c8fbfb
Update path to new repo name
tresat Apr 4, 2024
cb50f5b
Give Gradle a full 2 minutes to download the wrapper
tresat Apr 4, 2024
07d554b
Beginning to remove benchmarking to fix CI
tresat Apr 4, 2024
ea02dcc
Adjust for AndroidApplication changes
tresat Apr 4, 2024
54107d9
Adjust GH actions to avoid failures after partial declaritivization
tresat Apr 4, 2024
6b46340
Avoid running checks that won't run
tresat Apr 4, 2024
edcc7de
Create conventions "ecosystem plugin" and apply to Settings
tresat Apr 11, 2024
948b1d3
Use SoftwareType registration instead of plugin application in build …
tresat Apr 11, 2024
47bc4f1
Undo some unnecessary changes
tresat Apr 12, 2024
c56277e
Declarativize core/data
tresat Apr 12, 2024
b5ef5d2
Update to latest gradle nightly, project builds without local Gradle …
tresat Apr 15, 2024
980308b
Update actions script to fix issues
tresat Apr 15, 2024
19bf3db
Update plugin to register software types declaratively using the
tresat Apr 15, 2024
ab4ef8b
Update to latest wrapper
tresat Apr 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
69 changes: 38 additions & 31 deletions .github/workflows/Build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,14 @@ jobs:
run: |
./now-in-android/gradlew dependencyGuardBaseline -p now-in-android

- name: Push new Dependency Guard baselines if available
uses: stefanzweifel/git-auto-commit-action@v5
if: steps.dependencyguard_baseline.outcome == 'success'
with:
file_pattern: '**/dependencies/*.txt'
disable_globbing: true
commit_message: "🤖 Updates baselines for Dependency Guard"
# TODO: Skip this, the new repo checkout isn't working - probably fixable but unimportant
# - name: Push new Dependency Guard baselines if available
# uses: stefanzweifel/git-auto-commit-action@v5
# if: steps.dependencyguard_baseline.outcome == 'success'
# with:
# file_pattern: '**/dependencies/*.txt'
# disable_globbing: true
# commit_message: "🤖 Updates baselines for Dependency Guard"

- name: Run all local screenshot tests (Roborazzi)
id: screenshotsverify
Expand All @@ -98,30 +99,33 @@ jobs:
id: screenshotsrecord
if: steps.screenshotsverify.outcome == 'failure' && github.event_name == 'pull_request'
run: |
./gradlew recordRoborazziDemoDebug
./now-in-android/gradlew recordRoborazziDemoDebug -p now-in-android -x :app:hiltAggregateDepsDemoDebug -x :app:hiltAggregateDepsDemoDebugUnitTest -x :app:testDemoDebugUnitTest

- name: Push new screenshots if available
uses: stefanzweifel/git-auto-commit-action@v5
if: steps.screenshotsrecord.outcome == 'success'
with:
file_pattern: '*/*.png'
disable_globbing: true
commit_message: "🤖 Updates screenshots"
# TODO: Skip this, the new repo checkout isn't working - probably fixable but unimportant
# - name: Push new screenshots if available
# uses: stefanzweifel/git-auto-commit-action@v5
# if: steps.screenshotsrecord.outcome == 'success'
# with:
# file_pattern: '*/*.png'
# disable_globbing: true
# commit_message: "🤖 Updates screenshots"

# Run local tests after screenshot tests to avoid wrong UP-TO-DATE. TODO: Ignore screenshots.
- name: Run local tests
if: always()
run: ./now-in-android/gradlew testDemoDebug :lint:test -p now-in-android
# Replace task exclusions with `-Pandroidx.baselineprofile.skipgeneration` when
# https://android-review.googlesource.com/c/platform/frameworks/support/+/2602790 landed in a
# release build
- name: Build all build type and flavor permutations
run: ./now-in-android/gradlew :app:assemble :benchmarks:assemble
-x pixel6Api33ProdNonMinifiedReleaseAndroidTest
-x pixel6Api33DemoNonMinifiedReleaseAndroidTest
-x collectDemoNonMinifiedReleaseBaselineProfile
-x collectProdNonMinifiedReleaseBaselineProfile
-p now-in-android
run: ./now-in-android/gradlew testDemoDebug :lint:test -p now-in-android -x :app:hiltAggregateDepsDemoDebug -x :app:hiltAggregateDepsDemoDebugUnitTest -x :app:testDemoDebugUnitTest

# TODO: This won't work until we have flavors working in the declarative app and actually create the variants that app is expecting
# # Replace task exclusions with `-Pandroidx.baselineprofile.skipgeneration` when
# # https://android-review.googlesource.com/c/platform/frameworks/support/+/2602790 landed in a
# # release build
# - name: Build all build type and flavor permutations
# run: ./now-in-android/gradlew :app:assemble :benchmarks:assemble
# -x pixel6Api33ProdNonMinifiedReleaseAndroidTest
# -x pixel6Api33DemoNonMinifiedReleaseAndroidTest
# -x collectDemoNonMinifiedReleaseBaselineProfile
# -x collectProdNonMinifiedReleaseBaselineProfile
# -p now-in-android

- name: Upload build outputs (APKs)
uses: actions/upload-artifact@v4
Expand All @@ -136,8 +140,10 @@ jobs:
name: test-results
path: '**/build/test-results/test*UnitTest/**.xml'

# TODO: less linting required on this branch, full linting requires additional flavors
# run: ./now-in-android/gradlew :app:lintProdRelease :app-nia-catalog:lintRelease :lint:lint -p now-in-android
- name: Check lint
run: ./now-in-android/gradlew :app:lintProdRelease :app-nia-catalog:lintRelease :lint:lint -p now-in-android
run: ./now-in-android/gradlew :lint:lint -p now-in-android

- name: Upload lint reports (HTML)
if: always()
Expand All @@ -146,8 +152,9 @@ jobs:
name: lint-reports
path: '**/build/reports/lint-results-*.html'

- name: Check badging
run: ./now-in-android/gradlew :app:checkProdReleaseBadging -p now-in-android
# TODO: Skip badging until we have flavors working in the declarative app
# - name: Check badging
# run: ./now-in-android/gradlew :app:checkProdReleaseBadging -p now-in-android

androidTest:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -200,7 +207,7 @@ jobs:
uses: gradle/gradle-build-action@v2

- name: Build projects before running emulator
run: ./now-in-android/gradlew packageDemoDebug packageDemoDebugAndroidTest -p now-in-android
run: ./now-in-android/gradlew packageDemoDebug packageDemoDebugAndroidTest -p now-in-android -x :app:hiltAggregateDepsDemoDebug -x :app:hiltAggregateDepsDemoDebugUnitTest -x :app:hiltAggregateDepsDemoDebugAndroidTest -x :sync:work:hiltAggregateDepsDemoDebugAndroidTest

- name: Run instrumentation tests
uses: reactivecircus/android-emulator-runner@v2
Expand All @@ -210,7 +217,7 @@ jobs:
disable-animations: true
disk-size: 6000M
heap-size: 600M
script: ./now-in-android/gradlew connectedDemoDebugAndroidTest --daemon -p now-in-android
script: ./now-in-android/gradlew connectedDemoDebugAndroidTest --daemon -p now-in-android -x :app:hiltAggregateDepsDemoDebug -x :app:hiltAggregateDepsDemoDebugUnitTest -x :app:testDemoDebugUnitTest -x :app:hiltAggregateDepsDemoDebugAndroidTest -x :core:ui:connectedDemoDebugAndroidTest -x :core:designsystem:connectedDemoDebugAndroidTest -x :core:database:connectedDemoDebugAndroidTest -x :feature:bookmarks:connectedDemoDebugAndroidTest -x :feature:foryou:connectedDemoDebugAndroidTest -x :feature:interests:connectedDemoDebugAndroidTest -x :sync:work:hiltAggregateDepsDemoDebugAndroidTest -x :feature:settings:connectedDemoDebugAndroidTest -x :feature:topic:connectedDemoDebugAndroidTest -x :sync:work:connectedDemoDebugAndroidTest -x :feature:search:connectedDemoDebugAndroidTest -x :app:connectedDemoDebugAndroidTest

- name: Upload test reports
if: always()
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/Release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,15 @@ jobs:
- name: Accept Android licenses
run: yes | "$ANDROID_HOME"/cmdline-tools/latest/bin/sdkmanager --licenses || true

# TODO: This whole workflow won't work because of this step until we have flavors working in the declarative app and actually create demo-debug and demo-release variants
- name: Build release variant including baseline profile generation
run: ./gradlew :app:assembleDemoRelease
-Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=BaselineProfile
-Pandroid.testoptions.manageddevices.emulator.gpu="swiftshader_indirect"
-Pandroid.experimental.testOptions.managedDevices.emulator.showKernelLogging=true
-Pandroid.experimental.androidTest.numManagedDeviceShards=1
-Pandroid.experimental.testOptions.managedDevices.maxConcurrentDevices=1

- name: Create Release
id: create_release
uses: actions/create-release@v1
Expand Down
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
# Declarative Gradle DSL

This branch has been modified to work with the prototype of the Declarative Gradle DSL.

## Running

First, checkout the `declarativize-nia` branch of the [declarative-gradle](https://github.com/gradle/declarative-gradle) project into a **sibling directory** of this project:

```
declarative-gradle/
nowinandroid/
```

then run the following command in the `nowinandroid` directory to build the project:

```shell
gw buildDemoDebug -x :app:hiltAggregateDepsDemoDebug
```

![Now in Android](docs/images/nia-splash.jpg "Now in Android")

<a href="https://play.google.com/store/apps/details?id=com.google.samples.apps.nowinandroid"><img src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png" height="70"></a>
Expand Down
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ android {
debug {
applicationIdSuffix = NiaBuildType.DEBUG.applicationIdSuffix
}
val release = getByName("release") {
getByName("release") {
isMinifyEnabled = true
applicationIdSuffix = NiaBuildType.RELEASE.applicationIdSuffix
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ android {

// Use the same flavor dimensions as the application to allow generating Baseline Profiles on prod,
// which is more close to what will be shipped to users (no fake data), but has ability to run the
// benchmarks on demo, so we benchmark on stable data.
// benchmarks on demo, so we benchmark on stable data.
configureFlavors(this) { flavor ->
buildConfigField(
"String",
Expand Down
50 changes: 50 additions & 0 deletions build-logic/plugin-android-hilt-jacoco/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
`kotlin-dsl`
}

group = "com.google.samples.apps.nowinandroid.buildlogic"

// Configure the build-logic plugins to target JDK 17
// This matches the JDK used to build the project, and is not related to what is running on device.
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
}
}

dependencies {
implementation("org.gradle.experimental:plugin-android:0.1")
implementation("org.gradle.experimental:plugin-common:0.1")
implementation("org.jetbrains.kotlin.plugin.serialization:org.jetbrains.kotlin.plugin.serialization.gradle.plugin:1.9.21")
}

gradlePlugin {
plugins {
register("android-hilt-jacoco") {
id = "org.gradle.experimental.android-hilt-jacoco"
implementationClass = "org.gradle.api.experimental.android.ConventionalAndroidHiltJacocoPlugin"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.gradle.api.experimental.android;

import org.gradle.api.experimental.android.library.AndroidLibrary;
import org.gradle.api.experimental.android.library.AndroidLibraryBuildTypes;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.provider.Property;
import org.gradle.declarative.dsl.model.annotations.Restricted;

import javax.inject.Inject;

@Restricted
public abstract class ConventionalAndroidHiltJacocoLibrary implements AndroidLibrary {
private final KSPAndroidLibraryDependencies dependencies;
private final AndroidLibraryBuildTypes buildTypes;

@Inject
public ConventionalAndroidHiltJacocoLibrary() {
this.buildTypes = getObjectFactory().newInstance(AndroidLibraryBuildTypes.class);
this.dependencies = getObjectFactory().newInstance(KSPAndroidLibraryDependencies.class);
}

@Inject
public abstract ObjectFactory getObjectFactory();

@Restricted
public abstract Property<Boolean> getIncludeKotlinSerialization();

/**
* Static targets extension block.
*/
@Override
public AndroidLibraryBuildTypes getBuildTypes() {
return buildTypes;
}

/**
* Common dependencies for all targets.
*/
@Override
public KSPAndroidLibraryDependencies getDependencies() {
return dependencies;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.gradle.api.experimental.android;

import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.attributes.Attribute;
import org.gradle.api.experimental.android.library.StandaloneAndroidLibraryPlugin;
import org.gradle.api.internal.plugins.software.SoftwareType;

// TODO: Additional android configuration in AndroidLibraryConventionPlugin
// TODO: Lots of JaCoCo configuration (see AndroidLibraryJacocoConventionPlugin and Jacoco.kt)
// TODO: Apply and configure "nowinandroid.android.lint" plugin
// TODO: Add Conventional test dependencies
public abstract class ConventionalAndroidHiltJacocoPlugin implements Plugin<Project> {
@SuppressWarnings("UnstableApiUsage") // For SoftwareType
@SoftwareType(name = "conventionalAndroidHiltJacocoLibrary", modelPublicType=ConventionalAndroidHiltJacocoLibrary.class)
abstract public ConventionalAndroidHiltJacocoLibrary getConventionalAndroidHiltJacocoLibrary();

@Override
public void apply(Project project) {
ConventionalAndroidHiltJacocoLibrary dslModel = getConventionalAndroidHiltJacocoLibrary();

dslModel.getIncludeKotlinSerialization().convention(false);
if (dslModel.getIncludeKotlinSerialization().get()) {
project.getPluginManager().apply("kotlinx-serialization");
}

// Setup Android Library conventions
dslModel.getJdkVersion().convention(17);
dslModel.getCompileSdk().convention(34);

// Register an afterEvaluate listener before we apply the Android plugin to ensure we can
// run actions before Android does.
project.afterEvaluate(p -> StandaloneAndroidLibraryPlugin.linkDslModelToPlugin(p, dslModel));

// Apply the official Android plugin.
project.getPlugins().apply("com.android.library");
project.getPlugins().apply("org.jetbrains.kotlin.android");

// ... and Hilt plugins
project.getPlugins().apply("com.google.devtools.ksp");
project.getPlugins().apply("dagger.hilt.android.plugin");

// ...and the Jacoco plugin
project.getPlugins().apply("org.gradle.jacoco");

// Add Hilt deps
project.getDependencies().add("ksp", "com.google.dagger:hilt-android-compiler:2.50");
project.getDependencies().add("implementation", "com.google.dagger:hilt-android:2.50");

linkDslModelToPluginLazy(project, dslModel);
}

@SuppressWarnings("UnstableApiUsage")
private void linkDslModelToPluginLazy(Project project, ConventionalAndroidHiltJacocoLibrary dslModel) {
StandaloneAndroidLibraryPlugin.linkDslModelToPluginLazy(project, dslModel);
project.getConfigurations().getByName("ksp").getDependencies().addAllLater(dslModel.getDependencies().getKsp().getDependencies());
// TODO: This is super hacky - but otherwise these attributes are not set and project dependencies can't be resolved
project.getConfigurations().all(c -> {
if (c.getName().contains("debug") || c.getName().contains("Debug")) {
c.getAttributes().attribute(Attribute.of("com.android.build.api.attributes.ProductFlavor:contentType", String.class), "demo");
//> Cannot have two attributes with the same name but different types. This container already has an attribute named 'com.android.build.gradle.internal.attributes.VariantAttr' of type 'java.lang.String' and you are trying to store another one of type 'com.android.build.gradle.internal.attributes.VariantAttr'
// if (c.getAttributes().keySet().stream().noneMatch(a -> a.getName().equals("com.android.build.gradle.internal.attributes.VariantAttr"))) {
// c.getAttributes().attribute(Attribute.of("com.android.build.gradle.internal.attributes.VariantAttr", String.class), "demoDebug");
// }
c.getAttributes().attribute(Attribute.of("contentType", String.class), "demo");
}
if (c.getName().contains("release") || c.getName().contains("Release")) {
c.getAttributes().attribute(Attribute.of("com.android.build.api.attributes.ProductFlavor:contentType", String.class), "prod");
//> Cannot have two attributes with the same name but different types. This container already has an attribute named 'com.android.build.gradle.internal.attributes.VariantAttr' of type 'java.lang.String' and you are trying to store another one of type 'com.android.build.gradle.internal.attributes.VariantAttr'
// if (c.getAttributes().keySet().stream().noneMatch(a -> a.getName().equals("com.android.build.gradle.internal.attributes.VariantAttr"))) {
// c.getAttributes().attribute(Attribute.of("com.android.build.gradle.internal.attributes.VariantAttr", String.class), "prodDebug");
// }
c.getAttributes().attribute(Attribute.of("contentType", String.class), "prod");
}
});
}
}