blob: 4596db966cd5d053bbc58a4d2ce2fade91944294 [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 Willemsen15a8e792017-11-10 14:02:44 -080086var sdkVersion string
Dan Willemsen47e44a42017-10-05 13:28:16 -070087var useVersion string
88
Dan Willemsen2902fa72017-04-27 21:16:35 -070089type Dependency struct {
90 XMLName xml.Name `xml:"dependency"`
91
92 GroupId string `xml:"groupId"`
93 ArtifactId string `xml:"artifactId"`
94 Version string `xml:"version"`
95 Type string `xml:"type"`
96
97 Scope string `xml:"scope"`
98}
99
100type Pom struct {
101 XMLName xml.Name `xml:"http://maven.apache.org/POM/4.0.0 project"`
102
Dan Willemsen5f9d8a62017-10-05 14:01:31 -0700103 PomFile string `xml:"-"`
Dan Willemsen2902fa72017-04-27 21:16:35 -0700104 ArtifactFile string `xml:"-"`
105
106 GroupId string `xml:"groupId"`
107 ArtifactId string `xml:"artifactId"`
108 Version string `xml:"version"`
109 Packaging string `xml:"packaging"`
110
Dan Willemsen5f9d8a62017-10-05 14:01:31 -0700111 Dependencies []*Dependency `xml:"dependencies>dependency"`
Dan Willemsen2902fa72017-04-27 21:16:35 -0700112}
113
114func (p Pom) MkName() string {
115 return rewriteNames.Rewrite(p.ArtifactId)
116}
117
118func (p Pom) MkDeps() []string {
119 var ret []string
120 for _, d := range p.Dependencies {
121 if d.Type != "aar" {
122 continue
123 }
124 name := rewriteNames.Rewrite(d.ArtifactId)
125 ret = append(ret, name)
126 ret = append(ret, extraDeps[name]...)
127 }
128 return ret
129}
130
Dan Willemsen15a8e792017-11-10 14:02:44 -0800131func (p Pom) SdkVersion() string {
132 return sdkVersion
133}
134
Dan Willemsen5f9d8a62017-10-05 14:01:31 -0700135func (p *Pom) FixDepTypes(modules map[string]*Pom) {
136 for _, d := range p.Dependencies {
137 if d.Type != "" {
138 continue
139 }
140 if depPom, ok := modules[d.ArtifactId]; ok {
141 d.Type = depPom.Packaging
142 }
143 }
144}
145
Dan Willemsen2902fa72017-04-27 21:16:35 -0700146var mkTemplate = template.Must(template.New("mk").Parse(`
147include $(CLEAR_VARS)
148LOCAL_MODULE := {{.MkName}}
149LOCAL_MODULE_CLASS := JAVA_LIBRARIES
150LOCAL_UNINSTALLABLE_MODULE := true
151LOCAL_SRC_FILES := {{.ArtifactFile}}
152LOCAL_BUILT_MODULE_STEM := javalib.jar
153LOCAL_MODULE_SUFFIX := .{{.Packaging}}
154LOCAL_USE_AAPT2 := true
Dan Willemsen15a8e792017-11-10 14:02:44 -0800155LOCAL_SDK_VERSION := {{.SdkVersion}}
Dan Willemsen2902fa72017-04-27 21:16:35 -0700156LOCAL_STATIC_ANDROID_LIBRARIES := \
157{{range .MkDeps}} {{.}} \
158{{end}}
159include $(BUILD_PREBUILT)
160`))
161
Dan Willemsen5f9d8a62017-10-05 14:01:31 -0700162func parse(filename string) (*Pom, error) {
Dan Willemsen2902fa72017-04-27 21:16:35 -0700163 data, err := ioutil.ReadFile(filename)
164 if err != nil {
Dan Willemsen5f9d8a62017-10-05 14:01:31 -0700165 return nil, err
Dan Willemsen2902fa72017-04-27 21:16:35 -0700166 }
167
168 var pom Pom
169 err = xml.Unmarshal(data, &pom)
170 if err != nil {
Dan Willemsen5f9d8a62017-10-05 14:01:31 -0700171 return nil, err
Dan Willemsen2902fa72017-04-27 21:16:35 -0700172 }
173
Dan Willemsen47e44a42017-10-05 13:28:16 -0700174 if useVersion != "" && pom.Version != useVersion {
Dan Willemsen5f9d8a62017-10-05 14:01:31 -0700175 return nil, nil
Dan Willemsen47e44a42017-10-05 13:28:16 -0700176 }
177
Dan Willemsen2902fa72017-04-27 21:16:35 -0700178 if pom.Packaging == "" {
179 pom.Packaging = "jar"
180 }
181
Dan Willemsen5f9d8a62017-10-05 14:01:31 -0700182 pom.PomFile = filename
Dan Willemsen2902fa72017-04-27 21:16:35 -0700183 pom.ArtifactFile = strings.TrimSuffix(filename, ".pom") + "." + pom.Packaging
184
Dan Willemsen5f9d8a62017-10-05 14:01:31 -0700185 return &pom, nil
Dan Willemsen2902fa72017-04-27 21:16:35 -0700186}
187
188func main() {
189 flag.Usage = func() {
190 fmt.Fprintf(os.Stderr, `pom2mk, a tool to create Android.mk files from maven repos
191
192The tool will extract the necessary information from *.pom files to create an Android.mk whose
193aar libraries can be linked against when using AAPT2.
194
195Usage: %s [--rewrite <regex>=<replace>] [--extra-deps <module>=<module>[,<module>]] <dir>
196
197 -rewrite <regex>=<replace>
198 rewrite can be used to specify mappings between the artifactId in the pom files and module
199 names in the Android.mk files. This can be specified multiple times, the first matching
200 regex will be used.
201 -extra-deps <module>=<module>[,<module>]
202 Some Android.mk modules have transitive dependencies that must be specified when they are
203 depended upon (like android-support-v7-mediarouter requires android-support-v7-appcompat).
204 This may be specified multiple times to declare these dependencies.
Dan Willemsen15a8e792017-11-10 14:02:44 -0800205 -sdk-version <version>
206 Sets LOCAL_SDK_VERSION := <version> for all modules.
Dan Willemsen47e44a42017-10-05 13:28:16 -0700207 -use-version <version>
208 If the maven directory contains multiple versions of artifacts and their pom files,
209 -use-version can be used to only write makefiles for a specific version of those artifacts.
Dan Willemsen2902fa72017-04-27 21:16:35 -0700210 <dir>
211 The directory to search for *.pom files under.
212
213The makefile is written to stdout, to be put in the current directory (often as Android.mk)
214`, os.Args[0])
215 }
216
217 flag.Var(&extraDeps, "extra-deps", "Extra dependencies needed when depending on a module")
218 flag.Var(&rewriteNames, "rewrite", "Regex(es) to rewrite artifact names")
Dan Willemsen15a8e792017-11-10 14:02:44 -0800219 flag.StringVar(&sdkVersion, "sdk-version", "", "What to write to LOCAL_SDK_VERSION")
Dan Willemsen47e44a42017-10-05 13:28:16 -0700220 flag.StringVar(&useVersion, "use-version", "", "Only read artifacts of a specific version")
Dan Willemsen2902fa72017-04-27 21:16:35 -0700221 flag.Parse()
222
223 if flag.NArg() != 1 {
224 flag.Usage()
225 os.Exit(1)
226 }
227
228 dir := flag.Arg(0)
229 absDir, err := filepath.Abs(dir)
230 if err != nil {
231 fmt.Println(os.Stderr, "Failed to get absolute directory:", err)
232 os.Exit(1)
233 }
234
235 var filenames []string
236 err = filepath.Walk(absDir, func(path string, info os.FileInfo, err error) error {
237 if err != nil {
238 return err
239 }
240
241 name := info.Name()
242 if info.IsDir() {
243 if strings.HasPrefix(name, ".") {
244 return filepath.SkipDir
245 }
246 return nil
247 }
248
249 if strings.HasPrefix(name, ".") {
250 return nil
251 }
252
253 if strings.HasSuffix(name, ".pom") {
254 path, err = filepath.Rel(absDir, path)
255 if err != nil {
256 return err
257 }
258 filenames = append(filenames, filepath.Join(dir, path))
259 }
260 return nil
261 })
262 if err != nil {
263 fmt.Fprintln(os.Stderr, "Error walking files:", err)
264 os.Exit(1)
265 }
266
267 if len(filenames) == 0 {
268 fmt.Fprintln(os.Stderr, "Error: no *.pom files found under", dir)
269 os.Exit(1)
270 }
271
272 sort.Strings(filenames)
273
Dan Willemsen5f9d8a62017-10-05 14:01:31 -0700274 poms := []*Pom{}
275 modules := make(map[string]*Pom)
276 for _, filename := range filenames {
277 pom, err := parse(filename)
278 if err != nil {
279 fmt.Fprintln(os.Stderr, "Error converting", filename, err)
280 os.Exit(1)
281 }
282
283 if pom != nil {
284 poms = append(poms, pom)
285
286 if old, ok := modules[pom.ArtifactId]; ok {
287 fmt.Fprintln(os.Stderr, "Module", pom.ArtifactId, "defined twice:", old.PomFile, pom.PomFile)
288 os.Exit(1)
289 }
290
291 modules[pom.ArtifactId] = pom
292 }
293 }
294
295 for _, pom := range poms {
296 pom.FixDepTypes(modules)
297 }
298
Dan Willemsen2902fa72017-04-27 21:16:35 -0700299 fmt.Println("# Automatically generated with:")
300 fmt.Println("# pom2mk", strings.Join(proptools.ShellEscape(os.Args[1:]), " "))
301 fmt.Println("LOCAL_PATH := $(call my-dir)")
302
Dan Willemsen5f9d8a62017-10-05 14:01:31 -0700303 for _, pom := range poms {
304 err := mkTemplate.Execute(os.Stdout, pom)
Dan Willemsen2902fa72017-04-27 21:16:35 -0700305 if err != nil {
Dan Willemsen5f9d8a62017-10-05 14:01:31 -0700306 fmt.Fprintln(os.Stderr, "Error writing", pom.PomFile, pom.MkName(), err)
Dan Willemsen2902fa72017-04-27 21:16:35 -0700307 os.Exit(1)
308 }
309 }
310}