mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
Added basic implementation of ResourceTableRegistry
This commit is contained in:
@@ -55,14 +55,17 @@ android {
|
||||
|
||||
dependencies {
|
||||
implementation common.kotlin
|
||||
implementation project (':build-tools:layoutlib-api')
|
||||
implementation project (':logger')
|
||||
implementation project (':build-tools:jaxp')
|
||||
|
||||
api 'com.android.tools:common:30.2.2'
|
||||
implementation 'com.android.tools:annotations:30.2.2'
|
||||
implementation 'com.android.tools.build:aapt2-proto:7.2.2-7984345'
|
||||
implementation 'com.google.protobuf:protobuf-java:3.10.0'
|
||||
api aapt.common
|
||||
api project (':build-tools:layoutlib-api')
|
||||
|
||||
implementation aapt.annotations
|
||||
implementation aapt.proto
|
||||
implementation google.protobuf
|
||||
|
||||
testImplementation tests.junit
|
||||
testImplementation tests.robolectric
|
||||
testImplementation tests.google_truth
|
||||
}
|
||||
+134
-112
@@ -18,9 +18,9 @@
|
||||
package com.android.aaptcompiler
|
||||
|
||||
import com.android.aaptcompiler.proto.serializeTableToPb
|
||||
import com.android.utils.FileUtils
|
||||
import com.itsaky.androidide.layoutlib.resources.ResourceType
|
||||
import com.itsaky.androidide.layoutlib.resources.ResourceVisibility
|
||||
import com.android.utils.FileUtils
|
||||
import java.io.File
|
||||
|
||||
private const val VALUES_DIRECTORY_PREFIX = "values"
|
||||
@@ -33,29 +33,30 @@ private const val PNG_EXTENSION = "png"
|
||||
* The different options available while compiling resources.
|
||||
*
|
||||
* @param generateSymbolsPathData Path where the R.txt file should be written to. If null, symbols
|
||||
* will not be generated.
|
||||
* will not be generated.
|
||||
* @param visibility The visibility of resources in the given file to process. If specified, then
|
||||
* compilation of table files will fail if either the "public" or "public group" tags are
|
||||
* encountered.
|
||||
* compilation of table files will fail if either the "public" or "public group" tags are
|
||||
* encountered.
|
||||
* @param requirePngCrunching whether PNG file crunching is enabled
|
||||
* @param pseudolocalize Whether or not the pseudolocales en-XA and ar-XB are generated for default
|
||||
* string resources.
|
||||
* @param partialRFile file to output symbols defined in the resource file (currently only
|
||||
* supported for non-raw XML files (e.g. values or layouts) to match AAPT2's behaviour)
|
||||
* string resources.
|
||||
* @param partialRFile file to output symbols defined in the resource file (currently only supported
|
||||
* for non-raw XML files (e.g. values or layouts) to match AAPT2's behaviour)
|
||||
* @param legacyMode Whether or not to error or warn on positional args not being specified in a
|
||||
* translatable string with parameters.
|
||||
* translatable string with parameters.
|
||||
* @param verbose Whether or not to show verbose logs.
|
||||
* @param sourcePath Specified source path to be written in the compiled resource.
|
||||
*/
|
||||
data class ResourceCompilerOptions(
|
||||
val generateSymbolsPathData: ResourcePathData?= null,
|
||||
val generateSymbolsPathData: ResourcePathData? = null,
|
||||
val visibility: ResourceVisibility? = null,
|
||||
val requirePngCrunching: Boolean = false,
|
||||
val pseudolocalize: Boolean = false,
|
||||
val partialRFile: File? = null,
|
||||
val legacyMode: Boolean = false,
|
||||
val verbose: Boolean = false,
|
||||
val sourcePath: String? = null)
|
||||
val sourcePath: String? = null
|
||||
)
|
||||
|
||||
/**
|
||||
* Shows whether the ResourceCompiler can compile the given Resource File.
|
||||
@@ -68,8 +69,9 @@ fun canCompileResourceInJvm(file: File, requirePngCrunching: Boolean): Boolean {
|
||||
if (file.isHidden) return true
|
||||
|
||||
val pathData = extractPathData(file)
|
||||
if (pathData.resourceDirectory == VALUES_DIRECTORY_PREFIX
|
||||
&& pathData.extension == XML_EXTENSION) {
|
||||
if (
|
||||
pathData.resourceDirectory == VALUES_DIRECTORY_PREFIX && pathData.extension == XML_EXTENSION
|
||||
) {
|
||||
// file is a values table.
|
||||
return true
|
||||
} else {
|
||||
@@ -91,7 +93,7 @@ fun canCompileResourceInJvm(file: File, requirePngCrunching: Boolean): Boolean {
|
||||
*
|
||||
* @param file file to processed. How it is processed is determined by the resource compiler.
|
||||
* @param options the [ResourceCompilerOptions] that should be considered while compiling the
|
||||
* resource.
|
||||
* resource.
|
||||
*
|
||||
* Values files are compiled by extracting the all the resources and their values into a Resource
|
||||
* Table to be used in Linking.
|
||||
@@ -106,7 +108,11 @@ fun canCompileResourceInJvm(file: File, requirePngCrunching: Boolean): Boolean {
|
||||
* See [canCompileResourceInJvm] to see what is supported.
|
||||
*/
|
||||
fun compileResource(
|
||||
file: File, outputDirectory: File, options: ResourceCompilerOptions, logger: BlameLogger) {
|
||||
file: File,
|
||||
outputDirectory: File,
|
||||
options: ResourceCompilerOptions,
|
||||
logger: BlameLogger
|
||||
) {
|
||||
|
||||
// Skip hidden files.
|
||||
if (file.isHidden) {
|
||||
@@ -123,24 +129,28 @@ fun compileResource(
|
||||
compileFunction(pathData, outputDirectory, options, logger)
|
||||
} catch (e: Exception) {
|
||||
logger?.info("Failed to compile file", blameSource(pathData.source))
|
||||
val message =
|
||||
"Resource compilation failed (${e.message}. Cause: ${e.cause}). " +
|
||||
"Check logs for more details."
|
||||
throw ResourceCompilationException(message, e)
|
||||
val message =
|
||||
"Resource compilation failed (${e.message}. Cause: ${e.cause}). " +
|
||||
"Check logs for more details."
|
||||
throw ResourceCompilationException(message, e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCompileMethod(pathData: ResourcePathData, logger: BlameLogger):
|
||||
(ResourcePathData, File, ResourceCompilerOptions, BlameLogger) -> Unit {
|
||||
if (pathData.resourceDirectory == VALUES_DIRECTORY_PREFIX &&
|
||||
pathData.extension == XML_EXTENSION) {
|
||||
private fun getCompileMethod(
|
||||
pathData: ResourcePathData,
|
||||
logger: BlameLogger
|
||||
): (ResourcePathData, File, ResourceCompilerOptions, BlameLogger) -> Unit {
|
||||
if (
|
||||
pathData.resourceDirectory == VALUES_DIRECTORY_PREFIX && pathData.extension == XML_EXTENSION
|
||||
) {
|
||||
pathData.extension = RESOURCE_TABLE_EXTENSION
|
||||
return ::compileTable
|
||||
} else {
|
||||
val type = ResourceType.fromFolderName(pathData.resourceDirectory)
|
||||
if (type == null) {
|
||||
val errorMsg = "Invalid resource type '${pathData.resourceDirectory}'" +
|
||||
" for file ${pathData.file.absolutePath}"
|
||||
val errorMsg =
|
||||
"Invalid resource type '${pathData.resourceDirectory}'" +
|
||||
" for file ${pathData.file.absolutePath}"
|
||||
logger?.warning(errorMsg)
|
||||
error(errorMsg)
|
||||
}
|
||||
@@ -158,41 +168,45 @@ private fun getCompileMethod(pathData: ResourcePathData, logger: BlameLogger):
|
||||
/**
|
||||
* Compiles a xml values file into a flattened [ResourceTable] proto.
|
||||
*
|
||||
* After all resources are extracted from the given xml, if pseudolocalization is set,
|
||||
* pseudolocale strings are created for each default string resource in the xml. The result is
|
||||
* flattened to a file in the output directory.
|
||||
* After all resources are extracted from the given xml, if pseudolocalization is set, pseudolocale
|
||||
* strings are created for each default string resource in the xml. The result is flattened to a
|
||||
* file in the output directory.
|
||||
*
|
||||
* @param pathData the file to be processed.
|
||||
* @param outputDirectory the directory to which the compiled file is to be placed.
|
||||
* @throws IllegalStateException A failure occurred in processing this resource.
|
||||
*/
|
||||
private fun compileTable(
|
||||
pathData: ResourcePathData,
|
||||
outputDirectory: File,
|
||||
options: ResourceCompilerOptions,
|
||||
logger: BlameLogger) {
|
||||
pathData: ResourcePathData,
|
||||
outputDirectory: File,
|
||||
options: ResourceCompilerOptions,
|
||||
logger: BlameLogger
|
||||
) {
|
||||
val outputFile = File(outputDirectory, pathData.getIntermediateContainerFilename())
|
||||
logger?.info("Compiling XML table ${pathData.file.absolutePath} to $outputFile")
|
||||
|
||||
val table = ResourceTable(logger = logger)
|
||||
|
||||
val extractorOptions = TableExtractorOptions(
|
||||
translatable = !pathData.name.contains("donottranslate"),
|
||||
errorOnPositionalArgs = !options.legacyMode,
|
||||
visibility = options.visibility)
|
||||
val extractorOptions =
|
||||
TableExtractorOptions(
|
||||
translatable = !pathData.name.contains("donottranslate"),
|
||||
errorOnPositionalArgs = !options.legacyMode,
|
||||
visibility = options.visibility
|
||||
)
|
||||
val tableExtractor =
|
||||
TableExtractor(table, pathData.source, pathData.config, extractorOptions, logger)
|
||||
|
||||
pathData.file.inputStream().use {
|
||||
try {
|
||||
tableExtractor.extract(it)
|
||||
} catch (e: Exception) {
|
||||
// For merged values there's no need to re-write as we don't know which line failed. The
|
||||
// actual error will be raised by the table extractor.
|
||||
throw ResourceCompilationException(
|
||||
"Failed to compile values resource file ${pathData.file}", e
|
||||
)
|
||||
}
|
||||
try {
|
||||
tableExtractor.extract(it)
|
||||
} catch (e: Exception) {
|
||||
// For merged values there's no need to re-write as we don't know which line failed. The
|
||||
// actual error will be raised by the table extractor.
|
||||
throw ResourceCompilationException(
|
||||
"Failed to compile values resource file ${pathData.file}",
|
||||
e
|
||||
)
|
||||
}
|
||||
|
||||
// Adds the fake locales: en-XA and ar-XB for each default-defined string resource. This is used
|
||||
// for debugging apps with long text (en-XA) or rtl (ar-XB) language support.
|
||||
@@ -218,20 +232,23 @@ private fun compileTable(
|
||||
pkg.groups.forEach { group ->
|
||||
// Macros do not define resources and therefore don't belong in the R.txt.
|
||||
if (group.type != AaptResourceType.MACRO) {
|
||||
val javaType = if (group.type == AaptResourceType.STYLEABLE) "int[]" else "int"
|
||||
group.entries.forEach { entry ->
|
||||
val visibility = getVisibility(entry.value.values, group.type)
|
||||
builder.appendln("${visibility.getName()} $javaType ${group.type.tagName} ${entry.key}")
|
||||
if (group.type == AaptResourceType.STYLEABLE) {
|
||||
// We also need to write down styleable children (entries)
|
||||
group.getStyleable(entry).entries.forEach {
|
||||
val childPackage =
|
||||
if (!it.name.pck.isNullOrEmpty()) "_${it.name.pck}" else ""
|
||||
// styleables and their children are always public
|
||||
builder.appendln("public int styleable ${entry.key}${childPackage}_${it.name.entry}")
|
||||
}
|
||||
}
|
||||
val javaType = if (group.type == AaptResourceType.STYLEABLE) "int[]" else "int"
|
||||
group.entries.forEach { entry ->
|
||||
val visibility = getVisibility(entry.value.values, group.type)
|
||||
builder.appendLine(
|
||||
"${visibility.getName()} $javaType ${group.type.tagName} ${entry.key}"
|
||||
)
|
||||
if (group.type == AaptResourceType.STYLEABLE) {
|
||||
// We also need to write down styleable children (entries)
|
||||
group.getStyleable(entry).entries.forEach {
|
||||
val childPackage = if (!it.name.pck.isNullOrEmpty()) "_${it.name.pck}" else ""
|
||||
// styleables and their children are always public
|
||||
builder.appendln(
|
||||
"public int styleable ${entry.key}${childPackage}_${it.name.entry}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -240,21 +257,24 @@ private fun compileTable(
|
||||
}
|
||||
}
|
||||
|
||||
private fun getVisibility(values: Collection<ResourceEntry>, type: AaptResourceType): ResourceVisibility {
|
||||
// all declare styleables need to be marked as public so they're visible in the R classes
|
||||
if (type == AaptResourceType.STYLEABLE) return ResourceVisibility.PUBLIC
|
||||
var foundPublic = false
|
||||
var foundPrivate = false
|
||||
values.forEach {
|
||||
// We only care about public and private since only these can clash
|
||||
if (it.visibility.level == ResourceVisibility.PUBLIC) foundPublic = true
|
||||
else if (it.visibility.level == ResourceVisibility.PRIVATE) foundPrivate = true
|
||||
}
|
||||
if (foundPublic && foundPrivate)
|
||||
error("Resource cannot be both public and private: ${type.tagName} ${values.first().name}")
|
||||
if (foundPublic) return ResourceVisibility.PUBLIC
|
||||
if (foundPrivate) return ResourceVisibility.PRIVATE
|
||||
return ResourceVisibility.PRIVATE_XML_ONLY // default
|
||||
private fun getVisibility(
|
||||
values: Collection<ResourceEntry>,
|
||||
type: AaptResourceType
|
||||
): ResourceVisibility {
|
||||
// all declare styleables need to be marked as public so they're visible in the R classes
|
||||
if (type == AaptResourceType.STYLEABLE) return ResourceVisibility.PUBLIC
|
||||
var foundPublic = false
|
||||
var foundPrivate = false
|
||||
values.forEach {
|
||||
// We only care about public and private since only these can clash
|
||||
if (it.visibility.level == ResourceVisibility.PUBLIC) foundPublic = true
|
||||
else if (it.visibility.level == ResourceVisibility.PRIVATE) foundPrivate = true
|
||||
}
|
||||
if (foundPublic && foundPrivate)
|
||||
error("Resource cannot be both public and private: ${type.tagName} ${values.first().name}")
|
||||
if (foundPublic) return ResourceVisibility.PUBLIC
|
||||
if (foundPrivate) return ResourceVisibility.PRIVATE
|
||||
return ResourceVisibility.PRIVATE_XML_ONLY // default
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -264,19 +284,21 @@ private fun getVisibility(values: Collection<ResourceEntry>, type: AaptResourceT
|
||||
* @param outputDirectory the directory to which the compiled file is to be placed.
|
||||
*/
|
||||
private fun compileFile(
|
||||
pathData: ResourcePathData,
|
||||
outputDirectory: File,
|
||||
options: ResourceCompilerOptions,
|
||||
logger: BlameLogger?) {
|
||||
pathData: ResourcePathData,
|
||||
outputDirectory: File,
|
||||
options: ResourceCompilerOptions,
|
||||
logger: BlameLogger?
|
||||
) {
|
||||
val outputFile = File(outputDirectory, pathData.getIntermediateContainerFilename())
|
||||
logger?.info("Compiling file ${pathData.file.absolutePath} to $outputFile")
|
||||
pathData.file.inputStream().use {
|
||||
val resourceFile = ResourceFile(
|
||||
ResourceName("", resourceTypeFromTag(pathData.resourceDirectory)!!, pathData.name),
|
||||
pathData.config,
|
||||
pathData.source,
|
||||
ResourceFile.Type.Unknown
|
||||
)
|
||||
val resourceFile =
|
||||
ResourceFile(
|
||||
ResourceName("", resourceTypeFromTag(pathData.resourceDirectory)!!, pathData.name),
|
||||
pathData.config,
|
||||
pathData.source,
|
||||
ResourceFile.Type.Unknown
|
||||
)
|
||||
|
||||
val container = Container(outputFile.outputStream(), 1)
|
||||
container.addFileEntry(it, resourceFile)
|
||||
@@ -306,34 +328,34 @@ private fun compileFile(
|
||||
* @throws IllegalStateException A failure occurred in processing this resource.
|
||||
*/
|
||||
private fun compileXml(
|
||||
pathData: ResourcePathData,
|
||||
outputDirectory: File,
|
||||
options: ResourceCompilerOptions,
|
||||
logger: BlameLogger?) {
|
||||
pathData: ResourcePathData,
|
||||
outputDirectory: File,
|
||||
options: ResourceCompilerOptions,
|
||||
logger: BlameLogger?
|
||||
) {
|
||||
val outputFile = File(outputDirectory, pathData.getIntermediateContainerFilename())
|
||||
logger?.info("Compiling xml file ${pathData.file.absolutePath} to $outputFile")
|
||||
|
||||
val fileToProcess = ResourceFile(
|
||||
ResourceName("", pathData.type!!, pathData.name),
|
||||
pathData.config,
|
||||
pathData.source,
|
||||
ResourceFile.Type.ProtoXml
|
||||
)
|
||||
val fileToProcess =
|
||||
ResourceFile(
|
||||
ResourceName("", pathData.type!!, pathData.name),
|
||||
pathData.config,
|
||||
pathData.source,
|
||||
ResourceFile.Type.ProtoXml
|
||||
)
|
||||
|
||||
pathData.file.inputStream().use {
|
||||
val xmlProcessor = XmlProcessor(source = pathData.source, logger = logger)
|
||||
try {
|
||||
xmlProcessor.process(fileToProcess, it)
|
||||
} catch (e: Exception) {
|
||||
throw ResourceCompilationException(
|
||||
"Failed to compile resource file: " +
|
||||
logger?.getOutputSource(blameSource(pathData.source)),
|
||||
e
|
||||
)
|
||||
}
|
||||
try {
|
||||
xmlProcessor.process(fileToProcess, it)
|
||||
} catch (e: Exception) {
|
||||
throw ResourceCompilationException(
|
||||
"Failed to compile resource file: " + logger?.getOutputSource(blameSource(pathData.source)),
|
||||
e
|
||||
)
|
||||
}
|
||||
|
||||
val container =
|
||||
Container(outputFile.outputStream(), xmlProcessor.xmlResources.size)
|
||||
val container = Container(outputFile.outputStream(), xmlProcessor.xmlResources.size)
|
||||
|
||||
for (resource in xmlProcessor.xmlResources) {
|
||||
container.addXmlEntry(resource)
|
||||
@@ -341,9 +363,9 @@ private fun compileXml(
|
||||
|
||||
if (options.partialRFile != null) {
|
||||
val builder = StringBuilder()
|
||||
builder.appendln("default int ${pathData.type.tagName} ${pathData.name}")
|
||||
builder.appendLine("default int ${pathData.type.tagName} ${pathData.name}")
|
||||
fileToProcess.exportedSymbols.forEach { id ->
|
||||
builder.appendln("default int id ${id.name.entry}")
|
||||
builder.appendLine("default int id ${id.name.entry}")
|
||||
}
|
||||
FileUtils.writeToFile(options.partialRFile, builder.toString())
|
||||
}
|
||||
@@ -359,18 +381,18 @@ private fun compileXml(
|
||||
*
|
||||
* If we are not running in debug mode, the png will undergo additional crunching.
|
||||
*
|
||||
* Finally, the processed file is written to the output directory. It is possible that no
|
||||
* processing is done to the png file, and if so it will just be written exactly as if it were
|
||||
* a raw file.
|
||||
* Finally, the processed file is written to the output directory. It is possible that no processing
|
||||
* is done to the png file, and if so it will just be written exactly as if it were a raw file.
|
||||
*
|
||||
* @param pathData the file to process.
|
||||
* @param outputDirectory the directory in which the processed file will be placed.
|
||||
*/
|
||||
private fun compilePng(
|
||||
pathData: ResourcePathData,
|
||||
outputDirectory: File,
|
||||
options: ResourceCompilerOptions,
|
||||
logger: BlameLogger?) {
|
||||
pathData: ResourcePathData,
|
||||
outputDirectory: File,
|
||||
options: ResourceCompilerOptions,
|
||||
logger: BlameLogger?
|
||||
) {
|
||||
logger?.info("Compiling image file ${pathData.file.absolutePath}")
|
||||
if (pathData.extension == PATCH_9_EXTENSION) {
|
||||
error("Patch 9 PNG processing is not supported with the JVM Android resource compiler.")
|
||||
|
||||
@@ -23,7 +23,7 @@ class ResourceTable(val validateResources: Boolean = false, val logger: BlameLog
|
||||
val stringPool = StringPool()
|
||||
|
||||
/** The list of packages in this table. */
|
||||
internal val packages = mutableListOf<ResourceTablePackage>()
|
||||
val packages = mutableListOf<ResourceTablePackage>()
|
||||
|
||||
/**
|
||||
* Set of dynamic packages that this table may reference. Their package names get encoded into the
|
||||
@@ -638,8 +638,7 @@ class ResourceTablePackage(var name: String = "", var id: Byte? = null) {
|
||||
}
|
||||
|
||||
fun findOrCreateGroup(type: AaptResourceType, groupId: Byte? = null): ResourceGroup {
|
||||
val group = findGroup(type, groupId)
|
||||
return when(group) {
|
||||
return when(val group = findGroup(type, groupId)) {
|
||||
null -> {
|
||||
val newGroup = ResourceGroup(type)
|
||||
newGroup.id = groupId
|
||||
@@ -705,7 +704,7 @@ class ResourceEntry(val name : String) {
|
||||
var allowNew: AllowNew? = null
|
||||
var overlayable: OverlayableItem? = null
|
||||
|
||||
internal val values = mutableListOf<ResourceConfigValue>()
|
||||
val values = mutableListOf<ResourceConfigValue>()
|
||||
|
||||
fun findValue(config: ConfigDescription, product: String = ""): ResourceConfigValue? {
|
||||
return values.find { it.config == config && it.product == product }
|
||||
|
||||
+507
-352
File diff suppressed because it is too large
Load Diff
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.aapt.logging
|
||||
|
||||
import com.android.utils.ILogger
|
||||
|
||||
/**
|
||||
* Implementation of [ILogger] which redirects all logs to AndroidIDE's logger implementation i.e.
|
||||
* [com.itsaky.androidide.utils.ILogger].
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
object IDELogger : ILogger {
|
||||
|
||||
private val log = com.itsaky.androidide.utils.ILogger.newInstance("AAPTCompilerLogger")
|
||||
|
||||
override fun error(t: Throwable?, msgFormat: String?, vararg args: Any?) {
|
||||
if (msgFormat != null) {
|
||||
log.error(String.format(msgFormat, args))
|
||||
}
|
||||
if (t != null) {
|
||||
log.error(t)
|
||||
}
|
||||
}
|
||||
|
||||
override fun warning(msgFormat: String?, vararg args: Any?) {
|
||||
if (msgFormat != null) {
|
||||
log.warn(String.format(msgFormat, args))
|
||||
}
|
||||
}
|
||||
|
||||
override fun info(msgFormat: String?, vararg args: Any?) {
|
||||
if (msgFormat != null) {
|
||||
log.info(String.format(msgFormat, args))
|
||||
}
|
||||
}
|
||||
|
||||
override fun verbose(msgFormat: String?, vararg args: Any?) {
|
||||
if (msgFormat != null) {
|
||||
log.verbose(String.format(msgFormat, args))
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -27,9 +27,10 @@ import kotlin.io.path.absolute
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.JUnit4
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
|
||||
/** @author Akash Yadav */
|
||||
@RunWith(JUnit4::class)
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
class CompilerTest {
|
||||
|
||||
@Test
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user