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(&regen, "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