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 15 package main 16 17 import ( 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 33 type RewriteNames []RewriteName 34 type RewriteName struct { 35 regexp *regexp.Regexp 36 repl string 37 } 38 39 func (r *RewriteNames) String() string { 40 return "" 41 } 42 43 func (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 59 func (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 68 var rewriteNames = RewriteNames{} 69 70 type ExtraDeps map[string][]string 71 72 func (d ExtraDeps) String() string { 73 return "" 74 } 75 76 func (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 85 var extraDeps = make(ExtraDeps) 86 87 type 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 98 type 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 111 func (p Pom) MkName() string { 112 return rewriteNames.Rewrite(p.ArtifactId) 113 } 114 115 func (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 128 var mkTemplate = template.Must(template.New("mk").Parse(` 129 include $(CLEAR_VARS) 130 LOCAL_MODULE := {{.MkName}} 131 LOCAL_MODULE_CLASS := JAVA_LIBRARIES 132 LOCAL_UNINSTALLABLE_MODULE := true 133 LOCAL_SRC_FILES := {{.ArtifactFile}} 134 LOCAL_BUILT_MODULE_STEM := javalib.jar 135 LOCAL_MODULE_SUFFIX := .{{.Packaging}} 136 LOCAL_USE_AAPT2 := true 137 LOCAL_STATIC_ANDROID_LIBRARIES := \ 138 {{range .MkDeps}} {{.}} \ 139 {{end}} 140 include $(BUILD_PREBUILT) 141 `)) 142 143 func 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 164 func main() { 165 flag.Usage = func() { 166 fmt.Fprintf(os.Stderr, `pom2mk, a tool to create Android.mk files from maven repos 167 168 The tool will extract the necessary information from *.pom files to create an Android.mk whose 169 aar libraries can be linked against when using AAPT2. 170 171 Usage: %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 184 The 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 } 255