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 preprocessed: true, 560 presigned: true, 561 {{- end}} 562 563} 564`)) 565 566var bpDepsTemplate = template.Must(template.New("bp").Parse(` 567{{.ImportModuleType}} { 568 name: "{{.BpName}}-nodeps", 569 {{.ImportProperty}}: ["{{.ArtifactFile}}"], 570 sdk_version: "{{.SdkVersion}}", 571 {{- if .Jetifier}} 572 jetifier: true, 573 {{- end}} 574 {{- if .IsHostAndDeviceModule}} 575 host_supported: true, 576 {{- end}} 577 {{- if not .IsHostOnly}} 578 apex_available: [ 579 "//apex_available:platform", 580 "//apex_available:anyapex", 581 ], 582 {{- end}} 583 {{- if .IsAar}} 584 min_sdk_version: "{{.MinSdkVersion}}", 585 static_libs: [ 586 {{- range .BpJarDeps}} 587 "{{.}}", 588 {{- end}} 589 {{- range .BpAarDeps}} 590 "{{.}}", 591 {{- end}} 592 {{- range .BpExtraStaticLibs}} 593 "{{.}}", 594 {{- end}} 595 ], 596 {{- if .BpExtraLibs}} 597 libs: [ 598 {{- range .BpExtraLibs}} 599 "{{.}}", 600 {{- end}} 601 ], 602 {{- end}} 603 {{- else if not .IsHostOnly}} 604 min_sdk_version: "{{.DefaultMinSdkVersion}}", 605 {{- end}} 606} 607 608{{.ModuleType}} { 609 name: "{{.BpName}}", 610 {{- if .IsDeviceModule}} 611 sdk_version: "{{.SdkVersion}}", 612 {{- if .IsHostAndDeviceModule}} 613 host_supported: true, 614 {{- end}} 615 {{- if not .IsHostOnly}} 616 apex_available: [ 617 "//apex_available:platform", 618 "//apex_available:anyapex", 619 ], 620 {{- end}} 621 {{- if .IsAar}} 622 min_sdk_version: "{{.MinSdkVersion}}", 623 manifest: "manifests/{{.BpName}}/AndroidManifest.xml", 624 {{- else if not .IsHostOnly}} 625 min_sdk_version: "{{.DefaultMinSdkVersion}}", 626 {{- end}} 627 {{- end}} 628 static_libs: [ 629 "{{.BpName}}-nodeps", 630 {{- range .BpJarDeps}} 631 "{{.}}", 632 {{- end}} 633 {{- range .BpAarDeps}} 634 "{{.}}", 635 {{- end}} 636 {{- range .BpExtraStaticLibs}} 637 "{{.}}", 638 {{- end}} 639 ], 640 {{- if .BpExtraLibs}} 641 libs: [ 642 {{- range .BpExtraLibs}} 643 "{{.}}", 644 {{- end}} 645 ], 646 {{- end}} 647 {{- if .BpOptionalUsesLibs}} 648 optional_uses_libs: [ 649 {{- range .BpOptionalUsesLibs}} 650 "{{.}}", 651 {{- end}} 652 ], 653 {{- end}} 654 java_version: "1.8", 655} 656`)) 657 658var bazelTemplate = template.Must(template.New("bp").Parse(` 659{{.BazelImportTargetType}} ( 660 name = "{{.BpName}}", 661 {{.BazelImportProperty}}: {{- if not .IsAar}}[{{- end}}"{{.ArtifactFile}}"{{- if not .IsAar}}]{{- end}}, 662 visibility = ["//visibility:public"], 663 {{- if .IsAar}} 664 deps = [ 665 {{- range .BazelJarDeps}} 666 "{{.}}", 667 {{- end}} 668 {{- range .BazelAarDeps}} 669 "{{.}}", 670 {{- end}} 671 {{- range .BpExtraStaticLibs}} 672 "{{.}}", 673 {{- end}} 674 {{- range .BpExtraLibs}} 675 "{{.}}", 676 {{- end}} 677 {{- range .BpOptionalUsesLibs}} 678 "{{.}}", 679 {{- end}} 680 ], 681 {{- end}} 682) 683`)) 684 685var bazelDepsTemplate = template.Must(template.New("bp").Parse(` 686{{.BazelImportTargetType}} ( 687 name = "{{.BpName}}", 688 {{.BazelImportProperty}} = {{- if not .IsAar}}[{{- end}}"{{.ArtifactFile}}"{{- if not .IsAar}}]{{- end}}, 689 visibility = ["//visibility:public"], 690 exports = [ 691 {{- range .BazelJarDeps}} 692 "{{.}}", 693 {{- end}} 694 {{- range .BazelAarDeps}} 695 "{{.}}", 696 {{- end}} 697 {{- range .BpExtraStaticLibs}} 698 "{{.}}", 699 {{- end}} 700 {{- range .BpExtraLibs}} 701 "{{.}}", 702 {{- end}} 703 {{- range .BpOptionalUsesLibs}} 704 "{{.}}", 705 {{- end}} 706 ], 707) 708`)) 709 710func parse(filename string) (*Pom, error) { 711 data, err := ioutil.ReadFile(filename) 712 if err != nil { 713 return nil, err 714 } 715 716 var pom Pom 717 err = xml.Unmarshal(data, &pom) 718 if err != nil { 719 return nil, err 720 } 721 722 if useVersion != "" && pom.Version != useVersion { 723 return nil, nil 724 } 725 726 if pom.Packaging == "" { 727 pom.Packaging = "jar" 728 } 729 730 pom.PomFile = filename 731 pom.ArtifactFile = strings.TrimSuffix(filename, ".pom") + "." + pom.Packaging 732 733 return &pom, nil 734} 735 736func rerunForRegen(filename string) error { 737 buf, err := ioutil.ReadFile(filename) 738 if err != nil { 739 return err 740 } 741 742 scanner := bufio.NewScanner(bytes.NewBuffer(buf)) 743 744 // Skip the first line in the file 745 for i := 0; i < 2; i++ { 746 if !scanner.Scan() { 747 if scanner.Err() != nil { 748 return scanner.Err() 749 } else { 750 return fmt.Errorf("unexpected EOF") 751 } 752 } 753 } 754 755 // Extract the old args from the file 756 line := scanner.Text() 757 if strings.HasPrefix(line, "// pom2bp ") { // .bp file 758 line = strings.TrimPrefix(line, "// pom2bp ") 759 } else if strings.HasPrefix(line, "// pom2mk ") { // .bp file converted from .mk file 760 line = strings.TrimPrefix(line, "// pom2mk ") 761 } else if strings.HasPrefix(line, "# pom2mk ") { // .mk file 762 line = strings.TrimPrefix(line, "# pom2mk ") 763 } else if strings.HasPrefix(line, "# pom2bp ") { // Bazel BUILD file 764 line = strings.TrimPrefix(line, "# pom2bp ") 765 } else { 766 return fmt.Errorf("unexpected second line: %q", line) 767 } 768 args := strings.Split(line, " ") 769 lastArg := args[len(args)-1] 770 args = args[:len(args)-1] 771 772 // Append all current command line args except -regen <file> to the ones from the file 773 for i := 1; i < len(os.Args); i++ { 774 if os.Args[i] == "-regen" || os.Args[i] == "--regen" { 775 i++ 776 } else { 777 args = append(args, os.Args[i]) 778 } 779 } 780 args = append(args, lastArg) 781 782 cmd := os.Args[0] + " " + strings.Join(args, " ") 783 // Re-exec pom2bp with the new arguments 784 output, err := exec.Command("/bin/sh", "-c", cmd).Output() 785 if exitErr, _ := err.(*exec.ExitError); exitErr != nil { 786 return fmt.Errorf("failed to run %s\n%s", cmd, string(exitErr.Stderr)) 787 } else if err != nil { 788 return err 789 } 790 791 // If the old file was a .mk file, replace it with a .bp file 792 if filepath.Ext(filename) == ".mk" { 793 os.Remove(filename) 794 filename = strings.TrimSuffix(filename, ".mk") + ".bp" 795 } 796 797 return ioutil.WriteFile(filename, output, 0666) 798} 799 800func main() { 801 flag.Usage = func() { 802 fmt.Fprintf(os.Stderr, `pom2bp, a tool to create Android.bp files from maven repos 803 804The tool will extract the necessary information from *.pom files to create an Android.bp whose 805aar libraries can be linked against when using AAPT2. 806 807Usage: %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>] 808 809 -rewrite <regex>=<replace> 810 rewrite can be used to specify mappings between Maven projects and Android.bp modules. The -rewrite 811 option can be specified multiple times. When determining the Android.bp module for a given Maven 812 project, mappings are searched in the order they were specified. The first <regex> matching 813 either the Maven project's <groupId>:<artifactId> or <artifactId> will be used to generate 814 the Android.bp module name using <replace>. If no matches are found, <artifactId> is used. 815 -exclude <module> 816 Don't put the specified module in the Android.bp file. 817 -extra-static-libs <module>=<module>[,<module>] 818 Some Android.bp modules have transitive static dependencies that must be specified when they 819 are depended upon (like android-support-v7-mediarouter requires android-support-v7-appcompat). 820 This may be specified multiple times to declare these dependencies. 821 -extra-libs <module>=<module>[,<module>] 822 Some Android.bp modules have transitive runtime dependencies that must be specified when they 823 are depended upon (like androidx.test.rules requires android.test.base). 824 This may be specified multiple times to declare these dependencies. 825 -optional-uses-libs <module>=<module>[,<module>] 826 Some Android.bp modules have optional dependencies (typically specified with <uses-library> in 827 the module's AndroidManifest.xml) that must be specified when they are depended upon (like 828 androidx.window:window optionally requires androidx.window:window-extensions). 829 This may be specified multiple times to declare these dependencies. 830 -sdk-version <version> 831 Sets sdk_version: "<version>" for all modules. 832 -default-min-sdk-version 833 The default min_sdk_version to use for a module if one cannot be mined from AndroidManifest.xml 834 -use-version <version> 835 If the maven directory contains multiple versions of artifacts and their pom files, 836 -use-version can be used to only write Android.bp files for a specific version of those artifacts. 837 -write-cmd 838 Whether to write the command line arguments used to generate the build file as a comment at 839 the top of the build file itself. 840 -jetifier 841 Sets jetifier: true for all modules. 842 <dir> 843 The directory to search for *.pom files under. 844 The contents are written to stdout, to be put in the current directory (often as Android.bp) 845 -regen <file> 846 Read arguments from <file> and overwrite it (if it ends with .bp) or move it to .bp (if it 847 ends with .mk). 848 849`, os.Args[0]) 850 } 851 852 var regen string 853 var pom2build bool 854 var prepend string 855 856 flag.Var(&excludes, "exclude", "Exclude module") 857 flag.Var(&extraStaticLibs, "extra-static-libs", "Extra static dependencies needed when depending on a module") 858 flag.Var(&extraLibs, "extra-libs", "Extra runtime dependencies needed when depending on a module") 859 flag.Var(&optionalUsesLibs, "optional-uses-libs", "Extra optional dependencies needed when depending on a module") 860 flag.Var(&rewriteNames, "rewrite", "Regex(es) to rewrite artifact names") 861 flag.Var(&hostModuleNames, "host", "Specifies that the corresponding module (specified in the form 'module.group:module.artifact') is a host module") 862 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.") 863 flag.StringVar(&sdkVersion, "sdk-version", "", "What to write to sdk_version") 864 flag.StringVar(&defaultMinSdkVersion, "default-min-sdk-version", "24", "Default min_sdk_version to use, if one is not available from AndroidManifest.xml. Default: 24") 865 flag.StringVar(&useVersion, "use-version", "", "Only read artifacts of a specific version") 866 flag.BoolVar(&staticDeps, "static-deps", false, "Statically include direct dependencies") 867 flag.BoolVar(&writeCmd, "write-cmd", true, "Write command line arguments as a comment") 868 flag.BoolVar(&jetifier, "jetifier", false, "Sets jetifier: true on all modules") 869 flag.StringVar(®en, "regen", "", "Rewrite specified file") 870 flag.BoolVar(&pom2build, "pom2build", false, "If true, will generate a Bazel BUILD file *instead* of a .bp file") 871 flag.StringVar(&prepend, "prepend", "", "Path to a file containing text to insert at the beginning of the generated build file") 872 flag.Parse() 873 874 if regen != "" { 875 err := rerunForRegen(regen) 876 if err != nil { 877 fmt.Fprintln(os.Stderr, err) 878 os.Exit(1) 879 } 880 os.Exit(0) 881 } 882 883 if flag.NArg() == 0 { 884 fmt.Fprintln(os.Stderr, "Directory argument is required") 885 os.Exit(1) 886 } else if flag.NArg() > 1 { 887 fmt.Fprintln(os.Stderr, "Multiple directories provided:", strings.Join(flag.Args(), " ")) 888 os.Exit(1) 889 } 890 891 dir := flag.Arg(0) 892 absDir, err := filepath.Abs(dir) 893 if err != nil { 894 fmt.Fprintln(os.Stderr, "Failed to get absolute directory:", err) 895 os.Exit(1) 896 } 897 898 var filenames []string 899 err = filepath.Walk(absDir, func(path string, info os.FileInfo, err error) error { 900 if err != nil { 901 return err 902 } 903 904 name := info.Name() 905 if info.IsDir() { 906 if strings.HasPrefix(name, ".") { 907 return filepath.SkipDir 908 } 909 return nil 910 } 911 912 if strings.HasPrefix(name, ".") { 913 return nil 914 } 915 916 if strings.HasSuffix(name, ".pom") { 917 path, err = filepath.Rel(absDir, path) 918 if err != nil { 919 return err 920 } 921 filenames = append(filenames, filepath.Join(dir, path)) 922 } 923 return nil 924 }) 925 if err != nil { 926 fmt.Fprintln(os.Stderr, "Error walking files:", err) 927 os.Exit(1) 928 } 929 930 if len(filenames) == 0 { 931 fmt.Fprintln(os.Stderr, "Error: no *.pom files found under", dir) 932 os.Exit(1) 933 } 934 935 sort.Strings(filenames) 936 937 poms := []*Pom{} 938 modules := make(map[string]*Pom) 939 duplicate := false 940 for _, filename := range filenames { 941 pom, err := parse(filename) 942 if err != nil { 943 fmt.Fprintln(os.Stderr, "Error converting", filename, err) 944 os.Exit(1) 945 } 946 947 if pom != nil { 948 key := pom.BpName() 949 if excludes[key] { 950 continue 951 } 952 953 if old, ok := modules[key]; ok { 954 fmt.Fprintln(os.Stderr, "Module", key, "defined twice:", old.PomFile, pom.PomFile) 955 duplicate = true 956 } 957 958 poms = append(poms, pom) 959 modules[key] = pom 960 } 961 } 962 if duplicate { 963 os.Exit(1) 964 } 965 966 if pom2build { 967 if err := InitRefreshMod(poms); err != nil { 968 fmt.Fprintf(os.Stderr, "Error in refreshmod: %s", err) 969 os.Exit(1) 970 } 971 BazelifyExtraDeps(extraStaticLibs, modules) 972 BazelifyExtraDeps(extraLibs, modules) 973 BazelifyExtraDeps(optionalUsesLibs, modules) 974 } 975 976 for _, pom := range poms { 977 if pom.IsAar() { 978 err := pom.ExtractMinSdkVersion() 979 if err != nil { 980 fmt.Fprintf(os.Stderr, "Error reading manifest for %s: %s", pom.ArtifactFile, err) 981 os.Exit(1) 982 } 983 } 984 pom.FixDeps(modules) 985 if pom2build { 986 pom.GetBazelDepNames(modules) 987 } 988 } 989 990 buf := &bytes.Buffer{} 991 commentString := "//" 992 if pom2build { 993 commentString = "#" 994 } 995 996 fmt.Fprintln(buf, commentString, "This is a generated file. Do not modify directly.") 997 998 if writeCmd { 999 fmt.Fprintln(buf, commentString, "Automatically generated with:") 1000 fmt.Fprintln(buf, commentString, "pom2bp", strings.Join(proptools.ShellEscapeList(os.Args[1:]), " ")) 1001 } 1002 1003 if prepend != "" { 1004 contents, err := ioutil.ReadFile(prepend) 1005 if err != nil { 1006 fmt.Fprintln(os.Stderr, "Error reading", prepend, err) 1007 os.Exit(1) 1008 } 1009 fmt.Fprintln(buf, string(contents)) 1010 } 1011 1012 depsTemplate := bpDepsTemplate 1013 template := bpTemplate 1014 if pom2build { 1015 depsTemplate = bazelDepsTemplate 1016 template = bazelTemplate 1017 } 1018 1019 for _, pom := range poms { 1020 var err error 1021 if staticDeps && !pom.IsApk() { 1022 err = depsTemplate.Execute(buf, pom) 1023 } else { 1024 err = template.Execute(buf, pom) 1025 } 1026 if err != nil { 1027 fmt.Fprintln(os.Stderr, "Error writing", pom.PomFile, pom.BpName(), err) 1028 os.Exit(1) 1029 } 1030 } 1031 1032 if pom2build { 1033 os.Stdout.WriteString(buf.String()) 1034 } else { 1035 out, err := bpfix.Reformat(buf.String()) 1036 if err != nil { 1037 fmt.Fprintln(os.Stderr, "Error formatting output", err) 1038 os.Exit(1) 1039 } 1040 os.Stdout.WriteString(out) 1041 } 1042 1043} 1044