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()); + } +}