1// Copyright 2017 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 main 16 17import ( 18 "archive/zip" 19 "bufio" 20 "bytes" 21 "encoding/xml" 22 "flag" 23 "fmt" 24 "io/ioutil" 25 "os" 26 "os/exec" 27 "path" 28 "path/filepath" 29 "regexp" 30 "sort" 31 "strings" 32 "text/template" 33 34 "github.com/google/blueprint/proptools" 35 36 "android/soong/bpfix/bpfix" 37) 38 39type RewriteNames []RewriteName 40type RewriteName struct { 41 regexp *regexp.Regexp 42 repl string 43} 44 45func (r *RewriteNames) String() string { 46 return "" 47} 48 49func (r *RewriteNames) Set(v string) error { 50 split := strings.SplitN(v, "=", 2) 51 if len(split) != 2 { 52 return fmt.Errorf("Must be in the form of <regex>=<replace>") 53 } 54 regex, err := regexp.Compile(split[0]) 55 if err != nil { 56 return nil 57 } 58 *r = append(*r, RewriteName{ 59 regexp: regex, 60 repl: split[1], 61 }) 62 return nil 63} 64 65func (r *RewriteNames) MavenToBp(groupId string, artifactId string) string { 66 for _, r := range *r { 67 if r.regexp.MatchString(groupId + ":" + artifactId) { 68 return r.regexp.ReplaceAllString(groupId+":"+artifactId, r.repl) 69 } else if r.regexp.MatchString(artifactId) { 70 return r.regexp.ReplaceAllString(artifactId, r.repl) 71 } 72 } 73 return artifactId 74} 75 76var rewriteNames = RewriteNames{} 77 78type ExtraDeps map[string][]string 79 80func (d ExtraDeps) String() string { 81 return "" 82} 83 84func (d ExtraDeps) Set(v string) error { 85 split := strings.SplitN(v, "=", 2) 86 if len(split) != 2 { 87 return fmt.Errorf("Must be in the form of <module>=<module>[,<module>]") 88 } 89 d[split[0]] = strings.Split(split[1], ",") 90 return nil 91} 92 93var extraStaticLibs = make(ExtraDeps) 94 95var extraLibs = make(ExtraDeps) 96 97var optionalUsesLibs = make(ExtraDeps) 98 99type Exclude map[string]bool 100 101func (e Exclude) String() string { 102 return "" 103} 104 105func (e Exclude) Set(v string) error { 106 e[v] = true 107 return nil 108} 109 110var excludes = make(Exclude) 111 112type HostModuleNames map[string]bool 113 114func (n HostModuleNames) IsHostModule(groupId string, artifactId string) bool { 115 _, found := n[groupId+":"+artifactId] 116 return found 117} 118 119func (n HostModuleNames) String() string { 120 return "" 121} 122 123func (n HostModuleNames) Set(v string) error { 124 n[v] = true 125 return nil 126} 127 128var hostModuleNames = HostModuleNames{} 129 130type HostAndDeviceModuleNames map[string]bool 131 132func (n HostAndDeviceModuleNames) IsHostAndDeviceModule(groupId string, artifactId string) bool { 133 _, found := n[groupId+":"+artifactId] 134 135 return found 136} 137 138func (n HostAndDeviceModuleNames) String() string { 139 return "" 140} 141 142func (n HostAndDeviceModuleNames) Set(v string) error { 143 n[v] = true 144 return nil 145} 146 147var hostAndDeviceModuleNames = HostAndDeviceModuleNames{} 148 149var sdkVersion string 150var defaultMinSdkVersion string 151var useVersion string 152var staticDeps bool 153var writeCmd bool 154var jetifier bool 155 156func InList(s string, list []string) bool { 157 for _, l := range list { 158 if l == s { 159 return true 160 } 161 } 162 163 return false 164} 165 166type Dependency struct { 167 XMLName xml.Name `xml:"dependency"` 168 169 BpTarget string `xml:"-"` 170 BazelTarget string `xml:"-"` 171 172 GroupId string `xml:"groupId"` 173 ArtifactId string `xml:"artifactId"` 174 Version string `xml:"version"` 175 Type string `xml:"type"` 176 Scope string `xml:"scope"` 177} 178 179func (d Dependency) BpName() string { 180 if d.BpTarget == "" { 181 d.BpTarget = rewriteNames.MavenToBp(d.GroupId, d.ArtifactId) 182 } 183 return d.BpTarget 184} 185 186type Pom struct { 187 XMLName xml.Name `xml:"http://maven.apache.org/POM/4.0.0 project"` 188 189 PomFile string `xml:"-"` 190 ArtifactFile string `xml:"-"` 191 BpTarget string `xml:"-"` 192 MinSdkVersion string `xml:"-"` 193 194 GroupId string `xml:"groupId"` 195 ArtifactId string `xml:"artifactId"` 196 Version string `xml:"version"` 197 Packaging string `xml:"packaging"` 198 199 Dependencies []*Dependency `xml:"dependencies>dependency"` 200} 201 202func (p Pom) IsAar() bool { 203 return p.Packaging == "aar" 204} 205 206func (p Pom) IsJar() bool { 207 return p.Packaging == "jar" 208} 209 210func (p Pom) IsApk() bool { 211 return p.Packaging == "apk" 212} 213 214func (p Pom) IsHostModule() bool { 215 return hostModuleNames.IsHostModule(p.GroupId, p.ArtifactId) 216} 217 218func (p Pom) IsDeviceModule() bool { 219 return !p.IsHostModule() 220} 221 222func (p Pom) IsHostAndDeviceModule() bool { 223 return hostAndDeviceModuleNames.IsHostAndDeviceModule(p.GroupId, p.ArtifactId) 224} 225 226func (p Pom) IsHostOnly() bool { 227 return p.IsHostModule() && !p.IsHostAndDeviceModule() 228} 229 230func (p Pom) ModuleType() string { 231 if p.IsAar() { 232 return "android_library" 233 } else if p.IsHostOnly() { 234 return "java_library_host" 235 } else { 236 return "java_library_static" 237 } 238} 239 240func (p Pom) BazelTargetType() string { 241 if p.IsAar() { 242 return "android_library" 243 } else { 244 return "java_library" 245 } 246} 247 248func (p Pom) ImportModuleType() string { 249 if p.IsAar() { 250 return "android_library_import" 251 } else if p.IsApk() { 252 return "android_app_import" 253 } else if p.IsHostOnly() { 254 return "java_import_host" 255 } else { 256 return "java_import" 257 } 258} 259 260func (p Pom) BazelImportTargetType() string { 261 if p.IsAar() { 262 return "aar_import" 263 } else if p.IsApk() { 264 return "apk_import" 265 } else { 266 return "java_import" 267 } 268} 269 270func (p Pom) ImportProperty() string { 271 if p.IsAar() { 272 return "aars" 273 } else if p.IsApk() { 274 return "apk" 275 } else { 276 return "jars" 277 } 278} 279 280func (p Pom) BazelImportProperty() string { 281 if p.IsAar() { 282 return "aar" 283 } else if p.IsApk() { 284 return "apk" 285 } else { 286 return "jars" 287 } 288} 289 290func (p Pom) BpName() string { 291 if p.BpTarget == "" { 292 p.BpTarget = rewriteNames.MavenToBp(p.GroupId, p.ArtifactId) 293 } 294 return p.BpTarget 295} 296 297func (p Pom) BpJarDeps() []string { 298 return p.BpDeps("jar", []string{"compile", "runtime"}) 299} 300 301func (p Pom) BpAarDeps() []string { 302 return p.BpDeps("aar", []string{"compile", "runtime"}) 303} 304 305func (p Pom) BazelJarDeps() []string { 306 return p.BazelDeps("jar", []string{"compile", "runtime"}) 307} 308 309func (p Pom) BazelAarDeps() []string { 310 return p.BazelDeps("aar", []string{"compile", "runtime"}) 311} 312 313func (p Pom) BpExtraStaticLibs() []string { 314 return extraStaticLibs[p.BpName()] 315} 316 317func (p Pom) BpExtraLibs() []string { 318 return extraLibs[p.BpName()] 319} 320 321func (p Pom) BpOptionalUsesLibs() []string { 322 return optionalUsesLibs[p.BpName()] 323} 324 325// BpDeps obtains dependencies filtered by type and scope. The results of this 326// method are formatted as Android.bp targets, e.g. run through MavenToBp rules. 327func (p Pom) BpDeps(typeExt string, scopes []string) []string { 328 var ret []string 329 for _, d := range p.Dependencies { 330 if d.Type != typeExt || !InList(d.Scope, scopes) { 331 continue 332 } 333 name := rewriteNames.MavenToBp(d.GroupId, d.ArtifactId) 334 ret = append(ret, name) 335 } 336 return ret 337} 338 339// BazelDeps obtains dependencies filtered by type and scope. The results of this 340// method are formatted as Bazel BUILD targets. 341func (p Pom) BazelDeps(typeExt string, scopes []string) []string { 342 var ret []string 343 for _, d := range p.Dependencies { 344 if d.Type != typeExt || !InList(d.Scope, scopes) { 345 continue 346 } 347 ret = append(ret, d.BazelTarget) 348 } 349 return ret 350} 351 352func PathModVars() (string, string, string) { 353 cmd := "/bin/bash" 354 androidTop := os.Getenv("ANDROID_BUILD_TOP") 355 envSetupSh := path.Join(androidTop, "build/envsetup.sh") 356 return cmd, androidTop, envSetupSh 357} 358 359func InitRefreshMod(poms []*Pom) error { 360 cmd, _, envSetupSh := PathModVars() 361 // refreshmod is expensive, so if pathmod is already working we can skip it. 362 _, err := exec.Command(cmd, "-c", ". "+envSetupSh+" && pathmod "+poms[0].BpName()).Output() 363 if exitErr, _ := err.(*exec.ExitError); exitErr != nil || err != nil { 364 _, err := exec.Command(cmd, "-c", ". "+envSetupSh+" && refreshmod").Output() 365 if exitErr, _ := err.(*exec.ExitError); exitErr != nil { 366 return fmt.Errorf("failed to run %s\n%s\ntry running lunch.", cmd, string(exitErr.Stderr)) 367 } else if err != nil { 368 return err 369 } 370 } 371 return nil 372} 373 374func BazelifyExtraDeps(extraDeps ExtraDeps, modules map[string]*Pom) error { 375 for _, deps := range extraDeps { 376 for _, dep := range deps { 377 bazelName, err := BpNameToBazelTarget(dep, modules) 378 if err != nil { 379 return err 380 } 381 dep = bazelName 382 } 383 384 } 385 return nil 386} 387 388func (p *Pom) GetBazelDepNames(modules map[string]*Pom) error { 389 for _, d := range p.Dependencies { 390 bazelName, err := BpNameToBazelTarget(d.BpName(), modules) 391 if err != nil { 392 return err 393 } 394 d.BazelTarget = bazelName 395 } 396 return nil 397} 398 399func BpNameToBazelTarget(bpName string, modules map[string]*Pom) (string, error) { 400 cmd, androidTop, envSetupSh := PathModVars() 401 402 if _, ok := modules[bpName]; ok { 403 // We've seen the POM for this dependency, it will be local to the output BUILD file 404 return ":" + bpName, nil 405 } else { 406 // we don't have the POM for this artifact, find and use the fully qualified target name. 407 output, err := exec.Command(cmd, "-c", ". "+envSetupSh+" && pathmod "+bpName).Output() 408 if exitErr, _ := err.(*exec.ExitError); exitErr != nil { 409 return "", fmt.Errorf("failed to run %s %s\n%s", cmd, bpName, string(exitErr.Stderr)) 410 } else if err != nil { 411 return "", err 412 } 413 relPath := "" 414 for _, line := range strings.Fields(string(output)) { 415 if strings.Contains(line, androidTop) { 416 relPath = strings.TrimPrefix(line, androidTop) 417 relPath = strings.TrimLeft(relPath, "/") 418 } 419 } 420 return "//" + relPath + ":" + bpName, nil 421 } 422} 423 424func (p Pom) SdkVersion() string { 425 return sdkVersion 426} 427 428func (p Pom) DefaultMinSdkVersion() string { 429 return defaultMinSdkVersion 430} 431 432func (p Pom) Jetifier() bool { 433 return jetifier 434} 435 436func (p *Pom) FixDeps(modules map[string]*Pom) { 437 for _, d := range p.Dependencies { 438 if d.Type == "" { 439 if depPom, ok := modules[d.BpName()]; ok { 440 // We've seen the POM for this dependency, use its packaging 441 // as the dependency type rather than Maven spec default. 442 d.Type = depPom.Packaging 443 } else { 444 // Dependency type was not specified and we don't have the POM 445 // for this artifact, use the default from Maven spec. 446 d.Type = "jar" 447 } 448 } 449 if d.Scope == "" { 450 // Scope was not specified, use the default from Maven spec. 451 d.Scope = "compile" 452 } 453 } 454} 455 456// ExtractMinSdkVersion extracts the minSdkVersion from the AndroidManifest.xml file inside an aar file, or sets it 457// to "current" if it is not present. 458func (p *Pom) ExtractMinSdkVersion() error { 459 aar, err := zip.OpenReader(p.ArtifactFile) 460 if err != nil { 461 return err 462 } 463 defer aar.Close() 464 465 var manifest *zip.File 466 for _, f := range aar.File { 467 if f.Name == "AndroidManifest.xml" { 468 manifest = f 469 break 470 } 471 } 472 473 if manifest == nil { 474 return fmt.Errorf("failed to find AndroidManifest.xml in %s", p.ArtifactFile) 475 } 476 477 r, err := manifest.Open() 478 if err != nil { 479 return err 480 } 481 defer r.Close() 482 483 decoder := xml.NewDecoder(r) 484 485 manifestData := struct { 486 XMLName xml.Name `xml:"manifest"` 487 Uses_sdk struct { 488 MinSdkVersion string `xml:"http://schemas.android.com/apk/res/android minSdkVersion,attr"` 489 } `xml:"uses-sdk"` 490 }{} 491 492 err = decoder.Decode(&manifestData) 493 if err != nil { 494 return err 495 } 496 497 p.MinSdkVersion = manifestData.Uses_sdk.MinSdkVersion 498 if p.MinSdkVersion == "" { 499 p.MinSdkVersion = "current" 500 } 501 502 return nil 503} 504 505var bpTemplate = template.Must(template.New("bp").Parse(` 506{{.ImportModuleType}} { 507 name: "{{.BpName}}", 508 {{- if .IsApk}} 509 {{.ImportProperty}}: "{{.ArtifactFile}}", 510 {{- else}} 511 {{.ImportProperty}}: ["{{.ArtifactFile}}"], 512 sdk_version: "{{.SdkVersion}}", 513 {{- end}} 514 {{- if .Jetifier}} 515 jetifier: true, 516 {{- end}} 517 {{- if .IsHostAndDeviceModule}} 518 host_supported: true, 519 {{- end}} 520 {{- if not .IsHostOnly}} 521 apex_available: [ 522 "//apex_available:platform", 523 "//apex_available:anyapex", 524 ], 525 {{- end}} 526 {{- if .IsAar}} 527 min_sdk_version: "{{.MinSdkVersion}}", 528 static_libs: [ 529 {{- range .BpJarDeps}} 530 "{{.}}", 531 {{- end}} 532 {{- range .BpAarDeps}} 533 "{{.}}", 534 {{- end}} 535 {{- range .BpExtraStaticLibs}} 536 "{{.}}", 537 {{- end}} 538 ], 539 {{- if .BpExtraLibs}} 540 libs: [ 541 {{- range .BpExtraLibs}} 542 "{{.}}", 543 {{- end}} 544 ], 545 {{- end}} 546 {{- if .BpOptionalUsesLibs}} 547 optional_uses_libs: [ 548 {{- range .BpOptionalUsesLibs}} 549 "{{.}}", 550 {{- end}} 551 ], 552 {{- end}} 553 {{- else if not .IsHostOnly}} 554 {{- if not .IsApk}} 555 min_sdk_version: "{{.DefaultMinSdkVersion}}", 556 {{- end}} 557 {{- end}} 558 {{- if .IsApk}} 559 presigned: true 560 {{- end}} 561 562} 563`)) 564 565var bpDepsTemplate = template.Must(template.New("bp").Parse(` 566{{.ImportModuleType}} { 567 name: "{{.BpName}}-nodeps", 568 {{.ImportProperty}}: ["{{.ArtifactFile}}"], 569 sdk_version: "{{.SdkVersion}}", 570 {{- if .Jetifier}} 571 jetifier: true, 572 {{- end}} 573 {{- if .IsHostAndDeviceModule}} 574 host_supported: true, 575 {{- end}} 576 {{- if not .IsHostOnly}} 577 apex_available: [ 578 "//apex_available:platform", 579 "//apex_available:anyapex", 580 ], 581 {{- end}} 582 {{- if .IsAar}} 583 min_sdk_version: "{{.MinSdkVersion}}", 584 static_libs: [ 585 {{- range .BpJarDeps}} 586 "{{.}}", 587 {{- end}} 588 {{- range .BpAarDeps}} 589 "{{.}}", 590 {{- end}} 591 {{- range .BpExtraStaticLibs}} 592 "{{.}}", 593 {{- end}} 594 ], 595 {{- if .BpExtraLibs}} 596 libs: [ 597 {{- range .BpExtraLibs}} 598 "{{.}}", 599 {{- end}} 600 ], 601 {{- end}} 602 {{- else if not .IsHostOnly}} 603 min_sdk_version: "{{.DefaultMinSdkVersion}}", 604 {{- end}} 605} 606 607{{.ModuleType}} { 608 name: "{{.BpName}}", 609 {{- if .IsDeviceModule}} 610 sdk_version: "{{.SdkVersion}}", 611 {{- if .IsHostAndDeviceModule}} 612 host_supported: true, 613 {{- end}} 614 {{- if not .IsHostOnly}} 615 apex_available: [ 616 "//apex_available:platform", 617 "//apex_available:anyapex", 618 ], 619 {{- end}} 620 {{- if .IsAar}} 621 min_sdk_version: "{{.MinSdkVersion}}", 622 manifest: "manifests/{{.BpName}}/AndroidManifest.xml", 623 {{- else if not .IsHostOnly}} 624 min_sdk_version: "{{.DefaultMinSdkVersion}}", 625 {{- end}} 626 {{- end}} 627 static_libs: [ 628 "{{.BpName}}-nodeps", 629 {{- range .BpJarDeps}} 630 "{{.}}", 631 {{- end}} 632 {{- range .BpAarDeps}} 633 "{{.}}", 634 {{- end}} 635 {{- range .BpExtraStaticLibs}} 636 "{{.}}", 637 {{- end}} 638 ], 639 {{- if .BpExtraLibs}} 640 libs: [ 641 {{- range .BpExtraLibs}} 642 "{{.}}", 643 {{- end}} 644 ], 645 {{- end}} 646 {{- if .BpOptionalUsesLibs}} 647 optional_uses_libs: [ 648 {{- range .BpOptionalUsesLibs}} 649 "{{.}}", 650 {{- end}} 651 ], 652 {{- end}} 653 java_version: "1.7", 654} 655`)) 656 657var bazelTemplate = template.Must(template.New("bp").Parse(` 658{{.BazelImportTargetType}} ( 659 name = "{{.BpName}}", 660 {{.BazelImportProperty}}: {{- if not .IsAar}}[{{- end}}"{{.ArtifactFile}}"{{- if not .IsAar}}]{{- end}}, 661 visibility = ["//visibility:public"], 662 {{- if .IsAar}} 663 deps = [ 664 {{- range .BazelJarDeps}} 665 "{{.}}", 666 {{- end}} 667 {{- range .BazelAarDeps}} 668 "{{.}}", 669 {{- end}} 670 {{- range .BpExtraStaticLibs}} 671 "{{.}}", 672 {{- end}} 673 {{- range .BpExtraLibs}} 674 "{{.}}", 675 {{- end}} 676 {{- range .BpOptionalUsesLibs}} 677 "{{.}}", 678 {{- end}} 679 ], 680 {{- end}} 681) 682`)) 683 684var bazelDepsTemplate = template.Must(template.New("bp").Parse(` 685{{.BazelImportTargetType}} ( 686 name = "{{.BpName}}", 687 {{.BazelImportProperty}} = {{- if not .IsAar}}[{{- end}}"{{.ArtifactFile}}"{{- if not .IsAar}}]{{- end}}, 688 visibility = ["//visibility:public"], 689 exports = [ 690 {{- range .BazelJarDeps}} 691 "{{.}}", 692 {{- end}} 693 {{- range .BazelAarDeps}} 694 "{{.}}", 695 {{- end}} 696 {{- range .BpExtraStaticLibs}} 697 "{{.}}", 698 {{- end}} 699 {{- range .BpExtraLibs}} 700 "{{.}}", 701 {{- end}} 702 {{- range .BpOptionalUsesLibs}} 703 "{{.}}", 704 {{- end}} 705 ], 706) 707`)) 708 709func parse(filename string) (*Pom, error) { 710 data, err := ioutil.ReadFile(filename) 711 if err != nil { 712 return nil, err 713 } 714 715 var pom Pom 716 err = xml.Unmarshal(data, &pom) 717 if err != nil { 718 return nil, err 719 } 720 721 if useVersion != "" && pom.Version != useVersion { 722 return nil, nil 723 } 724 725 if pom.Packaging == "" { 726 pom.Packaging = "jar" 727 } 728 729 pom.PomFile = filename 730 pom.ArtifactFile = strings.TrimSuffix(filename, ".pom") + "." + pom.Packaging 731 732 return &pom, nil 733} 734 735func rerunForRegen(filename string) error { 736 buf, err := ioutil.ReadFile(filename) 737 if err != nil { 738 return err 739 } 740 741 scanner := bufio.NewScanner(bytes.NewBuffer(buf)) 742 743 // Skip the first line in the file 744 for i := 0; i < 2; i++ { 745 if !scanner.Scan() { 746 if scanner.Err() != nil { 747 return scanner.Err() 748 } else { 749 return fmt.Errorf("unexpected EOF") 750 } 751 } 752 } 753 754 // Extract the old args from the file 755 line := scanner.Text() 756 if strings.HasPrefix(line, "// pom2bp ") { // .bp file 757 line = strings.TrimPrefix(line, "// pom2bp ") 758 } else if strings.HasPrefix(line, "// pom2mk ") { // .bp file converted from .mk file 759 line = strings.TrimPrefix(line, "// pom2mk ") 760 } else if strings.HasPrefix(line, "# pom2mk ") { // .mk file 761 line = strings.TrimPrefix(line, "# pom2mk ") 762 } else if strings.HasPrefix(line, "# pom2bp ") { // Bazel BUILD file 763 line = strings.TrimPrefix(line, "# pom2bp ") 764 } else { 765 return fmt.Errorf("unexpected second line: %q", line) 766 } 767 args := strings.Split(line, " ") 768 lastArg := args[len(args)-1] 769 args = args[:len(args)-1] 770 771 // Append all current command line args except -regen <file> to the ones from the file 772 for i := 1; i < len(os.Args); i++ { 773 if os.Args[i] == "-regen" || os.Args[i] == "--regen" { 774 i++ 775 } else { 776 args = append(args, os.Args[i]) 777 } 778 } 779 args = append(args, lastArg) 780 781 cmd := os.Args[0] + " " + strings.Join(args, " ") 782 // Re-exec pom2bp with the new arguments 783 output, err := exec.Command("/bin/sh", "-c", cmd).Output() 784 if exitErr, _ := err.(*exec.ExitError); exitErr != nil { 785 return fmt.Errorf("failed to run %s\n%s", cmd, string(exitErr.Stderr)) 786 } else if err != nil { 787 return err 788 } 789 790 // If the old file was a .mk file, replace it with a .bp file 791 if filepath.Ext(filename) == ".mk" { 792 os.Remove(filename) 793 filename = strings.TrimSuffix(filename, ".mk") + ".bp" 794 } 795 796 return ioutil.WriteFile(filename, output, 0666) 797} 798 799func main() { 800 flag.Usage = func() { 801 fmt.Fprintf(os.Stderr, `pom2bp, a tool to create Android.bp files from maven repos 802 803The tool will extract the necessary information from *.pom files to create an Android.bp whose 804aar libraries can be linked against when using AAPT2. 805 806Usage: %s [--rewrite <regex>=<replace>] [--exclude <module>] [--extra-static-libs <module>=<module>[,<module>]] [--extra-libs <module>=<module>[,<module>]] [--optional-uses-libs <module>=<module>[,<module>]] [<dir>] [-regen <file>] 807 808 -rewrite <regex>=<replace> 809 rewrite can be used to specify mappings between Maven projects and Android.bp modules. The -rewrite 810 option can be specified multiple times. When determining the Android.bp module for a given Maven 811 project, mappings are searched in the order they were specified. The first <regex> matching 812 either the Maven project's <groupId>:<artifactId> or <artifactId> will be used to generate 813 the Android.bp module name using <replace>. If no matches are found, <artifactId> is used. 814 -exclude <module> 815 Don't put the specified module in the Android.bp file. 816 -extra-static-libs <module>=<module>[,<module>] 817 Some Android.bp modules have transitive static dependencies that must be specified when they 818 are depended upon (like android-support-v7-mediarouter requires android-support-v7-appcompat). 819 This may be specified multiple times to declare these dependencies. 820 -extra-libs <module>=<module>[,<module>] 821 Some Android.bp modules have transitive runtime dependencies that must be specified when they 822 are depended upon (like androidx.test.rules requires android.test.base). 823 This may be specified multiple times to declare these dependencies. 824 -optional-uses-libs <module>=<module>[,<module>] 825 Some Android.bp modules have optional dependencies (typically specified with <uses-library> in 826 the module's AndroidManifest.xml) that must be specified when they are depended upon (like 827 androidx.window:window optionally requires androidx.window:window-extensions). 828 This may be specified multiple times to declare these dependencies. 829 -sdk-version <version> 830 Sets sdk_version: "<version>" for all modules. 831 -default-min-sdk-version 832 The default min_sdk_version to use for a module if one cannot be mined from AndroidManifest.xml 833 -use-version <version> 834 If the maven directory contains multiple versions of artifacts and their pom files, 835 -use-version can be used to only write Android.bp files for a specific version of those artifacts. 836 -write-cmd 837 Whether to write the command line arguments used to generate the build file as a comment at 838 the top of the build file itself. 839 -jetifier 840 Sets jetifier: true for all modules. 841 <dir> 842 The directory to search for *.pom files under. 843 The contents are written to stdout, to be put in the current directory (often as Android.bp) 844 -regen <file> 845 Read arguments from <file> and overwrite it (if it ends with .bp) or move it to .bp (if it 846 ends with .mk). 847 848`, os.Args[0]) 849 } 850 851 var regen string 852 var pom2build bool 853 var prepend string 854 855 flag.Var(&excludes, "exclude", "Exclude module") 856 flag.Var(&extraStaticLibs, "extra-static-libs", "Extra static dependencies needed when depending on a module") 857 flag.Var(&extraLibs, "extra-libs", "Extra runtime dependencies needed when depending on a module") 858 flag.Var(&optionalUsesLibs, "optional-uses-libs", "Extra optional dependencies needed when depending on a module") 859 flag.Var(&rewriteNames, "rewrite", "Regex(es) to rewrite artifact names") 860 flag.Var(&hostModuleNames, "host", "Specifies that the corresponding module (specified in the form 'module.group:module.artifact') is a host module") 861 flag.Var(&hostAndDeviceModuleNames, "host-and-device", "Specifies that the corresponding module (specified in the form 'module.group:module.artifact') is both a host and device module.") 862 flag.StringVar(&sdkVersion, "sdk-version", "", "What to write to sdk_version") 863 flag.StringVar(&defaultMinSdkVersion, "default-min-sdk-version", "24", "Default min_sdk_version to use, if one is not available from AndroidManifest.xml. Default: 24") 864 flag.StringVar(&useVersion, "use-version", "", "Only read artifacts of a specific version") 865 flag.BoolVar(&staticDeps, "static-deps", false, "Statically include direct dependencies") 866 flag.BoolVar(&writeCmd, "write-cmd", true, "Write command line arguments as a comment") 867 flag.BoolVar(&jetifier, "jetifier", false, "Sets jetifier: true on all modules") 868 flag.StringVar(®en, "regen", "", "Rewrite specified file") 869 flag.BoolVar(&pom2build, "pom2build", false, "If true, will generate a Bazel BUILD file *instead* of a .bp file") 870 flag.StringVar(&prepend, "prepend", "", "Path to a file containing text to insert at the beginning of the generated build file") 871 flag.Parse() 872 873 if regen != "" { 874 err := rerunForRegen(regen) 875 if err != nil { 876 fmt.Fprintln(os.Stderr, err) 877 os.Exit(1) 878 } 879 os.Exit(0) 880 } 881 882 if flag.NArg() == 0 { 883 fmt.Fprintln(os.Stderr, "Directory argument is required") 884 os.Exit(1) 885 } else if flag.NArg() > 1 { 886 fmt.Fprintln(os.Stderr, "Multiple directories provided:", strings.Join(flag.Args(), " ")) 887 os.Exit(1) 888 } 889 890 dir := flag.Arg(0) 891 absDir, err := filepath.Abs(dir) 892 if err != nil { 893 fmt.Fprintln(os.Stderr, "Failed to get absolute directory:", err) 894 os.Exit(1) 895 } 896 897 var filenames []string 898 err = filepath.Walk(absDir, func(path string, info os.FileInfo, err error) error { 899 if err != nil { 900 return err 901 } 902 903 name := info.Name() 904 if info.IsDir() { 905 if strings.HasPrefix(name, ".") { 906 return filepath.SkipDir 907 } 908 return nil 909 } 910 911 if strings.HasPrefix(name, ".") { 912 return nil 913 } 914 915 if strings.HasSuffix(name, ".pom") { 916 path, err = filepath.Rel(absDir, path) 917 if err != nil { 918 return err 919 } 920 filenames = append(filenames, filepath.Join(dir, path)) 921 } 922 return nil 923 }) 924 if err != nil { 925 fmt.Fprintln(os.Stderr, "Error walking files:", err) 926 os.Exit(1) 927 } 928 929 if len(filenames) == 0 { 930 fmt.Fprintln(os.Stderr, "Error: no *.pom files found under", dir) 931 os.Exit(1) 932 } 933 934 sort.Strings(filenames) 935 936 poms := []*Pom{} 937 modules := make(map[string]*Pom) 938 duplicate := false 939 for _, filename := range filenames { 940 pom, err := parse(filename) 941 if err != nil { 942 fmt.Fprintln(os.Stderr, "Error converting", filename, err) 943 os.Exit(1) 944 } 945 946 if pom != nil { 947 key := pom.BpName() 948 if excludes[key] { 949 continue 950 } 951 952 if old, ok := modules[key]; ok { 953 fmt.Fprintln(os.Stderr, "Module", key, "defined twice:", old.PomFile, pom.PomFile) 954 duplicate = true 955 } 956 957 poms = append(poms, pom) 958 modules[key] = pom 959 } 960 } 961 if duplicate { 962 os.Exit(1) 963 } 964 965 if pom2build { 966 if err := InitRefreshMod(poms); err != nil { 967 fmt.Fprintf(os.Stderr, "Error in refreshmod: %s", err) 968 os.Exit(1) 969 } 970 BazelifyExtraDeps(extraStaticLibs, modules) 971 BazelifyExtraDeps(extraLibs, modules) 972 BazelifyExtraDeps(optionalUsesLibs, modules) 973 } 974 975 for _, pom := range poms { 976 if pom.IsAar() { 977 err := pom.ExtractMinSdkVersion() 978 if err != nil { 979 fmt.Fprintf(os.Stderr, "Error reading manifest for %s: %s", pom.ArtifactFile, err) 980 os.Exit(1) 981 } 982 } 983 pom.FixDeps(modules) 984 if pom2build { 985 pom.GetBazelDepNames(modules) 986 } 987 } 988 989 buf := &bytes.Buffer{} 990 commentString := "//" 991 if pom2build { 992 commentString = "#" 993 } 994 995 fmt.Fprintln(buf, commentString, "This is a generated file. Do not modify directly.") 996 997 if writeCmd { 998 fmt.Fprintln(buf, commentString, "Automatically generated with:") 999 fmt.Fprintln(buf, commentString, "pom2bp", strings.Join(proptools.ShellEscapeList(os.Args[1:]), " ")) 1000 } 1001 1002 if prepend != "" { 1003 contents, err := ioutil.ReadFile(prepend) 1004 if err != nil { 1005 fmt.Fprintln(os.Stderr, "Error reading", prepend, err) 1006 os.Exit(1) 1007 } 1008 fmt.Fprintln(buf, string(contents)) 1009 } 1010 1011 depsTemplate := bpDepsTemplate 1012 template := bpTemplate 1013 if pom2build { 1014 depsTemplate = bazelDepsTemplate 1015 template = bazelTemplate 1016 } 1017 1018 for _, pom := range poms { 1019 var err error 1020 if staticDeps && !pom.IsApk() { 1021 err = depsTemplate.Execute(buf, pom) 1022 } else { 1023 err = template.Execute(buf, pom) 1024 } 1025 if err != nil { 1026 fmt.Fprintln(os.Stderr, "Error writing", pom.PomFile, pom.BpName(), err) 1027 os.Exit(1) 1028 } 1029 } 1030 1031 if pom2build { 1032 os.Stdout.WriteString(buf.String()) 1033 } else { 1034 out, err := bpfix.Reformat(buf.String()) 1035 if err != nil { 1036 fmt.Fprintln(os.Stderr, "Error formatting output", err) 1037 os.Exit(1) 1038 } 1039 os.Stdout.WriteString(out) 1040 } 1041 1042} 1043