blob: ac29a2a9850f3107b5ac27a34b8c470f2eaf6446 [file] [log] [blame]
Dan Willemsen2902fa72017-04-27 21:16:35 -07001// 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 "encoding/xml"
19 "flag"
20 "fmt"
Dan Willemsen2902fa72017-04-27 21:16:35 -070021 "io/ioutil"
22 "os"
23 "path/filepath"
24 "regexp"
25 "sort"
26 "strings"
27 "text/template"
28
29 "github.com/google/blueprint/proptools"
30)
31
32type RewriteNames []RewriteName
33type RewriteName struct {
34 regexp *regexp.Regexp
35 repl string
36}
37
38func (r *RewriteNames) String() string {
39 return ""
40}
41
42func (r *RewriteNames) Set(v string) error {
43 split := strings.SplitN(v, "=", 2)
44 if len(split) != 2 {
45 return fmt.Errorf("Must be in the form of <regex>=<replace>")
46 }
47 regex, err := regexp.Compile(split[0])
48 if err != nil {
49 return nil
50 }
51 *r = append(*r, RewriteName{
52 regexp: regex,
53 repl: split[1],
54 })
55 return nil
56}
57
58func (r *RewriteNames) Rewrite(name string) string {
59 for _, r := range *r {
60 if r.regexp.MatchString(name) {
61 return r.regexp.ReplaceAllString(name, r.repl)
62 }
63 }
64 return name
65}
66
67var rewriteNames = RewriteNames{}
68
69type ExtraDeps map[string][]string
70
71func (d ExtraDeps) String() string {
72 return ""
73}
74
75func (d ExtraDeps) Set(v string) error {
76 split := strings.SplitN(v, "=", 2)
77 if len(split) != 2 {
78 return fmt.Errorf("Must be in the form of <module>=<module>[,<module>]")
79 }
80 d[split[0]] = strings.Split(split[1], ",")
81 return nil
82}
83
84var extraDeps = make(ExtraDeps)
85
Dan Willemsen47e44a42017-10-05 13:28:16 -070086var useVersion string
87
Dan Willemsen2902fa72017-04-27 21:16:35 -070088type Dependency struct {
89 XMLName xml.Name `xml:"dependency"`
90
91 GroupId string `xml:"groupId"`
92 ArtifactId string `xml:"artifactId"`
93 Version string `xml:"version"`
94 Type string `xml:"type"`
95
96 Scope string `xml:"scope"`
97}
98
99type Pom struct {
100 XMLName xml.Name `xml:"http://maven.apache.org/POM/4.0.0 project"`
101
Dan Willemsen5f9d8a62017-10-05 14:01:31 -0700102 PomFile string `xml:"-"`
Dan Willemsen2902fa72017-04-27 21:16:35 -0700103 ArtifactFile string `xml:"-"`
104
105 GroupId string `xml:"groupId"`
106 ArtifactId string `xml:"artifactId"`
107 Version string `xml:"version"`
108 Packaging string `xml:"packaging"`
109
Dan Willemsen5f9d8a62017-10-05 14:01:31 -0700110 Dependencies []*Dependency `xml:"dependencies>dependency"`
Dan Willemsen2902fa72017-04-27 21:16:35 -0700111}
112
113func (p Pom) MkName() string {
114 return rewriteNames.Rewrite(p.ArtifactId)
115}
116
117func (p Pom) MkDeps() []string {
118 var ret []string
119 for _, d := range p.Dependencies {
120 if d.Type != "aar" {
121 continue
122 }
123 name := rewriteNames.Rewrite(d.ArtifactId)
124 ret = append(ret, name)
125 ret = append(ret, extraDeps[name]...)
126 }
127 return ret
128}
129
Dan Willemsen5f9d8a62017-10-05 14:01:31 -0700130func (p *Pom) FixDepTypes(modules map[string]*Pom) {
131 for _, d := range p.Dependencies {
132 if d.Type != "" {
133 continue
134 }
135 if depPom, ok := modules[d.ArtifactId]; ok {
136 d.Type = depPom.Packaging
137 }
138 }
139}
140
Dan Willemsen2902fa72017-04-27 21:16:35 -0700141var mkTemplate = template.Must(template.New("mk").Parse(`
142include $(CLEAR_VARS)
143LOCAL_MODULE := {{.MkName}}
144LOCAL_MODULE_CLASS := JAVA_LIBRARIES
145LOCAL_UNINSTALLABLE_MODULE := true
146LOCAL_SRC_FILES := {{.ArtifactFile}}
147LOCAL_BUILT_MODULE_STEM := javalib.jar
148LOCAL_MODULE_SUFFIX := .{{.Packaging}}
149LOCAL_USE_AAPT2 := true
150LOCAL_STATIC_ANDROID_LIBRARIES := \
151{{range .MkDeps}} {{.}} \
152{{end}}
153include $(BUILD_PREBUILT)
154`))
155
Dan Willemsen5f9d8a62017-10-05 14:01:31 -0700156func parse(filename string) (*Pom, error) {
Dan Willemsen2902fa72017-04-27 21:16:35 -0700157 data, err := ioutil.ReadFile(filename)
158 if err != nil {
Dan Willemsen5f9d8a62017-10-05 14:01:31 -0700159 return nil, err
Dan Willemsen2902fa72017-04-27 21:16:35 -0700160 }
161
162 var pom Pom
163 err = xml.Unmarshal(data, &pom)
164 if err != nil {
Dan Willemsen5f9d8a62017-10-05 14:01:31 -0700165 return nil, err
Dan Willemsen2902fa72017-04-27 21:16:35 -0700166 }
167
Dan Willemsen47e44a42017-10-05 13:28:16 -0700168 if useVersion != "" && pom.Version != useVersion {
Dan Willemsen5f9d8a62017-10-05 14:01:31 -0700169 return nil, nil
Dan Willemsen47e44a42017-10-05 13:28:16 -0700170 }
171
Dan Willemsen2902fa72017-04-27 21:16:35 -0700172 if pom.Packaging == "" {
173 pom.Packaging = "jar"
174 }
175
Dan Willemsen5f9d8a62017-10-05 14:01:31 -0700176 pom.PomFile = filename
Dan Willemsen2902fa72017-04-27 21:16:35 -0700177 pom.ArtifactFile = strings.TrimSuffix(filename, ".pom") + "." + pom.Packaging
178
Dan Willemsen5f9d8a62017-10-05 14:01:31 -0700179 return &pom, nil
Dan Willemsen2902fa72017-04-27 21:16:35 -0700180}
181
182func main() {
183 flag.Usage = func() {
184 fmt.Fprintf(os.Stderr, `pom2mk, a tool to create Android.mk files from maven repos
185
186The tool will extract the necessary information from *.pom files to create an Android.mk whose
187aar libraries can be linked against when using AAPT2.
188
189Usage: %s [--rewrite <regex>=<replace>] [--extra-deps <module>=<module>[,<module>]] <dir>
190
191 -rewrite <regex>=<replace>
192 rewrite can be used to specify mappings between the artifactId in the pom files and module
193 names in the Android.mk files. This can be specified multiple times, the first matching
194 regex will be used.
195 -extra-deps <module>=<module>[,<module>]
196 Some Android.mk modules have transitive dependencies that must be specified when they are
197 depended upon (like android-support-v7-mediarouter requires android-support-v7-appcompat).
198 This may be specified multiple times to declare these dependencies.
Dan Willemsen47e44a42017-10-05 13:28:16 -0700199 -use-version <version>
200 If the maven directory contains multiple versions of artifacts and their pom files,
201 -use-version can be used to only write makefiles for a specific version of those artifacts.
Dan Willemsen2902fa72017-04-27 21:16:35 -0700202 <dir>
203 The directory to search for *.pom files under.
204
205The makefile is written to stdout, to be put in the current directory (often as Android.mk)
206`, os.Args[0])
207 }
208
209 flag.Var(&extraDeps, "extra-deps", "Extra dependencies needed when depending on a module")
210 flag.Var(&rewriteNames, "rewrite", "Regex(es) to rewrite artifact names")
Dan Willemsen47e44a42017-10-05 13:28:16 -0700211 flag.StringVar(&useVersion, "use-version", "", "Only read artifacts of a specific version")
Dan Willemsen2902fa72017-04-27 21:16:35 -0700212 flag.Parse()
213
214 if flag.NArg() != 1 {
215 flag.Usage()
216 os.Exit(1)
217 }
218
219 dir := flag.Arg(0)
220 absDir, err := filepath.Abs(dir)
221 if err != nil {
222 fmt.Println(os.Stderr, "Failed to get absolute directory:", err)
223 os.Exit(1)
224 }
225
226 var filenames []string
227 err = filepath.Walk(absDir, func(path string, info os.FileInfo, err error) error {
228 if err != nil {
229 return err
230 }
231
232 name := info.Name()
233 if info.IsDir() {
234 if strings.HasPrefix(name, ".") {
235 return filepath.SkipDir
236 }
237 return nil
238 }
239
240 if strings.HasPrefix(name, ".") {
241 return nil
242 }
243
244 if strings.HasSuffix(name, ".pom") {
245 path, err = filepath.Rel(absDir, path)
246 if err != nil {
247 return err
248 }
249 filenames = append(filenames, filepath.Join(dir, path))
250 }
251 return nil
252 })
253 if err != nil {
254 fmt.Fprintln(os.Stderr, "Error walking files:", err)
255 os.Exit(1)
256 }
257
258 if len(filenames) == 0 {
259 fmt.Fprintln(os.Stderr, "Error: no *.pom files found under", dir)
260 os.Exit(1)
261 }
262
263 sort.Strings(filenames)
264
Dan Willemsen5f9d8a62017-10-05 14:01:31 -0700265 poms := []*Pom{}
266 modules := make(map[string]*Pom)
267 for _, filename := range filenames {
268 pom, err := parse(filename)
269 if err != nil {
270 fmt.Fprintln(os.Stderr, "Error converting", filename, err)
271 os.Exit(1)
272 }
273
274 if pom != nil {
275 poms = append(poms, pom)
276
277 if old, ok := modules[pom.ArtifactId]; ok {
278 fmt.Fprintln(os.Stderr, "Module", pom.ArtifactId, "defined twice:", old.PomFile, pom.PomFile)
279 os.Exit(1)
280 }
281
282 modules[pom.ArtifactId] = pom
283 }
284 }
285
286 for _, pom := range poms {
287 pom.FixDepTypes(modules)
288 }
289
Dan Willemsen2902fa72017-04-27 21:16:35 -0700290 fmt.Println("# Automatically generated with:")
291 fmt.Println("# pom2mk", strings.Join(proptools.ShellEscape(os.Args[1:]), " "))
292 fmt.Println("LOCAL_PATH := $(call my-dir)")
293
Dan Willemsen5f9d8a62017-10-05 14:01:31 -0700294 for _, pom := range poms {
295 err := mkTemplate.Execute(os.Stdout, pom)
Dan Willemsen2902fa72017-04-27 21:16:35 -0700296 if err != nil {
Dan Willemsen5f9d8a62017-10-05 14:01:31 -0700297 fmt.Fprintln(os.Stderr, "Error writing", pom.PomFile, pom.MkName(), err)
Dan Willemsen2902fa72017-04-27 21:16:35 -0700298 os.Exit(1)
299 }
300 }
301}