├── app ├── .gitignore ├── src │ └── main │ │ ├── res │ │ ├── values │ │ │ ├── strings.xml │ │ │ ├── styles.xml │ │ │ └── colors.xml │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ └── layout │ │ │ └── activity_main.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── eastwood │ │ └── demo │ │ └── app │ │ └── MainActivity.java ├── proguard-rules.pro └── build.gradle ├── kotlin ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ └── values │ │ │ │ └── strings.xml │ │ ├── AndroidManifest.xml │ │ ├── mis │ │ │ └── com │ │ │ │ └── eastwood │ │ │ │ └── demo │ │ │ │ └── kotlin │ │ │ │ ├── IKotlin.kt │ │ │ │ └── JavaObject.java │ │ └── java │ │ │ └── com │ │ │ └── eastwood │ │ │ └── demo │ │ │ └── kotlin │ │ │ └── KotlinService.kt │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── eastwood │ │ │ └── demo │ │ │ └── kotlin │ │ │ └── ExampleUnitTest.java │ └── androidTest │ │ └── java │ │ └── com │ │ └── eastwood │ │ └── demo │ │ └── kotlin │ │ └── ExampleInstrumentedTest.java ├── mis.gradle ├── proguard-rules.pro └── build.gradle ├── library ├── .gitignore ├── src │ └── main │ │ ├── res │ │ └── values │ │ │ └── strings.xml │ │ ├── AndroidManifest.xml │ │ ├── mis │ │ └── com │ │ │ └── eastwood │ │ │ └── demo │ │ │ └── library │ │ │ ├── ILibraryService.java │ │ │ └── LibraryModel.java │ │ └── java │ │ └── com │ │ └── eastwood │ │ └── demo │ │ └── library │ │ ├── LibraryService.java │ │ └── MisAutoArrowBow.java ├── mis.gradle ├── build.gradle └── proguard-rules.pro ├── mis-core ├── .gitignore ├── src │ └── main │ │ ├── res │ │ └── values │ │ │ └── strings.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── eastwood │ │ └── common │ │ └── mis │ │ └── MisService.java ├── proguard-rules.pro └── build.gradle ├── micro-module ├── .gitignore ├── main │ ├── build.gradle │ └── src │ │ └── main │ │ ├── res │ │ └── values │ │ │ └── strings.xml │ │ ├── AndroidManifest.xml │ │ ├── mis │ │ └── com │ │ │ └── eastwood │ │ │ └── demo │ │ │ └── micromodule │ │ │ └── main │ │ │ └── IModuleService.java │ │ └── java │ │ └── com │ │ └── eastwood │ │ └── demo │ │ └── micromodule │ │ └── main │ │ └── ModuleService.java ├── p_common │ ├── build.gradle │ └── src │ │ └── main │ │ └── AndroidManifest.xml ├── p_kotlin │ ├── src │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── mis │ │ │ └── com │ │ │ │ └── eastwood │ │ │ │ └── demo │ │ │ │ └── micromodule │ │ │ │ └── kotlin │ │ │ │ └── IMicroModuleKotlin.kt │ │ │ └── kotlin │ │ │ └── com │ │ │ └── eastwood │ │ │ └── demo │ │ │ └── micromodule │ │ │ └── kotlin │ │ │ └── MicroModuleKotlinService.kt │ └── build.gradle ├── proguard-rules.pro ├── build.gradle └── mis.gradle ├── mis-plugin ├── settings.gradle ├── src │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── gradle-plugins │ │ │ └── mis.properties │ │ └── groovy │ │ └── com │ │ └── eastwood │ │ └── tools │ │ └── plugins │ │ └── mis │ │ ├── core │ │ ├── SourceFile.groovy │ │ ├── SourceSet.groovy │ │ ├── extension │ │ │ ├── OnMisExtensionListener.groovy │ │ │ ├── Dependencies.groovy │ │ │ ├── CompileOptions.groovy │ │ │ ├── Publication.groovy │ │ │ └── MisExtension.groovy │ │ ├── RuntimeUtil.groovy │ │ ├── MisUtil.groovy │ │ ├── Digraph.java │ │ ├── PublicationManager.groovy │ │ └── JarUtil.groovy │ │ ├── CompileMisTask.groovy │ │ ├── FlatDirModuleComponentSelector.groovy │ │ └── MisPlugin.groovy ├── build.gradle ├── gradlew.bat └── gradlew ├── picture ├── 1.png ├── 2.png └── 3.png ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── settings.gradle ├── .gitignore ├── LICENSE ├── gradle.properties ├── gradlew.bat ├── gradlew └── README.md /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /kotlin/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /library/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /mis-core/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /micro-module/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /micro-module/main/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | 3 | } -------------------------------------------------------------------------------- /micro-module/p_common/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | 3 | } -------------------------------------------------------------------------------- /mis-plugin/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'mis-plugin' 2 | 3 | -------------------------------------------------------------------------------- /picture/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EastWoodYang/Mis/HEAD/picture/1.png -------------------------------------------------------------------------------- /picture/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EastWoodYang/Mis/HEAD/picture/2.png -------------------------------------------------------------------------------- /picture/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EastWoodYang/Mis/HEAD/picture/3.png -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | mis-dev 3 | 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EastWoodYang/Mis/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /mis-core/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | MIS 3 | 4 | -------------------------------------------------------------------------------- /kotlin/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | kotlin 3 | 4 | -------------------------------------------------------------------------------- /library/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | library 3 | 4 | -------------------------------------------------------------------------------- /mis-plugin/src/main/resources/META-INF/gradle-plugins/mis.properties: -------------------------------------------------------------------------------- 1 | implementation-class=com.eastwood.tools.plugins.mis.MisPlugin -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | includeBuild './mis-plugin' 2 | include ':mis-core' 3 | 4 | include ':app', ':library', ':micro-module', ':kotlin' -------------------------------------------------------------------------------- /micro-module/main/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | micro-module 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EastWoodYang/Mis/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EastWoodYang/Mis/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EastWoodYang/Mis/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EastWoodYang/Mis/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EastWoodYang/Mis/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EastWoodYang/Mis/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EastWoodYang/Mis/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EastWoodYang/Mis/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EastWoodYang/Mis/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EastWoodYang/Mis/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /kotlin/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /micro-module/main/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /mis-plugin/src/main/groovy/com/eastwood/tools/plugins/mis/core/SourceFile.groovy: -------------------------------------------------------------------------------- 1 | package com.eastwood.tools.plugins.mis.core 2 | 3 | class SourceFile { 4 | String path 5 | String name 6 | Long lastModified 7 | } -------------------------------------------------------------------------------- /micro-module/p_common/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /mis-plugin/src/main/groovy/com/eastwood/tools/plugins/mis/core/SourceSet.groovy: -------------------------------------------------------------------------------- 1 | package com.eastwood.tools.plugins.mis.core 2 | 3 | class SourceSet { 4 | String path 5 | Map lastModifiedSourceFile 6 | } -------------------------------------------------------------------------------- /kotlin/src/main/mis/com/eastwood/demo/kotlin/IKotlin.kt: -------------------------------------------------------------------------------- 1 | package com.eastwood.demo.kotlin 2 | 3 | /** 4 | * 5 | * @author eastwood 6 | * createDate: 2019-01-21 7 | */ 8 | interface IKotlin { 9 | 10 | fun test(i: Int) 11 | 12 | } -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /micro-module/p_kotlin/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /library/src/main/mis/com/eastwood/demo/library/ILibraryService.java: -------------------------------------------------------------------------------- 1 | package com.eastwood.demo.library; 2 | 3 | /** 4 | * @author eastwood 5 | * createDate: 2018-06-14 6 | */ 7 | public interface ILibraryService { 8 | 9 | LibraryModel getLibraryInfo(); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /kotlin/src/main/java/com/eastwood/demo/kotlin/KotlinService.kt: -------------------------------------------------------------------------------- 1 | package com.eastwood.demo.kotlin 2 | 3 | /** 4 | * 5 | * @author eastwood 6 | * createDate: 2019-01-21 7 | */ 8 | class KotlinService : IKotlin { 9 | 10 | override fun test(i: Int) { 11 | 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /micro-module/p_kotlin/build.gradle: -------------------------------------------------------------------------------- 1 | // MicroModule build file where you can add configuration options to publish MicroModule(aar) to Maven 2 | // and declare MicroModule dependencies. 3 | 4 | dependencies { 5 | implementation fileTree(dir: 'p_kotlin/libs', include: ['*.jar']) 6 | } 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 08 22:20:44 CST 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip 7 | -------------------------------------------------------------------------------- /micro-module/p_kotlin/src/main/mis/com/eastwood/demo/micromodule/kotlin/IMicroModuleKotlin.kt: -------------------------------------------------------------------------------- 1 | package com.eastwood.demo.micromodule.kotlin 2 | 3 | /** 4 | * 5 | * @author eastwood 6 | * createDate: 2019-01-22 7 | */ 8 | interface IMicroModuleKotlin { 9 | 10 | fun test() 11 | 12 | } -------------------------------------------------------------------------------- /kotlin/src/main/mis/com/eastwood/demo/kotlin/JavaObject.java: -------------------------------------------------------------------------------- 1 | package com.eastwood.demo.kotlin; 2 | 3 | /** 4 | * @author eastwood 5 | * createDate: 2019-01-21 6 | */ 7 | public class JavaObject implements IKotlin { 8 | 9 | @Override 10 | public void test(int i) { 11 | 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /micro-module/main/src/main/mis/com/eastwood/demo/micromodule/main/IModuleService.java: -------------------------------------------------------------------------------- 1 | package com.eastwood.demo.micromodule.main; 2 | 3 | /** 4 | * @author eastwood 5 | * createDate: 2018-06-12 6 | */ 7 | public interface IModuleService { 8 | 9 | void set(); 10 | 11 | void get(); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /mis-plugin/src/main/groovy/com/eastwood/tools/plugins/mis/core/extension/OnMisExtensionListener.groovy: -------------------------------------------------------------------------------- 1 | package com.eastwood.tools.plugins.mis.core.extension 2 | 3 | import org.gradle.api.Project 4 | 5 | interface OnMisExtensionListener { 6 | 7 | void onPublicationAdded(Project childProject, Publication publication) 8 | 9 | } 10 | -------------------------------------------------------------------------------- /micro-module/p_kotlin/src/main/kotlin/com/eastwood/demo/micromodule/kotlin/MicroModuleKotlinService.kt: -------------------------------------------------------------------------------- 1 | package com.eastwood.demo.micromodule.kotlin 2 | 3 | /** 4 | * 5 | * @author eastwood 6 | * createDate: 2019-01-22 7 | */ 8 | class MicroModuleKotlinService : IMicroModuleKotlin { 9 | 10 | override fun test() { 11 | 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /mis-core/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /micro-module/main/src/main/java/com/eastwood/demo/micromodule/main/ModuleService.java: -------------------------------------------------------------------------------- 1 | package com.eastwood.demo.micromodule.main; 2 | 3 | /** 4 | * @author eastwood 5 | * createDate: 2018-06-12 6 | */ 7 | public class ModuleService implements IModuleService { 8 | 9 | @Override 10 | public void set() { 11 | 12 | } 13 | 14 | @Override 15 | public void get() { 16 | 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /library/src/main/java/com/eastwood/demo/library/LibraryService.java: -------------------------------------------------------------------------------- 1 | package com.eastwood.demo.library; 2 | 3 | 4 | /** 5 | * @author eastwood 6 | * createDate: 2018-06-14 7 | */ 8 | public class LibraryService implements ILibraryService { 9 | 10 | @Override 11 | public LibraryModel getLibraryInfo() { 12 | LibraryModel libraryInfo = new LibraryModel(); 13 | libraryInfo.setName("library"); 14 | return libraryInfo; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /library/src/main/mis/com/eastwood/demo/library/LibraryModel.java: -------------------------------------------------------------------------------- 1 | package com.eastwood.demo.library; 2 | 3 | import com.google.gson.Gson; 4 | 5 | /** 6 | * @author eastwood 7 | * createDate: 2018-06-14 8 | */ 9 | public class LibraryModel { 10 | 11 | private String name; 12 | 13 | public String getName() { 14 | Gson gson; 15 | return name; 16 | } 17 | 18 | public void setName(String name) { 19 | this.name = name; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /kotlin/src/test/java/com/eastwood/demo/kotlin/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.eastwood.demo.kotlin; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Android Studio Navigation editor temp files 24 | .navigation/ 25 | .externalNativeBuild 26 | 27 | # Android Studio captures folder 28 | captures/ 29 | 30 | # Intellij 31 | *.iml 32 | .idea 33 | -------------------------------------------------------------------------------- /library/mis.gradle: -------------------------------------------------------------------------------- 1 | mis { 2 | publications { 3 | main { 4 | groupId 'com.eastwood.demo' 5 | artifactId 'library-sdk' 6 | // version '1.0.0' // 初次配置时不设置,发布至maven时设置 7 | 8 | dependencies { 9 | // 只支持 compileOnly 和 implementation 10 | 11 | // compileOnly 'com.google.code.gson:gson:2.8.1' 12 | // or 13 | implementation 'com.google.code.gson:gson:2.8.1' 14 | 15 | // compileOnly misPublication('com.eastwood.demo:module-main-sdk') 16 | } 17 | } 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /library/src/main/java/com/eastwood/demo/library/MisAutoArrowBow.java: -------------------------------------------------------------------------------- 1 | package com.eastwood.demo.library; 2 | 3 | import com.eastwood.common.autoinject.AutoBowArrow; 4 | import com.eastwood.common.autoinject.IAutoBowArrow; 5 | import com.eastwood.common.mis.MisService; 6 | 7 | @AutoBowArrow(target = "register") 8 | public class MisAutoArrowBow implements IAutoBowArrow { 9 | 10 | @Override 11 | public void shoot() { 12 | MisService.register(ILibraryService.class, LibraryService.class); 13 | // or 14 | // MisService.register(ILibraryService.class, new LibraryService()); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 EastWood Yang 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /mis-plugin/src/main/groovy/com/eastwood/tools/plugins/mis/core/extension/Dependencies.groovy: -------------------------------------------------------------------------------- 1 | package com.eastwood.tools.plugins.mis.core.extension 2 | 3 | class Dependencies { 4 | 5 | List implementation 6 | List compileOnly 7 | 8 | void implementation(Object value) { 9 | if (implementation == null) { 10 | implementation = new ArrayList<>() 11 | } 12 | implementation.add(value) 13 | } 14 | 15 | void compileOnly(Object value) { 16 | if (compileOnly == null) { 17 | compileOnly = new ArrayList<>() 18 | } 19 | compileOnly.add(value) 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /library/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 27 5 | 6 | defaultConfig { 7 | minSdkVersion 14 8 | targetSdkVersion 27 9 | versionCode 1 10 | versionName "1.0" 11 | } 12 | 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | 20 | } 21 | 22 | dependencies { 23 | implementation 'com.eastwood.common:auto-inject:1.0.0' 24 | 25 | implementation project(':mis-core') 26 | // or 27 | // implementation 'com.eastwood.common:mis:1.0.0' 28 | 29 | } 30 | -------------------------------------------------------------------------------- /kotlin/mis.gradle: -------------------------------------------------------------------------------- 1 | mis { 2 | publications { 3 | main { 4 | groupId 'com.eastwood.demo' 5 | artifactId 'kotlin-sdk' 6 | // version '1.0.0' // 初次配置时不设置,发布至maven时设置 7 | 8 | dependencies { 9 | // 只支持 compileOnly 和 implementation 10 | 11 | // compileOnly 'com.google.code.gson:gson:2.8.1' 12 | // or 13 | // implementation 'com.google.code.gson:gson:2.8.1' 14 | 15 | // compileOnly misPublication('com.eastwood.demo:module-main-sdk') 16 | 17 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 18 | } 19 | } 20 | 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /mis-core/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\Dev_tools\SDK/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /mis-plugin/src/main/groovy/com/eastwood/tools/plugins/mis/core/extension/CompileOptions.groovy: -------------------------------------------------------------------------------- 1 | package com.eastwood.tools.plugins.mis.core.extension 2 | 3 | import org.gradle.api.JavaVersion 4 | 5 | class CompileOptions { 6 | 7 | JavaVersion sourceCompatibility = JavaVersion.current().isJava8Compatible() 8 | ? JavaVersion.VERSION_1_8 : JavaVersion.VERSION_1_6 9 | JavaVersion targetCompatibility = JavaVersion.current().isJava8Compatible() 10 | ? JavaVersion.VERSION_1_8 : JavaVersion.VERSION_1_6 11 | 12 | void sourceCompatibility(Object value) { 13 | sourceCompatibility = value 14 | } 15 | 16 | void targetCompatibility(Object value) { 17 | targetCompatibility = value 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | # org.gradle.debug=true -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /kotlin/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /library/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /micro-module/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /kotlin/src/androidTest/java/com/eastwood/demo/kotlin/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.eastwood.demo.kotlin; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.eastwood.demo.kotlin.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/eastwood/demo/app/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.eastwood.demo.app; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.widget.Toast; 6 | 7 | import com.eastwood.common.autoinject.AutoTarget; 8 | import com.eastwood.common.mis.MisService; 9 | import com.eastwood.demo.library.ILibraryService; 10 | import com.eastwood.demo.library.LibraryModel; 11 | 12 | /** 13 | * @author eastwood 14 | * createDate: 2018-06-08 15 | */ 16 | public class MainActivity extends Activity { 17 | 18 | @Override 19 | protected void onCreate(Bundle savedInstanceState) { 20 | super.onCreate(savedInstanceState); 21 | setContentView(R.layout.activity_main); 22 | 23 | register(); 24 | 25 | ILibraryService libraryService = MisService.getService(ILibraryService.class); 26 | LibraryModel libraryModel = libraryService.getLibraryInfo(); 27 | Toast.makeText(this, libraryModel.getName(), Toast.LENGTH_LONG).show(); 28 | } 29 | 30 | @AutoTarget 31 | private void register() { 32 | 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /mis-plugin/src/main/groovy/com/eastwood/tools/plugins/mis/CompileMisTask.groovy: -------------------------------------------------------------------------------- 1 | package com.eastwood.tools.plugins.mis 2 | 3 | import com.eastwood.tools.plugins.mis.core.JarUtil 4 | import com.eastwood.tools.plugins.mis.core.PublicationManager 5 | import com.eastwood.tools.plugins.mis.core.extension.Publication 6 | import org.gradle.api.DefaultTask 7 | import org.gradle.api.tasks.TaskAction 8 | 9 | class CompileMisTask extends DefaultTask { 10 | 11 | Publication publication 12 | 13 | @TaskAction 14 | void compileSource() { 15 | def project = getProject() 16 | def releaseJar = JarUtil.packJavaSourceJar(project, publication, MisPlugin.androidJarPath, MisPlugin.misExtension.compileOptions, false) 17 | if (releaseJar == null) { 18 | throw new RuntimeException("nothing to push.") 19 | } 20 | JarUtil.packJavaDocSourceJar(publication) 21 | 22 | PublicationManager publicationManager = PublicationManager.getInstance() 23 | if (publication.versionNew != null) { 24 | publication.version = publication.versionNew 25 | } 26 | publicationManager.addPublication(publication) 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /micro-module/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'micro-module' 2 | apply plugin: 'com.android.library' 3 | 4 | apply plugin: 'kotlin-android' 5 | apply plugin: 'kotlin-android-extensions' 6 | 7 | android { 8 | compileSdkVersion 27 9 | 10 | defaultConfig { 11 | minSdkVersion 14 12 | targetSdkVersion 27 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | androidExtensions { 17 | experimental = true 18 | } 19 | } 20 | 21 | buildTypes { 22 | release { 23 | minifyEnabled false 24 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 25 | } 26 | } 27 | 28 | compileOptions { 29 | sourceCompatibility JavaVersion.VERSION_1_8 30 | targetCompatibility JavaVersion.VERSION_1_8 31 | } 32 | 33 | } 34 | 35 | microModule { 36 | include ':p_kotlin' 37 | 38 | include ':p_common' 39 | 40 | } 41 | 42 | dependencies { 43 | implementation 'com.eastwood.common:auto-inject:1.0.0' 44 | 45 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 46 | 47 | implementation project(':mis-core') 48 | // or 49 | // implementation 'com.eastwood.common:mis:1.0.0' 50 | 51 | } 52 | -------------------------------------------------------------------------------- /mis-plugin/src/main/groovy/com/eastwood/tools/plugins/mis/core/extension/Publication.groovy: -------------------------------------------------------------------------------- 1 | package com.eastwood.tools.plugins.mis.core.extension 2 | 3 | import com.eastwood.tools.plugins.mis.core.SourceSet 4 | import org.gradle.util.ConfigureUtil 5 | 6 | class Publication { 7 | 8 | String name 9 | String sourceSetName 10 | File buildDir 11 | 12 | String project 13 | 14 | SourceSet misSourceSet 15 | 16 | String groupId 17 | String artifactId 18 | String version 19 | 20 | String versionNew 21 | 22 | Dependencies dependencies 23 | 24 | Closure sourceFilter 25 | 26 | boolean invalid 27 | boolean hit 28 | boolean useLocal 29 | 30 | Publication(final String name) { 31 | this.name = name 32 | } 33 | 34 | void groupId(String groupId) { 35 | this.groupId = groupId 36 | } 37 | 38 | void artifactId(String artifactId) { 39 | this.artifactId = artifactId 40 | } 41 | 42 | void version(String version) { 43 | this.version = version 44 | } 45 | 46 | void dependencies(Closure closure) { 47 | dependencies = new Dependencies() 48 | ConfigureUtil.configure(closure, dependencies) 49 | } 50 | 51 | void sourceFilter(Closure closure) { 52 | this.sourceFilter = closure 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /kotlin/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | apply plugin: 'kotlin-android' 4 | 5 | apply plugin: 'kotlin-android-extensions' 6 | 7 | android { 8 | compileSdkVersion 27 9 | 10 | defaultConfig { 11 | minSdkVersion 14 12 | targetSdkVersion 27 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | androidExtensions { 17 | experimental = true 18 | } 19 | 20 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 21 | 22 | } 23 | 24 | buildTypes { 25 | release { 26 | minifyEnabled false 27 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 28 | } 29 | } 30 | 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | } 36 | 37 | dependencies { 38 | implementation fileTree(dir: 'libs', include: ['*.jar']) 39 | 40 | implementation 'com.android.support:appcompat-v7:27.1.1' 41 | testImplementation 'junit:junit:4.12' 42 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 43 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 44 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 45 | } 46 | -------------------------------------------------------------------------------- /mis-plugin/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'groovy' 2 | 3 | group 'com.eastwood.tools.plugins' 4 | 5 | repositories { 6 | jcenter() 7 | google() 8 | } 9 | 10 | dependencies { 11 | compile gradleApi() 12 | compile localGroovy() 13 | compile 'com.android.tools.build:gradle:3.0.0' 14 | compile 'org.jetbrains.kotlin:kotlin-compiler-embeddable:1.3.10' 15 | } 16 | 17 | apply plugin: 'maven' 18 | 19 | def groupId = 'com.eastwood.tools.plugins' 20 | def artifactId = 'mis' 21 | def version = '2.1.0' 22 | 23 | def localReleaseDest = "${buildDir}/release/${version}" 24 | 25 | uploadArchives { 26 | repositories { 27 | mavenDeployer { 28 | pom.groupId = groupId 29 | pom.artifactId = artifactId 30 | pom.version = version 31 | // Add other pom properties here if you want (developer details / licenses) 32 | repository(url: "file://${localReleaseDest}") 33 | } 34 | } 35 | } 36 | 37 | 38 | task zipRelease(type: Zip) { 39 | from localReleaseDest 40 | destinationDir buildDir 41 | archiveName "release-${version}.zip" 42 | } 43 | 44 | task generateRelease { 45 | doLast { 46 | println "Release ${version} can be found at ${localReleaseDest}/" 47 | println "Release ${version} zipped can be found ${buildDir}/release-${version}.zip" 48 | } 49 | } 50 | 51 | generateRelease.dependsOn(uploadArchives) 52 | generateRelease.dependsOn(zipRelease) -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 27 5 | defaultConfig { 6 | minSdkVersion 14 7 | targetSdkVersion 27 8 | versionCode 1 9 | versionName "1.0" 10 | } 11 | 12 | buildTypes { 13 | release { 14 | minifyEnabled false 15 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 16 | } 17 | } 18 | 19 | } 20 | 21 | dependencies { 22 | implementation 'com.eastwood.common:auto-inject:1.0.0' 23 | 24 | implementation project(':mis-core') 25 | // or 26 | // implementation 'com.eastwood.common:mis:1.0.0' 27 | 28 | implementation project(':library') 29 | 30 | implementation misPublication('com.eastwood.demo:library-sdk') 31 | implementation misPublication('com.eastwood.demo:kotlin-sdk') 32 | // implementation misPublication('com.eastwood.demo:module-main-sdk') 33 | // implementation misPublication('com.eastwood.demo:module-common-sdk') 34 | // implementation misPublication('com.eastwood.demo:module-kotlin-sdk') 35 | 36 | // after library-sdk push to maven, you need specify a version. 37 | // e.g. 38 | // implementation misPublication('com.eastwood.demo:library-sdk:1.0.0-SNAPSHOT') 39 | // or 40 | // implementation 'com.eastwood.demo:library-sdk:1.0.0-SNAPSHOT' 41 | 42 | } 43 | 44 | 45 | apply plugin: 'auto-inject' 46 | 47 | autoInject { 48 | showLog = true 49 | ignorePackages = ['android', 'com/google'] 50 | } 51 | -------------------------------------------------------------------------------- /mis-plugin/src/main/groovy/com/eastwood/tools/plugins/mis/core/extension/MisExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.eastwood.tools.plugins.mis.core.extension 2 | 3 | import org.gradle.api.Action 4 | import org.gradle.api.NamedDomainObjectContainer 5 | import org.gradle.api.Project 6 | import org.gradle.api.artifacts.dsl.RepositoryHandler 7 | import org.gradle.util.ConfigureUtil 8 | 9 | class MisExtension { 10 | 11 | int compileSdkVersion 12 | CompileOptions compileOptions 13 | Action configure 14 | 15 | Project childProject 16 | OnMisExtensionListener listener 17 | Map publicationMap 18 | 19 | MisExtension(OnMisExtensionListener listener) { 20 | this.listener = listener 21 | this.publicationMap = new HashMap<>() 22 | compileOptions = new CompileOptions() 23 | } 24 | 25 | void compileSdkVersion(int version) { 26 | compileSdkVersion = version 27 | } 28 | 29 | void publications(Closure closure) { 30 | NamedDomainObjectContainer publications = childProject.container(Publication) 31 | ConfigureUtil.configure(closure, publications) 32 | publications.each { 33 | listener.onPublicationAdded(childProject, it) 34 | } 35 | } 36 | 37 | void compileOptions(Closure closure) { 38 | ConfigureUtil.configure(closure, compileOptions) 39 | } 40 | 41 | void repositories(Action configure) { 42 | this.configure = configure 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /mis-plugin/src/main/groovy/com/eastwood/tools/plugins/mis/core/RuntimeUtil.groovy: -------------------------------------------------------------------------------- 1 | package com.eastwood.tools.plugins.mis.core 2 | 3 | import org.apache.commons.io.IOUtils 4 | 5 | import java.util.concurrent.ExecutorService 6 | import java.util.concurrent.Executors 7 | 8 | public final class RuntimeUtil { 9 | private RuntimeUtil() { 10 | 11 | } 12 | 13 | public static int exec(String[] cmdArray, String[] env, File dir) { 14 | Process process = null; 15 | try { 16 | process = Runtime.getRuntime().exec(cmdArray, env, dir); 17 | readStream(process.getInputStream(), process.getErrorStream()); 18 | return process.waitFor() 19 | } finally { 20 | if (process != null) { 21 | process.destroy() 22 | } 23 | } 24 | } 25 | 26 | private static void readStream(InputStream... inputStreams) { 27 | final ExecutorService executor = Executors.newFixedThreadPool(inputStreams.length) 28 | inputStreams.each { 29 | executor.execute(new Runnable() { 30 | @Override 31 | void run() { 32 | try { 33 | BufferedReader br = new BufferedReader(new InputStreamReader(it, "GBK")); 34 | String line 35 | while ((line = br.readLine()) != null) { 36 | println(line) 37 | } 38 | } finally { 39 | IOUtils.close(it) 40 | } 41 | } 42 | }) 43 | }; 44 | executor.shutdown() 45 | } 46 | } -------------------------------------------------------------------------------- /micro-module/mis.gradle: -------------------------------------------------------------------------------- 1 | mis { 2 | publications { 3 | 'main/src/main' { 4 | groupId 'com.eastwood.demo' 5 | artifactId 'module-main-sdk' 6 | // version '1.0.0' // 初次配置时不设置,发布至maven时设置 7 | 8 | dependencies { 9 | // 只支持 compileOnly 和 implementation 10 | 11 | // compileOnly 'com.google.code.gson:gson:2.8.1' 12 | // or 13 | // implementation 'com.google.code.gson:gson:2.8.1' 14 | 15 | // compileOnly misPublication('com.eastwood.demo:module-main-sdk') 16 | 17 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 18 | } 19 | } 20 | 21 | 'p_common/src/main' { 22 | groupId 'com.eastwood.demo' 23 | artifactId 'module-common-sdk' 24 | // version '1.0.0' // 初次配置时不设置,发布至maven时设置 25 | 26 | dependencies { 27 | // 只支持 compileOnly 和 implementation 28 | 29 | // compileOnly 'com.google.code.gson:gson:2.8.1' 30 | // or 31 | // implementation 'com.google.code.gson:gson:2.8.1' 32 | 33 | // compileOnly misPublication('com.eastwood.demo:module-main-sdk') 34 | 35 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 36 | } 37 | } 38 | 39 | 'p_kotlin/src/main' { 40 | groupId 'com.eastwood.demo' 41 | artifactId 'module-kotlin-sdk' 42 | // version '1.0.0' // 初次配置时不设置,发布至maven时设置 43 | 44 | dependencies { 45 | // 只支持 compileOnly 和 implementation 46 | 47 | // compileOnly 'com.google.code.gson:gson:2.8.1' 48 | // or 49 | // implementation 'com.google.code.gson:gson:2.8.1' 50 | 51 | // compileOnly misPublication('com.eastwood.demo:module-main-sdk') 52 | 53 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 54 | } 55 | } 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /mis-core/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 27 5 | 6 | defaultConfig { 7 | minSdkVersion 14 8 | targetSdkVersion 27 9 | versionCode 1 10 | versionName '1.0.1' 11 | } 12 | 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | implementation 'com.android.support:appcompat-v7:27.1.1' 23 | } 24 | 25 | 26 | //apply plugin: 'maven' 27 | // 28 | //def groupId = 'com.eastwood.common' 29 | //def artifactId = 'mis' 30 | //def version = '1.0.0' 31 | // 32 | //def localReleaseDest = "${buildDir}/release/${version}" 33 | // 34 | //task androidJavadocs(type: Javadoc) { 35 | // failOnError = false 36 | // source = android.sourceSets.main.java.srcDirs 37 | // ext.androidJar = "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar" 38 | // classpath += files(ext.androidJar) 39 | //} 40 | // 41 | //task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { 42 | // classifier = 'javadoc' 43 | // from androidJavadocs.destinationDir 44 | //} 45 | // 46 | //task androidSourcesJar(type: Jar) { 47 | // classifier = 'sources' 48 | // from android.sourceSets.main.java.srcDirs 49 | //} 50 | // 51 | //uploadArchives { 52 | // repositories.mavenDeployer { 53 | // pom.groupId = groupId 54 | // pom.artifactId = artifactId 55 | // pom.version = version 56 | // // Add other pom properties here if you want (developer details / licenses) 57 | // repository(url: "file://${localReleaseDest}") 58 | // } 59 | //} 60 | // 61 | //task zipRelease(type: Zip) { 62 | // from localReleaseDest 63 | // destinationDir buildDir 64 | // archiveName "release-${version}.zip" 65 | //} 66 | // 67 | //task generateRelease { 68 | // doLast { 69 | // println "Release ${version} can be found at ${localReleaseDest}/" 70 | // println "Release ${version} zipped can be found ${buildDir}/release-${version}.zip" 71 | // } 72 | //} 73 | // 74 | //generateRelease.dependsOn(uploadArchives) 75 | //generateRelease.dependsOn(zipRelease) 76 | // 77 | // 78 | //artifacts { 79 | // archives androidSourcesJar 80 | // archives androidJavadocsJar 81 | //} -------------------------------------------------------------------------------- /mis-plugin/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /mis-core/src/main/java/com/eastwood/common/mis/MisService.java: -------------------------------------------------------------------------------- 1 | package com.eastwood.common.mis; 2 | 3 | import android.support.annotation.NonNull; 4 | import android.support.v4.util.ArrayMap; 5 | 6 | import java.lang.ref.WeakReference; 7 | 8 | public class MisService { 9 | 10 | private static ArrayMap sServiceArrayMap; 11 | private static ArrayMap> sWeekServiceArrayMap; 12 | 13 | public static void register(@NonNull Class serviceKey, @NonNull Object serviceObjectOrClass) { 14 | if (!serviceKey.isInterface()) { 15 | throw new IllegalArgumentException("register service key must be interface class."); 16 | } 17 | 18 | if (serviceObjectOrClass.getClass().isInterface()) { 19 | throw new IllegalArgumentException("register service object must not be interface."); 20 | } 21 | 22 | Class realClass = serviceObjectOrClass instanceof Class ? (Class) serviceObjectOrClass : serviceObjectOrClass.getClass(); 23 | if (!serviceKey.isAssignableFrom(realClass)) { 24 | throw new IllegalArgumentException(String.format("register service object must implement interface %s.", serviceKey)); 25 | } 26 | 27 | if (sServiceArrayMap == null) { 28 | sServiceArrayMap = new ArrayMap<>(); 29 | } 30 | sServiceArrayMap.put(serviceKey, serviceObjectOrClass); 31 | } 32 | 33 | public static void unregister(Class serviceKey) { 34 | if (serviceKey == null || sServiceArrayMap == null) return; 35 | sServiceArrayMap.remove(serviceKey); 36 | } 37 | 38 | public static T getService(Class serviceKey) { 39 | if (sServiceArrayMap == null) return null; 40 | 41 | Object object = sServiceArrayMap.get(serviceKey); 42 | if (object == null) return null; 43 | 44 | if (object instanceof Class) { 45 | Object result = null; 46 | if (sWeekServiceArrayMap == null) { 47 | sWeekServiceArrayMap = new ArrayMap<>(); 48 | } 49 | WeakReference cachedObject = sWeekServiceArrayMap.get(serviceKey); 50 | if (cachedObject != null && cachedObject.get() != null) { 51 | result = cachedObject.get(); 52 | } else { 53 | try { 54 | result = ((Class) object).newInstance(); 55 | sWeekServiceArrayMap.put(serviceKey, new WeakReference<>(result)); 56 | } catch (IllegalAccessException e) { 57 | e.printStackTrace(); 58 | } catch (InstantiationException e) { 59 | e.printStackTrace(); 60 | } 61 | } 62 | return (T) result; 63 | } else { 64 | return (T) object; 65 | } 66 | } 67 | 68 | } -------------------------------------------------------------------------------- /mis-plugin/src/main/groovy/com/eastwood/tools/plugins/mis/FlatDirModuleComponentSelector.groovy: -------------------------------------------------------------------------------- 1 | package com.eastwood.tools.plugins.mis 2 | 3 | import org.gradle.api.artifacts.ModuleIdentifier 4 | import org.gradle.api.artifacts.VersionConstraint 5 | import org.gradle.api.artifacts.component.ComponentIdentifier 6 | import org.gradle.api.artifacts.component.ModuleComponentIdentifier 7 | import org.gradle.api.artifacts.component.ModuleComponentSelector 8 | import org.gradle.api.attributes.AttributeContainer 9 | import org.gradle.api.capabilities.Capability 10 | import org.gradle.api.internal.artifacts.DefaultModuleIdentifier 11 | import org.gradle.api.internal.artifacts.ImmutableVersionConstraint 12 | import org.gradle.api.internal.artifacts.dependencies.DefaultImmutableVersionConstraint 13 | import org.gradle.api.internal.attributes.ImmutableAttributes 14 | import org.gradle.internal.component.external.model.DefaultModuleComponentSelector 15 | 16 | public class FlatDirModuleComponentSelector implements ModuleComponentSelector { 17 | 18 | private final ModuleIdentifier moduleIdentifier 19 | private final ImmutableVersionConstraint versionConstraint 20 | private final ImmutableAttributes attributes 21 | private final List requestedCapabilities 22 | private final int hashCode 23 | 24 | private FlatDirModuleComponentSelector(ModuleIdentifier module, ImmutableVersionConstraint version, ImmutableAttributes attributes, List requestedCapabilities) { 25 | assert module != null: "module cannot be null" 26 | 27 | assert version != null: "version cannot be null" 28 | 29 | assert attributes != null: "attributes cannot be null" 30 | 31 | assert requestedCapabilities != null: "capabilities cannot be null" 32 | 33 | this.moduleIdentifier = module 34 | this.versionConstraint = version 35 | this.attributes = attributes 36 | this.requestedCapabilities = requestedCapabilities 37 | this.hashCode = Objects.hash(version, module, attributes, requestedCapabilities) 38 | } 39 | 40 | public String getDisplayName() { 41 | String group = this.moduleIdentifier.getGroup() 42 | String module = this.moduleIdentifier.getName() 43 | String version = this.getVersion() 44 | StringBuilder builder = new StringBuilder(group.length() + module.length() + this.versionConstraint.getRequiredVersion().length() + 2) 45 | builder.append(group) 46 | builder.append(":") 47 | builder.append(module) 48 | if (version.length() > 0) { 49 | builder.append(":") 50 | builder.append(version) 51 | } 52 | 53 | if (this.versionConstraint.getBranch() != null) { 54 | builder.append(" (branch: ") 55 | builder.append(this.versionConstraint.getBranch()) 56 | builder.append(")") 57 | } 58 | 59 | return builder.toString() 60 | } 61 | 62 | public String getGroup() { 63 | return this.moduleIdentifier.getGroup() 64 | } 65 | 66 | public String getModule() { 67 | return this.moduleIdentifier.getName() 68 | } 69 | 70 | public String getVersion() { 71 | return this.versionConstraint.getRequiredVersion().isEmpty() ? this.versionConstraint.getPreferredVersion() : this.versionConstraint.getRequiredVersion() 72 | } 73 | 74 | public VersionConstraint getVersionConstraint() { 75 | return this.versionConstraint 76 | } 77 | 78 | public ModuleIdentifier getModuleIdentifier() { 79 | return this.moduleIdentifier 80 | } 81 | 82 | public AttributeContainer getAttributes() { 83 | return this.attributes 84 | } 85 | 86 | List getRequestedCapabilities() { 87 | return this.requestedCapabilities 88 | } 89 | 90 | public boolean matchesStrictly(ComponentIdentifier identifier) { 91 | assert identifier != null: "identifier cannot be null" 92 | 93 | if (identifier instanceof ModuleComponentIdentifier) { 94 | ModuleComponentIdentifier moduleComponentIdentifier = (ModuleComponentIdentifier) identifier 95 | if (this.moduleIdentifier.getGroup() == moduleComponentIdentifier.getGroup() 96 | && this.moduleIdentifier.getName() == moduleComponentIdentifier.getModule()) { 97 | return true 98 | } 99 | } 100 | return false 101 | } 102 | 103 | public boolean equals(Object o) { 104 | if (o != null && o instanceof DefaultModuleComponentSelector) { 105 | DefaultModuleComponentSelector selector = (DefaultModuleComponentSelector) o 106 | if (this.moduleIdentifier.getName() == selector.moduleIdentifier.getName() && 107 | this.moduleIdentifier.getGroup() == selector.moduleIdentifier.getGroup()) { 108 | return true 109 | } 110 | } 111 | return false 112 | } 113 | 114 | public int hashCode() { 115 | return this.hashCode 116 | } 117 | 118 | public String toString() { 119 | return this.getDisplayName() 120 | } 121 | 122 | public static ModuleComponentSelector newSelector(String name) { 123 | return new FlatDirModuleComponentSelector(DefaultModuleIdentifier.newId("", name), DefaultImmutableVersionConstraint.of(), ImmutableAttributes.EMPTY, new ArrayList()) 124 | } 125 | } -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /mis-plugin/src/main/groovy/com/eastwood/tools/plugins/mis/core/MisUtil.groovy: -------------------------------------------------------------------------------- 1 | package com.eastwood.tools.plugins.mis.core 2 | 3 | import com.android.build.gradle.AppExtension 4 | import com.android.build.gradle.BaseExtension 5 | import com.android.build.gradle.LibraryExtension 6 | import org.gradle.api.Project 7 | 8 | class MisUtil { 9 | 10 | static addMisSourceSets(Project project) { 11 | BaseExtension baseExtension = project.extensions.getByName('android') 12 | addMisSourceSets(baseExtension, 'main') 13 | if(baseExtension instanceof AppExtension) { 14 | AppExtension appExtension = (AppExtension) baseExtension 15 | appExtension.getApplicationVariants().each { 16 | addMisSourceSets(baseExtension, it.buildType.name) 17 | it.productFlavors.each { 18 | addMisSourceSets(baseExtension, it.name) 19 | } 20 | if(it.productFlavors.size() >= 1) { 21 | if(it.productFlavors.size() > 1) { 22 | addMisSourceSets(baseExtension, it.flavorName) 23 | } 24 | addMisSourceSets(baseExtension, it.name) 25 | } 26 | } 27 | } else if(baseExtension instanceof LibraryExtension) { 28 | LibraryExtension libraryExtension = (LibraryExtension) baseExtension 29 | libraryExtension.getLibraryVariants().each { 30 | addMisSourceSets(baseExtension, it.buildType.name) 31 | it.productFlavors.each { 32 | addMisSourceSets(baseExtension, it.name) 33 | } 34 | if(it.productFlavors.size() >= 1) { 35 | if(it.productFlavors.size() > 1) { 36 | addMisSourceSets(baseExtension, it.flavorName) 37 | } 38 | addMisSourceSets(baseExtension, it.name) 39 | } 40 | } 41 | } 42 | 43 | } 44 | 45 | static addMisSourceSets(BaseExtension baseExtension, String name) { 46 | def obj = baseExtension.sourceSets.getByName(name) 47 | obj.java.srcDirs.each { 48 | obj.aidl.srcDirs(it.absolutePath.replace('java', 'mis')) 49 | } 50 | } 51 | 52 | static boolean hasAndroidPlugin(Project project) { 53 | if (project.plugins.findPlugin("com.android.application") || project.plugins.findPlugin("android") || 54 | project.plugins.findPlugin("com.android.test")) { 55 | return true 56 | } else if (project.plugins.findPlugin("com.android.library") || project.plugins.findPlugin("android-library")) { 57 | return true 58 | } else { 59 | return false 60 | } 61 | } 62 | 63 | static String getAndroidJarPath(Project project, int compileSdkVersion) { 64 | def androidHome 65 | def env = System.getenv() 66 | if (env['ANDROID_HOME'] != null) { 67 | androidHome = env['ANDROID_HOME'] 68 | } else { 69 | def localProperties = new File(project.rootProject.rootDir, "local.properties") 70 | if (localProperties.exists()) { 71 | Properties properties = new Properties() 72 | localProperties.withInputStream { instr -> 73 | properties.load(instr) 74 | } 75 | androidHome = properties.getProperty('sdk.dir') 76 | } 77 | } 78 | 79 | if (compileSdkVersion == 0) { 80 | throw new RuntimeException("mis compileSdkVersion is not specified.") 81 | } 82 | 83 | def androidJar = new File(androidHome, "/platforms/android-${compileSdkVersion}/android.jar") 84 | if (!androidJar.exists()) { 85 | throw new RuntimeException("Failed to find Platform SDK with path: platforms;android-$compileSdkVersion") 86 | } 87 | return androidJar.absolutePath 88 | } 89 | 90 | static void copyFile(File source, File target) { 91 | try { 92 | InputStream input = new FileInputStream(source) 93 | OutputStream output = new FileOutputStream(target) 94 | byte[] buf = new byte[1024] 95 | int bytesRead 96 | while ((bytesRead = input.read(buf)) > 0) { 97 | output.write(buf, 0, bytesRead) 98 | } 99 | input.close() 100 | output.close() 101 | } catch (IOException e) { 102 | e.printStackTrace() 103 | } 104 | } 105 | 106 | static String[] filterGAV(Object value) { 107 | String groupId = null, artifactId = null, version = null 108 | if (value instanceof String) { 109 | String[] values = value.split(":") 110 | if (values.length >= 3) { 111 | groupId = values[0] 112 | artifactId = values[1] 113 | version = values[2] 114 | } else if (values.length == 2) { 115 | groupId = values[0] 116 | artifactId = values[1] 117 | version = null 118 | } 119 | } else if (value instanceof Map) { 120 | groupId = value.groupId 121 | artifactId = value.artifactId 122 | version = value.version 123 | } 124 | 125 | if (version == "") { 126 | version = null 127 | } 128 | 129 | if (groupId == null || artifactId == null) { 130 | throw new IllegalArgumentException("'${value}' is illege argument of misPublication(), the following types/formats are supported:" + 131 | "\n - String or CharSequence values, for example 'org.gradle:gradle-core:1.0'." + 132 | "\n - Maps, for example [groupId: 'org.gradle', artifactId: 'gradle-core', version: '1.0'].") 133 | } 134 | 135 | return [groupId, artifactId, version] 136 | } 137 | 138 | } -------------------------------------------------------------------------------- /mis-plugin/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MIS 2 | 模块接口服务(Module Interface Service) 3 | 4 | MIS主要解决的问题是如何在一个模块内维护其对外暴露的接口(包括打包发布),而不是把接口和接口实现分离到两个不同的模块。 5 | 6 | 7 | 8 | ## Usage 9 | 10 | #### 引用 mis 插件 11 | 12 | 在根项目的build.gradle中添加mis插件的**classpath**: 13 | ``` 14 | buildscript { 15 | dependencies { 16 | ... 17 | classpath 'com.eastwood.tools.plugins:mis:2.1.0' 18 | } 19 | } 20 | ``` 21 | 22 | 在根项目的build.gradle中添加mis插件的**相关配置**: 23 | ``` 24 | ... 25 | 26 | apply plugin: 'mis' 27 | 28 | mis { 29 | 30 | compileSdkVersion 27 31 | 32 | compileOptions { 33 | sourceCompatibility JavaVersion.VERSION_1_8 34 | targetCompatibility JavaVersion.VERSION_1_8 35 | } 36 | 37 | } 38 | ``` 39 | * compileSdkVersion 同 android { compileSdkVersion ... } 40 | * compileOptions 同 android { compileOptions { ... } } 41 | 42 | #### 创建 mis 目录 43 | 44 | **Gradle Sync**后,在**java**同级目录创建**mis**文件夹 45 | 46 | 47 | 48 | #### 定义接口,并实现接口服务 49 | 50 | 直接在**mis**文件夹下,创建对应的包名、接口类和数据Model。并在**java**文件夹下实现接口服务。 51 | 52 | 53 | 54 | #### 在模块目录内,创建单独的mis.gradle文件, 并在内声明mis对应的publication 55 | 56 | mis.gradle: 57 | ``` 58 | mis { 59 | publications { 60 | main { 61 | groupId 'com.eastwood.demo' 62 | artifactId 'library-sdk' 63 | // version '1.0.0-SNAPSHOT' 64 | 65 | dependencies { 66 | compileOnly 'com.google.code.gson:gson:2.8.1' 67 | } 68 | } 69 | } 70 | ... 71 | } 72 | ``` 73 | 74 | * `main`指的是`src/main/java`中的`main`,除了`main`之外,其值还可以为 build types和product flavors对应的值,即对应目录下的mis。比如与`src/debug/java`对应的`src/debug/mis`。 75 | 76 | * `groupId`、`artifactId`、`version`对应的是Maven的[GAV](https://maven.apache.org/guides/mini/guide-naming-conventions.html)。**初次配置时不设置`version`,发布至maven时设置`version`。** 77 | 78 | * 在`dependencies`中可声明该mis Publication编译和运行时需用到的第三方库,仅支持`compileOnly`和`implementation`。如果mis文件夹下的类使用了kotlin语法,需要添加kotlin相关的依赖,比如'org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version'。 79 | 80 | #### 发布mis publication 至 Maven 81 | 在根项目的build.gradle中添加mis插件的**repositories配置**: 82 | ``` 83 | mis { 84 | 85 | compileSdkVersion 27 86 | ... 87 | 88 | repositories { 89 | maven { 90 | url "http://***" 91 | credentials { 92 | username '***' 93 | password '***' 94 | } 95 | } 96 | } 97 | ... 98 | } 99 | ``` 100 | 101 | * 发布用到的插件是`maven-publish`,其中`repositories`相关设置请查阅[# Maven Publish Plugin](https://docs.gradle.org/current/userguide/publishing_maven.html#publishing_maven:repositories) 102 | 103 | **Gradle Sync**后,打开Gradle Tasks View,选择**publishMis...PublicationToMavenRepository**执行发布任务。 104 | 105 | 106 | 107 | 其中publishMis...PublicationToMavenLocal 是发布至本地maven。如果使用本地maven,请将`mavenLocal()`添加至根项目的build.gradle中,比如: 108 | ``` 109 | allprojects { 110 | repositories { 111 | google() 112 | jcenter() 113 | mavenLocal() 114 | } 115 | } 116 | ``` 117 | 118 | *** 119 | 上文介绍了如何通过mis插件创建接口并发布到maven,接下来介绍接口的注册和调用。 120 | 121 | #### 注册接口服务 122 | 注册接口需要有个服务容器来管理接口和接口的实现对象。mis提供了一个简单的**MisService**服务容器,可根据自己项目实际情况自行选用。 123 | 124 | 在模块build.gradle中添加**MisService**服务容器库的引用: 125 | ``` 126 | dependencies { 127 | implementation 'com.eastwood.common:mis:1.0.0' 128 | } 129 | ``` 130 | 131 | 在**MisService**服务容器中注册服务,可以使用 服务接口 + 服务接口的实现对象 **或** 服务接口的实现类 进行注册,例如: 132 | ``` 133 | // 服务接口 + 服务接口的实现对象 134 | MisService.register(ILibraryService.class, new LibraryService()); 135 | 136 | // 服务接口 + 服务接口的实现类 137 | MisService.register(ILibraryService.class, LibraryService.class); 138 | ``` 139 | 140 | 在Demo样例中,接口所在的模块通过AutoInject在编译期主动注册接口。(这推荐下# **[AutoInject](https://github.com/EastWoodYang/AutoInject)**) 141 | 142 | #### 获取接口服务 143 | 144 | 在其他模块build.gradle中添加mis库,以及发布至maven的接口库: 145 | 146 | ``` 147 | dependencies { 148 | implementation 'com.eastwood.common:mis:1.0.0' 149 | implementation 'com.eastwood.demo:library-sdk:1.0.0-SNAPSHOT' 150 | } 151 | ``` 152 | 153 | Gradle Sync后,就可以通过接口在**MisService**服务容器中查找对应的接口服务并调用,比如: 154 | ``` 155 | ILibraryService libraryService = MisService.getService(ILibraryService.class); 156 | libraryService.getLibraryInfo() 157 | ``` 158 | 159 | ## Q&A 160 | #### 1.mis目录下的类会参与编译吗? 161 | 不会。虽然`mis`目录下的类能被`java`目录下的类直接引用,但不会参与编译,真正参与编译的是该`mis`目录生成的jar包,其位于当前工程`.gradle/mis`下。在当前工程Sync&Build的时候,mis插件会对这些配置了publication的`mis`目录进行编译打包生成jar包,并且依赖该jar包。 162 | 163 | `mis`目录下的类之所以能被`java`目录下的类直接引用,是因为`mis`目录被设置为sourceSets aidl的src目录,而Android Studio对sourceSets aidl的src目录有特别支持。 164 | 165 | #### 2.没有Maven私服,所有模块都在一个工程下,其他模块怎么引用接口? 166 | 不设置`publication`的`version`。通过`misPublication`声明依赖,比如: 167 | ``` 168 | dependencies { 169 | ... 170 | implementation misPublication('com.eastwood.demo:library-sdk') 171 | } 172 | ``` 173 | `misPublication`运行机理是会自动在当前工程`.gradle/mis`下查找是否有对应的mis提供的jar包。如果有,就使用对应的mis提供的jar包;如果没有且指定了`version`,就使用maven上的jar包。 174 | 175 | #### 3.将接口发布到maven后,其他模块通过misPublication声明依赖,那jar包用的是`.gradle/mis`下的还是maven上的? 176 | 接口被发布到maven后,其`.gradle/mis`下的jar包会被删除,接口所在的模块根据`publication`中设置的GAV使用maven上的jar包。如果其他模块通过misPublication声明对其依赖,比如: 177 | ``` 178 | dependencies { 179 | ... 180 | implementation misPublication('com.eastwood.demo:library-sdk') 181 | // 或 implementation misPublication('com.eastwood.demo:library-sdk:1.0.0-SNAPSHOT') 182 | } 183 | ``` 184 | 不管`misPublication`中是否设置了的`version`,都会使用maven上的jar包,其版本同接口所在的模块`publication`中的GAV。 185 | 186 | 当`mis`目录下类发生实质性的修改后(生成不同的jar包),在当前工程Sync&Build的时,会在`.gradle/mis`下的重新生成jar包,接口所在的模块不管`publication`中是否设置`version`,都使用`.gradle/mis`下的jar包。如果其他模块通过misPublication声明对其依赖,不管`misPublication`中是否设置的`version`,都会使用`.gradle/mis`下的jar包。 187 | 188 | #### 4.为什么在Gradle Tasks View中找不到`publishing`相关发布Task? 189 | 初次发布时,请检查对应的`publication`是否已经设置的`version`,以及是否添加相关`repositories`。 190 | 191 | #### 5.在mis publication `dependencies {}`中声明的依赖会作用于mis publication所在的模块吗? 192 | mis publication `dependencies {}`是用于声明mis publication编译和运行时需用到的第三方库,但不会作用于mis publication所在的模块。 193 | 在开发的时候,`src/java`下的类能引用到mis依赖的第三方库类,但编译时会报错,提示找不到对应的类。 194 | 产生这种情况的原因是mis插件在Gradle sync时,将声明的依赖传递给mis publication所在的模块。但clean或build时不传递。 195 | 196 | #### 6.如果mis publication 依赖于其他mis publication怎么处理? 197 | 建议使用`misPublication`包裹GAV,比如: 198 | ``` 199 | mis { 200 | publications { 201 | main { 202 | groupId 'com.eastwood.demo' 203 | artifactId 'library-sdk' 204 | 205 | dependencies { 206 | implementation misPublication('com.eastwood.demo:module-main-sdk:1.0.0') 207 | } 208 | } 209 | } 210 | 211 | repositories { 212 | ... 213 | } 214 | 215 | } 216 | ``` 217 | 这里`misPublication`的作用是,如果所依赖的mis publication也在当前项目,那么与其所提供jar包的方式保持一致。 218 | 219 | ## Question or Idea 220 | 有问题或想法可以直接加我微信: EastWoodYang 221 | 222 | ## License 223 | 224 | ``` 225 | Copyright 2018 EastWood Yang 226 | 227 | Licensed under the Apache License, Version 2.0 (the "License"); 228 | you may not use this file except in compliance with the License. 229 | You may obtain a copy of the License at 230 | 231 | http://www.apache.org/licenses/LICENSE-2.0 232 | 233 | Unless required by applicable law or agreed to in writing, software 234 | distributed under the License is distributed on an "AS IS" BASIS, 235 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 236 | See the License for the specific language governing permissions and 237 | limitations under the License. 238 | ``` 239 | -------------------------------------------------------------------------------- /mis-plugin/src/main/groovy/com/eastwood/tools/plugins/mis/core/Digraph.java: -------------------------------------------------------------------------------- 1 | package com.eastwood.tools.plugins.mis.core; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.LinkedList; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.Queue; 9 | import java.util.Stack; 10 | 11 | /** 12 | * An example class for directed graphs. The vertex type can be specified. 13 | * There are no edge costs/weights. 14 | *

15 | * Written for CS211, Nov 2006. 16 | * 17 | * @author Paul Chew 18 | */ 19 | public class Digraph { 20 | 21 | /** 22 | * The implementation here is basically an adjacency list, but instead 23 | * of an array of lists, a Map is used to map each vertex to its list of 24 | * adjacent vertices. 25 | */ 26 | private Map> neighbors = new HashMap>(); 27 | 28 | /** 29 | * String representation of dependencyGraph. 30 | */ 31 | public String toString() { 32 | StringBuffer s = new StringBuffer(); 33 | for (V v : neighbors.keySet()) s.append("\n " + v + " -> " + neighbors.get(v)); 34 | return s.toString(); 35 | } 36 | 37 | /** 38 | * Add a vertex to the dependencyGraph. Nothing happens if vertex is already in dependencyGraph. 39 | */ 40 | public void add(V vertex) { 41 | if (neighbors.containsKey(vertex)) return; 42 | neighbors.put(vertex, new ArrayList()); 43 | } 44 | 45 | /** 46 | * True iff dependencyGraph contains vertex. 47 | */ 48 | public boolean contains(V vertex) { 49 | return neighbors.containsKey(vertex); 50 | } 51 | 52 | /** 53 | * Add an edge to the dependencyGraph; if either vertex does not exist, it's added. 54 | * This implementation allows the creation of multi-edges and self-loops. 55 | */ 56 | public void add(V from, V to) { 57 | this.add(from); 58 | this.add(to); 59 | neighbors.get(from).add(to); 60 | } 61 | 62 | /** 63 | * Remove an edge from the dependencyGraph. Nothing happens if no such edge. 64 | * 65 | * @throws IllegalArgumentException if either vertex doesn't exist. 66 | */ 67 | public void remove(V from, V to) { 68 | if (!(this.contains(from) && this.contains(to))) 69 | throw new IllegalArgumentException("Nonexistent vertex"); 70 | neighbors.get(from).remove(to); 71 | } 72 | 73 | /** 74 | * Report (as a Map) the out-degree of each vertex. 75 | */ 76 | public Map outDegree() { 77 | Map result = new HashMap(); 78 | for (V v : neighbors.keySet()) result.put(v, neighbors.get(v).size()); 79 | return result; 80 | } 81 | 82 | /** 83 | * Report (as a Map) the in-degree of each vertex. 84 | */ 85 | public Map inDegree() { 86 | Map result = new HashMap(); 87 | for (V v : neighbors.keySet()) result.put(v, 0); // All in-degrees are 0 88 | for (V from : neighbors.keySet()) { 89 | for (V to : neighbors.get(from)) { 90 | result.put(to, result.get(to) + 1); // Increment in-degree 91 | } 92 | } 93 | return result; 94 | } 95 | 96 | /** 97 | * Report (as a List) the topological sort of the vertices; null for no such sort. 98 | */ 99 | public List topSort() { 100 | Map degree = inDegree(); 101 | // Determine all vertices with zero in-degree 102 | Stack zeroVerts = new Stack(); // Stack as good as any here 103 | for (V v : degree.keySet()) { 104 | if (degree.get(v) == 0) zeroVerts.push(v); 105 | } 106 | // Determine the topological order 107 | List result = new ArrayList(); 108 | while (!zeroVerts.isEmpty()) { 109 | V v = zeroVerts.pop(); // Choose a vertex with zero in-degree 110 | result.add(v); // Vertex v is next in topol order 111 | // "Remove" vertex v by updating its neighbors 112 | for (V neighbor : neighbors.get(v)) { 113 | degree.put(neighbor, degree.get(neighbor) - 1); 114 | // Remember any vertices that now have zero in-degree 115 | if (degree.get(neighbor) == 0) zeroVerts.push(neighbor); 116 | } 117 | } 118 | // Check that we have used the entire dependencyGraph (if not, there was a cycle) 119 | if (result.size() != neighbors.size()) return null; 120 | return result; 121 | } 122 | 123 | /** 124 | * True iff dependencyGraph is a dag (directed acyclic dependencyGraph). 125 | */ 126 | public boolean isDag() { 127 | return topSort() != null; 128 | } 129 | 130 | /** 131 | * Report (as a Map) the bfs distance to each vertex from the start vertex. 132 | * The distance is an Integer; the value null is used to represent infinity 133 | * (implying that the corresponding node cannot be reached). 134 | */ 135 | public Map bfsDistance(V start) { 136 | Map distance = new HashMap(); 137 | // Initially, all distance are infinity, except start node 138 | for (V v : neighbors.keySet()) distance.put(v, null); 139 | distance.put(start, 0); 140 | // Process nodes in queue order 141 | Queue queue = new LinkedList(); 142 | queue.offer(start); // Place start node in queue 143 | while (!queue.isEmpty()) { 144 | V v = queue.remove(); 145 | int vDist = distance.get(v); 146 | // Update neighbors 147 | for (V neighbor : neighbors.get(v)) { 148 | if (distance.get(neighbor) != null) continue; // Ignore if already done 149 | distance.put(neighbor, vDist + 1); 150 | queue.offer(neighbor); 151 | } 152 | } 153 | return distance; 154 | } 155 | 156 | // /** 157 | // * Main program (for testing). 158 | // */ 159 | // public static void main (String[] args) { 160 | // // Create a Graph with Integer nodes 161 | // Digraph graph = new Digraph(); 162 | // graph.add(0, 1); graph.add(0, 2); graph.add(0, 3); 163 | // graph.add(1, 2); graph.add(1, 3); graph.add(2, 3); 164 | // graph.add(2, 4); graph.add(4, 5); graph.add(5, 6); // Tetrahedron with tail 165 | // System.out.println("The current dependencyGraph: " + graph); 166 | // System.out.println("In-degrees: " + graph.inDegree()); 167 | // System.out.println("Out-degrees: " + graph.outDegree()); 168 | // System.out.println("A topological sort of the vertices: " + graph.topSort()); 169 | // System.out.println("The dependencyGraph " + (graph.isDag()?"is":"is not") + " a dag"); 170 | // System.out.println("BFS distances starting from " + 0 + ": " + graph.bfsDistance(0)); 171 | // System.out.println("BFS distances starting from " + 1 + ": " + graph.bfsDistance(1)); 172 | // System.out.println("BFS distances starting from " + 2 + ": " + graph.bfsDistance(2)); 173 | // graph.add(4, 1); // Create a cycle 174 | // System.out.println("Cycle created"); 175 | // System.out.println("The current dependencyGraph: " + graph); 176 | // System.out.println("In-degrees: " + graph.inDegree()); 177 | // System.out.println("Out-degrees: " + graph.outDegree()); 178 | // System.out.println("A topological sort of the vertices: " + graph.topSort()); 179 | // System.out.println("The dependencyGraph " + (graph.isDag()?"is":"is not") + " a dag"); 180 | // System.out.println("BFS distances starting from " + 2 + ": " + graph.bfsDistance(2)); 181 | // } 182 | } -------------------------------------------------------------------------------- /mis-plugin/src/main/groovy/com/eastwood/tools/plugins/mis/core/PublicationManager.groovy: -------------------------------------------------------------------------------- 1 | package com.eastwood.tools.plugins.mis.core 2 | 3 | import com.eastwood.tools.plugins.mis.core.extension.Publication 4 | import org.gradle.api.GradleException 5 | import org.gradle.api.Project 6 | import org.w3c.dom.Document 7 | import org.w3c.dom.Element 8 | import org.w3c.dom.NodeList 9 | 10 | import javax.xml.parsers.DocumentBuilderFactory 11 | import javax.xml.transform.OutputKeys 12 | import javax.xml.transform.Transformer 13 | import javax.xml.transform.TransformerFactory 14 | import javax.xml.transform.dom.DOMSource 15 | import javax.xml.transform.stream.StreamResult 16 | 17 | class PublicationManager { 18 | 19 | private static PublicationManager sPublicationManager 20 | 21 | private File misDir 22 | private Map publicationManifest 23 | 24 | Digraph dependencyGraph 25 | Map publicationDependencies 26 | 27 | static getInstance() { 28 | if (sPublicationManager == null) { 29 | sPublicationManager = new PublicationManager() 30 | } 31 | return sPublicationManager 32 | } 33 | 34 | void loadManifest(Project rootProject, File misDir) { 35 | this.misDir = misDir 36 | 37 | publicationManifest = new HashMap<>() 38 | dependencyGraph = new Digraph() 39 | publicationDependencies = new HashMap<>() 40 | 41 | rootProject.gradle.buildFinished { 42 | if (it.failure != null) { 43 | return 44 | } 45 | saveManifest() 46 | } 47 | 48 | File publicationManifest = new File(misDir, 'publicationManifest.xml') 49 | if (!publicationManifest.exists()) { 50 | return 51 | } 52 | 53 | DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance() 54 | Document document = builderFactory.newDocumentBuilder().parse(publicationManifest) 55 | NodeList publicationNodeList = document.documentElement.getElementsByTagName("publication") 56 | for (int i = 0; i < publicationNodeList.getLength(); i++) { 57 | Element publicationElement = (Element) publicationNodeList.item(i) 58 | 59 | Publication publication = new Publication() 60 | publication.project = publicationElement.getAttribute("project") 61 | publication.groupId = publicationElement.getAttribute("groupId") 62 | publication.artifactId = publicationElement.getAttribute("artifactId") 63 | publication.version = publicationElement.getAttribute("version") 64 | if (publication.version == "") publication.version = null 65 | publication.invalid = Boolean.valueOf(publicationElement.getAttribute("invalid")) 66 | 67 | if (!publication.invalid) { 68 | NodeList sourceSetNodeList = publicationElement.getElementsByTagName("sourceSet") 69 | Element sourceSetElement = (Element) sourceSetNodeList.item(0) 70 | SourceSet sourceSet = new SourceSet() 71 | sourceSet.path = sourceSetElement.getAttribute("path") 72 | sourceSet.lastModifiedSourceFile = new HashMap<>() 73 | NodeList fileNodeList = sourceSetElement.getElementsByTagName("file") 74 | for (int k = 0; k < fileNodeList.getLength(); k++) { 75 | Element fileElement = (Element) fileNodeList.item(k) 76 | SourceFile sourceFile = new SourceFile() 77 | sourceFile.path = fileElement.getAttribute("path") 78 | sourceFile.lastModified = fileElement.getAttribute("lastModified").toLong() 79 | sourceSet.lastModifiedSourceFile.put(sourceFile.path, sourceFile) 80 | } 81 | publication.misSourceSet = sourceSet 82 | } 83 | 84 | this.publicationManifest.put(publication.groupId + '-' + publication.artifactId, publication) 85 | } 86 | 87 | } 88 | 89 | private void saveManifest() { 90 | if (!misDir.exists()) { 91 | misDir.mkdirs() 92 | } 93 | File publicationManifest = new File(misDir, 'publicationManifest.xml') 94 | 95 | DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance() 96 | Document document = builderFactory.newDocumentBuilder().newDocument() 97 | Element manifestElement = document.createElement("manifest") 98 | this.publicationManifest.each { 99 | Publication publication = it.value 100 | if (!publication.hit || publication.invalid) return 101 | 102 | Element publicationElement = document.createElement('publication') 103 | publicationElement.setAttribute('project', publication.project) 104 | publicationElement.setAttribute('groupId', publication.groupId) 105 | publicationElement.setAttribute('artifactId', publication.artifactId) 106 | publicationElement.setAttribute('version', publication.version) 107 | publicationElement.setAttribute('invalid', publication.invalid ? "true" : "false") 108 | 109 | if (!publication.invalid) { 110 | Element sourceSetElement = document.createElement('sourceSet') 111 | sourceSetElement.setAttribute('path', publication.misSourceSet.path) 112 | publication.misSourceSet.lastModifiedSourceFile.each { 113 | SourceFile sourceFile = it.value 114 | Element sourceFileElement = document.createElement('file') 115 | sourceFileElement.setAttribute('path', sourceFile.path) 116 | sourceFileElement.setAttribute('lastModified', sourceFile.lastModified.toString()) 117 | sourceSetElement.appendChild(sourceFileElement) 118 | } 119 | publicationElement.appendChild(sourceSetElement) 120 | } 121 | manifestElement.appendChild(publicationElement) 122 | } 123 | 124 | Transformer transformer = TransformerFactory.newInstance().newTransformer() 125 | transformer.setOutputProperty(OutputKeys.INDENT, "yes") 126 | transformer.setOutputProperty(OutputKeys.CDATA_SECTION_ELEMENTS, "yes") 127 | transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2") 128 | transformer.transform(new DOMSource(manifestElement), new StreamResult(publicationManifest)) 129 | } 130 | 131 | void addDependencyGraph(Publication publication) { 132 | def key = publication.groupId + '-' + publication.artifactId 133 | publicationDependencies.put(key, publication) 134 | dependencyGraph.add(key) 135 | if (publication.dependencies != null) { 136 | if (publication.dependencies.implementation != null) { 137 | publication.dependencies.implementation.each { 138 | if (it instanceof String && it.startsWith('mis-')) { 139 | String[] gav = MisUtil.filterGAV(it.replace('mis-', '')) 140 | dependencyGraph.add(key, gav[0] + '-' + gav[1]) 141 | if (!dependencyGraph.isDag()) { 142 | def misPublication = gav[0] + ':' + gav[1] + (gav[2] == null ? (":" + gav[2]) : "") 143 | throw new RuntimeException("Circular dependency between mis publication '${publication.groupId}:${publication.artifactId}' and '${misPublication}'.") 144 | } 145 | } 146 | } 147 | } 148 | 149 | if (publication.dependencies.compileOnly != null) { 150 | publication.dependencies.compileOnly.each { 151 | if (it instanceof String && it.startsWith('mis-')) { 152 | String[] gav = MisUtil.filterGAV(it.replace('mis-', '')) 153 | dependencyGraph.add(key, gav[0] + '-' + gav[1]) 154 | if (!dependencyGraph.isDag()) { 155 | def misPublication = gav[0] + ':' + gav[1] + (gav[2] == null ? (":" + gav[2]) : "") 156 | throw new RuntimeException("Circular dependency between mis publication '${publication.groupId}:${publication.artifactId}' and '${misPublication}'.") 157 | } 158 | } 159 | } 160 | } 161 | } 162 | } 163 | 164 | boolean hasModified(Publication publication) { 165 | Publication lastPublication = publicationManifest.get(publication.groupId + '-' + publication.artifactId) 166 | if (lastPublication == null) { 167 | return true 168 | } 169 | 170 | if (publication.invalid != lastPublication.invalid) { 171 | return true 172 | } 173 | 174 | return hasModifiedSourceSet(publication.misSourceSet, lastPublication.misSourceSet) 175 | } 176 | 177 | private boolean hasModifiedSourceSet(SourceSet sourceSet1, SourceSet sourceSet2) { 178 | return hasModifiedSourceFile(sourceSet1.lastModifiedSourceFile, sourceSet2.lastModifiedSourceFile) 179 | } 180 | 181 | private boolean hasModifiedSourceFile(Map map1, Map map2) { 182 | if (map1.size() != map2.size()) { 183 | return true 184 | } 185 | for (Map.Entry entry1 : map1.entrySet()) { 186 | SourceFile sourceFile1 = entry1.getValue() 187 | SourceFile sourceFile2 = map2.get(entry1.getKey()) 188 | if (sourceFile2 == null) { 189 | return true 190 | } 191 | if (sourceFile1.lastModified != sourceFile2.lastModified) { 192 | return true 193 | } 194 | } 195 | return false 196 | } 197 | 198 | void addPublication(Publication publication) { 199 | publicationManifest.put(publication.groupId + '-' + publication.artifactId, publication) 200 | } 201 | 202 | Publication getPublication(String groupId, String artifactId) { 203 | return publicationManifest.get(groupId + '-' + artifactId) 204 | } 205 | 206 | Publication getPublicationByKey(String key) { 207 | return publicationManifest.get(key) 208 | } 209 | 210 | List getPublicationByProject(Project project) { 211 | String displayName = project.getDisplayName() 212 | String projectName = displayName.substring(displayName.indexOf("'") + 1, displayName.lastIndexOf("'")) 213 | 214 | List publications = new ArrayList<>() 215 | publicationManifest.each { 216 | if (projectName == it.value.project) { 217 | publications.add(it.value) 218 | } 219 | } 220 | return publications 221 | } 222 | 223 | void hitPublication(Publication publication) { 224 | Publication existsPublication = publicationManifest.get(publication.groupId + '-' + publication.artifactId) 225 | if (existsPublication == null) return 226 | 227 | if (existsPublication.hit) { 228 | validPublication(publication, existsPublication) 229 | } else { 230 | existsPublication.hit = true 231 | } 232 | } 233 | 234 | private void validPublication(Publication publication, Publication existsPublication) { 235 | if (publication.project != existsPublication.project) { 236 | throw new GradleException("Already exists publication " + existsPublication.groupId + ":" + existsPublication.artifactId + " in project '${existsPublication.project}'.") 237 | } 238 | } 239 | 240 | } -------------------------------------------------------------------------------- /mis-plugin/src/main/groovy/com/eastwood/tools/plugins/mis/core/JarUtil.groovy: -------------------------------------------------------------------------------- 1 | package com.eastwood.tools.plugins.mis.core 2 | 3 | import com.eastwood.tools.plugins.mis.core.extension.CompileOptions 4 | import com.eastwood.tools.plugins.mis.core.extension.Publication 5 | import org.gradle.api.GradleException 6 | import org.gradle.api.JavaVersion 7 | import org.gradle.api.Project 8 | import org.gradle.api.artifacts.Configuration 9 | import org.gradle.internal.jvm.Jvm 10 | import org.jetbrains.kotlin.cli.common.ExitCode 11 | import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler 12 | 13 | import java.util.jar.JarEntry 14 | import java.util.jar.JarFile 15 | import java.util.zip.ZipFile 16 | 17 | class JarUtil { 18 | 19 | static File packJavaSourceJar(Project project, Publication publication, String androidJarPath, CompileOptions compileOptions, boolean vars) { 20 | publication.buildDir.deleteDir() 21 | publication.buildDir.mkdirs() 22 | def sourceDir = new File(publication.buildDir, "source") 23 | sourceDir.mkdirs() 24 | def classesDir = new File(publication.buildDir, "classes") 25 | classesDir.mkdirs() 26 | def outputsDir = new File(publication.buildDir, "outputs") 27 | outputsDir.mkdirs() 28 | 29 | def argFiles = [] 30 | filterJavaSource(new File(publication.misSourceSet.path), publication.misSourceSet.path, sourceDir, argFiles, publication.sourceFilter) 31 | 32 | if (argFiles.size() == 0) { 33 | return null 34 | } 35 | 36 | def name = "mis[${publication.groupId}-${publication.artifactId}]Classpath" 37 | Configuration configuration = project.configurations.create(name) 38 | if (publication.dependencies != null) { 39 | if (publication.dependencies.implementation != null) { 40 | publication.dependencies.implementation.each { 41 | project.dependencies.add(name, it) 42 | } 43 | } 44 | if (publication.dependencies.compileOnly != null) { 45 | publication.dependencies.compileOnly.each { 46 | project.dependencies.add(name, it) 47 | } 48 | } 49 | } 50 | def classPath = [androidJarPath] 51 | configuration.copy().files.each { 52 | if (it.name.endsWith('.aar')) { 53 | classPath << getAARClassesJar(it) 54 | } else { 55 | classPath << it.absolutePath 56 | } 57 | } 58 | project.configurations.remove(configuration) 59 | 60 | return generateJavaSourceJar(classesDir, argFiles, classPath, compileOptions, vars) 61 | } 62 | 63 | static File packJavaDocSourceJar(Publication publication) { 64 | def javaSource = new File(publication.buildDir, "javaSource") 65 | javaSource.deleteDir() 66 | javaSource.mkdirs() 67 | 68 | filterJavaDocSource(new File(publication.misSourceSet.path), publication.misSourceSet.path, javaSource) 69 | 70 | return generateJavaDocSourceJar(javaSource) 71 | } 72 | 73 | static boolean compareMavenJar(Project project, Publication publication, String localPath) { 74 | String filePath = null 75 | String fileName = publication.artifactId + "-" + publication.version + ".jar" 76 | def name = "mis[${publication.groupId}-${publication.artifactId}]Classpath" 77 | Configuration configuration = project.configurations.create(name) 78 | project.dependencies.add(name, publication.groupId + ":" + publication.artifactId + ":" + publication.version) 79 | configuration.copy().files.each { 80 | if (it.name.endsWith(fileName)) { 81 | filePath = it.absolutePath 82 | } 83 | } 84 | project.configurations.remove(configuration) 85 | 86 | if (filePath == null) return false 87 | return compareJar(localPath, filePath) 88 | } 89 | 90 | private static File generateJavaSourceJar(File classesDir, 91 | List argFiles, 92 | List classPath, 93 | CompileOptions compileOptions, 94 | boolean vars) { 95 | boolean keepParameters = vars && Jvm.current().javaVersion >= JavaVersion.VERSION_1_8 96 | List javaFiles = new ArrayList<>() 97 | List kotlinFiles = new ArrayList<>() 98 | argFiles.each { 99 | if (it.endsWith('.java')) { 100 | javaFiles.add(it) 101 | } else if (it.endsWith('.kt')) { 102 | kotlinFiles.add(it) 103 | } 104 | } 105 | 106 | if (!kotlinFiles.isEmpty()) { 107 | K2JVMCompiler compiler = new K2JVMCompiler() 108 | def args = new ArrayList() 109 | args.addAll(argFiles) 110 | args.add('-d') 111 | args.add(classesDir.absolutePath) 112 | args.add('-no-stdlib') 113 | if (keepParameters) { 114 | args.add('-java-parameters') 115 | } 116 | 117 | JavaVersion targetCompatibility = compileOptions.targetCompatibility 118 | def target = targetCompatibility.toString() 119 | if (!targetCompatibility.isJava8() && !targetCompatibility.isJava6()) { 120 | throw new GradleException("Failure to compile mis kotlin source to bytecode: unknown JVM target version: $target, supported versions: 1.6, 1.8\nTry:\n " + 121 | " mis {\n" + 122 | " ...\n" + 123 | " compileOptions {\n" + 124 | " sourceCompatibility JavaVersion.VERSION_1_8\n" + 125 | " targetCompatibility JavaVersion.VERSION_1_8\n" + 126 | " }\n" + 127 | " }") 128 | } 129 | 130 | 131 | args.add('-jvm-target') 132 | args.add(target) 133 | 134 | if (classPath.size() > 0) { 135 | args.add('-classpath') 136 | args.add(classPath.join(File.pathSeparator)) 137 | } 138 | 139 | ExitCode exitCode = compiler.exec(System.out, (String[]) args.toArray()) 140 | if (exitCode != ExitCode.OK) { 141 | throw new GradleException("Failure to compile mis kotlin source to bytecode.") 142 | } 143 | 144 | new File(classesDir, '/META-INF').deleteDir() 145 | 146 | classPath.add(classesDir.absolutePath) 147 | } 148 | 149 | if (!javaFiles.isEmpty()) { 150 | LinkedList paras = new LinkedList(); 151 | paras.add('javac') 152 | paras.add('-parameters') 153 | paras.add('-d') 154 | paras.add(classesDir.getAbsolutePath()) 155 | paras.add('-encoding') 156 | paras.add('UTF-8') 157 | paras.add('-target') 158 | paras.add(compileOptions.targetCompatibility.toString()) 159 | paras.add('-source') 160 | paras.add(compileOptions.sourceCompatibility.toString()) 161 | 162 | paras.add('-classpath') 163 | paras.add(classPath.join(File.pathSeparator)) 164 | paras.addAll(javaFiles); 165 | 166 | String[] javacParameters = (String[]) paras.toArray(new String[paras.size()]) 167 | def result = RuntimeUtil.exec(javacParameters, null, classesDir) 168 | if (result != 0) { 169 | throw new GradleException("Failure to compile mis java source to bytecode.\n" + "\nExecute command:\n" + javacParameters) 170 | } 171 | } 172 | 173 | def p = "jar cvf outputs/classes.jar -C classes . ".execute(null, classesDir.parentFile) 174 | def result = p.waitFor() 175 | p.destroy() 176 | p = null 177 | if (result != 0) { 178 | throw new RuntimeException("failure to package classes.jar: \n" + p.err.text) 179 | } 180 | 181 | return new File(classesDir.parentFile, 'outputs/classes.jar') 182 | } 183 | 184 | private static File generateJavaDocSourceJar(File sourceDir) { 185 | def p = "jar cvf ../outputs/classes-source.jar .".execute(null, sourceDir) 186 | def result = p.waitFor() 187 | if (result != 0) { 188 | throw new RuntimeException("failure to make mis-sdk java source directory: \n" + p.err.text) 189 | } 190 | def sourceJar = new File(sourceDir.parentFile, 'outputs/classes-source.jar') 191 | return sourceJar 192 | } 193 | 194 | private static void filterJavaSource(File file, String prefix, File sourceDir, 195 | def argFiles, Closure sourceFilter) { 196 | if (file.isDirectory()) { 197 | file.listFiles().each { File childFile -> 198 | filterJavaSource(childFile, prefix, sourceDir, argFiles, sourceFilter) 199 | } 200 | } else { 201 | if (file.name.endsWith(".java") || file.name.endsWith('.kt')) { 202 | def packageName = file.parent.replace(prefix, "") 203 | def targetParent = new File(sourceDir, packageName) 204 | if (!targetParent.exists()) targetParent.mkdirs() 205 | def target = new File(targetParent, file.name) 206 | MisUtil.copyFile(file, target) 207 | argFiles << target.absolutePath 208 | 209 | if (sourceFilter != null) { 210 | sourceFilter.call(target) 211 | } 212 | } 213 | } 214 | } 215 | 216 | private static void filterJavaDocSource(File file, String prefix, File javaDocDir) { 217 | if (file.isDirectory()) { 218 | file.listFiles().each { File childFile -> 219 | filterJavaDocSource(childFile, prefix, javaDocDir) 220 | } 221 | } else { 222 | if (file.name.endsWith(".java") || file.name.endsWith('.kt')) { 223 | def packageName = file.parent.replace(prefix, "") 224 | def targetParent = new File(javaDocDir, packageName) 225 | if (!targetParent.exists()) targetParent.mkdirs() 226 | def target = new File(targetParent, file.name) 227 | MisUtil.copyFile(file, target) 228 | } 229 | } 230 | } 231 | 232 | private static boolean compareJar(String jar1, String jar2) { 233 | try { 234 | JarFile jarFile1 = new JarFile(jar1) 235 | JarFile jarFile2 = new JarFile(jar2) 236 | if (jarFile1.size() != jarFile2.size()) 237 | return false 238 | 239 | Enumeration entries = jarFile1.entries() 240 | while (entries.hasMoreElements()) { 241 | JarEntry jarEntry1 = (JarEntry) entries.nextElement() 242 | if (!jarEntry1.name.endsWith(".class")) 243 | continue 244 | 245 | JarEntry jarEntry2 = jarFile2.getJarEntry(jarEntry1.getName()) 246 | if (jarEntry2 == null) { 247 | return false 248 | } 249 | InputStream stream1 = jarFile1.getInputStream(jarEntry1) 250 | byte[] bytes1 = stream1.bytes 251 | bytes1 = Arrays.copyOfRange(bytes1, 8, bytes1.length) 252 | stream1.close() 253 | 254 | InputStream stream2 = jarFile2.getInputStream(jarEntry2) 255 | byte[] bytes2 = stream2.bytes 256 | bytes2 = Arrays.copyOfRange(bytes2, 8, bytes2.length) 257 | stream2.close() 258 | 259 | if (!Arrays.equals(bytes1, bytes2)) { 260 | return false 261 | } 262 | } 263 | jarFile1.close() 264 | jarFile2.close() 265 | } catch (IOException e) { 266 | return false 267 | } 268 | return true 269 | } 270 | 271 | private static String getAARClassesJar(File input) { 272 | def jarFile = new File(input.getParent(), 'classes.jar') 273 | if (jarFile.exists()) return jarFile 274 | 275 | def zip = new ZipFile(input) 276 | zip.entries().each { 277 | if (it.isDirectory()) return 278 | if (it.name == 'classes.jar') { 279 | def fos = new FileOutputStream(jarFile) 280 | fos.write(zip.getInputStream(it).bytes) 281 | fos.close() 282 | } 283 | } 284 | zip.close() 285 | return jarFile.absolutePath 286 | } 287 | 288 | } -------------------------------------------------------------------------------- /mis-plugin/src/main/groovy/com/eastwood/tools/plugins/mis/MisPlugin.groovy: -------------------------------------------------------------------------------- 1 | package com.eastwood.tools.plugins.mis 2 | 3 | import com.android.build.gradle.AppPlugin 4 | import com.android.build.gradle.LibraryPlugin 5 | import com.eastwood.tools.plugins.mis.core.* 6 | import com.eastwood.tools.plugins.mis.core.extension.MisExtension 7 | import com.eastwood.tools.plugins.mis.core.extension.OnMisExtensionListener 8 | import com.eastwood.tools.plugins.mis.core.extension.Publication 9 | import org.gradle.api.GradleException 10 | import org.gradle.api.Plugin 11 | import org.gradle.api.Project 12 | import org.gradle.api.publish.maven.MavenPublication 13 | 14 | class MisPlugin implements Plugin { 15 | 16 | static File misDir 17 | static MisExtension misExtension 18 | 19 | static String androidJarPath 20 | 21 | static PublicationManager publicationManager 22 | 23 | Project project 24 | 25 | void apply(Project project) { 26 | this.project = project 27 | 28 | if (project == project.rootProject) { 29 | misDir = new File(project.projectDir, '.gradle/mis') 30 | if (!misDir.exists()) { 31 | misDir.mkdirs() 32 | } 33 | 34 | project.gradle.getStartParameter().taskNames.each { 35 | if (it == 'clean') { 36 | if (!misDir.deleteDir()) { 37 | throw new RuntimeException("unable to delete dir " + misDir.absolutePath) 38 | } 39 | misDir.mkdirs() 40 | } 41 | } 42 | 43 | project.repositories { 44 | flatDir { 45 | dirs misDir.absolutePath 46 | } 47 | } 48 | 49 | publicationManager = PublicationManager.getInstance() 50 | publicationManager.loadManifest(project, misDir) 51 | 52 | misExtension = project.extensions.create('mis', MisExtension, new OnMisExtensionListener() { 53 | @Override 54 | void onPublicationAdded(Project childProject, Publication publication) { 55 | initPublication(childProject, publication) 56 | publicationManager.addDependencyGraph(publication) 57 | } 58 | }) 59 | 60 | project.allprojects.each { 61 | if (it == project) return 62 | Project childProject = it 63 | childProject.repositories { 64 | flatDir { 65 | dirs misDir.absolutePath 66 | } 67 | } 68 | 69 | childProject.plugins.whenObjectAdded { 70 | if (it instanceof AppPlugin || it instanceof LibraryPlugin) { 71 | childProject.pluginManager.apply('mis') 72 | } 73 | } 74 | } 75 | 76 | project.afterEvaluate { 77 | 78 | androidJarPath = MisUtil.getAndroidJarPath(project, misExtension.compileSdkVersion) 79 | 80 | com.eastwood.tools.plugins.mis.core.extension.Dependencies.metaClass.misPublication { String value -> 81 | String[] gav = MisUtil.filterGAV(value) 82 | return 'mis-' + gav[0] + ':' + gav[1] + ':' + gav[2] 83 | } 84 | 85 | project.allprojects.each { 86 | if (it == project) return 87 | Project childProject = it 88 | def misScript = new File(childProject.projectDir, 'mis.gradle') 89 | if (misScript.exists()) { 90 | misExtension.childProject = childProject 91 | project.apply from: misScript 92 | } 93 | } 94 | 95 | List topSort = publicationManager.dependencyGraph.topSort() 96 | Collections.reverse(topSort) 97 | topSort.each { 98 | Publication publication = publicationManager.publicationDependencies.get(it) 99 | if (publication == null) { 100 | return 101 | } 102 | 103 | Project childProject = project.findProject(publication.project) 104 | 105 | filterPublicationDependencies(publication) 106 | 107 | if (publication.version != null) { 108 | handleMavenJar(childProject, publication) 109 | } else { 110 | handleLocalJar(childProject, publication) 111 | } 112 | publicationManager.hitPublication(publication) 113 | } 114 | 115 | } 116 | return 117 | } 118 | 119 | if (!MisUtil.hasAndroidPlugin(project)) { 120 | throw new GradleException("The android or android-library plugin must be applied to the project.") 121 | } 122 | 123 | project.dependencies.metaClass.misPublication { Object value -> 124 | String[] gav = MisUtil.filterGAV(value) 125 | return getPublication(gav[0], gav[1]) 126 | } 127 | 128 | List publications = publicationManager.getPublicationByProject(project) 129 | project.dependencies { 130 | publications.each { 131 | implementation getPublication(it.groupId, it.artifactId) 132 | } 133 | } 134 | if (project.gradle.startParameter.taskNames.isEmpty()) { 135 | publications.each { 136 | addPublicationDependencies(it) 137 | } 138 | } 139 | 140 | project.afterEvaluate { 141 | MisUtil.addMisSourceSets(project) 142 | 143 | List publicationList = publicationManager.getPublicationByProject(project) 144 | List publicationPublishList = new ArrayList<>() 145 | publicationList.each { 146 | if (it.version != null) { 147 | publicationPublishList.add(it) 148 | } 149 | } 150 | 151 | if (publicationPublishList.size() > 0) { 152 | project.plugins.apply('maven-publish') 153 | def publishing = project.extensions.getByName('publishing') 154 | if (misExtension.configure != null) { 155 | publishing.repositories misExtension.configure 156 | } 157 | 158 | publicationPublishList.each { 159 | createPublishTask(it) 160 | } 161 | } 162 | } 163 | } 164 | 165 | def filterPublicationDependencies(Publication publication) { 166 | if (publication.dependencies != null) { 167 | if (publication.dependencies.compileOnly != null) { 168 | List compileOnly = new ArrayList<>() 169 | publication.dependencies.compileOnly.each { 170 | if (it instanceof String && it.startsWith('mis-')) { 171 | String[] gav = MisUtil.filterGAV(it.replace('mis-', '')) 172 | Publication existPublication = publicationManager.getPublicationByKey(gav[0] + '-' + gav[1]) 173 | if (existPublication != null) { 174 | if (existPublication.useLocal) { 175 | compileOnly.add(':mis-' + existPublication.groupId + '-' + existPublication.artifactId + ':') 176 | } else { 177 | compileOnly.add(existPublication.groupId + ':' + existPublication.artifactId + ':' + existPublication.version) 178 | } 179 | } 180 | } else { 181 | compileOnly.add(it) 182 | } 183 | } 184 | publication.dependencies.compileOnly = compileOnly 185 | } 186 | if (publication.dependencies.implementation != null) { 187 | List implementation = new ArrayList<>() 188 | publication.dependencies.implementation.each { 189 | if (it instanceof String && it.startsWith('mis-')) { 190 | String[] gav = MisUtil.filterGAV(it.replace('mis-', '')) 191 | Publication existPublication = publicationManager.getPublicationByKey(gav[0] + '-' + gav[1]) 192 | if (existPublication != null) { 193 | if (existPublication.useLocal) { 194 | implementation.add(':mis-' + existPublication.groupId + '-' + existPublication.artifactId + ':') 195 | } else { 196 | implementation.add(existPublication.groupId + ':' + existPublication.artifactId + ':' + existPublication.version) 197 | } 198 | } 199 | } else { 200 | implementation.add(it) 201 | } 202 | } 203 | publication.dependencies.implementation = implementation 204 | } 205 | } 206 | } 207 | 208 | def handleLocalJar(Project project, Publication publication) { 209 | File target = new File(misDir, 'mis-' + publication.groupId + '-' + publication.artifactId + '.jar') 210 | 211 | if (publication.invalid) { 212 | publicationManager.addPublication(publication) 213 | if (target.exists()) { 214 | target.delete() 215 | } 216 | return 217 | } 218 | 219 | if (target.exists()) { 220 | boolean hasModifiedSource = publicationManager.hasModified(publication) 221 | if (!hasModifiedSource) { 222 | publication.invalid = false 223 | publication.useLocal = true 224 | publicationManager.addPublication(publication) 225 | return 226 | } 227 | } 228 | 229 | File releaseJar = JarUtil.packJavaSourceJar(project, publication, androidJarPath, misExtension.compileOptions, true) 230 | if (releaseJar == null) { 231 | publication.invalid = true 232 | publicationManager.addPublication(publication) 233 | if (target.exists()) { 234 | target.delete() 235 | } 236 | return 237 | } 238 | 239 | MisUtil.copyFile(releaseJar, target) 240 | publication.invalid = false 241 | publication.useLocal = true 242 | publicationManager.addPublication(publication) 243 | } 244 | 245 | def handleMavenJar(Project project, Publication publication) { 246 | File target = new File(misDir, 'mis-' + publication.groupId + '-' + publication.artifactId + '.jar') 247 | if (publication.invalid) { 248 | publicationManager.addPublication(publication) 249 | if (target.exists()) { 250 | target.delete() 251 | } 252 | return 253 | } 254 | 255 | boolean hasModifiedSource = publicationManager.hasModified(publication) 256 | 257 | if (target.exists()) { 258 | if (hasModifiedSource) { 259 | def releaseJar = JarUtil.packJavaSourceJar(project, publication, androidJarPath, misExtension.compileOptions, true) 260 | if (releaseJar == null) { 261 | publication.invalid = true 262 | publicationManager.addPublication(publication) 263 | if (target.exists()) { 264 | target.delete() 265 | } 266 | return 267 | } 268 | MisUtil.copyFile(releaseJar, target) 269 | } 270 | publication.invalid = false 271 | publication.useLocal = true 272 | publicationManager.addPublication(publication) 273 | } else if (!hasModifiedSource) { 274 | Publication lastPublication = publicationManager.getPublication(publication.groupId, publication.artifactId) 275 | if (lastPublication.version != publication.version) { 276 | publication.versionNew = publication.version 277 | publication.version = lastPublication.version 278 | } 279 | publication.invalid = false 280 | publication.useLocal = false 281 | publicationManager.addPublication(publication) 282 | return 283 | } else { 284 | def releaseJar = JarUtil.packJavaSourceJar(project, publication, androidJarPath, misExtension.compileOptions, false) 285 | if (releaseJar == null) { 286 | publication.invalid = true 287 | publicationManager.addPublication(publication) 288 | if (target.exists()) { 289 | target.delete() 290 | } 291 | return 292 | } 293 | 294 | Publication lastPublication = publicationManager.getPublication(publication.groupId, publication.artifactId) 295 | if (lastPublication == null) { 296 | lastPublication = publication 297 | } 298 | boolean equals = JarUtil.compareMavenJar(project, lastPublication, releaseJar.absolutePath) 299 | if (equals) { 300 | if (target.exists()) { 301 | target.delete() 302 | } 303 | publication.useLocal = false 304 | } else { 305 | releaseJar = JarUtil.packJavaSourceJar(project, publication, androidJarPath, misExtension.compileOptions, true) 306 | MisUtil.copyFile(releaseJar, target) 307 | publication.useLocal = true 308 | } 309 | publication.invalid = false 310 | publicationManager.addPublication(publication) 311 | } 312 | } 313 | 314 | void initPublication(Project project, Publication publication) { 315 | String displayName = project.getDisplayName() 316 | publication.project = displayName.substring(displayName.indexOf("'") + 1, displayName.lastIndexOf("'")) 317 | def buildMis = new File(project.projectDir, 'build/mis') 318 | 319 | publication.sourceSetName = publication.name 320 | publication.buildDir = new File(buildMis, publication.name) 321 | 322 | SourceSet misSourceSet = new SourceSet() 323 | def misDir 324 | if (publication.sourceSetName.contains('/')) { 325 | misDir = new File(project.projectDir, publication.sourceSetName + '/mis/') 326 | } else { 327 | misDir = new File(project.projectDir, 'src/' + publication.sourceSetName + '/mis/') 328 | } 329 | misSourceSet.path = misDir.absolutePath 330 | misSourceSet.lastModifiedSourceFile = new HashMap<>() 331 | project.fileTree(misDir).each { 332 | if (it.name.endsWith('.java') || it.name.endsWith('.kt')) { 333 | SourceFile sourceFile = new SourceFile() 334 | sourceFile.path = it.path 335 | sourceFile.lastModified = it.lastModified() 336 | misSourceSet.lastModifiedSourceFile.put(sourceFile.path, sourceFile) 337 | } 338 | } 339 | 340 | publication.misSourceSet = misSourceSet 341 | publication.invalid = misSourceSet.lastModifiedSourceFile.isEmpty() 342 | } 343 | 344 | def getPublication(String groupId, String artifactId) { 345 | Publication publication = publicationManager.getPublication(groupId, artifactId) 346 | if (publication != null) { 347 | if (publication.invalid) { 348 | return [] 349 | } else if (publication.useLocal) { 350 | return ':mis-' + publication.groupId + '-' + publication.artifactId + ':' 351 | } else { 352 | return publication.groupId + ':' + publication.artifactId + ':' + publication.version 353 | } 354 | } else { 355 | return [] 356 | } 357 | } 358 | 359 | void addPublicationDependencies(Publication publication) { 360 | if (publication.dependencies == null) return 361 | project.dependencies { 362 | if (publication.dependencies.compileOnly != null) { 363 | publication.dependencies.compileOnly.each { 364 | compileOnly it 365 | } 366 | } 367 | if (publication.dependencies.implementation != null) { 368 | publication.dependencies.implementation.each { 369 | implementation it 370 | } 371 | } 372 | } 373 | } 374 | 375 | void createPublishTask(Publication publication) { 376 | def taskName = 'compileMis' + publication.artifactId.capitalize() + 'Source' 377 | def compileTask = project.getTasks().findByName(taskName) 378 | if (compileTask == null) { 379 | compileTask = project.getTasks().create(taskName, CompileMisTask.class) 380 | compileTask.publication = publication 381 | compileTask.dependsOn 'clean' 382 | } 383 | 384 | def publicationName = 'Mis' + publication.artifactId.capitalize() 385 | String publishTaskNamePrefix = "publish${publicationName}PublicationTo" 386 | project.tasks.whenTaskAdded { 387 | if (it.name.startsWith(publishTaskNamePrefix)) { 388 | it.dependsOn compileTask 389 | it.doLast { 390 | new File(misDir, 'mis-' + publication.groupId + '-' + publication.artifactId + '.jar').delete() 391 | } 392 | } 393 | } 394 | createPublishingPublication(publication, publicationName) 395 | } 396 | 397 | void createPublishingPublication(Publication publication, String publicationName) { 398 | def publishing = project.extensions.getByName('publishing') 399 | MavenPublication mavenPublication = publishing.publications.maybeCreate(publicationName, MavenPublication) 400 | mavenPublication.groupId = publication.groupId 401 | mavenPublication.artifactId = publication.artifactId 402 | mavenPublication.version = publication.versionNew != null ? publication.versionNew : publication.version 403 | mavenPublication.pom.packaging = 'jar' 404 | 405 | def outputsDir = new File(publication.buildDir, "outputs") 406 | mavenPublication.artifact source: new File(outputsDir, "classes.jar") 407 | mavenPublication.artifact source: new File(outputsDir, "classes-source.jar"), classifier: 'sources' 408 | 409 | if (publication.dependencies != null) { 410 | mavenPublication.pom.withXml { 411 | def dependenciesNode = asNode().appendNode('dependencies') 412 | if (publication.dependencies.implementation != null) { 413 | publication.dependencies.implementation.each { 414 | def gav = it.split(":") 415 | if (gav[1].startsWith('mis-')) { 416 | Publication dependencyPublication = publicationManager.getPublicationByKey(gav[1].replace('mis-', '')) 417 | if (dependencyPublication.useLocal) { 418 | throw new RuntimeException("mis publication [$dependencyPublication.groupId:$dependencyPublication.artifactId] has not publish yet.") 419 | } 420 | } 421 | def dependencyNode = dependenciesNode.appendNode('dependency') 422 | dependencyNode.appendNode('groupId', gav[0]) 423 | dependencyNode.appendNode('artifactId', gav[1]) 424 | dependencyNode.appendNode('version', gav[2]) 425 | dependencyNode.appendNode('scope', 'implementation') 426 | } 427 | } 428 | } 429 | } 430 | 431 | } 432 | 433 | } --------------------------------------------------------------------------------