blob: e6144a56647d67fb204cfb1e39a635ddebe85d1d [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"
21 "io"
22 "io/ioutil"
23 "os"
24 "path/filepath"
25 "regexp"
26 "sort"
27 "strings"
28 "text/template"
29
30 "github.com/google/blueprint/proptools"
31)
32
33type RewriteNames []RewriteName
34type RewriteName struct {
35 regexp *regexp.Regexp
36 repl string
37}
38
39func (r *RewriteNames) String() string {
40 return ""
41}
42
43func (r *RewriteNames) Set(v string) error {
44 split := strings.SplitN(v, "=", 2)
45 if len(split) != 2 {
46 return fmt.Errorf("Must be in the form of <regex>=<replace>")
47 }
48 regex, err := regexp.Compile(split[0])
49 if err != nil {
50 return nil
51 }
52 *r = append(*r, RewriteName{
53 regexp: regex,
54 repl: split[1],
55 })
56 return nil
57}
58
59func (r *RewriteNames) Rewrite(name string) string {
60 for _, r := range *r {
61 if r.regexp.MatchString(name) {
62 return r.regexp.ReplaceAllString(name, r.repl)
63 }
64 }
65 return name
66}
67
68var rewriteNames = RewriteNames{}
69
70type ExtraDeps map[string][]string
71
72func (d ExtraDeps) String() string {
73 return ""
74}
75
76func (d ExtraDeps) Set(v string) error {
77 split := strings.SplitN(v, "=", 2)
78 if len(split) != 2 {
79 return fmt.Errorf("Must be in the form of <module>=<module>[,<module>]")
80 }
81 d[split[0]] = strings.Split(split[1], ",")
82 return nil
83}
84
85var extraDeps = make(ExtraDeps)
86
87type Dependency struct {
88 XMLName xml.Name `xml:"dependency"`
89
90 GroupId string `xml:"groupId"`
91 ArtifactId string `xml:"artifactId"`
92 Version string `xml:"version"`
93 Type string `xml:"type"`
94
95 Scope string `xml:"scope"`
96}
97
98type Pom struct {
99 XMLName xml.Name `xml:"http://maven.apache.org/POM/4.0.0 project"`
100
101 ArtifactFile string `xml:"-"`
102
103 GroupId string `xml:"groupId"`
104 ArtifactId string `xml:"artifactId"`
105 Version string `xml:"version"`
106 Packaging string `xml:"packaging"`
107
108 Dependencies []Dependency `xml:"dependencies>dependency"`
109}
110
111func (p Pom) MkName() string {
112 return rewriteNames.Rewrite(p.ArtifactId)
113}
114
115func (p Pom) MkDeps() []string {
116 var ret []string
117 for _, d := range p.Dependencies {
118 if d.Type != "aar" {
119 continue
120 }
121 name := rewriteNames.Rewrite(d.ArtifactId)
122 ret = append(ret, name)
123 ret = append(ret, extraDeps[name]...)
124 }
125 return ret
126}
127
128var mkTemplate = template.Must(template.New("mk").Parse(`
129include $(CLEAR_VARS)
130LOCAL_MODULE := {{.MkName}}
131LOCAL_MODULE_CLASS := JAVA_LIBRARIES
132LOCAL_UNINSTALLABLE_MODULE := true
133LOCAL_SRC_FILES := {{.ArtifactFile}}
134LOCAL_BUILT_MODULE_STEM := javalib.jar
135LOCAL_MODULE_SUFFIX := .{{.Packaging}}
136LOCAL_USE_AAPT2 := true
137LOCAL_STATIC_ANDROID_LIBRARIES := \
138{{range .MkDeps}} {{.}} \
139{{end}}
140include $(BUILD_PREBUILT)
141`))
142
143func convert(filename string, out io.Writer) error {
144 data, err := ioutil.ReadFile(filename)
145 if err != nil {
146 return err
147 }
148
149 var pom Pom
150 err = xml.Unmarshal(data, &pom)
151 if err != nil {
152 return err
153 }
154
155 if pom.Packaging == "" {
156 pom.Packaging = "jar"
157 }
158
159 pom.ArtifactFile = strings.TrimSuffix(filename, ".pom") + "." + pom.Packaging
160
161 return mkTemplate.Execute(out, pom)
162}
163
164func main() {
165 flag.Usage = func() {
166 fmt.Fprintf(os.Stderr, `pom2mk, a tool to create Android.mk files from maven repos
167
168The tool will extract the necessary information from *.pom files to create an Android.mk whose
169aar libraries can be linked against when using AAPT2.
170
171Usage: %s [--rewrite <regex>=<replace>] [--extra-deps <module>=<module>[,<module>]] <dir>
172
173 -rewrite <regex>=<replace>
174 rewrite can be used to specify mappings between the artifactId in the pom files and module
175 names in the Android.mk files. This can be specified multiple times, the first matching
176 regex will be used.
177 -extra-deps <module>=<module>[,<module>]
178 Some Android.mk modules have transitive dependencies that must be specified when they are
179 depended upon (like android-support-v7-mediarouter requires android-support-v7-appcompat).
180 This may be specified multiple times to declare these dependencies.
181 <dir>
182 The directory to search for *.pom files under.
183
184The makefile is written to stdout, to be put in the current directory (often as Android.mk)
185`, os.Args[0])
186 }
187
188 flag.Var(&extraDeps, "extra-deps", "Extra dependencies needed when depending on a module")
189 flag.Var(&rewriteNames, "rewrite", "Regex(es) to rewrite artifact names")
190 flag.Parse()
191
192 if flag.NArg() != 1 {
193 flag.Usage()
194 os.Exit(1)
195 }
196
197 dir := flag.Arg(0)
198 absDir, err := filepath.Abs(dir)
199 if err != nil {
200 fmt.Println(os.Stderr, "Failed to get absolute directory:", err)
201 os.Exit(1)
202 }
203
204 var filenames []string
205 err = filepath.Walk(absDir, func(path string, info os.FileInfo, err error) error {
206 if err != nil {
207 return err
208 }
209
210 name := info.Name()
211 if info.IsDir() {
212 if strings.HasPrefix(name, ".") {
213 return filepath.SkipDir
214 }
215 return nil
216 }
217
218 if strings.HasPrefix(name, ".") {
219 return nil
220 }
221
222 if strings.HasSuffix(name, ".pom") {
223 path, err = filepath.Rel(absDir, path)
224 if err != nil {
225 return err
226 }
227 filenames = append(filenames, filepath.Join(dir, path))
228 }
229 return nil
230 })
231 if err != nil {
232 fmt.Fprintln(os.Stderr, "Error walking files:", err)
233 os.Exit(1)
234 }
235
236 if len(filenames) == 0 {
237 fmt.Fprintln(os.Stderr, "Error: no *.pom files found under", dir)
238 os.Exit(1)
239 }
240
241 sort.Strings(filenames)
242
243 fmt.Println("# Automatically generated with:")
244 fmt.Println("# pom2mk", strings.Join(proptools.ShellEscape(os.Args[1:]), " "))
245 fmt.Println("LOCAL_PATH := $(call my-dir)")
246
247 for _, filename := range filenames {
248 err := convert(filename, os.Stdout)
249 if err != nil {
250 fmt.Fprintln(os.Stderr, "Error converting", filename, err)
251 os.Exit(1)
252 }
253 }
254}