@@ -22,14 +22,18 @@ import org.gradle.api.tasks.OutputFile
22
22
import org.gradle.api.tasks.TaskAction
23
23
24
24
/**
25
- * Generates size measurements after building the test apps and outputs them as a text-format
26
- * protocol buffer report.
25
+ * Generates size measurements after building the test apps.
26
+ *
27
+ * <p >This task can run in two modes. The first mode, is a dependency for {@link
28
+ * UploadMeasurementsTask} and generates a JSON file with measurement information. This file
29
+ * references database IDs, and is not considered to be human-friendly. The second mode outputs a
30
+ * table to standard out with more useful information. These modes can be toggled by adding the
31
+ * {@code pull_request } flag to the task. See the README for more details.
27
32
*
28
33
* <p >This task requires two properties, an SDK map, as input, and the report, as output. The map is
29
34
* used to convert project names and build variants into the SDK identifiers used by the database.
30
- * The report path is where the output should be stored. Additionally, a project property, {@code
31
- * pull_request } is used in the report. Excluding this value will send a human-readable version
32
- * to standard out.
35
+ * The report path is where the output should be stored. These properties are not used when the task
36
+ * is run in the second, humna-friendly mode. However, they are still required to be specified.
33
37
*/
34
38
public class GenerateMeasurementsTask extends DefaultTask {
35
39
@@ -76,59 +80,58 @@ public class GenerateMeasurementsTask extends DefaultTask {
76
80
77
81
@TaskAction
78
82
def generate () {
79
- // Check if we need to run human-readable or upload mode.
83
+ def variants = project. android. applicationVariants
84
+
85
+ // Check if we need to run human-readable table or JSON upload mode.
80
86
if (project. hasProperty(" pull_request" )) {
81
87
def pullRequestNumber = project. properties[" pull_request" ]
82
- def sdkMap = createSdkMap()
83
- def sizes = calculateSizesForUpload(sdkMap, project. android. applicationVariants)
84
- def report = createReportForUpload(pullRequestNumber, sizes)
85
-
86
- reportFile. withWriter {
87
- it. write(report)
88
- }
88
+ generateJson(pullRequestNumber, variants)
89
89
} else {
90
- def sizes = calculateHumanReadableSizes(project. android. applicationVariants)
91
- printHumanReadableReport(sizes)
90
+ generateTable(variants)
92
91
}
93
92
}
94
93
95
- private def calculateHumanReadableSizes (variants ) {
96
- def sizes = [:]
97
- def processor = {flavor , build , size ->
98
- sizes[new Tuple2 (flavor, build)] = size
94
+ private def generateJson (pullRequestNumber , variants ) {
95
+ def sdkMap = createSdkMap()
96
+ def builder = new ApkSizeJsonBuilder (pullRequestNumber)
97
+ def variantProcessor = {projectName , buildType , apkSize ->
98
+ def name = " $projectName -$buildType "
99
+ def sdkId = sdkMap[name]
100
+
101
+ if (sdkId == null ) {
102
+ throw new IllegalStateException (" $name not included in SDK map" )
103
+ }
104
+
105
+ builder. addApkSize(sdkId, apkSize)
99
106
}
100
107
101
- calculateSizesFor(variants, processor)
102
- return sizes
108
+ calculateSizes(variants, variantProcessor)
109
+ reportFile. withWriter {
110
+ it. write(builder. toJsonString())
111
+ }
103
112
}
104
113
105
- private def calculateSizesForUpload (sdkMap , variants ) {
106
- def sizes = [:]
107
- def processor = { flavor , build , size ->
108
- def name = " ${ flavor} -${ build} "
109
- def sdk = sdkMap[name];
110
-
111
- if (sdk == null ) {
112
- throw new IllegalStateException (" $name not included in SDK map" )
113
- }
114
- sizes[sdk] = size
114
+ private def generateTable (variants ) {
115
+ def builder = new ApkSizeTableBuilder ()
116
+ def variantProcessor = {projectName , buildType , apkSize ->
117
+ builder. addApkSize(projectName, buildType, apkSize)
115
118
}
116
119
117
- calculateSizesFor (variants, processor )
118
- return sizes
120
+ calculateSizes (variants, variantProcessor )
121
+ project . logger . quiet(builder . toTableString())
119
122
}
120
123
121
- private def calculateSizesFor (variants , processor ) {
124
+ private def calculateSizes (variants , processor ) {
122
125
// Each variant should have exactly one APK. If there are multiple APKs, then this file is
123
- // out of sync with our Gradle configuration, and this task fails. If an APK is missing, it
124
- // is silently ignored, and the APKs from the other variants will be used to build the
125
- // report.
126
+ // out of sync with our Gradle configuration, and this task fails. If an APK is missing, it
127
+ // is silently ignored, and the APKs from the other variants will be used to build the
128
+ // report.
126
129
variants. each { variant ->
127
130
def flavorName = variant. flavorName
128
131
def buildType = variant. buildType. name
129
132
def apks = variant. outputs. findAll { it. outputFile. name. endsWith(" .apk" ) }
130
133
if (apks. size() > 1 ) {
131
- def msg = " ${ flavorName} -${ buildType} produced more than one APK"
134
+ def msg = " ${ flavorName} -${ buildType} produced more than one APK"
132
135
throw new IllegalStateException (msg)
133
136
}
134
137
@@ -140,77 +143,6 @@ public class GenerateMeasurementsTask extends DefaultTask {
140
143
}
141
144
}
142
145
143
- private def printHumanReadableReport (sizes ) {
144
- project. logger. quiet(" |------------------ APK Sizes ------------------|" )
145
- project. logger. quiet(" |-- project --|-- build type --|-- size in bytes --|" )
146
-
147
- sizes. each { key , value ->
148
- def line = sprintf (" |%-19s|%-19s|%-21s|" , key. first, key. second, value)
149
- project. logger. quiet(line)
150
- }
151
- }
152
-
153
- // TODO(allisonbm): Remove hard-coding protocol buffers. This code manually generates the
154
- // text-format protocol buffer report. This eliminates requiring buildSrc to depend on the
155
- // uploader (or simply, the protocol buffer library), but this isn't the most scalable option.
156
- private def createReportForUpload (pullRequestNumber , sizes ) {
157
- def sdkId = 0
158
- def apkSize = 0
159
-
160
- def pullRequestGroup = """
161
- groups {
162
- table_name: "PullRequests"
163
- column_names: "pull_request_id"
164
- measurements {
165
- values {
166
- int_value: ${ pullRequestNumber}
167
- }
168
- }
169
- }
170
- """
171
-
172
- def sizeGroupHeader = """
173
- groups {
174
- table_name: "ApkSizes"
175
- column_names: "sdk_id"
176
- column_names: "pull_request_id"
177
- column_names: "apk_size"
178
- """
179
-
180
- def sizeGroupEntry = """
181
- measurements {
182
- values {
183
- int_value: ${ ->sdkId}
184
- }
185
- values {
186
- int_value: ${ pullRequestNumber}
187
- }
188
- values {
189
- int_value: ${ ->apkSize}
190
- }
191
- }
192
- """
193
-
194
- def sizeGroupFooter = """
195
- }
196
- """
197
-
198
-
199
- def builder = new StringBuilder ()
200
- builder. append(pullRequestGroup)
201
- builder. append(sizeGroupHeader)
202
-
203
- sizes. each { key , value ->
204
- // sdkId and apkSize are lazily interpolated into sizeGroupEntry.
205
- sdkId = key
206
- apkSize = value
207
- builder. append(sizeGroupEntry)
208
- }
209
-
210
- builder. append(sizeGroupFooter)
211
- return builder. toString()
212
- }
213
-
214
146
private def createSdkMap () {
215
147
def map = [:]
216
148
0 commit comments