) {
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