From 1d324bf32c45602b265a85e14f2a8f79f26b290b Mon Sep 17 00:00:00 2001 From: cjk Date: Thu, 25 Sep 2025 17:19:18 +0000 Subject: [PATCH] Add support for setting 'labels' in prebuilt rules In this commit, we add support for setting the 'labels' field to the template for the prebuilt macros. This allows for setting specific lables on dependencies, which can be used for validation and querying logic. --- CHANGELOG.md | 15 ++- Usage.md | 16 ++- build.gradle | 11 ++ buildSrc/build.gradle | 3 + .../composer/java/PrebuiltRuleComposer.java | 20 +++ .../core/manager/DependencyManager.java | 5 +- .../ExternalDependenciesExtension.java | 7 ++ .../template/config/OkbuckPrebuilt.rocker.raw | 5 +- .../okbuck/template/java/Prebuilt.rocker.raw | 7 ++ .../java/PrebuiltRuleComposerTest.java | 118 ++++++++++++++++++ 10 files changed, 203 insertions(+), 4 deletions(-) create mode 100644 buildSrc/src/test/java/com/uber/okbuck/composer/java/PrebuiltRuleComposerTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index b32d0a946..0c17a950d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ * Bug fix to handle export dependencies file path correctly. ### Version 0.53.3 -* Add configuration cleanCacheDir to conditionally delete the cache directory or +* Add configuration cleanCacheDir to conditionally delete the cache directory or just the existing dependency rules files ### Version 0.54.0 @@ -27,3 +27,16 @@ ### Version 0.54.4 * Added support for using Android Lint 31.3+ + +### Version 0.54.5 +* Added `labelsMap` configuration to `externalDependencies` block for adding custom labels to prebuilt dependency rules +* Migrated Robolectric's deprecated code to recommended alternatives: + - Added `:libraries:robolectric-base` to Gradle modules + - Added missing jUnit dependency to robolectric-base + - Replaced deprecated `getAppManifest()` with `getManifestFactory()` and created `BuckManifestFactory` +* Updated GitHub Actions workflows: + - Updated runner image to `ubuntu-24.04` (ubuntu-20.04 is deprecated) + - Updated `actions/checkout` to v4 + - Updated `actions/setup-java` to v4 with temurin distribution + - Removed rxPermissions and XLog dependencies + - Updated to Python 3.8 diff --git a/Usage.md b/Usage.md index b27ec7e68..5f50eea9c 100644 --- a/Usage.md +++ b/Usage.md @@ -61,10 +61,20 @@ okbuck { experimental { transform = true } - + externalDependencies { cache = "3rdparty/jvm" cleanCacheDir = true + labelsMap = [ + "com.example:library:1.0.0": [ + "category=utility", + "license=apache-2.0" + ], + "junit:junit:4.13.2": [ + "category=testing", + "test_framework=true" + ] + ] } } @@ -85,6 +95,10 @@ please read the [Exopackage wiki](https://github.com/uber/okbuck/wiki/Exopackage + `extraBuckOpts` provides a hook to add additional configuration options for buck [android_binary](https://buckbuild.com/rule/android_binary.html) rules + `wrapper` is used to configure creation of the buck wrapper script. - `repo` - The git url of any custom buck fork. Default is none. ++ `externalDependencies` block configures external dependency resolution and generation: ++ - `cache` - Specifies the folder where external dependency rules are generated. Default is `.okbuck/ext` ++ - `cleanCacheDir` - Whether to delete the cache directory before generating dependency rules. Default is `true` ++ - `labelsMap` - Map of dependency coordinates to labels for prebuilt rules. Keys are Maven coordinates in format `"groupId:artifactId:version"`, values are lists of arbitrary strings. An example usecase could be to tag all test dependencies to easily be able to query them. + The keys used to configure various options can be for - All buildTypes and flavors i.e `app` - All buildTypes of a particular flavor i.e 'appDemo' diff --git a/build.gradle b/build.gradle index 6b97412f2..2ef7c8732 100644 --- a/build.gradle +++ b/build.gradle @@ -237,6 +237,17 @@ okbuck { "autoValueGson", "autoValueParcel", ] + // Example: Add labels for specific dependencies + labelsMap = [ + "com.example:library:1.0.0": [ + "category=utility", + "license=apache-2.0" + ], + "junit:junit:4.13.2": [ + "category=testing", + "test_framework=true" + ] + ] } dependencies { diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 1babceb17..f0b8963d7 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -40,6 +40,8 @@ dependencies { annotationProcessor deps.apt.autoValue annotationProcessor deps.build.nullaway + testAnnotationProcessor deps.build.nullaway + errorprone deps.build.erroproneCompiler errorproneJavac deps.build.errorproneJavac @@ -60,6 +62,7 @@ dependencies { implementation deps.external.gson testImplementation deps.test.junit + testImplementation deps.test.mockito } rocker { diff --git a/buildSrc/src/main/java/com/uber/okbuck/composer/java/PrebuiltRuleComposer.java b/buildSrc/src/main/java/com/uber/okbuck/composer/java/PrebuiltRuleComposer.java index d66e24438..a942c107f 100644 --- a/buildSrc/src/main/java/com/uber/okbuck/composer/java/PrebuiltRuleComposer.java +++ b/buildSrc/src/main/java/com/uber/okbuck/composer/java/PrebuiltRuleComposer.java @@ -11,14 +11,27 @@ import com.uber.okbuck.template.core.Rule; import com.uber.okbuck.template.java.Prebuilt; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; +import javax.annotation.Nullable; public class PrebuiltRuleComposer extends JvmBuckRuleComposer { private PrebuiltRuleComposer() {} + // Visibile for testing + static ImmutableSet getLabels(OExternalDependency dependency, @Nullable Map> labelsMap) { + List labels = (labelsMap == null) + ? Collections.emptyList() + : labelsMap.getOrDefault(dependency.getMavenCoordsForValidation(), Collections.emptyList()); + + return labels == null ? ImmutableSet.of() : ImmutableSet.copyOf(labels); + } + /** * @param dependencies External Dependencies whose rule needs to be created * @return List of rules @@ -26,6 +39,11 @@ private PrebuiltRuleComposer() {} @SuppressWarnings("NullAway") public static List compose( Collection dependencies, HashMap shaSum256) { + return compose(dependencies, shaSum256, null); + } + + public static List compose( + Collection dependencies, HashMap shaSum256, Map> labelsMap) { return dependencies .stream() .peek( @@ -58,6 +76,8 @@ public static List compose( rule.sourcesSha256(sourcesSha256); }); + rule.labels(getLabels(dependency, labelsMap)); + rule.ruleType(RuleType.PREBUILT.getBuckName()) .deps(external(dependency.getDeps())) .name(dependency.getTargetName()); diff --git a/buildSrc/src/main/java/com/uber/okbuck/core/manager/DependencyManager.java b/buildSrc/src/main/java/com/uber/okbuck/core/manager/DependencyManager.java index efb84c152..d6cead01b 100644 --- a/buildSrc/src/main/java/com/uber/okbuck/core/manager/DependencyManager.java +++ b/buildSrc/src/main/java/com/uber/okbuck/core/manager/DependencyManager.java @@ -438,8 +438,11 @@ private void processDependencies( ImmutableList.Builder rulesBuilder = ImmutableList.builder(); rulesBuilder.addAll(LocalPrebuiltRuleComposer.compose(localPrebuiltDependencies.build())); + + Map> labelsMap = externalDependenciesExtension.getLabelsMap(); + rulesBuilder.addAll( - PrebuiltRuleComposer.compose(prebuiltDependencies.build(), sha256Cache)); + PrebuiltRuleComposer.compose(prebuiltDependencies.build(), sha256Cache, labelsMap)); rulesBuilder.addAll( HttpFileRuleComposer.compose(httpFileDependencies.build(), sha256Cache)); diff --git a/buildSrc/src/main/java/com/uber/okbuck/extension/ExternalDependenciesExtension.java b/buildSrc/src/main/java/com/uber/okbuck/extension/ExternalDependenciesExtension.java index b13a8c8af..c03e599e3 100644 --- a/buildSrc/src/main/java/com/uber/okbuck/extension/ExternalDependenciesExtension.java +++ b/buildSrc/src/main/java/com/uber/okbuck/extension/ExternalDependenciesExtension.java @@ -73,6 +73,9 @@ public class ExternalDependenciesExtension { /** Set the path to the sha256sum caches of external dependency artifacts */ @Input private String sha256Cache = OkBuckGradlePlugin.DEFAULT_OKBUCK_SHA256; + /** Map of dependency coordinates to labels for prebuilt rules */ + @Input private Map> labelsMap = new HashMap<>(); + @Nullable private Set allowAllVersionsSet; public ExternalDependenciesExtension() {} @@ -183,4 +186,8 @@ public String getSha256Cache() { public boolean shouldCleanCacheDir() { return cleanCacheDir; } + + public Map> getLabelsMap() { + return labelsMap; + } } diff --git a/buildSrc/src/main/rocker/com/uber/okbuck/template/config/OkbuckPrebuilt.rocker.raw b/buildSrc/src/main/rocker/com/uber/okbuck/template/config/OkbuckPrebuilt.rocker.raw index efee40e5d..7a9b39f33 100644 --- a/buildSrc/src/main/rocker/com/uber/okbuck/template/config/OkbuckPrebuilt.rocker.raw +++ b/buildSrc/src/main/rocker/com/uber/okbuck/template/config/OkbuckPrebuilt.rocker.raw @@ -13,7 +13,8 @@ def @(okbuckPrebuiltRule)( deps = None, enable_jetifier = False, first_level = False, - testonly = False): + testonly = False, + labels = None): if deps == None: deps = [] @@ -64,6 +65,7 @@ def @(okbuckPrebuiltRule)( deps = deps, visibility = visibility, enable_jetifier = enable_jetifier, + labels = labels, ) elif prebuilt_type == "jar": @(prebuiltJarRule)( @@ -74,6 +76,7 @@ def @(okbuckPrebuiltRule)( deps = deps, visibility = visibility, enable_jetifier = enable_jetifier, + labels = labels, ) else: fail("okbuck_prebuilt not supported for type {}".format(prebuilt_type)) diff --git a/buildSrc/src/main/rocker/com/uber/okbuck/template/java/Prebuilt.rocker.raw b/buildSrc/src/main/rocker/com/uber/okbuck/template/java/Prebuilt.rocker.raw index 48751e96c..95dbef476 100644 --- a/buildSrc/src/main/rocker/com/uber/okbuck/template/java/Prebuilt.rocker.raw +++ b/buildSrc/src/main/rocker/com/uber/okbuck/template/java/Prebuilt.rocker.raw @@ -13,6 +13,13 @@ boolean firstLevel, } @if (firstLevel) { first_level = True, +} +@if (valid(labels)) { + labels = [ + @for (label : sorted(labels)) { + "@label", + } + ], } maven_coords = "@mavenCoords", sha256 = "@sha256", diff --git a/buildSrc/src/test/java/com/uber/okbuck/composer/java/PrebuiltRuleComposerTest.java b/buildSrc/src/test/java/com/uber/okbuck/composer/java/PrebuiltRuleComposerTest.java new file mode 100644 index 000000000..509c7c70f --- /dev/null +++ b/buildSrc/src/test/java/com/uber/okbuck/composer/java/PrebuiltRuleComposerTest.java @@ -0,0 +1,118 @@ +package com.uber.okbuck.composer.java; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.uber.okbuck.core.dependency.OExternalDependency; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.junit.Test; + +public class PrebuiltRuleComposerTest { + + @Test + public void getLabels_withValidLabelsMap_returnsLabels() { + // Arrange + OExternalDependency dependency = mock(OExternalDependency.class); + Map> labelsMap = new HashMap<>(); + List expectedLabels = new ArrayList<>(); + expectedLabels.add("test_label=value1,value2"); + labelsMap.put("com.example:test-artifact:1.0.0", expectedLabels); + + when(dependency.getMavenCoordsForValidation()).thenReturn("com.example:test-artifact:1.0.0"); + + // Act + Set result = PrebuiltRuleComposer.getLabels(dependency, labelsMap); + + // Assert + assertNotNull(result); + assertEquals(expectedLabels.size(), result.size()); + assertTrue(result.contains("test_label=value1,value2")); + } + + @Test + public void getLabels_withNullLabelsMap_returnsEmptySet() { + // Arrange + OExternalDependency dependency = mock(OExternalDependency.class); + + // Act + Set result = PrebuiltRuleComposer.getLabels(dependency, null); + + // Assert + assertNotNull(result); + assertTrue(result.isEmpty()); + } + + @Test + public void getLabels_withEmptyLabelsMap_returnsEmptySet() { + // Arrange + OExternalDependency dependency = mock(OExternalDependency.class); + Map> emptyLabelsMap = new HashMap<>(); + + // Act + Set result = PrebuiltRuleComposer.getLabels(dependency, emptyLabelsMap); + + // Assert + assertNotNull(result); + assertTrue(result.isEmpty()); + } + + @Test + public void getLabels_withMissingKey_returnsEmptySet() { + // Arrange + OExternalDependency dependency = mock(OExternalDependency.class); + Map> labelsMap = new HashMap<>(); + List labels = new ArrayList<>(); + labels.add("other_label=other_value"); + labelsMap.put("com.other:other-artifact:2.0.0", labels); + + when(dependency.getMavenCoordsForValidation()).thenReturn("com.example:test-artifact:1.0.0"); + + // Act + Set result = PrebuiltRuleComposer.getLabels(dependency, labelsMap); + + // Assert + assertNotNull(result); + assertTrue(result.isEmpty()); + } + + @Test + public void getLabels_withNullValue_returnsEmptySet() { + // Arrange + OExternalDependency dependency = mock(OExternalDependency.class); + Map> labelsMap = new HashMap<>(); + labelsMap.put("com.example:test-artifact:1.0.0", null); + + when(dependency.getMavenCoordsForValidation()).thenReturn("com.example:test-artifact:1.0.0"); + + // Act + Set result = PrebuiltRuleComposer.getLabels(dependency, labelsMap); + + // Assert + assertNotNull(result); + assertTrue(result.isEmpty()); + } + + @Test + public void getLabels_withEmptyValue_returnsEmptySet() { + // Arrange + OExternalDependency dependency = mock(OExternalDependency.class); + Map> labelsMap = new HashMap<>(); + labelsMap.put("com.example:test-artifact:1.0.0", new ArrayList<>()); + + when(dependency.getMavenCoordsForValidation()).thenReturn("com.example:test-artifact:1.0.0"); + + // Act + Set result = PrebuiltRuleComposer.getLabels(dependency, labelsMap); + + // Assert + assertNotNull(result); + assertTrue(result.isEmpty()); + } +}