Support several res/ directories

This commit is contained in:
Yan Zhulanow
2015-04-14 19:07:35 +03:00
parent 7a685b8c56
commit 3645dfc5af
22 changed files with 183 additions and 50 deletions
@@ -34,11 +34,8 @@ import org.jetbrains.kotlin.lang.resolve.android.*
import org.jetbrains.kotlin.psi.JetFile
public object AndroidConfigurationKeys {
public val ANDROID_RES_PATH: CompilerConfigurationKey<String> = CompilerConfigurationKey.create<String>("android resources search path")
public val ANDROID_RES_PATH: CompilerConfigurationKey<List<String>> = CompilerConfigurationKey.create<List<String>>("android resources search path")
public val ANDROID_MANIFEST: CompilerConfigurationKey<String> = CompilerConfigurationKey.create<String>("android manifest file")
public val SUPPORT_V4: CompilerConfigurationKey<String> = CompilerConfigurationKey.create<String>("'true' if compiled with support-v4 library")
}
@@ -46,7 +43,7 @@ public class AndroidCommandLineProcessor : CommandLineProcessor {
companion object {
public val ANDROID_COMPILER_PLUGIN_ID: String = "org.jetbrains.kotlin.android"
public val RESOURCE_PATH_OPTION: CliOption = CliOption("androidRes", "<path>", "Android resources path")
public val RESOURCE_PATH_OPTION: CliOption = CliOption("androidRes", "<path>", "Android resources path", allowMultipleOccurrences = true)
public val MANIFEST_FILE_OPTION: CliOption = CliOption("androidManifest", "<path>", "Android manifest file")
public val SUPPORT_V4_OPTION: CliOption = CliOption("supportV4", "<path>", "Support android-v4 library", required = false)
}
@@ -57,7 +54,11 @@ public class AndroidCommandLineProcessor : CommandLineProcessor {
override fun processOption(option: CliOption, value: String, configuration: CompilerConfiguration) {
when (option) {
RESOURCE_PATH_OPTION -> configuration.put(AndroidConfigurationKeys.ANDROID_RES_PATH, value)
RESOURCE_PATH_OPTION -> {
val paths = configuration.getList(AndroidConfigurationKeys.ANDROID_RES_PATH).toArrayList()
paths.add(value)
configuration.put(AndroidConfigurationKeys.ANDROID_RES_PATH, paths)
}
MANIFEST_FILE_OPTION -> configuration.put(AndroidConfigurationKeys.ANDROID_MANIFEST, value)
SUPPORT_V4_OPTION -> configuration.put(AndroidConfigurationKeys.SUPPORT_V4, value)
else -> throw CliOptionProcessingException("Unknown option: ${option.name}")
@@ -55,8 +55,8 @@ public abstract class AndroidResourceManager(val project: Project) {
return allChildren
}
val resDirectory = fileManager.findFileByUrl("file://" + info.mainResDirectory)
val allChildren = resDirectory?.getAllChildren() ?: listOf()
val resDirectories = info.mainResDirectories.map { fileManager.findFileByUrl("file://$it") }
val allChildren = resDirectories.flatMap { it?.getAllChildren() ?: listOf() }
return allChildren
.filter { it.getParent().getName().startsWith("layout") && it.getName().toLowerCase().endsWith(".xml") }
@@ -66,10 +66,11 @@ public abstract class AndroidResourceManager(val project: Project) {
.mapValues { it.getValue().sortBy { it.getParent().getName().length() } }
}
fun getMainResDirectory(): VirtualFile? {
fun getModuleResDirectories(): List<VirtualFile> {
val info = androidModuleInfo
if (info == null) return null
return VirtualFileManager.getInstance().findFileByUrl("file://" + info.mainResDirectory)
if (info == null) return listOf()
val fileManager = VirtualFileManager.getInstance()
return info.mainResDirectories.map { fileManager.findFileByUrl("file://" + it) }
}
companion object {
@@ -28,7 +28,7 @@ import kotlin.properties.Delegates
public class CliAndroidResourceManager(
project: Project,
private val manifestPath: String,
private val mainResDirectory: String
private val mainResDirectory: List<String>
) : AndroidResourceManager(project) {
override val androidModuleInfo by Delegates.lazy {
@@ -30,11 +30,11 @@ import com.intellij.psi.util.CachedValueProvider.Result
public class CliAndroidUIXmlProcessor(
project: Project,
private val manifestPath: String,
private val mainResDirectory: String
private val mainResDirectories: List<String>
) : AndroidUIXmlProcessor(project) {
override val resourceManager: CliAndroidResourceManager by Delegates.lazy {
CliAndroidResourceManager(project, manifestPath, mainResDirectory)
CliAndroidResourceManager(project, manifestPath, mainResDirectories)
}
override val cachedSources: CachedValue<List<AndroidSyntheticFile>> by Delegates.lazy {
@@ -16,7 +16,7 @@
package org.jetbrains.kotlin.lang.resolve.android
public data class AndroidModuleInfo(val applicationPackage: String, val mainResDirectory: String?)
public data class AndroidModuleInfo(val applicationPackage: String, val mainResDirectories: List<String>)
public abstract class AndroidResource(val id: String) {
public abstract val className: String
@@ -0,0 +1,33 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.myapp"
android:versionCode="1"
android:versionName="1.0" >
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-sdk
android:minSdkVersion="18"
android:targetSdkVersion="18" />
<permission android:name="android"></permission>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.Sample" >
<activity
android:name="com.example.android.basiccontactables.MainActivity"
android:label="@string/app_name"
android:launchMode="singleTop">
<meta-data
android:name="android.app.searchable"
android:resource="@xml/searchable" />
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
@@ -0,0 +1,15 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/button2"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</FrameLayout>
@@ -0,0 +1,15 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<FrameLayout
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/button3"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</FrameLayout>
@@ -0,0 +1,32 @@
package kotlinx.android.synthetic.test
import android.app.*
import android.view.*
import android.widget.*
import android.webkit.*
import android.inputmethodservice.*
import android.opengl.*
import android.appwidget.*
import android.support.v4.app.*
import android.support.v4.view.*
import android.support.v4.widget.*
import kotlin.internal.flexible.ft
val android.app.Activity.button: ft<View, View?>
get() = findViewById(0)
val android.app.Fragment.button: ft<View, View?>
get() = getView().findViewById(0)
val android.app.Activity.button2: ft<Button, Button?>
get() = findViewById(0) as? Button
val android.app.Fragment.button2: ft<Button, Button?>
get() = getView().findViewById(0) as? Button
val android.app.Activity.button3: ft<Button, Button?>
get() = findViewById(0) as? Button
val android.app.Fragment.button3: ft<Button, Button?>
get() = getView().findViewById(0) as? Button
@@ -0,0 +1,23 @@
package kotlinx.android.synthetic.test.view
import android.app.*
import android.view.*
import android.widget.*
import android.webkit.*
import android.inputmethodservice.*
import android.opengl.*
import android.appwidget.*
import android.support.v4.app.*
import android.support.v4.view.*
import android.support.v4.widget.*
import kotlin.internal.flexible.ft
val android.view.View.button: ft<View, View?>
get() = findViewById(0)
val android.view.View.button2: ft<Button, Button?>
get() = findViewById(0) as? Button
val android.view.View.button3: ft<Button, Button?>
get() = findViewById(0) as? Button
@@ -41,10 +41,10 @@ public abstract class AbstractAndroidBoxTest : AbstractBlackBoxCodegenTest() {
}
private fun createEnvironmentForConfiguration(configuration: CompilerConfiguration, path: String) {
val resPath = path + "layout/"
val layoutPaths = File(path).listFiles { it.name.startsWith("layout") && it.isDirectory() }!!.map { "$path${it.name}/" }
val manifestPath = path + "AndroidManifest.xml"
val supportV4 = File(path).name.startsWith("support")
myEnvironment = createAndroidTestEnvironment(configuration, resPath, manifestPath, supportV4)
myEnvironment = createAndroidTestEnvironment(configuration, layoutPaths, manifestPath, supportV4)
}
public fun doCompileAgainstAndroidSdkTest(path: String) {
@@ -30,10 +30,10 @@ public abstract class AbstractAndroidBytecodeShapeTest : AbstractBytecodeTextTes
}
private fun createEnvironmentForConfiguration(configuration: CompilerConfiguration, path: String) {
val resPath = path + "res/layout/"
val layoutPaths = getResPaths(path)
val manifestPath = path + "../AndroidManifest.xml"
val supportV4 = File(path).name.startsWith("support")
myEnvironment = createAndroidTestEnvironment(configuration, resPath, manifestPath, supportV4)
myEnvironment = createAndroidTestEnvironment(configuration, layoutPaths, manifestPath, supportV4)
}
public override fun doTest(path: String) {
@@ -34,7 +34,8 @@ public abstract class AbstractAndroidXml2KConversionTest : UsefulTestCase() {
val testDirectory = File(path)
val jetCoreEnvironment = getEnvironment()
val parser = CliAndroidUIXmlProcessor(jetCoreEnvironment.project, path + "AndroidManifest.xml", path + "/res")
val layoutPaths = getResPaths(path)
val parser = CliAndroidUIXmlProcessor(jetCoreEnvironment.project, path + "AndroidManifest.xml", layoutPaths)
parser.supportV4 = testDirectory.name.startsWith("support")
val actual = parser.parse(false).toMap { it.name }
@@ -30,15 +30,16 @@ import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.lang.resolve.android.CliAndroidUIXmlProcessor
import java.io.File
private class AndroidTestExternalDeclarationsProvider(
val project: Project,
val resPath: String,
val resPaths: List<String>,
val manifestPath: String,
val supportV4: Boolean
) : ExternalDeclarationsProvider {
override fun getExternalDeclarations(moduleInfo: ModuleInfo?): Collection<JetFile> {
val parser = CliAndroidUIXmlProcessor(project, manifestPath, resPath)
val parser = CliAndroidUIXmlProcessor(project, manifestPath, resPaths)
parser.supportV4 = supportV4
return parser.parseToPsi() ?: listOf()
}
@@ -46,15 +47,19 @@ private class AndroidTestExternalDeclarationsProvider(
fun UsefulTestCase.createAndroidTestEnvironment(
configuration: CompilerConfiguration,
resPath: String,
resPaths: List<String>,
manifestPath: String,
supportV4: Boolean
): KotlinCoreEnvironment {
configuration.put(AndroidConfigurationKeys.ANDROID_RES_PATH, resPath)
configuration.put(AndroidConfigurationKeys.ANDROID_RES_PATH, resPaths)
configuration.put(AndroidConfigurationKeys.ANDROID_MANIFEST, manifestPath)
val myEnvironment = KotlinCoreEnvironment.createForTests(getTestRootDisposable()!!, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
val project = myEnvironment.project
ExternalDeclarationsProvider.registerExtension(project, AndroidTestExternalDeclarationsProvider(project, resPath, manifestPath, supportV4))
ExternalDeclarationsProvider.registerExtension(project, AndroidTestExternalDeclarationsProvider(project, resPaths, manifestPath, supportV4))
ExpressionCodegenExtension.registerExtension(project, AndroidExpressionCodegenExtension())
return myEnvironment
}
fun getResPaths(path: String): List<String> {
return File(path).listFiles { it.name.startsWith("res") && it.isDirectory() }!!.map { "$path${it.name}/" }
}