Skip to content

Commit

Permalink
Merge pull request #90 from gradle/tt/declarativize-nia-app-catalog
Browse files Browse the repository at this point in the history
Add support for NiA :app-nia-catalog project to use declarative syntax
  • Loading branch information
tresat committed Jun 14, 2024
2 parents 0e62c53 + b503d6b commit 0180fbb
Show file tree
Hide file tree
Showing 8 changed files with 265 additions and 47 deletions.
19 changes: 14 additions & 5 deletions unified-prototype/unified-plugin/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,31 +1,40 @@
[versions]
# Kotlin Libs
# Find latest version at: https://kotlinlang.org/docs/releases.html#release-details
kotlin = "1.9.23"
ksp = "1.9.23-1.0.20"

# Android Libs
# Find latest version at: https://developer.android.com/reference/tools/gradle-api
android = "8.3.0"
androidTools = "31.3.0"
hilt = "2.50"
room = "2.6.1"
protobuf = "0.9.4"

#Other Libs
apache-commons = "3.3.1"
dependency-guard = "0.4.3"
truth = "1.4.2"
protobuf = "0.9.4"

[libraries]
# Kotlin dependencies
# Kotlin Libs
kotlin-multiplatform = { module = "org.jetbrains.kotlin.multiplatform:org.jetbrains.kotlin.multiplatform.gradle.plugin", version.ref = "kotlin" }
ksp-plugin = { module = "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" }
kotlin-jvm = { module = "org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin", version.ref = "kotlin" }
kotlin-serialization-plugin = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kotlin" }

# Android dependencies
# Android Libs
android-agp-application = { module = "com.android.application:com.android.application.gradle.plugin", version.ref = "android" }
android-kotlin-android = { module = "org.jetbrains.kotlin.android:org.jetbrains.kotlin.android.gradle.plugin", version.ref = "kotlin" }
android-tools-common = { module = "com.android.tools:common", version.ref = "androidTools" }
hilt-android-plugin = { module = "com.google.dagger:hilt-android-gradle-plugin", version.ref = "hilt" }
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt"}
hilt-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hilt" }
protobuf-plugin = { module = "com.google.protobuf:protobuf-gradle-plugin", version.ref = "protobuf" }
room-plugin = { module = "androidx.room:room-gradle-plugin", version.ref = "room" }

# Other libraries
#Other Libs
apache-commons-lang = { module = "org.apache.commons:commons-lang3", version.ref = "apache-commons" }
dependency-guard-plugin = { module = "com.dropbox.dependency-guard:com.dropbox.dependency-guard.gradle.plugin", version.ref = "dependency-guard"}
truth = { module = "com.google.truth:truth", version.ref = "truth" }
protobuf-plugin = { module = "com.google.protobuf:protobuf-gradle-plugin", version.ref = "protobuf" }
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,16 @@ dependencies {
api(libs.android.kotlin.android)

implementation(project(":plugin-jvm"))
implementation(libs.dependency.guard.plugin)
implementation(libs.ksp.plugin)
implementation(libs.hilt.android.plugin)
implementation(libs.kotlin.serialization.plugin)
implementation(libs.room.plugin)
implementation(libs.protobuf.plugin)

implementation(libs.apache.commons.lang)
implementation(libs.android.tools.common)
implementation(libs.truth)
}

testing {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.android.build.api.dsl.ApplicationBaseFlavor;
import org.gradle.api.Action;
import org.gradle.api.experimental.android.AndroidSoftware;
import org.gradle.api.experimental.android.extensions.DependencyGuard;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.Nested;
import org.gradle.declarative.dsl.model.annotations.Configuring;
Expand Down Expand Up @@ -61,4 +62,14 @@ default void dependencies(Action<? super AndroidApplicationDependencies> action)
default void buildTypes(Action<? super AndroidApplicationBuildTypes> action) {
action.execute(getBuildTypes());
}

@Nested
DependencyGuard getDependencyGuard();

@Configuring
default void dependencyGuard(Action<? super DependencyGuard> action) {
DependencyGuard dependencyGuard = getDependencyGuard();
dependencyGuard.getEnabled().set(true);
action.execute(dependencyGuard);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ public void apply(Project project) {

AndroidApplication dslModel = getAndroidApplication();

// Setup application-specific conventions
dslModel.getDependencyGuard().getEnabled().convention(false);

// Register an afterEvaluate listener before we apply the Android plugin to ensure we can
// run actions before Android does.
project.afterEvaluate(p -> linkDslModelToPlugin(p, dslModel));
Expand Down Expand Up @@ -58,38 +61,7 @@ private void linkDslModelToPlugin(Project project, AndroidApplication dslModel)

// TODO: All this configuration should be moved to the NiA project
if (NiaSupport.isNiaProject(project)) {
// ProductFlavors are automatically added by the LIBRARY plugin via NiA support only, ATM, so we
// need to make sure any Android APPLICATION projects have the necessary attributes for project deps to work.
configureContentTypeAttributes(project);

NiaSupport.configureNiaApplication(project, dslModel);
}
}

private void configureContentTypeAttributes(Project project) {
// These attributes must be set to avoid Ambiguous Variants resolution errors between the
// demoDebugRuntimeElements and prodDebugRuntimeElements for project dependencies in NiA
project.getConfigurations().configureEach(c -> {
AttributeContainer attributes = c.getAttributes();
String lowerConfName = c.getName().toLowerCase();

Attribute<ProductFlavorAttr> contentTypeFlavorAttr = ProductFlavorAttr.of("contentType");
if (!attributes.contains(contentTypeFlavorAttr)) {
if (lowerConfName.contains("debug")) {
attributes.attribute(contentTypeFlavorAttr, project.getObjects().named(ProductFlavorAttr.class, "demo"));
} else if (lowerConfName.contains("release")) {
attributes.attribute(contentTypeFlavorAttr, project.getObjects().named(ProductFlavorAttr.class, "prod"));
}
}

Attribute<String> contentTypeStringAttr = Attribute.of("contentType", String.class);
if (!attributes.contains(contentTypeStringAttr)) {
if (lowerConfName.contains("debug")) {
attributes.attribute(contentTypeStringAttr, "demo");
} else if (lowerConfName.contains("release")) {
attributes.attribute(contentTypeStringAttr, "prod");
}
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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.extensions;

import org.gradle.api.provider.Property;
import org.gradle.declarative.dsl.model.annotations.Restricted;

@Restricted
public interface DependencyGuard {
@Restricted
Property<Boolean> getEnabled();

@Restricted
Property<String> getConfigurationName();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* 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.nia;

import org.gradle.api.DefaultTask;
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.CacheableTask;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.OutputDirectory;
import org.gradle.api.tasks.PathSensitive;
import org.gradle.api.tasks.PathSensitivity;
import org.gradle.api.tasks.TaskAction;
import org.gradle.language.base.plugins.LifecycleBasePlugin;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import static com.google.common.truth.Truth.assertWithMessage;

@CacheableTask
public abstract class CheckBadgingTask extends DefaultTask {

// In order for the task to be up-to-date when the inputs have not changed,
// the task must declare an output, even if it's not used. Tasks with no
// output are always run regardless of whether the inputs changed
@OutputDirectory
public abstract DirectoryProperty getOutput();

@PathSensitive(PathSensitivity.NONE)
@InputFile
public abstract RegularFileProperty getGoldenBadging();

@PathSensitive(PathSensitivity.NONE)
@InputFile
public abstract RegularFileProperty getGeneratedBadging();

@Input
public abstract Property<String> getUpdateBadgingTaskName();

@Override
public String getGroup() {
return LifecycleBasePlugin.VERIFICATION_GROUP;
}

@TaskAction
public void taskAction() {
try {
String goldenBadgingContent = new String(Files.readAllBytes(Paths.get(getGoldenBadging().get().getAsFile().getAbsolutePath())));
String generatedBadgingContent = new String(Files.readAllBytes(Paths.get(getGeneratedBadging().get().getAsFile().getAbsolutePath())));
assertWithMessage(
"Generated badging is different from golden badging! " +
"If this change is intended, run ./gradlew " + getUpdateBadgingTaskName().get()
).that(generatedBadgingContent).isEqualTo(goldenBadgingContent);
} catch (IOException e) {
throw new RuntimeException("Failed to read badging files", e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* 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.nia;

import org.gradle.api.DefaultTask;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.tasks.CacheableTask;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.OutputFile;
import org.gradle.api.tasks.PathSensitive;
import org.gradle.api.tasks.PathSensitivity;
import org.gradle.api.tasks.TaskAction;
import org.gradle.process.ExecOperations;

import javax.inject.Inject;
import java.io.FileOutputStream;
import java.io.OutputStream;

@CacheableTask
public abstract class GenerateBadgingTask extends DefaultTask {
@OutputFile
public abstract RegularFileProperty getBadging();

@PathSensitive(PathSensitivity.NONE)
@InputFile
public abstract RegularFileProperty getApk();

@PathSensitive(PathSensitivity.NONE)
@InputFile
public abstract RegularFileProperty getAapt2Executable();

@Inject
public abstract ExecOperations getExecOperations();

@TaskAction
public void taskAction() {
getExecOperations().exec(execSpec -> {
execSpec.commandLine(
getAapt2Executable().get().getAsFile().getAbsolutePath(),
"dump",
"badging",
getApk().get().getAsFile().getAbsolutePath()
);

try (OutputStream os = new FileOutputStream(getBadging().get().getAsFile())) {
execSpec.setStandardOutput(os);
} catch (Exception e) {
throw new RuntimeException("Failed to write badging output", e);
}
});
}
}
Loading

0 comments on commit 0180fbb

Please sign in to comment.