I have a mvn_package
rule prototype in this pull request. The points you raised are crucial; it takes a bit to tame Maven's conventions and expectations in Bazel's stricter action execution environment.
Maven expects to be executed from a directory containing the pom.xml
You can use the -f
flag to specify the location of the pom.xml
.
e.g. mvn package -f %s -DskipTests -DbazelOutputDir=%s -Pbazel" % (ctx.file.pom_xml.dirname,output_jar.dirname)
Maven writes output to a subdirectory of the input filetree
I created a custom profile in the pom.xml
to for a custom Maven define, and passed that define the above command line:
<profiles>
<profile>
<id>bazel</id>
<build>
<directory>${bazelOutputDir}</directory>
</build>
</profile>
</profiles>
Maven is happiest when settings.xml contains absolute paths (to local maven repository for the .war, stored in git)
This is unfortunate, and you'd have to find a way around it or disable sandboxing :(
Maven uses $HOME/.m2 for caching
As long as the Bazel action inputs and outputs are declared correctly in the Starlark rule, you should not need to interact with Maven's .m2 cache directory since Bazel has its own action cache.
For completeness, here's the proof of concept of a Starlark rule to package a .jar, so you'll need to maybe tweak it for a .war:
def _mvn_package_impl(ctx):
inputs = []
inputs.extend(ctx.files.srcs)
inputs.append(ctx.file.pom_xml)
output_jar = ctx.actions.declare_file("target/%s-%s.jar" % (ctx.attr.artifact_id, ctx.attr.version))
target_dir = ctx.actions.declare_directory("target")
outputs = [output_jar, target_dir]
# -Djar.finalName=custom-jar-name
ctx.actions.run_shell(
inputs = inputs,
outputs = outputs,
mnemonic = "MvnPackage",
progress_message = "Running 'mvn package' for %s" % output_jar.short_path,
command = "mvn package -f %s -DskipTests -DbazelOutputDir=%s -Pbazel" % (ctx.file.pom_xml.dirname,output_jar.dirname),
)
return [
DefaultInfo(
files = depset(outputs),
),
JavaInfo(
output_jar = output_jar,
compile_jar = output_jar,
),
]
mvn_package = rule(
implementation = _mvn_package_impl,
attrs = {
"srcs": attr.label_list(allow_files = True, allow_empty = False),
"pom_xml": attr.label(allow_single_file = True, mandatory = True),
"artifact_id": attr.string(mandatory = True),
"group_id": attr.string(mandatory = True),
"version": attr.string(mandatory = True),
},
)
and using the above rule in a BUILD file:
load("@rules_jvm_external//:mvn.bzl", "mvn_package")
mvn_package(
name = "hello_maven",
srcs = [
"//src/main/java/hello:srcs",
],
pom_xml = "//:pom.xml",
group_id = "org.springframework",
artifact_id = "gs-spring-boot",
version = "0.1.0",
)
java_binary(
name = "hello_maven_app",
runtime_deps = [":hello_maven"],
main_class = "hello.Application",
)