blob: d0f2e9586c7468673a83bab30fc823ea794aa640 [file] [log] [blame]
load(":coverage.bzl", "coverage_java_test")
load(":functions.bzl", "create_java_compiler_args_srcs", "create_option_file", "explicit_target", "label_workspace_path", "workspace_path")
load(":groovy.bzl", "groovy_impl")
load(":kotlin.bzl", "kotlin_impl")
load(":utils.bzl", "fileset", "java_jarjar", "singlejar")
# This is a custom implementation of label "tags".
# A label of the form:
# "//package/directory:rule[tag1, tag2]"
# Gets split up into a tuple containing the label, and the array of tags:
# ("//package/directory:rule", ["tag1", "tag2"])
# Returns the split up tuple.
def _get_label_and_tags(label):
if not label.endswith("]"):
return label, []
rfind = label.rfind("[")
if rfind == -1:
print("Malformed tagged label: " + label)
return label, []
return label[:rfind], [tag.strip() for tag in label[rfind + 1:-1].split(",")]
# Returns the paths of the given files relative to any of the roots. Were
# files is a list of File objects, and roots is a list strings represnting
# paths of directories relative to the package
# If a file is not found to be inside any of the given roots, it is ignored.
def relative_paths(ctx, files, roots):
package_prefixes = ctx.attr.package_prefixes
translated_package_prefixes = {root: prefix.replace(".", "/") for (root, prefix) in package_prefixes.items()}
paths = []
for file in files:
for root in roots:
path = label_workspace_path(ctx.label) + "/" + root
if file.path.startswith(path):
relpath = file.path[len(path) + 1:]
if root in translated_package_prefixes:
relpath = translated_package_prefixes[root] + "/" + relpath
paths.append((relpath, file))
return paths
def resources_impl(ctx, name, roots, resources, resources_jar):
zipper_args = ["c", resources_jar.path]
zipper_files = "".join([k + "=" + v.path + "\n" for k, v in relative_paths(ctx, resources, roots)])
zipper_list = create_option_file(ctx, name + ".res.lst", zipper_files)
zipper_args += ["@" + zipper_list.path]
ctx.action(
inputs = resources + [zipper_list],
outputs = [resources_jar],
executable = ctx.executable._zipper,
arguments = zipper_args,
progress_message = "Creating zip...",
mnemonic = "zipper",
)
def accumulate_provider(provider, deps, runtime, compile_time):
deps += [provider]
runtime += provider.transitive_runtime_jars
compile_time += provider.full_compile_jars
return deps, runtime, compile_time
def _iml_module_jar_impl(
ctx,
name,
roots,
java_srcs,
kotlin_srcs,
groovy_srcs,
form_srcs,
resources,
output_jar,
java_deps,
form_deps,
exports,
friends,
transitive_compile_time_jars,
transitive_runtime_jars):
jars = []
sourcepath = []
forms = []
java_jar = ctx.actions.declare_file(name + ".java.jar") if java_srcs else None
kotlin_jar = ctx.actions.declare_file(name + ".kotlin.jar") if kotlin_srcs else None
groovy_jar = ctx.actions.declare_file(name + ".groovy.jar") if groovy_srcs else None
# Groovy
if groovy_srcs:
groovy_deps = [java_jar] if java_jar else []
groovy_deps += [kotlin_jar] if kotlin_jar else []
groovy_deps += list(transitive_runtime_jars)
stub_jar = ctx.actions.declare_file(name + ".groovy_stubs.src.jar")
groovy_impl(ctx, roots, groovy_srcs, groovy_deps, transitive_runtime_jars, groovy_jar, stub_jar)
sourcepath += [stub_jar]
jars += [groovy_jar]
# Kotlin
kotlin_providers = []
if kotlin_srcs:
kotlin_providers += [kotlin_impl(
ctx,
name,
roots,
java_srcs,
kotlin_srcs,
transitive_compile_time_jars,
ctx.attr.package_prefixes,
kotlin_jar,
friends,
)]
jars += [kotlin_jar]
# Java
if java_srcs:
compiled_java = ctx.actions.declare_file(name + ".pjava.jar") if form_srcs else java_jar
formc_input_jars = [compiled_java] + ([kotlin_jar] if kotlin_jar else [])
java_provider = java_common.compile(
ctx,
source_files = java_srcs,
output = compiled_java,
deps = java_deps + kotlin_providers,
javac_opts = java_common.default_javac_opts(ctx, java_toolchain_attr = "_java_toolchain"),
java_toolchain = ctx.attr._java_toolchain,
host_javabase = ctx.attr._host_javabase,
sourcepath = sourcepath,
)
# Forms
if form_srcs:
forms += relative_paths(ctx, form_srcs, roots)
args, option_files = create_java_compiler_args_srcs(
ctx,
[form.path for form in form_srcs] + [k + "=" + v.path for k, v in form_deps] + [f.path for f in formc_input_jars],
java_jar,
transitive_runtime_jars,
)
ctx.action(
inputs = [v for _, v in form_deps] + form_srcs + formc_input_jars + option_files + transitive_runtime_jars.to_list(),
outputs = [java_jar],
mnemonic = "formc",
arguments = args,
executable = ctx.executable._formc,
)
jars += [java_jar]
if form_srcs and not java_srcs:
fail("Forms only supported with java sources")
# Resources
if resources:
resources_jar = ctx.actions.declare_file(name + ".res.jar")
resources_impl(ctx, name, roots, resources, resources_jar)
jars += [resources_jar]
ctx.action(
inputs = jars,
outputs = [output_jar],
mnemonic = "module",
arguments = [output_jar.path] + [jar.path for jar in jars],
executable = ctx.executable._singlejar,
)
providers = []
providers += [java_common.create_provider(
compile_time_jars = [output_jar],
runtime_jars = [output_jar],
transitive_compile_time_jars = [output_jar] + list(transitive_compile_time_jars),
transitive_runtime_jars = [output_jar] + list(transitive_runtime_jars),
use_ijar = False,
)]
providers += exports
return java_common.merge(providers), forms
def _iml_module_impl(ctx):
names = [iml.basename[:-4] for iml in ctx.files.iml_files if iml.basename.endswith(".iml")]
transitive_data = depset()
java_deps = []
form_deps = []
transitive_runtime_jars = depset(order = "preorder")
transitive_compile_time_jars = depset(order = "preorder")
for this_dep in ctx.attr.deps:
if hasattr(this_dep, "module"):
transitive_data += this_dep.module.transitive_data
form_deps += this_dep.module.forms
if java_common.provider in this_dep:
java_deps, transitive_runtime_jars, transitive_compile_time_jars = accumulate_provider(
this_dep[java_common.provider],
java_deps,
transitive_runtime_jars,
transitive_compile_time_jars,
)
test_java_deps = []
test_form_deps = []
transitive_test_runtime_jars = depset([ctx.outputs.production_jar], order = "preorder")
transitive_test_compile_time_jars = depset([ctx.outputs.production_jar], order = "preorder")
for this_dep in ctx.attr.test_deps:
if java_common.provider in this_dep:
test_java_deps, transitive_test_runtime_jars, transitive_test_compile_time_jars = accumulate_provider(
this_dep[java_common.provider],
test_java_deps,
transitive_test_runtime_jars,
transitive_test_compile_time_jars,
)
if hasattr(this_dep, "module"):
transitive_data += this_dep.module.transitive_data
test_form_deps += this_dep.module.test_forms
test_java_deps, transitive_test_runtime_jars, transitive_test_compile_time_jars = accumulate_provider(
this_dep.module.test_provider,
test_java_deps,
transitive_test_runtime_jars,
transitive_test_compile_time_jars,
)
exports = []
test_exports = []
for export in ctx.attr.exports:
if java_common.provider in export:
exports += [export[java_common.provider]]
if hasattr(export, "module"):
test_exports += [export.module.test_provider]
module_jars = ctx.outputs.production_jar
module_runtime = transitive_runtime_jars
transitive_data += depset(ctx.files.iml_files + ctx.files.data)
main_provider, main_forms = _iml_module_jar_impl(
ctx,
ctx.label.name,
ctx.attr.roots,
ctx.files.java_srcs,
ctx.files.kotlin_srcs,
ctx.files.groovy_srcs,
ctx.files.form_srcs,
ctx.files.resources,
ctx.outputs.production_jar,
java_deps,
form_deps,
exports,
[],
transitive_compile_time_jars,
transitive_runtime_jars,
)
test_provider, test_forms = _iml_module_jar_impl(
ctx,
ctx.label.name + "_test",
ctx.attr.test_roots,
ctx.files.java_test_srcs,
ctx.files.kotlin_test_srcs,
ctx.files.groovy_test_srcs,
ctx.files.form_test_srcs,
ctx.files.test_resources,
ctx.outputs.test_jar,
[main_provider] + test_java_deps,
test_form_deps,
exports + test_exports,
[ctx.outputs.production_jar],
transitive_test_compile_time_jars,
transitive_test_runtime_jars,
)
return struct(
module = struct(
module_jars = module_jars,
module_runtime = module_runtime,
transitive_data = transitive_data,
forms = main_forms,
test_forms = test_forms,
java_deps = java_deps,
test_provider = test_provider,
main_provider = main_provider,
names = names,
),
providers = [main_provider],
)
_iml_module_ = rule(
attrs = {
"iml_files": attr.label_list(
allow_files = True,
allow_empty = False,
mandatory = True,
),
"java_srcs": attr.label_list(allow_files = True),
"kotlin_srcs": attr.label_list(allow_files = True),
"groovy_srcs": attr.label_list(allow_files = True),
"form_srcs": attr.label_list(allow_files = True),
"java_test_srcs": attr.label_list(allow_files = True),
"kotlin_test_srcs": attr.label_list(allow_files = True),
"groovy_test_srcs": attr.label_list(allow_files = True),
"form_test_srcs": attr.label_list(allow_files = True),
"javacopts": attr.string_list(),
"resources": attr.label_list(allow_files = True),
"test_resources": attr.label_list(allow_files = True),
"package_prefixes": attr.string_dict(),
"test_class": attr.string(),
"exports": attr.label_list(),
"roots": attr.string_list(),
"test_roots": attr.string_list(),
"deps": attr.label_list(),
"test_deps": attr.label_list(),
"data": attr.label_list(allow_files = True),
"test_data": attr.label_list(allow_files = True),
"_java_toolchain": attr.label(default = Label("@bazel_tools//tools/jdk:current_java_toolchain")),
"_host_javabase": attr.label(default = Label("@bazel_tools//tools/jdk:current_host_java_runtime")),
"_zipper": attr.label(
default = Label("@bazel_tools//tools/zip:zipper"),
cfg = "host",
executable = True,
),
"_singlejar": attr.label(
default = Label("//tools/base/bazel:singlejar"),
cfg = "host",
executable = True,
),
"_kotlinc": attr.label(
default = Label("//tools/base/bazel:kotlinc"),
cfg = "host",
executable = True,
),
"_bootclasspath": attr.label(
default = Label("//prebuilts/studio/jdk:bootclasspath"),
cfg = "host",
),
"_kotlin": attr.label(
default = Label("//prebuilts/tools/common/kotlin-plugin-ij:Kotlin/kotlinc/lib/kotlin-stdlib"),
allow_files = True,
),
"_groovyc": attr.label(
default = Label("//tools/base/bazel:groovyc"),
cfg = "host",
executable = True,
),
"_groovystub": attr.label(
default = Label("//tools/base/bazel:groovy_stub_gen"),
cfg = "host",
executable = True,
),
"_formc": attr.label(
executable = True,
cfg = "host",
default = Label("//tools/base/bazel:formc"),
allow_files = True,
),
},
fragments = ["java"],
outputs = {
"production_jar": "%{name}.jar",
"test_jar": "%{name}_test.jar",
},
implementation = _iml_module_impl,
)
def _iml_runtime_impl(ctx):
providers = [ctx.attr.iml_module.module.main_provider]
module_runtime = depset()
transitive_data = depset()
names = []
module_jars = None
for dep in ctx.attr.runtime_deps:
if java_common.provider in dep:
providers += [dep[java_common.provider]]
if hasattr(dep, "runtime_info"):
fail("runtime should not depend on runtime")
if hasattr(dep, "module"):
module_runtime += dep.module.module_runtime
module_jars = dep.module.module_jars
transitive_data += dep.module.transitive_data
names = dep.module.names
combined_provider = java_common.merge(providers)
# for name in ctx.attr.iml_module.module.names:
module_runtime += combined_provider.transitive_runtime_jars
return struct(
providers = [combined_provider],
runtime_info = struct(
module_runtime = module_runtime,
module_jars = module_jars,
transitive_data = transitive_data,
names = names,
),
)
_iml_runtime = rule(
attrs = {
"iml_module": attr.label(),
"runtime_deps": attr.label_list(),
},
fragments = ["java"],
implementation = _iml_runtime_impl,
)
def _iml_test_module_impl(ctx):
providers = [ctx.attr.iml_module.module.test_provider]
for dep in ctx.attr.runtime_deps:
if java_common.provider in dep:
providers += [dep[java_common.provider]]
if hasattr(dep, "module"):
providers += [dep.module.test_provider]
combined = java_common.merge(providers)
return struct(
providers = [combined],
)
_iml_test_module_ = rule(
attrs = {
"iml_module": attr.label(),
"runtime_deps": attr.label_list(),
},
fragments = ["java"],
implementation = _iml_test_module_impl,
)
# Macro implementation of an iml_module "rule".
# This rule corresponds to the building artifacts needed to build an IntelliJ module.
# Instantiating this rule looks similar to an .iml definition:
#
# iml_module(
# # The name of the module used to generate the rules
# name = "module_name",
# # A list of directories containing the sources (.iml use directories as oposed to files)
# srcs = ["src/main/java"],
# # The directories with the test sources.
# test_srcs = ["src/test/java"],
# # The directories with the production resources
# resources = ["src/main/resources"]
# # The directories with the test resources
# test_resources = ["src/test/resources"],
# # A tag enhanced list of dependency tags. These dependencies can contain a
# # list of tags after the label. The supported tags are:
# # module: It treats the dependency as a module dependency, making
# # production and test sources depend on each other correctly.
# # test: This dependency is included and available for test sources only.
# deps = [
# "//path/to/module:name[module]",
# "//path/to/libs:junit-4.12[test]",
# "//a/module/only/needed/in/tests:name[module, test]",
# "//a/standard/java/dependency:dep",
# ],
# )
#
# This macro generates the following rules visible to the user:
#
# "module_name": A Java library compiled from the production sources
# "module_name_testlib": A Java library compiled from the test sources
# "module_name_tests": A java test rule that runs the tests found in "libmodule_name_testlib.jar"
def iml_module(
name,
srcs = [],
package_prefixes = {},
test_srcs = [],
exclude = [],
resources = [],
res_zips = [],
test_resources = [],
deps = [],
runtime_deps = [],
test_runtime_deps = [],
manual_test_runtime_deps = [],
visibility = [],
exports = [],
plugins = [],
javacopts = [],
test_data = [],
test_timeout = "moderate",
test_class = "com.android.testutils.JarTestSuite",
test_shard_count = None,
tags = None,
test_tags = None,
back_target = 0,
iml_files = None,
bundle_data = [],
test_main_class = None,
back_deps = []):
if name == "intellij.groovy":
test_srcs = [] # workaround for b/111900968
prod_deps = []
test_deps = []
for dep in deps:
label, label_tags = _get_label_and_tags(dep)
if "test" not in label_tags:
prod_deps += [label]
test_deps += [label]
srcs = split_srcs(srcs, resources, exclude)
split_test_srcs = split_srcs(test_srcs, test_resources, exclude)
_iml_module_(
name = name,
tags = tags,
visibility = visibility,
java_srcs = srcs.javas,
kotlin_srcs = srcs.kotlins,
groovy_srcs = srcs.groovies,
form_srcs = srcs.forms,
resources = srcs.resources,
roots = srcs.roots,
java_test_srcs = split_test_srcs.javas,
kotlin_test_srcs = split_test_srcs.kotlins,
groovy_test_srcs = split_test_srcs.groovies,
form_test_srcs = split_test_srcs.forms,
test_resources = split_test_srcs.resources,
test_roots = split_test_srcs.roots,
package_prefixes = package_prefixes,
javacopts = javacopts,
iml_files = iml_files,
exports = exports,
deps = prod_deps + ["//tools/base/bazel:langtools"],
test_deps = test_deps + ["//tools/base/bazel:langtools"],
data = bundle_data,
test_data = test_data,
test_class = test_class,
)
_iml_runtime(
name = name + "_runtime",
tags = tags,
iml_module = ":" + name,
runtime_deps = runtime_deps + [":" + name],
visibility = visibility,
)
# Only add test utils to other than itself.
test_utils = [] if name == "studio.testutils" else ["//tools/base/testutils:studio.android.sdktools.testutils"]
_iml_test_module_(
name = name + "_testlib",
tags = tags,
iml_module = ":" + name,
testonly = True,
visibility = visibility,
runtime_deps = runtime_deps + test_runtime_deps + [
":" + name + "_runtime",
"//tools/base/bazel:langtools",
] + test_utils,
)
test_tags = tags + test_tags if tags and test_tags else (tags if tags else test_tags)
if test_srcs:
coverage_java_test(
name = name + "_tests",
tags = test_tags,
runtime_deps = manual_test_runtime_deps + [":" + name + "_testlib"],
timeout = test_timeout,
shard_count = test_shard_count,
data = test_data,
jvm_flags = ["-Dtest.suite.jar=" + name + "_test.jar"],
test_class = test_class,
visibility = visibility,
main_class = test_main_class,
)
def split_srcs(src_dirs, res_dirs, exclude):
roots = src_dirs + res_dirs
exts = ["java", "kt", "groovy", "DS_Store", "form", "flex"]
excludes = []
for root in roots:
excludes += [root + "/**/*." + ext for ext in exts]
resources = native.glob(
include = [src + "/**" for src in roots],
exclude = excludes,
)
javas = native.glob([src + "/**/*.java" for src in src_dirs], exclude)
kotlins = native.glob([src + "/**/*.kt" for src in src_dirs], exclude)
groovies = native.glob([src + "/**/*.groovy" for src in src_dirs], exclude)
forms = native.glob([src + "/**/*.form" for src in src_dirs], exclude)
return struct(
roots = roots,
resources = resources,
javas = javas,
kotlins = kotlins,
groovies = groovies,
forms = forms,
)
def _iml_project_impl(ctx):
imls = []
deps = []
inputs = []
module_jars = dict()
module_runtime = dict()
transitive_data = depset()
for dep in ctx.attr.modules:
if hasattr(dep, "runtime_info"):
for name in dep.runtime_info.names:
module_runtime[name] = dep.runtime_info.module_runtime
module_jars[name] = dep.runtime_info.module_jars
transitive_data += dep.runtime_info.transitive_data
if hasattr(dep, "module"):
fail("Don't depend on modules directly: " + str(dep.label))
for dep in ctx.attr.libraries + ctx.attr.modules:
if java_common.provider in dep:
transitive_data += dep[java_common.provider].transitive_runtime_jars
text = ""
transitive_data += module_jars.values()
for name, files in module_runtime.items():
transitive_data += files
text += name + ": " + module_jars[name].path
for file in files:
text += ":" + file.path
text += "\n"
module_info = ctx.new_file(ctx.label.name + ".module_info")
ctx.file_action(
output = module_info,
content = text,
)
artifacts = ""
for dep in ctx.attr.artifacts:
artifacts += dep.label.name + ": " + dep.artifact.path + "\n"
artifact_info = ctx.new_file(ctx.label.name + ".artifact_info")
ctx.file_action(
output = artifact_info,
content = artifacts,
)
outs = [ctx.outputs.win, ctx.outputs.win32, ctx.outputs.mac, ctx.outputs.linux, ctx.outputs.output]
args = [
"--win",
ctx.outputs.win.path,
"--win32",
ctx.outputs.win32.path,
"--mac",
ctx.outputs.mac.path,
"--linux",
ctx.outputs.linux.path,
"--bin_dir",
ctx.var["BINDIR"],
"--gen_dir",
ctx.var["GENDIR"],
"--build",
ctx.file.build.path,
"--tmp",
module_info.path + ".tmp",
"--out",
ctx.outputs.output.path,
"--module_info",
module_info.path,
"--artifact_info",
artifact_info.path,
]
ctx.action(
mnemonic = "Ant",
inputs = [ctx.file.build, module_info, artifact_info] + ctx.files.data + ctx.files.artifacts + list(transitive_data),
outputs = outs,
executable = ctx.executable.ant,
# We cannot enable this yet, because Mac's sandbox throws an error
# execution_requirements = { "block-network" : "1" },
arguments = args,
)
_iml_project = rule(
attrs = {
"modules": attr.label_list(
non_empty = True,
),
"artifacts": attr.label_list(
),
"libraries": attr.label_list(
),
"data": attr.label_list(
allow_files = True,
),
"deps": attr.label_list(
),
"ant": attr.label(
executable = True,
cfg = "host",
),
"build": attr.label(
allow_files = True,
single_file = True,
),
},
outputs = {
"win": "%{name}.win.zip",
"win32": "%{name}.win32.zip",
"mac": "%{name}.mac.zip",
"linux": "%{name}.tar.gz",
"output": "%{name}.log",
},
implementation = _iml_project_impl,
)
def normalize_label(label):
if ":" not in label:
label = label + ":" + label[label.rfind("/") + 1:]
return label
def iml_project(name, modules = [], **kwargs):
normalized_modules = [normalize_label(module) for module in modules]
_iml_project(
name = name,
testonly = True, # since "_testlib" entries are added programmatically to modules list here
modules = [n + "_runtime" for n in normalized_modules] + [n + "_testlib" for n in normalized_modules],
**kwargs
)
def _iml_artifact_impl(ctx):
jars = []
if ctx.files.files:
files_jar = ctx.actions.declare_file(ctx.label.name + ".files.jar")
zipper_args = ["c", files_jar.path]
zipper_files = "".join([d + "/" + f.basename + "=" + f.path + "\n" for d, f in zip(ctx.attr.dirs, ctx.files.files)])
zipper_list = create_option_file(ctx, ctx.label.name + ".files.lst", zipper_files)
zipper_args += ["@" + zipper_list.path]
ctx.action(
inputs = ctx.files.files + [zipper_list],
outputs = [files_jar],
executable = ctx.executable._zipper,
arguments = zipper_args,
progress_message = "Creating files artifact jar...",
mnemonic = "zipper",
)
jars += [files_jar]
for module in ctx.attr.modules:
if java_common.provider in module:
jars += list(module[java_common.provider].compile_jars)
ctx.action(
inputs = jars,
outputs = [ctx.outputs.artifact],
mnemonic = "artifact",
arguments = [ctx.outputs.artifact.path] + [jar.path for jar in jars],
executable = ctx.executable._singlejar,
)
return struct(artifact = ctx.outputs.artifact)
_iml_artifact = rule(
attrs = {
"dirs": attr.string_list(),
"files": attr.label_list(allow_files = True),
"modules": attr.label_list(),
"_zipper": attr.label(
default = Label("@bazel_tools//tools/zip:zipper"),
cfg = "host",
executable = True,
),
"_singlejar": attr.label(
default = Label("//tools/base/bazel:singlejar"),
cfg = "host",
executable = True,
),
},
outputs = {
"artifact": "%{name}.jar",
},
implementation = _iml_artifact_impl,
)
def iml_artifact(name, dirs = {}, modules = [], **kwargs):
# Reverse the map so we can use a label to string dict
files = dict()
for d, fs in dirs.items():
for f in fs:
files[f] = d
dir_list = []
file_list = []
for d, fs in dirs.items():
for f in fs:
dir_list += [d]
file_list += [f]
_iml_artifact(
name = name,
dirs = dir_list,
files = file_list,
modules = modules,
**kwargs
)