From 1489f0f4cae3e9247a70e4003ab76bef45c5b986 Mon Sep 17 00:00:00 2001 From: Timothy Klim Date: Mon, 7 Dec 2020 05:02:15 -0800 Subject: [PATCH] Support Scala3 .tasty files **ijar** tool support Kotlin modules by [not stripped out](https://github.com/bazelbuild/bazel/commit/088d8de6584864adaec82712c8ace601404afd0b) files in `META-INF/*.kotlin_module`. This PR add support for new Scala 3 (and Scala 2.13.4+) [TASTy](https://dotty.epfl.ch/docs/reference/metaprogramming/toc.html) format to have ability to use Scala 3 modules. Closes #12529. PiperOrigin-RevId: 346068234 --- third_party/ijar/ijar.cc | 13 ++++++-- third_party/ijar/test/BUILD | 26 +++++++++++++++ third_party/ijar/test/GenScalaTasty.java | 41 ++++++++++++++++++++++++ third_party/ijar/test/IjarTests.java | 12 ++++++- 4 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 third_party/ijar/test/GenScalaTasty.java diff --git a/third_party/ijar/ijar.cc b/third_party/ijar/ijar.cc index c86e9bf78f2fba..1e51291d3e13d3 100644 --- a/third_party/ijar/ijar.cc +++ b/third_party/ijar/ijar.cc @@ -37,6 +37,8 @@ const char *CLASS_EXTENSION = ".class"; const size_t CLASS_EXTENSION_LENGTH = strlen(CLASS_EXTENSION); const char *KOTLIN_MODULE_EXTENSION = ".kotlin_module"; const size_t KOTLIN_MODULE_EXTENSION_LENGTH = strlen(KOTLIN_MODULE_EXTENSION); +const char *SCALA_TASTY_EXTENSION = ".tasty"; +const size_t SCALA_TASTY_EXTENSION_LENGTH = strlen(SCALA_TASTY_EXTENSION); const char *MANIFEST_DIR_PATH = "META-INF/"; const size_t MANIFEST_DIR_PATH_LENGTH = strlen(MANIFEST_DIR_PATH); @@ -101,9 +103,15 @@ static bool IsKotlinModule(const char *filename, const size_t filename_len) { KOTLIN_MODULE_EXTENSION_LENGTH); } +static bool IsScalaTasty(const char *filename, const size_t filename_len) { + return EndsWith(filename, filename_len, SCALA_TASTY_EXTENSION, + SCALA_TASTY_EXTENSION_LENGTH); +} + bool JarStripperProcessor::Accept(const char *filename, const u4 /*attr*/) { const size_t filename_len = strlen(filename); - if (IsKotlinModule(filename, filename_len)) { + if (IsKotlinModule(filename, filename_len) || + IsScalaTasty(filename, filename_len)) { return true; } if (filename_len < CLASS_EXTENSION_LENGTH || @@ -129,7 +137,8 @@ void JarStripperProcessor::Process(const char *filename, const u4 /*attr*/, if (verbose) { fprintf(stderr, "INFO: StripClass: %s\n", filename); } - if (IsModuleInfo(filename) || IsKotlinModule(filename, strlen(filename))) { + if (IsModuleInfo(filename) || IsKotlinModule(filename, strlen(filename)) || + IsScalaTasty(filename, strlen(filename))) { u1 *q = builder_->NewFile(filename, 0); memcpy(q, data, size); builder_->FinishFile(size, /* compress: */ false, /* compute_crc: */ true); diff --git a/third_party/ijar/test/BUILD b/third_party/ijar/test/BUILD index 0c17b151270a04..daf0f16a78bbcf 100644 --- a/third_party/ijar/test/BUILD +++ b/third_party/ijar/test/BUILD @@ -267,6 +267,31 @@ genrule( tools = ["//third_party/ijar"], ) +java_binary( + name = "GenScalaTasty", + testonly = 1, + srcs = ["GenScalaTasty.java"], + main_class = "GenScalaTasty", + deps = ["//third_party:guava"], +) + +genrule( + name = "scala_tasty", + testonly = 1, + outs = ["scala_tasty.jar"], + cmd = "$(location :GenScalaTasty) $@", + tools = [":GenScalaTasty"], +) + +genrule( + name = "scala_tasty-interface", + testonly = 1, + srcs = [":scala_tasty.jar"], + outs = ["scala_tasty-interface.jar"], + cmd = "$(location //third_party/ijar) $< $@", + tools = ["//third_party/ijar"], +) + java_test( name = "IjarTests", size = "small", @@ -295,6 +320,7 @@ java_test( ":liblocal_and_anonymous_lib.jar", ":local_and_anonymous-interface.jar", ":module_info-interface.jar", + ":scala_tasty-interface.jar", ], tags = ["zip"], test_class = "IjarTests", diff --git a/third_party/ijar/test/GenScalaTasty.java b/third_party/ijar/test/GenScalaTasty.java new file mode 100644 index 00000000000000..0f0e1b1443ed16 --- /dev/null +++ b/third_party/ijar/test/GenScalaTasty.java @@ -0,0 +1,41 @@ +// Copyright 2020 The Bazel Authors. All rights reserved. +// +// 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. + +import static java.nio.charset.StandardCharsets.UTF_8; + +import com.google.common.io.ByteStreams; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.jar.JarOutputStream; +import java.util.zip.ZipEntry; + +/** A generator for a jar file containing a .tasty file, and one real class file. */ +public class GenScalaTasty { + public static void main(String[] args) throws IOException { + try (JarOutputStream jos = new JarOutputStream(Files.newOutputStream(Paths.get(args[0])))) { + addEntry(jos, "Bar.tasty"); + jos.write("hello".getBytes(UTF_8)); + + addEntry(jos, "java/lang/String.class"); + ByteStreams.copy(String.class.getResourceAsStream("/java/lang/String.class"), jos); + } + } + + private static void addEntry(JarOutputStream jos, String name) throws IOException { + ZipEntry ze = new ZipEntry(name); + ze.setTime(0); + jos.putNextEntry(ze); + } +} diff --git a/third_party/ijar/test/IjarTests.java b/third_party/ijar/test/IjarTests.java index c81311bc39c5cf..7563f599ca39c0 100644 --- a/third_party/ijar/test/IjarTests.java +++ b/third_party/ijar/test/IjarTests.java @@ -248,7 +248,9 @@ static Map readJar(String path) throws IOException { Enumeration entries = jf.entries(); while (entries.hasMoreElements()) { JarEntry je = entries.nextElement(); - if (!je.getName().endsWith(".class") && !je.getName().endsWith(".kotlin_module")) { + if (!je.getName().endsWith(".class") + && !je.getName().endsWith(".kotlin_module") + && !je.getName().endsWith(".tasty")) { continue; } classes.put(je.getName(), ByteStreams.toByteArray(jf.getInputStream(je))); @@ -291,6 +293,14 @@ public void kotlinModule() throws Exception { assertThat(new String(lib.get("META-INF/bar.kotlin_module"), UTF_8)).isEqualTo("hello"); } + @Test + public void scalaTasty() throws Exception { + Map lib = readJar("third_party/ijar/test/scala_tasty-interface.jar"); + assertThat(lib.keySet()).containsExactly("java/lang/String.class", "Bar.tasty"); + // ijar passes scala tasty files through unmodified + assertThat(new String(lib.get("Bar.tasty"), UTF_8)).isEqualTo("hello"); + } + @Test public void testTargetLabel() throws Exception { try (JarFile jf =