1// Copyright 2015 Google Inc. All rights reserved. 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. 14 15package java 16 17// This file generates the final rules for compiling all Java. All properties related to 18// compiling should have been translated into javaBuilderFlags or another argument to the Transform* 19// functions. 20 21import ( 22 "path/filepath" 23 "strconv" 24 "strings" 25 26 "github.com/google/blueprint" 27 "github.com/google/blueprint/proptools" 28 29 "android/soong/android" 30) 31 32var ( 33 pctx = android.NewPackageContext("android/soong/java") 34 35 // Compiling java is not conducive to proper dependency tracking. The path-matches-class-name 36 // requirement leads to unpredictable generated source file names, and a single .java file 37 // will get compiled into multiple .class files if it contains inner classes. To work around 38 // this, all java rules write into separate directories and then are combined into a .jar file 39 // (if the rule produces .class files) or a .srcjar file (if the rule produces .java files). 40 // .srcjar files are unzipped into a temporary directory when compiled with javac. 41 javac = pctx.AndroidGomaStaticRule("javac", 42 blueprint.RuleParams{ 43 Command: `rm -rf "$outDir" "$annoDir" "$srcJarDir" && mkdir -p "$outDir" "$annoDir" "$srcJarDir" && ` + 44 `${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` + 45 `(if [ -s $srcJarDir/list ] || [ -s $out.rsp ] ; then ` + 46 `${config.SoongJavacWrapper} ${config.JavacWrapper}${config.JavacCmd} ${config.JavacHeapFlags} ${config.CommonJdkFlags} ` + 47 `$processorpath $processor $javacFlags $bootClasspath $classpath ` + 48 `-source $javaVersion -target $javaVersion ` + 49 `-d $outDir -s $annoDir @$out.rsp @$srcJarDir/list ; fi ) && ` + 50 `${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir && ` + 51 `rm -rf "$srcJarDir"`, 52 CommandDeps: []string{ 53 "${config.JavacCmd}", 54 "${config.SoongZipCmd}", 55 "${config.ZipSyncCmd}", 56 }, 57 CommandOrderOnly: []string{"${config.SoongJavacWrapper}"}, 58 Rspfile: "$out.rsp", 59 RspfileContent: "$in", 60 }, 61 "javacFlags", "bootClasspath", "classpath", "processorpath", "processor", "srcJars", "srcJarDir", 62 "outDir", "annoDir", "javaVersion") 63 64 turbine = pctx.AndroidStaticRule("turbine", 65 blueprint.RuleParams{ 66 Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` + 67 `${config.JavaCmd} -jar ${config.TurbineJar} --output $out.tmp ` + 68 `--temp_dir "$outDir" --sources @$out.rsp --source_jars $srcJars ` + 69 `--javacopts ${config.CommonJdkFlags} ` + 70 `$javacFlags -source $javaVersion -target $javaVersion -- $bootClasspath $classpath && ` + 71 `${config.Ziptime} $out.tmp && ` + 72 `(if cmp -s $out.tmp $out ; then rm $out.tmp ; else mv $out.tmp $out ; fi )`, 73 CommandDeps: []string{ 74 "${config.TurbineJar}", 75 "${config.JavaCmd}", 76 "${config.Ziptime}", 77 }, 78 Rspfile: "$out.rsp", 79 RspfileContent: "$in", 80 Restat: true, 81 }, 82 "javacFlags", "bootClasspath", "classpath", "srcJars", "outDir", "javaVersion") 83 84 jar = pctx.AndroidStaticRule("jar", 85 blueprint.RuleParams{ 86 Command: `${config.SoongZipCmd} -jar -o $out @$out.rsp`, 87 CommandDeps: []string{"${config.SoongZipCmd}"}, 88 Rspfile: "$out.rsp", 89 RspfileContent: "$jarArgs", 90 }, 91 "jarArgs") 92 93 zip = pctx.AndroidStaticRule("zip", 94 blueprint.RuleParams{ 95 Command: `${config.SoongZipCmd} -o $out @$out.rsp`, 96 CommandDeps: []string{"${config.SoongZipCmd}"}, 97 Rspfile: "$out.rsp", 98 RspfileContent: "$jarArgs", 99 }, 100 "jarArgs") 101 102 combineJar = pctx.AndroidStaticRule("combineJar", 103 blueprint.RuleParams{ 104 Command: `${config.MergeZipsCmd} --ignore-duplicates -j $jarArgs $out $in`, 105 CommandDeps: []string{"${config.MergeZipsCmd}"}, 106 }, 107 "jarArgs") 108 109 jarjar = pctx.AndroidStaticRule("jarjar", 110 blueprint.RuleParams{ 111 Command: "${config.JavaCmd} -jar ${config.JarjarCmd} process $rulesFile $in $out", 112 CommandDeps: []string{"${config.JavaCmd}", "${config.JarjarCmd}", "$rulesFile"}, 113 }, 114 "rulesFile") 115 116 packageCheck = pctx.AndroidStaticRule("packageCheck", 117 blueprint.RuleParams{ 118 Command: "rm -f $out && " + 119 "${config.PackageCheckCmd} $in $packages && " + 120 "touch $out", 121 CommandDeps: []string{"${config.PackageCheckCmd}"}, 122 }, 123 "packages") 124 125 jetifier = pctx.AndroidStaticRule("jetifier", 126 blueprint.RuleParams{ 127 Command: "${config.JavaCmd} -jar ${config.JetifierJar} -l error -o $out -i $in", 128 CommandDeps: []string{"${config.JavaCmd}", "${config.JetifierJar}"}, 129 }, 130 ) 131 132 zipalign = pctx.AndroidStaticRule("zipalign", 133 blueprint.RuleParams{ 134 Command: "if ! ${config.ZipAlign} -c -p 4 $in > /dev/null; then " + 135 "${config.ZipAlign} -f -p 4 $in $out; " + 136 "else " + 137 "cp -f $in $out; " + 138 "fi", 139 CommandDeps: []string{"${config.ZipAlign}"}, 140 }, 141 ) 142) 143 144func init() { 145 pctx.Import("android/soong/android") 146 pctx.Import("android/soong/java/config") 147} 148 149type javaBuilderFlags struct { 150 javacFlags string 151 bootClasspath classpath 152 classpath classpath 153 processorPath classpath 154 processor string 155 systemModules classpath 156 aidlFlags string 157 aidlDeps android.Paths 158 javaVersion string 159 160 errorProneExtraJavacFlags string 161 errorProneProcessorPath classpath 162 163 kotlincFlags string 164 kotlincClasspath classpath 165 166 proto android.ProtoFlags 167} 168 169func TransformJavaToClasses(ctx android.ModuleContext, outputFile android.WritablePath, shardIdx int, 170 srcFiles, srcJars android.Paths, flags javaBuilderFlags, deps android.Paths) { 171 172 // Compile java sources into .class files 173 desc := "javac" 174 if shardIdx >= 0 { 175 desc += strconv.Itoa(shardIdx) 176 } 177 178 transformJavaToClasses(ctx, outputFile, shardIdx, srcFiles, srcJars, flags, deps, "javac", desc) 179} 180 181func RunErrorProne(ctx android.ModuleContext, outputFile android.WritablePath, 182 srcFiles, srcJars android.Paths, flags javaBuilderFlags) { 183 184 flags.processorPath = append(flags.errorProneProcessorPath, flags.processorPath...) 185 186 if len(flags.errorProneExtraJavacFlags) > 0 { 187 if len(flags.javacFlags) > 0 { 188 flags.javacFlags += " " + flags.errorProneExtraJavacFlags 189 } else { 190 flags.javacFlags = flags.errorProneExtraJavacFlags 191 } 192 } 193 194 transformJavaToClasses(ctx, outputFile, -1, srcFiles, srcJars, flags, nil, 195 "errorprone", "errorprone") 196} 197 198func TransformJavaToHeaderClasses(ctx android.ModuleContext, outputFile android.WritablePath, 199 srcFiles, srcJars android.Paths, flags javaBuilderFlags) { 200 201 var deps android.Paths 202 deps = append(deps, srcJars...) 203 deps = append(deps, flags.bootClasspath...) 204 deps = append(deps, flags.classpath...) 205 206 var bootClasspath string 207 if len(flags.bootClasspath) == 0 && ctx.Device() { 208 // explicitly specify -bootclasspath "" if the bootclasspath is empty to 209 // ensure java does not fall back to the default bootclasspath. 210 bootClasspath = `--bootclasspath ""` 211 } else { 212 bootClasspath = strings.Join(flags.bootClasspath.FormTurbineClasspath("--bootclasspath "), " ") 213 } 214 215 ctx.Build(pctx, android.BuildParams{ 216 Rule: turbine, 217 Description: "turbine", 218 Output: outputFile, 219 Inputs: srcFiles, 220 Implicits: deps, 221 Args: map[string]string{ 222 "javacFlags": flags.javacFlags, 223 "bootClasspath": bootClasspath, 224 "srcJars": strings.Join(srcJars.Strings(), " "), 225 "classpath": strings.Join(flags.classpath.FormTurbineClasspath("--classpath "), " "), 226 "outDir": android.PathForModuleOut(ctx, "turbine", "classes").String(), 227 "javaVersion": flags.javaVersion, 228 }, 229 }) 230} 231 232// transformJavaToClasses takes source files and converts them to a jar containing .class files. 233// srcFiles is a list of paths to sources, srcJars is a list of paths to jar files that contain 234// sources. flags contains various command line flags to be passed to the compiler. 235// 236// This method may be used for different compilers, including javac and Error Prone. The rule 237// argument specifies which command line to use and desc sets the description of the rule that will 238// be printed at build time. The stem argument provides the file name of the output jar, and 239// suffix will be appended to various intermediate files and directories to avoid collisions when 240// this function is called twice in the same module directory. 241func transformJavaToClasses(ctx android.ModuleContext, outputFile android.WritablePath, 242 shardIdx int, srcFiles, srcJars android.Paths, 243 flags javaBuilderFlags, deps android.Paths, 244 intermediatesDir, desc string) { 245 246 deps = append(deps, srcJars...) 247 248 var bootClasspath string 249 if flags.javaVersion == "1.9" { 250 deps = append(deps, flags.systemModules...) 251 bootClasspath = flags.systemModules.FormJavaSystemModulesPath("--system=", ctx.Device()) 252 } else { 253 deps = append(deps, flags.bootClasspath...) 254 if len(flags.bootClasspath) == 0 && ctx.Device() { 255 // explicitly specify -bootclasspath "" if the bootclasspath is empty to 256 // ensure java does not fall back to the default bootclasspath. 257 bootClasspath = `-bootclasspath ""` 258 } else { 259 bootClasspath = flags.bootClasspath.FormJavaClassPath("-bootclasspath") 260 } 261 } 262 263 deps = append(deps, flags.classpath...) 264 deps = append(deps, flags.processorPath...) 265 266 processor := "-proc:none" 267 if flags.processor != "" { 268 processor = "-processor " + flags.processor 269 } 270 271 srcJarDir := "srcjars" 272 outDir := "classes" 273 annoDir := "anno" 274 if shardIdx >= 0 { 275 shardDir := "shard" + strconv.Itoa(shardIdx) 276 srcJarDir = filepath.Join(shardDir, srcJarDir) 277 outDir = filepath.Join(shardDir, outDir) 278 annoDir = filepath.Join(shardDir, annoDir) 279 } 280 ctx.Build(pctx, android.BuildParams{ 281 Rule: javac, 282 Description: desc, 283 Output: outputFile, 284 Inputs: srcFiles, 285 Implicits: deps, 286 Args: map[string]string{ 287 "javacFlags": flags.javacFlags, 288 "bootClasspath": bootClasspath, 289 "classpath": flags.classpath.FormJavaClassPath("-classpath"), 290 "processorpath": flags.processorPath.FormJavaClassPath("-processorpath"), 291 "processor": processor, 292 "srcJars": strings.Join(srcJars.Strings(), " "), 293 "srcJarDir": android.PathForModuleOut(ctx, intermediatesDir, srcJarDir).String(), 294 "outDir": android.PathForModuleOut(ctx, intermediatesDir, outDir).String(), 295 "annoDir": android.PathForModuleOut(ctx, intermediatesDir, annoDir).String(), 296 "javaVersion": flags.javaVersion, 297 }, 298 }) 299} 300 301func TransformResourcesToJar(ctx android.ModuleContext, outputFile android.WritablePath, 302 jarArgs []string, deps android.Paths) { 303 304 ctx.Build(pctx, android.BuildParams{ 305 Rule: jar, 306 Description: "jar", 307 Output: outputFile, 308 Implicits: deps, 309 Args: map[string]string{ 310 "jarArgs": strings.Join(proptools.NinjaAndShellEscapeList(jarArgs), " "), 311 }, 312 }) 313} 314 315func TransformJarsToJar(ctx android.ModuleContext, outputFile android.WritablePath, desc string, 316 jars android.Paths, manifest android.OptionalPath, stripDirEntries bool, filesToStrip []string, 317 dirsToStrip []string) { 318 319 var deps android.Paths 320 321 var jarArgs []string 322 if manifest.Valid() { 323 jarArgs = append(jarArgs, "-m ", manifest.String()) 324 deps = append(deps, manifest.Path()) 325 } 326 327 for _, dir := range dirsToStrip { 328 jarArgs = append(jarArgs, "-stripDir ", dir) 329 } 330 331 for _, file := range filesToStrip { 332 jarArgs = append(jarArgs, "-stripFile ", file) 333 } 334 335 // Remove any module-info.class files that may have come from prebuilt jars, they cause problems 336 // for downstream tools like desugar. 337 jarArgs = append(jarArgs, "-stripFile module-info.class") 338 339 if stripDirEntries { 340 jarArgs = append(jarArgs, "-D") 341 } 342 343 ctx.Build(pctx, android.BuildParams{ 344 Rule: combineJar, 345 Description: desc, 346 Output: outputFile, 347 Inputs: jars, 348 Implicits: deps, 349 Args: map[string]string{ 350 "jarArgs": strings.Join(jarArgs, " "), 351 }, 352 }) 353} 354 355func TransformJarJar(ctx android.ModuleContext, outputFile android.WritablePath, 356 classesJar android.Path, rulesFile android.Path) { 357 ctx.Build(pctx, android.BuildParams{ 358 Rule: jarjar, 359 Description: "jarjar", 360 Output: outputFile, 361 Input: classesJar, 362 Implicit: rulesFile, 363 Args: map[string]string{ 364 "rulesFile": rulesFile.String(), 365 }, 366 }) 367} 368 369func CheckJarPackages(ctx android.ModuleContext, outputFile android.WritablePath, 370 classesJar android.Path, permittedPackages []string) { 371 ctx.Build(pctx, android.BuildParams{ 372 Rule: packageCheck, 373 Description: "packageCheck", 374 Output: outputFile, 375 Input: classesJar, 376 Args: map[string]string{ 377 "packages": strings.Join(permittedPackages, " "), 378 }, 379 }) 380} 381 382func TransformJetifier(ctx android.ModuleContext, outputFile android.WritablePath, 383 inputFile android.Path) { 384 ctx.Build(pctx, android.BuildParams{ 385 Rule: jetifier, 386 Description: "jetifier", 387 Output: outputFile, 388 Input: inputFile, 389 }) 390} 391 392func GenerateMainClassManifest(ctx android.ModuleContext, outputFile android.WritablePath, mainClass string) { 393 ctx.Build(pctx, android.BuildParams{ 394 Rule: android.WriteFile, 395 Description: "manifest", 396 Output: outputFile, 397 Args: map[string]string{ 398 "content": "Main-Class: " + mainClass + "\n", 399 }, 400 }) 401} 402 403func TransformZipAlign(ctx android.ModuleContext, outputFile android.WritablePath, inputFile android.Path) { 404 ctx.Build(pctx, android.BuildParams{ 405 Rule: zipalign, 406 Description: "align", 407 Input: inputFile, 408 Output: outputFile, 409 }) 410} 411 412type classpath []android.Path 413 414func (x *classpath) FormJavaClassPath(optName string) string { 415 if optName != "" && !strings.HasSuffix(optName, "=") && !strings.HasSuffix(optName, " ") { 416 optName += " " 417 } 418 if len(*x) > 0 { 419 return optName + strings.Join(x.Strings(), ":") 420 } else { 421 return "" 422 } 423} 424 425// Returns a --system argument in the form javac expects with -source 1.9. If forceEmpty is true, 426// returns --system=none if the list is empty to ensure javac does not fall back to the default 427// system modules. 428func (x *classpath) FormJavaSystemModulesPath(optName string, forceEmpty bool) string { 429 if len(*x) > 1 { 430 panic("more than one system module") 431 } else if len(*x) == 1 { 432 return optName + strings.TrimSuffix((*x)[0].String(), "lib/modules") 433 } else if forceEmpty { 434 return optName + "none" 435 } else { 436 return "" 437 } 438} 439 440func (x *classpath) FormTurbineClasspath(optName string) []string { 441 if x == nil || *x == nil { 442 return nil 443 } 444 flags := make([]string, len(*x)) 445 for i, v := range *x { 446 flags[i] = optName + v.String() 447 } 448 449 return flags 450} 451 452// Convert a classpath to an android.Paths 453func (x *classpath) Paths() android.Paths { 454 return append(android.Paths(nil), (*x)...) 455} 456 457func (x *classpath) Strings() []string { 458 if x == nil { 459 return nil 460 } 461 ret := make([]string, len(*x)) 462 for i, path := range *x { 463 ret[i] = path.String() 464 } 465 return ret 466} 467