Giter Site home page Giter Site logo

square / bazel_maven_repository Goto Github PK

View Code? Open in Web Editor NEW
39.0 5.0 11.0 26.74 MB

A bazel ruleset creating a more idiomatic bazel representation of a maven repo using a pinned list of artifacts.

License: Apache License 2.0

Java 1.01% Shell 0.20% Kotlin 63.53% Starlark 35.26%

bazel_maven_repository's Introduction

Bazel Rules for Maven Repositories

A bazel ruleset creating an idiomatic bazel representation of a maven repository using a pinned list of artifacts.

Release: 1.2.0
Prerelease: 2.0.0-alpha-5

Version Sha
Release 1.2.0 9e23155895d2bfc60b35d2dfd88c91701892a7efba5afacdf00cebc0982229fe
Prerelease 2.0.0-alpha-5 fde80cafa02a2c034cc8086c158f500e7b6ceb16d251273a6cc82f1c0723e0e8

Table of Contents

Overview

Bazel Rules for Maven Repositories allow the specification of a list of artifacts which constitute maven repository's universe of deps, and exposes these artifacts via a bazel external workspace. The name of the repository specification rule becomes the repository name in Bazel. For instance the following specification:

MAVEN_REPOSITORY_RULES_VERSION = "1.2.0"
MAVEN_REPOSITORY_RULES_SHA = "9e23155895d2bfc60b35d2dfd88c91701892a7efba5afacdf00cebc0982229fe"
http_archive(
    name = "maven_repository_rules",
    urls = ["https://github.com/square/bazel_maven_repository/archive/%s.zip" % MAVEN_REPOSITORY_RULES_VERSION],
    type = "zip",
    strip_prefix = "bazel_maven_repository-%s" % MAVEN_REPOSITORY_RULES_VERSION,
    sha256 = MAVEN_REPOSITORY_RULES_SHA,
)
load("@maven_repository_rules//maven:maven.bzl", "maven_repository_specification")
maven_repository_specification(
    name = "maven",
    artifacts = {
        "com.google.guava:guava:25.0-jre": { "sha256": "3fd4341776428c7e0e5c18a7c10de129475b69ab9d30aeafbb5c277bb6074fa9" },
    }
)

... results in deps of the format @maven//com/google/guava:guava (which can be abbreviated to @maven//com/google/guava)

Dependency versions are resolved in the single artifact list. Only one version is permitted within a repository.

Note: bazel_maven_repository has no workspace dependencies, so adding it to your project will not result in any additional bazel repositories to be fetched. However, you specify .aars in your list, you will need the rules_android to be declared in your WORKSPACE

Supported Types

Currently .aar and .jar artifacts are supported. OSGI bundles are supported by assuming they are normal .jar artifacts (which they are, just have a packaging property of bundle and some extra metadata in META-INF of the .jar file).

.aar artifacts should be specified as "some.group:some-artifact:1.0:aar" (just append :aar onto the artifact spec string).

For any other types, please file a feature request, or supply a pull request. So long as there exists a proper bazel import or library rule to bring the artifact's file into bazel's dependency graph, it should be possible to support it.

Inter-artifact dependencies

This rule will, in the generated repository, infer inter-artifact dependencies from pom.xml files of those artifacts (pulling in only compile and runtime dependencies, and avoiding any systemPath dependencies). This avoids the bazel user having to over-specify the full set of dependency jars.

All artifacts, even if only transitively depended, must be specified with a pinned version in the artifacts dictionary. Any artifacts discovered in the inferred dependency search, which are not present in the main rule's artifact list will be flagged and the build will fail with an error. The error output should suggest configuration to either pin them or exclude them.

Coordinate Translation

Translation from maven group/artifact coordinates to bazel package/target coordinates is naive but orderly. The logic mirrors the layout of a maven repository, with groupId elements (separated by .) turning into a package hierarchy, and the artifactId turning into a bazel target.

For example: com.google.dagger:dagger-compiler:2.16 turns into @maven//com/google/dagger:dagger, and javax.inject:javax.inject:1 becomes @maven//javax/inject:javax_inject (since . is not a valid character for a target).

Artifact Configuration

Sha verification

Artifacts with SHA256 checksums can be added to artifacts, in the form:

    artifacts = {
        "com.google.guava:guava:25.0-jre": { "sha256": "3fd4341776428c7e0e5c18a7c10de129475b69ab9d30aeafbb5c277bb6074fa9" },
    }

Artifacts without SHA headers should configured as insecure, like so:

    artifacts = {
        "com.google.guava:guava:25.0-jre": { "insecure": True },
    }

The rules will reject artifacts without SHAs are not marked as "insecure".

Note: These rules cannot validate that the checksum is the right one, only that the one supplied in configuration matches the checksum of the file downloaded. It is the responsibility of the maintainer to use proper security practices and obtain the expected checksum from a trusted source.

Testonly

An artifact can be declared testonly - this forces the testonly = True flag for the generated target to be set, requiring that this artifact only be used by other testonly rules (e.g. test-libraries with testonly = true also set, or *_test rules.)

maven_repository_specification(
    name = "maven",
    artifacts = {
        "com.google.truth:truth:1.0": {"insecure": True, "testonly": True},
        # ... all the other deps.
    },
)

Exclude

Since 2.0.0

An artifact can have some (or all) of its direct dependencies pruned by use of the exclude property. For instance:

maven_repository_specification(
    name = "maven",
    artifacts = {
        "com.google.truth:truth:1.0": {
            "insecure": True,
            "exclude": ["com.google.auto.value:auto-value-annotations"],
        },
        # ... all the other deps.
    },
)

This results in truth omitting a dependency on the auto-value annotations - useful to avoid dependencies not specified as <optional>true</optional> in the pom.xml, but which are not necessary for correct operation of the artifact. A good example of this would be robolectric, which contains dependencies on a lot of maven download code which is not needed in a bazel build.

Note: This feature is incompatible with "deps": [...] and "build_snippets": "..." mechanisms.

Include

Since 2.0.0

An artifact can have additional dependencies added using the include property. For instance:

maven_repository_specification(
    name = "maven",
    artifacts = {
        "com.helpshift:android-helpshift-aar:7.8.0": {
            "insecure": True,
            "include": [
                # Bazel dependencies 
                "@//some/local/dependency", # in the root workspace of this build
                "@some_workspace//some/other/dep", # in some external workspace of this build

                # While theoretically in the root workspace, because this occurs inside the maven
                # workspace maven may interpret this as being inside the @maven workspace. Use
                # the @// prefix to force it into the root workspace
                "//some/local/dependency",

                # Useful for referencing some custom thing added by a snippet on another artifact.
                ":some_target_in_the_same_package",

                # Maven artifact coordinates, which will be interpreted relative to the presently
                # configured maven workspace. These will also result in errors if their versioned
                # artifacts have not been pinned in the artifact dictionary.
                "androidx.cardview:cardview",
                "androidx.recyclerview:recyclerview",
                "com.google.android.material:material",
            ]
        },
        # ... all the other deps.
    },
)

This results in the bazel workspace generating additional dependencies, converting maven-style some.group.id:artifactId pairs to @maven//some/group/id:artifactId and passing bazel-style dependencies (:foo, //foo/bar, @someWorkspace//foo/bar:baz) through as text, relying on bazel itself to resolve and complain if they are incorrect.

These additions are performed after automatically detected dependencies are processed (and after excludes are processed). It can be used to add missing deps from badly specified pom files, or it can add-back-in optional dependencies (which are not included by default).

Note: This feature is incompatible with "deps": [...] and "build_snippets": "..." mechanisms.

Deps

Since 2.0.0

An artifact can have its dependencies entirely specified (overriding the automated detection via maven resolution) using the deps property. For instance:

maven_repository_specification(
    name = "maven",
    artifacts = {
        "com.helpshift:android-helpshift-aar:7.8.0": {
            "insecure": True,
            "deps": [
                # Bazel dependencies 
                "@//some/local/dependency", # in the root workspace of this build
                "@some_workspace//some/other/dep", # in some external workspace of this build

                # While theoretically in the root workspace, because this occurs inside the maven
                # workspace maven may interpret this as being inside the @maven workspace. Use
                # the @// prefix to force it into the root workspace
                "//some/local/dependency",

                # Useful for referencing some custom thing added by a snippet on another artifact.
                ":some_target_in_the_same_package",

                # Maven artifact coordinates, which will be interpreted relative to the presently
                # configured maven workspace. These will also result in errors if their versioned
                # artifacts have not been pinned in the artifact dictionary.
                "androidx.cardview:cardview",
                "androidx.recyclerview:recyclerview",
                "com.google.android.material:material",
            ]
        },
        # ... all the other deps.
    },
)

This results in the bazel workspace ignoring the dependencies from resolved metadata, and using the supplied list instead. It converts maven-style some.group.id:artifactId pairs to @maven//some/group/id:artifactId. It passes bazel-style dependencies (:foo, //foo/bar, @someWorkspace//foo/bar:baz) through as text, relying on bazel itself to resolve and complain if they are incorrect.

Note: This feature is incompatible with "include": [...] "exclude": [...] and "build_snippets": "..." mechanisms, as it entirely takes responsibility for the deps list.

Substitution of build targets

Note: Because this feature entirely takes over snippet generation, it is more brittle than other features that may address the relevant use-cases. Please prefer those features over this one, if they can solve your problem, as it overrides a lot of validation and guarantees, and a given snippet may become stale with version changes of the artifacts specified.

It is useful, however, if you cannot address your case with other features.

One can provide a BUILD.bazel target snippet that will be substituted for the auto-generated target implied by a maven artifact. This is very useful for providing an annotation-processor-exporting alternative target. The substitution is naive, so the string needs to be appropriate and any rules need to be correct, contain the right dependencies, etc. To aid that it's also possible to (on a per-package basis) substitute dependencies on a given fully-qualified bazel target for another.

A simple use-case would be to substitute a target name (e.g. "mockito-core" -> "mockito") for cleaner/easier use in bazel:

maven_repository_specification(
    name = "maven",
    artifacts = {
        "org.mockito:mockito-core:2.20.1": {
            "sha256": "blahblahblah",
            "build_snippet": """maven_jvm_artifact(name = "mockito", artifact = "org.mockito:mockito-core:2.20.1")""",
        },
        # ... all the other deps.
    },
)

This would allow the following use in a BUILD.bazel file.

java_test(
  name = "MyTest",
  srcs = "MyTest.java",
  deps = [
    # ... other deps
    "@maven//org/mockito" # instead of "@maven//org/mockito:mockito-core"
  ],
)

More complex use-cases are possible, such as adding substitute targets with annotation processing java_plugin targets and exports. An example with Dagger would look like this (with the basic rule imports assumed):

DAGGER_PROCESSOR_SNIPPET = """
# use this target
java_library(name = "dagger", exports = [":dagger_api"], exported_plugins = [":dagger_plugin"])

# alternatively-named import of the raw dagger library.
raw_jvm_import(
    name = "dagger_api",
    jars = "@com_google_dagger_dagger//maven",    
)

java_plugin(
    name = "dagger_plugin",
    processor_class = "dagger.internal.codegen.ComponentProcessor",
    generates_api = True,
    deps = [":dagger_compiler"],
)
"""

The above is given as a substitution in the maven_repository_specification() rule. However, since the inferred dependencies of :dagger-compiler would create a dependency cycle because it includes :dagger as a dep, the specification rule also should include a dependency_target_substitution, to ensures that the inferred rules in the generated com/google/dagger/BUILD file consume :dagger_api instead of the wrapper replacement target, or use include and exclude statements on the com.google.dagger:dagger-compiler artifact to have it depend on the now-renamed target (see the test/test_workspace for an example of this)

maven_repository_specification(
    name = "maven",
    artifacts = {
        "com.google.dagger:dagger:2.20": {
            "sha256": "blahblahblah",
            "build_snippet": DAGGER_PROCESSOR_SNIPPET,
        },
        "com.google.dagger:dagger-compiler:2.20": { "sha256": "blahblahblah" },
        "com.google.dagger:dagger-producers:2.20": { "sha256": "blahblahblah" },
        "com.google.dagger:dagger-spi:2.20": { "sha256": "blahblahblah" },
        "com.google.code.findbugs:jsr305:3.0.2": { "sha256": "blahblahblah" },
        # ... all the other deps.
    },
    dependency_target_substitutes = {
        "com.google.dagger": {"@maven//com/google/dagger:dagger": "@maven//com/google/dagger:dagger_api"},
    }
)

Thereafter, any target with a dependency on (in this example) @maven//com/google/dagger will invoke annotation processing and generate any dagger-generated code. A similar pattern could be used for AutoFactory and AutoValue, configuring kotlinc plugins, etc.

Such snippet constants can be extracted into .bzl files and imported to keep the WORKSPACE file tidy. In the future some standard templates may be offered by this project, but not until deps validation is available, as it would be too easy to have templates' deps lists go out of date as versions bumped, if no other validation prevented it or notified about it.

Note: This feature is incompatible with "include": [...] "exclude": [...] and "deps": "..." mechanisms, as it entirely takes responsibility for the targets written into the build file, and therefore, any dependencies. It also overrides a lot of warnings.

Caching

Bazel Maven Repository 1.2.0 and prior: Bazel can cache artifacts if you provide sha256 hashes. These will make the artifacts candidates for the "content addressable" cache, which is machine-wide, and survives even bazel clean --expunge. The caveat for this is that if you put the wrong hash, and if that hash is to a file you already have downloaded, bazel's internal download mechanism will serve that file instead of the remote file. This can cause you to accidentally supply a wrong version, wrong artifact, or wrong kind of file if you're not careful. So take caution when recording the sha256 hashes, both for security and performance reasons. The hash will be preferred over the artifact as a way to identify the artifact, under the hood.

Pom.xml files are not cached, and will be re-downloaded when your workspace needs regenerating.

Bazel Maven Repository 2.0 and later: The rules will cache maven artifacts and metadata via the ~/.m2/repository "local repository" exactly as maven would.

Limitations

  • Doesn't support -SNAPSHOT dependencies (#5) (pending 2.0)
  • Doesn't support multiple versions of a dependency (by design).
  • Doesn't support multiple calls to maven_repository_specification() due to collisions in the implicit fetching rules it creates. This limitation will be lifted in a version. (#6) (pending 2.0)
  • Doesn't support -source.jar downloading and attachment. (#44) (pending 2.0)

Other Usage Notes

Caches

Because of the nature of bazel repository/workspace operation, updating the list of artifacts invalidates the analysis cache, and force a re-run of workspace operations (and possibly reduce incrementality of the next build). As of 2.0, the workspace generation is drastically faster.

Clogged WORKSPACE files

It may make sense, if one's maven universe gets big, to extract the list of artifacts into a constant in a separate file (e.g. maven_artifacts.bzl) and import it.

Kotlin

ijar (abi-jar) and inline functions

Bazel Maven Repository uses a raw_jvm_import target which does not invoke ijar as it is known to break Kotlin artifacts with inlined operators and inlined types.

rules_kotlin and maven integration paths

[rules_kotlin] currently break when running in full sandbox mode (without the kotlin compilation worker). Specifically, it misinterprets paths in the sandbox. Therefore, if using [rules_kotlin] it is crucial to include --strategy=KotlinCompile=worker either on the command-line, or in the project's .bazelrc or your personal .bazelrc. Otherwise, the annotation processor will fail to find the jar contents for annotation processors such as Dagger 2 or AutoValue or AutoFactory.

License

License Copyright 2018 Square, Inc.

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

http://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.

bazel_maven_repository's People

Contributors

0legg avatar 0xmatthewgroves avatar cgruber avatar inez avatar jin avatar kaled-a avatar kelvinlu avatar restingbull avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

bazel_maven_repository's Issues

Support SNAPSHOTs.

Note - this could be an "at the time of workspace evaluation" resolution, or it could involve pinned versions. Either way, the repository structure file system structure of http://some.repo.root/maven/com/google/dagger/2.20/dagger-2.20.jar doesn't work for snapshots, which are, for the standard "HEAD-SNAPSHOT" version dagger uses, more like: https://oss.sonatype.org/content/repositories/snapshots/com/google/dagger/dagger/HEAD-SNAPSHOT/dagger-HEAD-20190108.194818-169.jar

So this would need to either have pinned versions declared, and then test for the "${version}-${date}.${integer1}-${integer2} version pattern and go into the "${version}-SNAPSHOT" folder to fetch it, or it would need to test for "-SNAPSHOT" and pull the maven-metadata.xml file, grab the snapshot details, and construct the path that way.

Clean the kramer workspace by using a deps.bzl and a switch.

Per corbin's comment in #112:

Suggest extracting the maven_repository_rules into a deps.bzl and switch on import alias
e.g.
load("//:deps.bzl", define="define_local")
and load("//:deps.bzl", define="define_remote")

commenting out the alternative makes it messy in the WORKSPACE.

Transfer project to github.com/square

Most of this work I'm doing as a square employee (going forward) not on my own time, so there's no reason for it not to be a square project. Port the repo over to the square org.

Infer pom packaging from pom.xml metadata

Presently you must explicitly put the packaging in the artifact spec if it's not the default jar type. e.g. "com.android.support:cardview-v7:28.0.0:aar". This information is present in the pom.xml, and should be inferred from that source (unless overridden, e.g. to make a bundle type return a jar artifact.)

This makes a type of configuration error less relevant - forgetting to tack on :aar - which results in a download failure, rather than a "wrong type" error.

An alternative could be to just validate against the pom, but for cases where there's a deliberate choice to put in a different packaging, this would clobber that, so it's preferable to treat explicit spec as an override. This is also backwards compatible.

ERROR: Some dependencies were not pinned in the artifacts list.

Hi there,

I got the following error msg when added "@maven//com/google/guava:guava" to the deps list of an android_binary target. Thanks!

ERROR: BUILD:1:1: no such package '@maven//com/google/guava': Some dependencies of com.google.guava:guava:25.0-jre were not pinned in the artifacts list:
["org.checkerframework:checker-compat-qual:2.0.0", "com.google.errorprone:error_prone_annotations:2.1.3", "com.google.j2objc:j2objc-annotations:1.1", "org.codehaus.mojo:animal-sniffer-annotations:1.14"] and referenced by '//java/com/awesome/android/helloworld:helloworld'.

My environment:

  • Mac
  • Bazel version 0.23.0
  • Android Studio 3.2.1
  • bazel_maven_repository 1.0-rc5

My workspace file (copied from https://github.com/square/bazel_maven_repository):
https://github.com/senpo/awesome-bazel-android/blob/master/WORKSPACE

My build file:
https://github.com/senpo/awesome-bazel-android/blob/master/java/com/awesome/android/helloworld/BUILD

Support variable substitution in pom interpretation (pulling in <properties> metadata)

Some values are set in maven poms via ${foo.bar} variable substitution. These are inferred from <properties> sections. The tooling that interprets poms should collect properties and perform the substitution before evaluating.

A few special case properties may need to be handled, e.g. project.* properties, though we can probably get away with just handling the msot common subset of project.artifactId, project.groupId and project.version. We can add more later on an as needed basis.

(Note, this is ultimately dependent on including parent metadata as in #7)

Big regression in analysis phase in 1.1-rc1

Something introduced between 1.0 and 1.1-rc1 results in much slower analysis phase, in particular, it seems to be during the parsing/merging of the poms. It's unclear why, since (it seems) like the same work is being done, just shifted in order, but it needs debugging.

This is a blocker to 1.1

Support an alias configuration for artifacts

Presently artifacts are named for their artifactId (possibly munged). However, this is sometimes inconvenient, as in the case of something like hamcrest-core or assertj-core. There should be a way to specify an alias target with a different name, which will pass-through to the default target.

The alias should be publicly visible, and the mvn_jvm_import should be reduced in visibility to //visibility:@maven//__subpackages__ (generated with the appropriate workspace name in place of maven). This allows existing inter-dependencies logic to work unmodified, but the public alias will be the means by which deps not in the maven workspace reference this artifact.

e.g.:

maven_repository_specification(
    name = "maven",
    artifacts = {
        "org.mockito:mockito-core:2.7.5": { "sha256": "blah", "alias" = "mockito" }
    },
)

This would generate

alias(name = "mockito", actual = ":mockito_core")

maven_jvm_artifact(
    name = "mockito_core",
    artifact = "org.mockito:mockito-core:2.7.5",
    visibility = "//visibility:@maven//__subpackages__",
    deps = [
        "@maven//net/bytebuddy:byte_buddy",
        "@maven//net/bytebuddy:byte_buddy_agent",
        "@maven//org/objenesis",
    ]
)

Implements part of #52

Mis-shapen tag content can foil boolean variables. (e.g. <optional>)

Describe the bug
Extra data in the tag content of <optional> can result in it failing to properly calculate the boolean.

To Reproduce
Steps to reproduce the behavior:
Depend on org.reflections:reflections:0.9.11, which requires both guava and javassist, and only guava is added to the deps list.

of note, the pom includes:

        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>${javassist.version}</version>
            <optional>false
            </optional> <!-- case: when not actually scanning with javassist or if using {@link Reflections.collect()} -->
        </dependency>

The extra line wrap seems to bork the computation of the value (and somehow we get to "true"?!?!)

Expected behavior
Optional should be processed properly, even with odd whitespace.

Reduce build_snippet use by adding some common configuration features.

Tracking bug to expand configuration to support (not exhaustive). (Might move this to be just a project).

  • global aliasing (rewrite all references of @maven//foo/bar to @maven//foo/bar:other). Package level aliases (only for dependency references) are already implemented, this is a feature for public consumption of the dep.
  • shallow deps exclusions (e.g., trimming out deps not declared optional, but which are not necessary for the intended uses.
  • data dependencies (e.g. data = ["//my/dep/to/generate:robolectric-deps.properties"])
  • exports (exported dependencies)
  • extra build snippet logic (e.g. build_prefix) which can be useful for alias targets where the generated targets are perfectly serviceable and there is no reason to modify them. e.g. adding an aggregation target for hamcrest so @maven//org/hamcrest both works, and exports hamcrest-core and hamcrest-library (a very common setup).
  • annotation processing hookups
    • Two-artifact (dep on annotations, processor in a separate artifact, e.g. dagger)
    • One-artifact (core annotations and processor in one artifact, e.g. autofactory)
  • testonly
  • dependency exclusions

Probably more than this, but shallow deps and data deps are needed, for instance, to avoid having a build_snippet.

The criteria for this feature is that the configuration can't be worse/harder than just using a build_snippet. That's probably true anyway, but it really depends on specifics. with build_snippets you need to maintain the deps list, so even just getting that while doing some of these other features should make things simpler, but it definitely makes the master artifact list harder to keep as one-liner. ...though technically we could just encourage people to do:

ROBOLECTRIC_CONFIG = {
    "sha256" = "deadbeefabcdef1234567890",
    "data" : [
        "//path/to/generated:robolectric-deps.properties",
    ],
    "excluded_deps" : [
        "all.the.maven:crap",
    ],
}

and then in the artifact list it's just:

maven_repository_specification(
    name = "maven",
    artifacts = {
        "org.robolectric:robolectric:3.8": ROBOLECTRIC_CONFIG
    },
)

Include version information in the individual artifact fetch bazel repositories

Include version information in the individual artifact fetch bazel repositories, so that multiple maven repository declarations can share cached fetches, and avoid collisions of different versions.

Right now, under the hood, the system declares a @com_google_guava_guava bazel repo containing the pinned version of the dep. But if two maven_repository_specification() functions are invoked, which both contained different versions of guava, the latter one would re-define that bazel repo, interfering with the first one.

The right answer is that the full canonical coordinate needs to be part of the repo name (which is not directly used by developers anyway, but is pointed to by the @maven//com/google/guava:guava target (or whatever).

Also, we should prefix with "maven_artifact_" or something unique, so that a project which decides to use something else which might declare @com_google_guava_guava (the typical maven_jar rule pattern) won't collide.

Ultimately, something like @maven_repository_com_google_guava_guava_25_0_jre should be the repo name, so maven_repository_specification(name = "maven", artifacts=["com.google.guava:guava:25.0-jre"]) and maven_repository_specification(name = "other", artifacts = ["com.google.guava:guava:25.0-jre"])` will point at the same jar file, which is downloaded.

While this could result in one copy from one repo downloaded, and another repo declaration picking it up, as long as you use sha256 ones, it should break if there were two different file contents (represented in the hashes).

Plumb through jetifier configuration

Make jetifier properly configurable through the rule parameters, in particular configurable exclude list, both for dependencies remapping and jetifier invocation.

Support configuration to mark an artifact "testonly"

Bazel supports a "testonly=" flag on most rules, constraining the rules in the following way - a testonly rule can depend on regular and testonly rules, but a regular rule cannot depend on a testonly rule.

This is slightly inverted from Maven, which specifies the purpose of a dep at the point of inclusion (the deps list). Bazel specifies a target itself as testonly. Since tests and production targets are separate nodes in bazel (in maven they're one node with "flavored" deps), this handling has to be different.

This feature would allow the person specifying an artifact in the pinned artifact list as being testonly. e.g.

maven_repository_specification(
    name = "maven",
    artifacts = [
        "junit:junit:4.12": { "sha256": "blah", testonly = True },
    ],
)

Thus, one could not use the @maven//junit from any rule that was not also testonly=true. Test libraries (building test infrastructure) could of course safely mark themselves testonly=true.

This allows bazel to prevent leaking test deps into production code, by complaining loudly at build analysis time.

Note, the whole chain of deps connected up must be testonly, so if this feature is used for, say, Hamcrest (upon which junit relies) then junit must also be so marked. So it is an "infectious" feature. But that's by design.

Implements part of #52

Add support for fetching parent poms for inherited project metadata

POMs often don't declare versions, but delegate version management either to segments (#14) or sections (#15). These can be inherited, so fetching the parents is necessary to fully represent the metadata.

While skylark can't do recursion or infinite while loops, the likelihood of parent chains longer than 10 (or heck, 100) is low, and fetching POMs is relatively cheap, and duplicate calls should be cached, so just doing an iterative algorithm up to a fixed limit of iterations should suffice.

Ultimately we need to have a structure that stores the metadata information, but merges the parent metadata first.

com.google.cloud:google-cloud-core:1.37.0 has upstream deps which display with "None" version

Describe the bug
When downloading com.google.cloud:google-cloud-core:1.37.0, the standard dependencies-missing-from-list error lists some of the google cloud deps as having None as their version.

The pom seems to have versions specified in the parent pom's dependencyManagement section, so this may be a bug in the integration's section merging logic.

To Reproduce
Steps to reproduce the behavior:

  1. git clone http://github.com/cgruber/issue_repro
  2. cd issue_repro/bazel_maven_repository_missing_versions
  3. run bazel build //...
  4. Examine the deps list error and look for deps with version None

Expected behavior
The missing deps should be listed with the deps that are actually in the maven metadata.

Environment (please complete the following information):

  • OS: Mac OS
  • Bazel version

    Bazelisk version: v1.0
    Build label: 0.28.1
    Build target: bazel-out/darwin-opt/bin/src/main/java/com/google/devtools/build/lib/bazel/BazelServer_deploy.jar
    Build time: Fri Jul 19 15:22:50 2019 (1563549770)
    Build timestamp: 1563549770
    Build timestamp as int: 1563549770

  • Rules version: 1.0

Error downloading support-annotations-27.1.1.pom from https://repo1.maven.org/maven2

Describe the bug
When adding "com.android.support:support-annotations:27.1.1" to the artifacts list, the download task failed.

To Reproduce
Steps to reproduce the behavior:

  1. Add the following to WORKSPACE
android_sdk_repository(
    name = "androidsdk",
    api_level = 28, 
    build_tools_version = "28.0.3",
)

android_ndk_repository(
    name = "androidndk",
)

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

MAVEN_REPOSITORY_RULES_VERSION = "1.0-rc6"
MAVEN_REPOSITORY_RULES_SHA = "e29e57beef25e771acbeddd17b9b9c53d40f81a438018197110f5c2896682e3b"
http_archive(
    name = "maven_repository_rules",
    urls = ["https://github.com/square/bazel_maven_repository/archive/%s.zip" % MAVEN_REPOSITORY_RULES_VERSION],
    type = "zip",
    strip_prefix = "bazel_maven_repository-%s" % MAVEN_REPOSITORY_RULES_VERSION,
    sha256 = MAVEN_REPOSITORY_RULES_SHA,
)
load("@maven_repository_rules//maven:maven.bzl", "maven_repository_specification")
maven_repository_specification(
    name = "maven",
    artifacts = { 
        "com.github.bumptech.glide:glide:4.9.0:aar": { "insecure": True },
        "com.github.bumptech.glide:gifdecoder:4.9.0:aar": { "insecure": True },
        "com.github.bumptech.glide:disklrucache:4.9.0": { "insecure": True },
        "com.github.bumptech.glide:annotations:4.9.0": { "insecure": True },
        "com.android.support:support-annotations:27.1.1": { "insecure": True },
        "com.android.support:support-fragment:27.1.1:aar": { "insecure": True },
        "com.android.support:animated-vector-drawable:27.1.1:aar": { "insecure": True },
    },  
)
  1. Set up these conditions
  2. run bazel test //some/package
  3. See error
java.io.IOException: Error downloading [https://repo1.maven.org/maven2/com/android/support/support-annotations/27.1.1/support-annotations-27.1.1.pom] to /private/var/tmp/_bazel_senpo/b85ab36fb6cc4dbc6170d0fbd5034ea1/external/maven/com/android/support/support-annotations-27.1.1.pom: GET returned 404 Not Found and referenced by '//java/com/awesome/android/helloworld:helloworld'.

Expected behavior
A clear and concise description of what you expected to happen.

Environment (please complete the following information):

  • OS: [e.g. iOS] Mac
  • Bazel version 0.23.0
  • Rules version 1.0-rc6

Additional context
Add any other context about the problem here.

Repository resolution failure will fail build

Given the following:

    maven_repository_specification(
        name = "android_core_deps",
        artifacts = {
            "io.reactivex.rxjava2:rxjava:2.2.6": {"insecure": True},
        },
        repository_urls = [
            "https://bintray.com/bintray/jcenter",
            "https://maven.google.com",
            "https://repo1.maven.org/maven2",
        ],
    )

It will fail with:

INFO: Invocation ID: 24e5f05d-e5bd-4884-a716-567a7ad0330b
INFO: SHA256 (https://bintray.com/bintray/jcenter/io/reactivex/rxjava2/rxjava/2.2.6/rxjava-2.2.6.pom) = 98acc4b930c5c5d6a140ee22caa04f226645238c8ada543c9e9293d5935b6ec4
ERROR: Analysis of target '//android:publish_snapshot' failed; build aborted: no such package '@android_core_deps//io/reactivex/rxjava2': Traceback (most recent call last):
	File "/private/var/tmp/_bazel_steeve/309ac22c92b055a3e3f78825fe63406b/external/maven_repository_rules/maven/maven.bzl", line 192
		_get_dependencies_from_pom_files(ctx, artifact)
	File "/private/var/tmp/_bazel_steeve/309ac22c92b055a3e3f78825fe63406b/external/maven_repository_rules/maven/maven.bzl", line 143, in _get_dependencies_from_pom_files
		_get_inheritance_chain(ctx, _fetch_pom(ctx, artifact))
	File "/private/var/tmp/_bazel_steeve/309ac22c92b055a3e3f78825fe63406b/external/maven_repository_rules/maven/maven.bzl", line 122, in _get_inheritance_chain
		poms.parse(xml_text)
	File "/private/var/tmp/_bazel_steeve/309ac22c92b055a3e3f78825fe63406b/external/maven_repository_rules/maven/poms.bzl", line 228, in poms.parse
		xml.parse(xml_text)
	File "/private/var/tmp/_bazel_steeve/309ac22c92b055a3e3f78825fe63406b/external/maven_repository_rules/maven/xml.bzl", line 267, in xml.parse
		fail(("Unbalanced xml tree: closing t...)))
Unbalanced xml tree: closing tag </head> incorrectly matched with <meta> in xml
<!doctype html>
<!--[if IE 9]><html class="lt-ie10" lang="en" ><![endif]-->
<html class="no-js" lang="en">

That is because https://bintray.com/bintray/jcenter/io/reactivex/rxjava2/rxjava/2.2.6/rxjava-2.2.6.pom doesn't exist. Perhaps it should gracefully go to the other repo ?

xml parsing fails on CDATA sections.

parsing pom xml fails on <![CDATA[ ... ]]> sections, such as are in the pom for javax.annotation:javax.annotation-api:1.2 (as part of the javadoc configuration, which we don't care about).

The parser should consume the CDATA sections in an orderly way, though we don't need to keep them around (technically they should be kept, as they're valid xml entities, but we wont' need them, so we drop them on the floor.

Restructure pom processing into a (constrained) parser, with a clean structure or dict output

The existing logic just cuts up deps sections and processes those into target lists, but this short-cut will make adding deps info from other pom sections harder, as well as merging from parents.

The logic really should take a pom, parse out the various sections (properties, parent, dependencyManagement, and dependencies) and create either a struct() or a well-formed dictionary out of these metadata, so that (a) the parent metadata (similarly structured) can be cleanly merged into an "effective pom", and then after that merging (b) the inferred deps information can be applied to the dependency list. The first step is this one, getting a clean dictionary/struct from a pom.

Remove legacy use of both deps and exports in _maven_jvm_artifact

Presently, in _maven_jvm_artifact, the jar/aar import statements end up setting both deps and exports. This is due to a legacy usage of exports in the very early versions of this code in square. Once 1.0 is released, and we've migrated internally, we need to clean this up. It's pure backwards-compatibility stuff.

Support Jetpack's jettifier, for deps/bytecode rewriting.

Jetpack comes with a tool, deeply integrated into Gradle, called Jettifier. The net effect of the integration is that if you have this deps chain: your_lib -> third-party-lib -> android_support_lib a few things happen:

  1. You start using androix_support_libs instead in your direct deps.
  2. You start using the newer equivalent APIs in your code.
  3. You enable Jettifier, which:
    1. rewrites any deps on the older android_support_libs artifacts to the newer androidx ones transparently, inside gradle, intercepting the normal deps resolution logic.
    2. takes any artifacts and rewrites their code references in the bytecode to point at the new apis supplied by androidx/jetpack.

There is a standalone tool (jettifier_standalone) which can do the jar rewriting, and there is a published list of equivalencies which the tool honors, which can be consumed by any deps management. But the bazel_maven_repository rules are in the ecological niche of gradle's deps management here, so we need a feature to do the jetpack-required deps rewriting (probably taking in the file they publish, since a universal rewriting feature is probably useful, and can be useful for large-scale replacement of other kinds, without particularly extra work. However, the big deal will be incorporating the jettifier tool so that, if enabled, it will post-process downloaded jars appropriately.

Support (shallow) dependency exclusions

Some poms specify their deps overly broadly or otherwise differently than needed. Rather than being forced to use the build_snippet, having a simple exclusion mechanism should be supported.

Two formats should be supported - qualified and local. Qualified would be "group_id:artifact_id" where local would be ":artifact_id" (which would implicitly add the current artifact's group_id.

something like:

maven_bazel_repository(
    name = "maven",
    artifacts = {
        "foo.bar:baz:1.0": {
            "sha256": "1235....",
            "excludes": [":qaf", "com.google.guava:guava"],
        }
    },
)

This would ensure that foo.bar:qaf and com.google.guava:guava are not included in the list of converted direct dependencies. Any others will be included as normal. If a dependency is excluded that does not exist in the pom specification of foo.bar.baz:1.0 then that will be reported as an error.

Make a redirect system to allow non-snippet interdiction of generated targets

Right now, to configure an annotation processor or kt_compiler_plugin you need to use a build snippet, voiding the warranty (so to speak) on the other features of BMR, such as deps computation. You also have to fiddle with group-level substitutions, etc.

A more powerful approach would be to allow a redirection, in the following sense. If you, say, put in:

   "com.google.dagger:dagger.2.16": { "redirect": "//third_party/dagger" }

The following would happen:

  • the regular artifact raw_jvm_import would be generated under an alternate name iwth a standard convention
    • e.g. "dagger__original"
    • Unless modified, the visibility of the target would be the present package and the redirect target.
  • All local (in same generated package build file) references to that target would point at "dagger_original"
  • In place of the normal target location (e.g. @maven//com/google/dagger:dagger) would be an alias to the
    redirection target.

Thus, one could define in //third_party/dagger:

java_library(
    name = "dagger",
    exported_plugins = [":compiler_plugin"],
    exports = [ "@maven//com/google/dagger:dagger__original" ],
    visibility = ["//visibility:public"],
)

java_plugin(
    name = "compiler_plugin",
    processor_name = "a.b.c.whatever",
    deps = [ "@maven//com/google/dagger:dagger_compiler" ],
)

And any uses outside of the external workspace "maven"'s com/google/dagger package could reference @maven//com/google/dagger and the alias would point them at //third_party/dagger.

A subtask of #52

Support username/password auth pulled from .m2/settings.xml

In issue bazelbuild/bazel#7443 it is mentioned that the newer jvm_maven_import_external does not support user/password authentication in fetching using definitions in .m2/settings.xml.

We should look into this as an option, for cases where non-ssh authentication to non-public repositories is required (e.g., https with username/password).

Provide a target substitution mechanism (e.g., in com/google/dagger let deps on :dagger dep on :dagger_api)

Given that we allow build segment rewriting per-artifact, to allow things like the dagger dep to carry an exported_plugin, but also because we are about to have automated dependency discovery from pom files, this leads to odd cases. For instance, where (say), our wrapped :dagger target exports :dagger_plugin as an exported_plugin, and that depends on :dagger_compiler, the inferred deps list (from the pom) of :dagger_compiler would naively depend on :dagger. This creates a cycle. At present, we can verbosely just replace any build snippets that are affected, it becomes gross for something like dagger, where all the other :dagger-* packages depend on the core :dagger apis.

Having a mechanism to, for instance, say "for all of com/google/dagger/BUILD" any inferred deps on com.google.dagger:dagger should reference :dagger_api instead of :dagger we can specify these surgical changes more tersely.

I think it only makes sense to do these substitutions on a per-build-file basis, because it's really only to address these intra-BUILD situations, and we don't want ALL things to depend on :dagger_api, because it's only the implementation details we need to swap. We created the :dagger wrapper target precisely to enrich it, so we don't want to steer other consumers of dagger to the naive jar that doesn't have the exported_plugin.

So something like:

maven_repository_specification(
    name = "maven",
    artifacts = [ ... ],
    build_substitutions = {
        "com.google.dagger:dagger": DAGGER_TARGET_REPLACEMENT_SNIPPET,
    },
    target_substitutions = {
        "com.google.dagger": { "dagger": "dagger_api" }
    },

This means that, in the context of this group (and the package @maven/com/google/dagger and it's BUILD file) treat any dependency on dagger to be a dependency on dagger_api.

using maven_repository_rules breaks grpc-java rules

Describe the bug
Using maven_repository_rules breaks the grpc-java rules.

To Reproduce
repro repo: https://github.com/asv/bazel_maven_repository_bug

Steps to reproduce the behavior:

  1. git clone https://github.com/asv/bazel_maven_repository_bug
  2. run bazel build //:bug
  3. See error
ERROR: /home/asmirnov/.cache/bazel/_bazel_asmirnov/b53af1ba463655671a57e4aded67eb7f/external/io_grpc_grpc_java/BUILD.bazel:28:1: no such package '@com_google_code_findbugs_jsr305//jar': BUILD file not found on package path and referenced by '@io_grpc_grpc_java//:java_grpc_library_deps__do_not_reference'
ERROR: Analysis of target '//:bug' failed; build aborted: no such package '@com_google_code_findbugs_jsr305//jar': BUILD file not found on package path
INFO: Elapsed time: 0.463s
INFO: 0 processes.
FAILED: Build did NOT complete successfully (1 packages loaded, 10 targets configured)

Expected behavior
Target successfuly builded.

Environment (please complete the following information):

  • Ubuntu 18.04.2 LTS
  • Bazel 0.24.1
  • Rules 1.0

rule fails if the groupId has an element "build" (e.g. com.android.tools.build) on case-insensitive filesystems.

If you have a groupId containing build, e.g. com.android.tools.build, bazel will fail because it will try to read a BUILD file from the external/maven/com/android/tools folder. But build/ there is a folder, not a file. The file is missing, and couldn't be written anyway, as it would conflict with the folder.

Convert to using BUILD.bazel files instead of BUILD files to avoid this conflict.

Support a templating api for alternative target representation

a.k.a smarter snippets.

In cases where you want to substitute the build snippet, but don't want to lose all the computation, we should formalize the values passed to the internal default snippet templates so that external templates can make use of them. This would either mean just making build_snippets itself support these template values (you can use any or all of or none of them, but it'll flag unsupported template substitutions) or make a new separate feature for this, preserving the "override the warranty, you get what you paid for" raw text substitution for emergencies.

Probably making a new feature is preferable, and then seeing if the old feature can be eliminated would be the least disruptive.

The template vocabulary will need to be carefully considered - it should have at least all of the fields used by kramer today, but may need to have more, in cases of non-aar/jar/bundle alternatives.

A key use-case here is kotlin, where one might want to not use raw_jvm_import but substitute kt_jvm_import, or do something similar, across a large group of artifacts, where using string replacement and taking over all the deps management would obviate the value of the BMR system itself.

A subtask of #52

Make the configuration for an artifact a struct (using a well defined validating function)

Right now, the config is an arbitrary dictionary. Making it a struct (or built through a function with well defined parameters) can allow it to be a bit safer. Instead of:

"foo.bar:baz:1.0": {
    "sha256": "abcdef...",
    "build_snippet": SOME_SNIPPET,
}

You'd do

"foo.bar:baz:1.0": config(
    sha256 = "abcdef...",
    build_snippet = SOME_SNIPPET,
),

Using this would allow us to change the underlying mechanism for holding the data and communicating it, immunizing the user from those changes, as well as having more obvious errors earlier (can't call that function with an unknown param, for instance).

Incrementally update dependencies

Is your feature request related to a problem? Please describe.
I've been trying to get bazel_maven_repository working in my project for probably close to 12 hours now, and it's frustrating that whenever I change my dependencies at all, it has to regenerate the entire list which takes nearly 10 minutes. I'm considering moving back to gradle because I know once I get this working I'm never going to touch my dependency list again.

Describe the solution you'd like
I'd like to see an update to the version of a single dependency regenerate only the bzl files generated for that specific dependency. I don't see the reason to go back out to the internet and re-fetch all 100+ dependencies.

Describe alternatives you've considered
It's possible it could also be parallelized? At the end of the day I just don't want to be waiting 10 minutes to see if I did successfully fix all the typos, rinse and repeat for hours on end.

Additional context
I'm also curious how updates are handled internally at Square. This seems like a pretty big oversight for a tool like this. Do you have a local cache of all the artifacts you use or something like that to speed things along?

zip END header not found

Describe the bug
When using bazel_maven_repository, the build fails to compile with the error in the title: zip END header not found. Using rules_jvm_external in the same codebase is able to compile.

To Reproduce
Steps to reproduce the behavior:

  1. git clone [email protected]:changusmc/issue_maven_repository_rules.git
  2. cd issue_maven_repository_rules
  3. bazel build //...

Expected behavior
Build succeeds and jar is produced

Environment (please complete the following information):

  • OS: MacOSX 10.14.6 (18G95)
  • Bazel: 1.0.0
  • Rules: See repo

Additional context
Full error message can be seen in the README

Make the fetch repo use kramer so it can participate in the .m2 cache.

Kramer supports ~/.m2/repository (configurable) but the fetch-repo presently uses repository_ctx.download() which does not, and in the case of insecure (no sha) fetches, a common case, especially during developement, this means a re-download even when bits have been down for a while.

Also, by using kramer, the path is opened to snapshot support (#5)

Remove "cat" usage for reading pom files

This is critical to supporting Windows, and the repository_ctx.read_file() function is now in HEAD and this feature should be used instead of shelling out to cat as soon as it's in a stable release.

Problem with transitive compilation deps being available during compile

If you include, say, rxjava2 and use a type (say, Flowable) which relies on API from reactive-streams at compile time (Publisher), but you only have @maven//io/reactivex/rxjava2:rxjava in your deps, the compilation will fail on inability to access the Publisher class.

Additionally, if you do a bazel sync in intellij in a project doing the above, reactive-streams will not show up in the deps list.

In #48 we introduced a custom importer, but also removed an older hack where deps were also exported. This uncovered a problem with propagating transitive compilation deps which needs to be fixed. exported= was the wrong semantic, as you're not wanting to have it as if you declared it, and it basically blows away strict deps. But it does need to be on the compile-time classpath. We need to figure out how to forward such things appropriately.

Make classes.jar more IDE friendly

Right now, you end up with a lot of "classes-.jar" in your external workspace list in intellij. Renaming classes.jar to something more verbose can help disambiguate them in the external libraries list. Either artifactId-version-classes.jar (e.g. guava-18.0-classes.jar) would improve their visual appearance. possibly even adding some portion fo the group id to them. Definitely should do the former, need to consider the latter (adding group disambiguation).

Work out how to cleanly avoid kotlin/ijar issues with maven deps

Right now, we use java_imports under the hood to bring in jars. Such jars are then processed by ijar which doesn't respect kotlin's inline instructions, and strips out method bodies even when they're logically part of the ABI (which for kotlin inline functions they must be). There's an issue (bazelbuild/bazel#4549) to track it, but in the mean-time, bringing in maven deps via java_import is a problem.

What I would rather not do is add a dependency on the kotlin rules, since I don't want version variance in those rules to affect users of this maven integration.

One workaround is to just in your own artifacts declaration, include a snippet that replaces mvn_jvm_artifact with a gross direct kt_jvm_import statement pointing at the .jar file. It's gross, and for a large set of libraries, it would be a bit burdensome (though we can make a prefab template that assumes you use the rules_kotlin stuff in a typical way, but which you cut-and-paste if you don't).

The more correct thing would be to use kt_jvm_import under the hood, but this would create some dependencies on the kotlin rules that feel wrong. It could be mitigated with bind() use, but that's got its own set of problems.

Teh right answer is for the above-mentioned issue to be resolved, and ijar to do the right thing for kotlin binaries - now that Google is using kotlin internally, there's some incentive to have that happen, so maybe the build_snippet workaround is sufficient.

Support source jars if they exist.

We should be downloading source jars (but also have an environmental shut-off for ijars, so no time need be wasted on CI build and other builds). Unsure if it's possible to have bazel repository rules "download if possible" without failing the build if the file can't be found/downloaded. If not, then this needs to be a per-artifact config (governed by the global disabling feature). If so, then we can opportunistically download sources. Most sources are published, so it's less of an issue if they get missed.

Also need to check if the sources are ever consumed and compiled and executed, to see if this is a security risk vector.

Allow to print complete transitive sha256 list

Bazel repository cache will only function only if a sha256 is provided.

While we can provide sha256 for top level packages, there is a fundamental issue with inner, transitive packages.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.