在bazel java测试中将自定义文件夹添加到类路径
我正在尝试将一个大型代码库从maven迁移到bazel,我发现一些测试写入在bazel java测试中将自定义文件夹添加到类路径,java,maven,bazel,Java,Maven,Bazel,我正在尝试将一个大型代码库从maven迁移到bazel,我发现一些测试写入target/classes和target/testclasses,而生产代码将其作为类路径上的资源读取。这是因为maven surefire/failsafe默认从模块目录运行,并将target/classes和target/test classes添加到类路径中。 对于我来说,要迁移这个庞大的代码库,唯一合理的解决方案是创建target、target/classes和target/testclasses文件夹,并将最后
target/classes
和target/testclasses
,而生产代码将其作为类路径上的资源读取。这是因为maven surefire/failsafe默认从模块目录运行,并将target/classes
和target/test classes
添加到类路径中。
对于我来说,要迁移这个庞大的代码库,唯一合理的解决方案是创建target、target/classes和target/testclasses文件夹,并将最后两个添加到测试的类路径中。关于如何实现这一点有什么想法吗
如果您能告诉测试在哪里写入这些文件(如果
目标/类
和目标/测试类
是硬编码的),然后将测试运行转换为genrule
,那就谢谢了,然后,您可以将genrule的输出指定为生产二进制文件的*\u binary
规则的data
。我解决了第一部分,创建了目录。我仍然不知道如何将后两个添加到类路径中
从开始,我将其修改为
_OUTPUT = """import org.junit.runners.Suite;
import org.junit.runner.RunWith;
import org.junit.BeforeClass;
import java.io.File;
@RunWith(Suite.class)
@Suite.SuiteClasses({%s})
public class %s {
@BeforeClass
public static void setUp() throws Exception {
new File("./target").mkdir();
}
}
"""
_PREFIXES = ("org", "com", "edu")
# ...
我添加了@BeforeClass设置方法
我将其作为junit.bzl存储到项目中的第三方目录中
然后在生成文件中
load("//third_party:junit.bzl", "junit_tests")
junit_tests(
name = "my_bundled_test",
srcs = glob(["src/test/java/**/*.java"]),
data = glob(["src/test/resources/**"]),
resources = glob(["src/test/resources/**"]),
tags = [
# ...
],
runtime_deps = [
# ...
],
],
deps = [
# ...
],
)
现在,测试本身用一个setUp方法包装,它将为我创建一个目录。之后我不会删除它们,这可能是一个好主意
我需要目录中的测试资源(与bazel默认提供的jar文件相反)的原因是,我的测试将URI传递给
新文件输入流(新文件(URI))
。如果文件位于JAR中,URI将是file:/path/to/my.JAR/my.file
和测试的其余部分无法使用此类URI。另一种方法。不要生成测试套件,而是创建一个自定义javaagent和一个自定义类加载器。使用jvm\u标志来设置和配置它
javaagent有一个premain方法。这听起来像是一个自然的地方,可以在常规的main方法之前完成一些事情,即使它们与类插装、调试、覆盖率收集或javaagent的任何其他常见用途无关
自定义javaagent读取系统属性extra.dirs
,并创建其中指定的目录。然后,它读取属性extra.link.path
,并按照其中的指定创建符号链接,这样我就可以将资源放置在测试期望的位置,而不必复制它们
类加载器是必需的,这样我们就可以在运行时修改类路径而无需黑客攻击。最大的优点是该解决方案可以在Java10上运行
自定义类加载器读取系统属性extra.class.path
,并(实际上)在java.class.path
中的内容之前加上它
这样做意味着可以使用标准的bazel规则
建造
工具/构建
工具/ResourceJavaAgent.java
导入java.io.File;
导入java.io.IOException;
导入java.lang.instrument.Instrumentation;
导入java.net.MalformedURLException;
导入java.net.URL;
导入java.net.URLClassLoader;
导入java.nio.file.Files;
导入java.nio.file.Path;
导入java.nio.file.path;
导入java.util.array;
导入java.util.LinkedList;
导入java.util.List;
// https://stackoverflow.com/questions/60764/how-should-i-load-jars-dynamically-at-runtime
公共类ResourceJavaAgent扩展URLClassLoader{
私有最终类加载器父级;
public ResourceJavaAgent(类装入器父级)引发错误的FormedUrlexception{
super(buildClassPath(),null);
this.parent=parent;//我需要父对象作为SecretKeyFactory.getInstance(“PBKDF2WithHmacSHA1”)的备份;
System.out.println(“初始化url类加载器”);
}
私有静态URL[]buildClassPath()引发畸形的异常{
最后一个字符串JAVA\u CLASS\u PATH=“JAVA.CLASS.PATH”;
最后一个字符串EXTRA\u CLASS\u PATH=“EXTRA.CLASS.PATH”;
列表路径=新建LinkedList();
paths.addAll(Arrays.asList(System.getProperty(EXTRA_CLASS_PATH,”).split(File.pathSeparator));
paths.addAll(Arrays.asList(System.getProperty(JAVA_CLASS_PATH,”).split(File.pathSeparator));
URL[]URL=新URL[path.size()];
对于(int i=0;iruntime_classgen_dirs = ":".join([
"target/classes",
"target/test-classes",
])
java_test(
...,
jvm_flags = [
# agent
"-javaagent:$(location //tools:test-agent_deploy.jar)",
"-Dextra.dirs=" + runtime_classgen_dirs,
# classloader
"-Djava.system.class.loader=ResourceJavaAgent",
"-Dextra.class.path=" + runtime_classgen_dirs,
],
,,,,
deps = [
# not runtime_deps, cause https://github.com/bazelbuild/bazel/issues/1566
"//tools:test-agent_deploy.jartest-agent_deploy.jar"
],
...,
)
java_binary(
name = "test-agent",
testonly = True,
srcs = ["ResourceJavaAgent.java"],
deploy_manifest_lines = ["Premain-Class: ResourceJavaAgent"],
main_class = "ResourceJavaAgent",
visibility = ["//visibility:public"],
)
import java.io.File;
import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
// https://stackoverflow.com/questions/60764/how-should-i-load-jars-dynamically-at-runtime
public class ResourceJavaAgent extends URLClassLoader {
private final ClassLoader parent;
public ResourceJavaAgent(ClassLoader parent) throws MalformedURLException {
super(buildClassPath(), null);
this.parent = parent; // I need the parent as backup for SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
System.out.println("initializing url classloader");
}
private static URL[] buildClassPath() throws MalformedURLException {
final String JAVA_CLASS_PATH = "java.class.path";
final String EXTRA_CLASS_PATH = "extra.class.path";
List<String> paths = new LinkedList<>();
paths.addAll(Arrays.asList(System.getProperty(EXTRA_CLASS_PATH, "").split(File.pathSeparator)));
paths.addAll(Arrays.asList(System.getProperty(JAVA_CLASS_PATH, "").split(File.pathSeparator)));
URL[] urls = new URL[paths.size()];
for (int i = 0; i < paths.size(); i++) {
urls[i] = Paths.get(paths.get(i)).toUri().toURL(); // important only for resource url, really: this url must be absolute, to pass getClass().getResource("/users.properties").toURI()) with uri that isOpaque == false.
// System.out.println(urls[i]);
}
// this is for spawnVM functionality in tests
System.setProperty(JAVA_CLASS_PATH, System.getProperty(EXTRA_CLASS_PATH, "") + File.pathSeparator + System.getProperty(JAVA_CLASS_PATH));
return urls;
}
@Override
public Class<?> loadClass(String s) throws ClassNotFoundException {
try {
return super.loadClass(s);
} catch (ClassNotFoundException e) {
return parent.loadClass(s); // we search parent second, not first, as the default URLClassLoader would
}
}
private static void createRequestedDirs() {
for (String path : System.getProperty("extra.dirs", "").split(File.pathSeparator)) {
new File(path).mkdirs();
}
}
private static void createRequestedLinks() {
String linkPaths = System.getProperty("extra.link.path", null);
if (linkPaths == null) {
return;
}
for (String linkPath : linkPaths.split(",")) {
String[] fromTo = linkPath.split(":");
Path from = Paths.get(fromTo[0]);
Path to = Paths.get(fromTo[1]);
try {
Files.createSymbolicLink(from.toAbsolutePath(), to.toAbsolutePath());
} catch (IOException e) {
throw new IllegalArgumentException("Unable to create link " + linkPath, e);
}
}
}
public static void premain(String args, Instrumentation instrumentation) throws Exception {
createRequestedDirs();
createRequestedLinks();
}
}