• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&regen, "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