2018-04-16 22:52:10 +02:00
// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
2018-06-27 00:27:20 +02:00
"archive/zip"
2018-04-16 22:52:10 +02:00
"bufio"
"bytes"
"encoding/xml"
"flag"
"fmt"
"io/ioutil"
"os"
"os/exec"
2021-09-20 16:14:22 +02:00
"path"
2018-04-16 22:52:10 +02:00
"path/filepath"
"regexp"
"sort"
"strings"
"text/template"
"github.com/google/blueprint/proptools"
"android/soong/bpfix/bpfix"
)
type RewriteNames [ ] RewriteName
type RewriteName struct {
regexp * regexp . Regexp
repl string
}
func ( r * RewriteNames ) String ( ) string {
return ""
}
func ( r * RewriteNames ) Set ( v string ) error {
split := strings . SplitN ( v , "=" , 2 )
if len ( split ) != 2 {
return fmt . Errorf ( "Must be in the form of <regex>=<replace>" )
}
regex , err := regexp . Compile ( split [ 0 ] )
if err != nil {
return nil
}
* r = append ( * r , RewriteName {
regexp : regex ,
repl : split [ 1 ] ,
} )
return nil
}
func ( r * RewriteNames ) MavenToBp ( groupId string , artifactId string ) string {
for _ , r := range * r {
if r . regexp . MatchString ( groupId + ":" + artifactId ) {
return r . regexp . ReplaceAllString ( groupId + ":" + artifactId , r . repl )
} else if r . regexp . MatchString ( artifactId ) {
return r . regexp . ReplaceAllString ( artifactId , r . repl )
}
}
return artifactId
}
var rewriteNames = RewriteNames { }
type ExtraDeps map [ string ] [ ] string
func ( d ExtraDeps ) String ( ) string {
return ""
}
func ( d ExtraDeps ) Set ( v string ) error {
split := strings . SplitN ( v , "=" , 2 )
if len ( split ) != 2 {
return fmt . Errorf ( "Must be in the form of <module>=<module>[,<module>]" )
}
d [ split [ 0 ] ] = strings . Split ( split [ 1 ] , "," )
return nil
}
2019-04-16 12:35:20 +02:00
var extraStaticLibs = make ( ExtraDeps )
var extraLibs = make ( ExtraDeps )
2018-04-16 22:52:10 +02:00
2021-08-31 22:00:52 +02:00
var optionalUsesLibs = make ( ExtraDeps )
2018-04-16 22:52:10 +02:00
type Exclude map [ string ] bool
func ( e Exclude ) String ( ) string {
return ""
}
func ( e Exclude ) Set ( v string ) error {
e [ v ] = true
return nil
}
var excludes = make ( Exclude )
2018-08-24 20:30:13 +02:00
type HostModuleNames map [ string ] bool
func ( n HostModuleNames ) IsHostModule ( groupId string , artifactId string ) bool {
2018-08-30 00:36:33 +02:00
_ , found := n [ groupId + ":" + artifactId ]
2018-08-24 20:30:13 +02:00
return found
}
func ( n HostModuleNames ) String ( ) string {
return ""
}
func ( n HostModuleNames ) Set ( v string ) error {
n [ v ] = true
return nil
}
var hostModuleNames = HostModuleNames { }
2019-07-18 22:36:44 +02:00
type HostAndDeviceModuleNames map [ string ] bool
func ( n HostAndDeviceModuleNames ) IsHostAndDeviceModule ( groupId string , artifactId string ) bool {
_ , found := n [ groupId + ":" + artifactId ]
return found
}
func ( n HostAndDeviceModuleNames ) String ( ) string {
return ""
}
func ( n HostAndDeviceModuleNames ) Set ( v string ) error {
n [ v ] = true
return nil
}
var hostAndDeviceModuleNames = HostAndDeviceModuleNames { }
2018-04-16 22:52:10 +02:00
var sdkVersion string
2021-03-29 17:10:06 +02:00
var defaultMinSdkVersion string
2018-04-16 22:52:10 +02:00
var useVersion string
2019-04-22 06:37:39 +02:00
var staticDeps bool
2022-09-19 18:26:00 +02:00
var writeCmd bool
2019-04-21 06:47:14 +02:00
var jetifier bool
2018-04-16 22:52:10 +02:00
func InList ( s string , list [ ] string ) bool {
for _ , l := range list {
if l == s {
return true
}
}
return false
}
type Dependency struct {
XMLName xml . Name ` xml:"dependency" `
2021-09-20 16:14:22 +02:00
BpTarget string ` xml:"-" `
BazelTarget string ` xml:"-" `
2018-04-16 22:52:10 +02:00
GroupId string ` xml:"groupId" `
ArtifactId string ` xml:"artifactId" `
Version string ` xml:"version" `
Type string ` xml:"type" `
Scope string ` xml:"scope" `
}
func ( d Dependency ) BpName ( ) string {
if d . BpTarget == "" {
d . BpTarget = rewriteNames . MavenToBp ( d . GroupId , d . ArtifactId )
}
return d . BpTarget
}
type Pom struct {
XMLName xml . Name ` xml:"http://maven.apache.org/POM/4.0.0 project" `
2018-06-27 00:27:20 +02:00
PomFile string ` xml:"-" `
ArtifactFile string ` xml:"-" `
BpTarget string ` xml:"-" `
MinSdkVersion string ` xml:"-" `
2018-04-16 22:52:10 +02:00
GroupId string ` xml:"groupId" `
ArtifactId string ` xml:"artifactId" `
Version string ` xml:"version" `
Packaging string ` xml:"packaging" `
Dependencies [ ] * Dependency ` xml:"dependencies>dependency" `
}
func ( p Pom ) IsAar ( ) bool {
return p . Packaging == "aar"
}
func ( p Pom ) IsJar ( ) bool {
return p . Packaging == "jar"
}
2022-10-12 21:12:02 +02:00
func ( p Pom ) IsApk ( ) bool {
return p . Packaging == "apk"
}
2018-08-24 20:30:13 +02:00
func ( p Pom ) IsHostModule ( ) bool {
return hostModuleNames . IsHostModule ( p . GroupId , p . ArtifactId )
}
func ( p Pom ) IsDeviceModule ( ) bool {
return ! p . IsHostModule ( )
}
2019-07-18 22:36:44 +02:00
func ( p Pom ) IsHostAndDeviceModule ( ) bool {
return hostAndDeviceModuleNames . IsHostAndDeviceModule ( p . GroupId , p . ArtifactId )
}
2020-05-21 20:54:24 +02:00
func ( p Pom ) IsHostOnly ( ) bool {
return p . IsHostModule ( ) && ! p . IsHostAndDeviceModule ( )
}
2018-08-30 01:17:55 +02:00
func ( p Pom ) ModuleType ( ) string {
if p . IsAar ( ) {
return "android_library"
2020-05-21 20:54:24 +02:00
} else if p . IsHostOnly ( ) {
2018-08-30 01:17:55 +02:00
return "java_library_host"
} else {
return "java_library_static"
}
}
2021-09-20 16:14:22 +02:00
func ( p Pom ) BazelTargetType ( ) string {
if p . IsAar ( ) {
return "android_library"
} else {
return "java_library"
}
}
2018-08-30 01:17:55 +02:00
func ( p Pom ) ImportModuleType ( ) string {
if p . IsAar ( ) {
return "android_library_import"
2022-10-12 21:12:02 +02:00
} else if p . IsApk ( ) {
return "android_app_import"
2020-05-21 20:54:24 +02:00
} else if p . IsHostOnly ( ) {
2018-08-30 01:17:55 +02:00
return "java_import_host"
} else {
return "java_import"
}
}
2021-09-20 16:14:22 +02:00
func ( p Pom ) BazelImportTargetType ( ) string {
if p . IsAar ( ) {
return "aar_import"
2022-10-12 21:12:02 +02:00
} else if p . IsApk ( ) {
return "apk_import"
2021-09-20 16:14:22 +02:00
} else {
return "java_import"
}
}
2018-08-30 01:17:55 +02:00
func ( p Pom ) ImportProperty ( ) string {
if p . IsAar ( ) {
return "aars"
2022-10-12 21:12:02 +02:00
} else if p . IsApk ( ) {
return "apk"
2018-08-30 01:17:55 +02:00
} else {
return "jars"
}
}
2021-09-20 16:14:22 +02:00
func ( p Pom ) BazelImportProperty ( ) string {
if p . IsAar ( ) {
return "aar"
2022-10-12 21:12:02 +02:00
} else if p . IsApk ( ) {
return "apk"
2021-09-20 16:14:22 +02:00
} else {
return "jars"
}
}
2018-04-16 22:52:10 +02:00
func ( p Pom ) BpName ( ) string {
if p . BpTarget == "" {
p . BpTarget = rewriteNames . MavenToBp ( p . GroupId , p . ArtifactId )
}
return p . BpTarget
}
func ( p Pom ) BpJarDeps ( ) [ ] string {
return p . BpDeps ( "jar" , [ ] string { "compile" , "runtime" } )
}
func ( p Pom ) BpAarDeps ( ) [ ] string {
return p . BpDeps ( "aar" , [ ] string { "compile" , "runtime" } )
}
2021-09-20 16:14:22 +02:00
func ( p Pom ) BazelJarDeps ( ) [ ] string {
return p . BazelDeps ( "jar" , [ ] string { "compile" , "runtime" } )
}
func ( p Pom ) BazelAarDeps ( ) [ ] string {
return p . BazelDeps ( "aar" , [ ] string { "compile" , "runtime" } )
}
2019-04-16 12:35:20 +02:00
func ( p Pom ) BpExtraStaticLibs ( ) [ ] string {
return extraStaticLibs [ p . BpName ( ) ]
}
func ( p Pom ) BpExtraLibs ( ) [ ] string {
return extraLibs [ p . BpName ( ) ]
2018-04-16 22:52:10 +02:00
}
2021-08-31 22:00:52 +02:00
func ( p Pom ) BpOptionalUsesLibs ( ) [ ] string {
return optionalUsesLibs [ p . BpName ( ) ]
}
2018-04-16 22:52:10 +02:00
// BpDeps obtains dependencies filtered by type and scope. The results of this
// method are formatted as Android.bp targets, e.g. run through MavenToBp rules.
func ( p Pom ) BpDeps ( typeExt string , scopes [ ] string ) [ ] string {
var ret [ ] string
for _ , d := range p . Dependencies {
if d . Type != typeExt || ! InList ( d . Scope , scopes ) {
continue
}
name := rewriteNames . MavenToBp ( d . GroupId , d . ArtifactId )
ret = append ( ret , name )
}
return ret
}
2021-09-20 16:14:22 +02:00
// BazelDeps obtains dependencies filtered by type and scope. The results of this
// method are formatted as Bazel BUILD targets.
func ( p Pom ) BazelDeps ( typeExt string , scopes [ ] string ) [ ] string {
var ret [ ] string
for _ , d := range p . Dependencies {
if d . Type != typeExt || ! InList ( d . Scope , scopes ) {
continue
}
ret = append ( ret , d . BazelTarget )
}
return ret
}
func PathModVars ( ) ( string , string , string ) {
cmd := "/bin/bash"
androidTop := os . Getenv ( "ANDROID_BUILD_TOP" )
envSetupSh := path . Join ( androidTop , "build/envsetup.sh" )
return cmd , androidTop , envSetupSh
}
func InitRefreshMod ( poms [ ] * Pom ) error {
cmd , _ , envSetupSh := PathModVars ( )
// refreshmod is expensive, so if pathmod is already working we can skip it.
_ , err := exec . Command ( cmd , "-c" , ". " + envSetupSh + " && pathmod " + poms [ 0 ] . BpName ( ) ) . Output ( )
if exitErr , _ := err . ( * exec . ExitError ) ; exitErr != nil || err != nil {
_ , err := exec . Command ( cmd , "-c" , ". " + envSetupSh + " && refreshmod" ) . Output ( )
if exitErr , _ := err . ( * exec . ExitError ) ; exitErr != nil {
return fmt . Errorf ( "failed to run %s\n%s\ntry running lunch." , cmd , string ( exitErr . Stderr ) )
} else if err != nil {
return err
}
}
return nil
}
func BazelifyExtraDeps ( extraDeps ExtraDeps , modules map [ string ] * Pom ) error {
for _ , deps := range extraDeps {
for _ , dep := range deps {
bazelName , err := BpNameToBazelTarget ( dep , modules )
if err != nil {
return err
}
dep = bazelName
}
}
return nil
}
func ( p * Pom ) GetBazelDepNames ( modules map [ string ] * Pom ) error {
for _ , d := range p . Dependencies {
bazelName , err := BpNameToBazelTarget ( d . BpName ( ) , modules )
if err != nil {
return err
}
d . BazelTarget = bazelName
}
return nil
}
func BpNameToBazelTarget ( bpName string , modules map [ string ] * Pom ) ( string , error ) {
cmd , androidTop , envSetupSh := PathModVars ( )
if _ , ok := modules [ bpName ] ; ok {
// We've seen the POM for this dependency, it will be local to the output BUILD file
return ":" + bpName , nil
} else {
// we don't have the POM for this artifact, find and use the fully qualified target name.
output , err := exec . Command ( cmd , "-c" , ". " + envSetupSh + " && pathmod " + bpName ) . Output ( )
if exitErr , _ := err . ( * exec . ExitError ) ; exitErr != nil {
return "" , fmt . Errorf ( "failed to run %s %s\n%s" , cmd , bpName , string ( exitErr . Stderr ) )
} else if err != nil {
return "" , err
}
relPath := ""
for _ , line := range strings . Fields ( string ( output ) ) {
if strings . Contains ( line , androidTop ) {
relPath = strings . TrimPrefix ( line , androidTop )
relPath = strings . TrimLeft ( relPath , "/" )
}
}
return "//" + relPath + ":" + bpName , nil
}
}
2018-04-16 22:52:10 +02:00
func ( p Pom ) SdkVersion ( ) string {
return sdkVersion
}
2021-03-29 17:10:06 +02:00
func ( p Pom ) DefaultMinSdkVersion ( ) string {
return defaultMinSdkVersion
}
2019-04-21 06:47:14 +02:00
func ( p Pom ) Jetifier ( ) bool {
return jetifier
}
2018-04-16 22:52:10 +02:00
func ( p * Pom ) FixDeps ( modules map [ string ] * Pom ) {
for _ , d := range p . Dependencies {
if d . Type == "" {
if depPom , ok := modules [ d . BpName ( ) ] ; ok {
// We've seen the POM for this dependency, use its packaging
// as the dependency type rather than Maven spec default.
d . Type = depPom . Packaging
} else {
// Dependency type was not specified and we don't have the POM
// for this artifact, use the default from Maven spec.
d . Type = "jar"
}
}
if d . Scope == "" {
// Scope was not specified, use the default from Maven spec.
d . Scope = "compile"
}
}
}
2018-06-27 00:27:20 +02:00
// ExtractMinSdkVersion extracts the minSdkVersion from the AndroidManifest.xml file inside an aar file, or sets it
// to "current" if it is not present.
func ( p * Pom ) ExtractMinSdkVersion ( ) error {
aar , err := zip . OpenReader ( p . ArtifactFile )
if err != nil {
return err
}
defer aar . Close ( )
var manifest * zip . File
for _ , f := range aar . File {
if f . Name == "AndroidManifest.xml" {
manifest = f
break
}
}
if manifest == nil {
return fmt . Errorf ( "failed to find AndroidManifest.xml in %s" , p . ArtifactFile )
}
r , err := manifest . Open ( )
if err != nil {
return err
}
defer r . Close ( )
decoder := xml . NewDecoder ( r )
manifestData := struct {
XMLName xml . Name ` xml:"manifest" `
Uses_sdk struct {
MinSdkVersion string ` xml:"http://schemas.android.com/apk/res/android minSdkVersion,attr" `
} ` xml:"uses-sdk" `
} { }
err = decoder . Decode ( & manifestData )
if err != nil {
return err
}
p . MinSdkVersion = manifestData . Uses_sdk . MinSdkVersion
if p . MinSdkVersion == "" {
p . MinSdkVersion = "current"
}
return nil
}
2018-04-16 22:52:10 +02:00
var bpTemplate = template . Must ( template . New ( "bp" ) . Parse ( `
2019-04-22 06:37:39 +02:00
{ { . ImportModuleType } } {
name : "{{.BpName}}" ,
2022-10-12 21:12:02 +02:00
{ { - if . IsApk } }
{ { . ImportProperty } } : "{{.ArtifactFile}}" ,
{ { - else } }
2019-04-22 06:37:39 +02:00
{ { . ImportProperty } } : [ "{{.ArtifactFile}}" ] ,
sdk_version : "{{.SdkVersion}}" ,
2022-10-12 21:12:02 +02:00
{ { - end } }
2019-04-22 06:37:39 +02:00
{ { - if . Jetifier } }
jetifier : true ,
{ { - end } }
2019-07-18 22:36:44 +02:00
{ { - if . IsHostAndDeviceModule } }
host_supported : true ,
{ { - end } }
2020-05-21 20:54:24 +02:00
{ { - if not . IsHostOnly } }
apex_available : [
"//apex_available:platform" ,
"//apex_available:anyapex" ,
] ,
{ { - end } }
2019-04-22 06:37:39 +02:00
{ { - if . IsAar } }
min_sdk_version : "{{.MinSdkVersion}}" ,
static_libs : [
{ { - range . BpJarDeps } }
"{{.}}" ,
{ { - end } }
{ { - range . BpAarDeps } }
"{{.}}" ,
{ { - end } }
{ { - range . BpExtraStaticLibs } }
"{{.}}" ,
{ { - end } }
] ,
{ { - if . BpExtraLibs } }
libs : [
{ { - range . BpExtraLibs } }
"{{.}}" ,
{ { - end } }
] ,
2021-09-13 19:12:28 +02:00
{ { - end } }
2021-08-31 22:00:52 +02:00
{ { - if . BpOptionalUsesLibs } }
optional_uses_libs : [
{ { - range . BpOptionalUsesLibs } }
"{{.}}" ,
{ { - end } }
] ,
2019-04-22 06:37:39 +02:00
{ { - end } }
2021-04-06 21:21:34 +02:00
{ { - else if not . IsHostOnly } }
2022-10-12 21:12:02 +02:00
{ { - if not . IsApk } }
2021-04-06 21:21:34 +02:00
min_sdk_version : "{{.DefaultMinSdkVersion}}" ,
2019-04-22 06:37:39 +02:00
{ { - end } }
2022-10-12 21:12:02 +02:00
{ { - end } }
{ { - if . IsApk } }
2023-07-15 00:44:24 +02:00
preprocessed : true ,
presigned : true ,
2022-10-12 21:12:02 +02:00
{ { - end } }
2019-04-22 06:37:39 +02:00
}
` ) )
var bpDepsTemplate = template . Must ( template . New ( "bp" ) . Parse ( `
2018-08-30 01:17:55 +02:00
{ { . ImportModuleType } } {
2018-04-16 22:52:10 +02:00
name : "{{.BpName}}-nodeps" ,
2018-08-30 01:17:55 +02:00
{ { . ImportProperty } } : [ "{{.ArtifactFile}}" ] ,
sdk_version : "{{.SdkVersion}}" ,
2019-04-21 06:47:14 +02:00
{ { - if . Jetifier } }
jetifier : true ,
{ { - end } }
2019-07-18 22:36:44 +02:00
{ { - if . IsHostAndDeviceModule } }
host_supported : true ,
{ { - end } }
2020-05-21 20:54:24 +02:00
{ { - if not . IsHostOnly } }
apex_available : [
"//apex_available:platform" ,
"//apex_available:anyapex" ,
] ,
{ { - end } }
2018-08-30 01:17:55 +02:00
{ { - if . IsAar } }
2018-06-27 00:27:20 +02:00
min_sdk_version : "{{.MinSdkVersion}}" ,
2018-08-30 01:17:55 +02:00
static_libs : [
2019-04-10 20:07:15 +02:00
{ { - range . BpJarDeps } }
"{{.}}" ,
{ { - end } }
2018-08-30 01:17:55 +02:00
{ { - range . BpAarDeps } }
"{{.}}" ,
{ { - end } }
2019-04-16 12:35:20 +02:00
{ { - range . BpExtraStaticLibs } }
"{{.}}" ,
{ { - end } }
] ,
{ { - if . BpExtraLibs } }
libs : [
{ { - range . BpExtraLibs } }
2018-08-30 01:17:55 +02:00
"{{.}}" ,
{ { - end } }
] ,
{ { - end } }
2021-04-06 21:21:34 +02:00
{ { - else if not . IsHostOnly } }
min_sdk_version : "{{.DefaultMinSdkVersion}}" ,
2019-04-16 12:35:20 +02:00
{ { - end } }
2018-04-16 22:52:10 +02:00
}
2018-08-30 01:17:55 +02:00
{ { . ModuleType } } {
name : "{{.BpName}}" ,
{ { - if . IsDeviceModule } }
sdk_version : "{{.SdkVersion}}" ,
2019-07-18 22:36:44 +02:00
{ { - if . IsHostAndDeviceModule } }
host_supported : true ,
{ { - end } }
2020-05-21 20:54:24 +02:00
{ { - if not . IsHostOnly } }
apex_available : [
"//apex_available:platform" ,
"//apex_available:anyapex" ,
] ,
{ { - end } }
2018-08-30 01:17:55 +02:00
{ { - if . IsAar } }
2018-07-10 22:45:30 +02:00
min_sdk_version : "{{.MinSdkVersion}}" ,
2018-08-30 01:17:55 +02:00
manifest : "manifests/{{.BpName}}/AndroidManifest.xml" ,
2020-05-21 20:54:24 +02:00
{ { - else if not . IsHostOnly } }
2021-03-29 17:10:06 +02:00
min_sdk_version : "{{.DefaultMinSdkVersion}}" ,
2018-08-30 01:17:55 +02:00
{ { - end } }
{ { - end } }
2018-04-16 22:52:10 +02:00
static_libs : [
2018-08-30 01:17:55 +02:00
"{{.BpName}}-nodeps" ,
2019-04-10 20:07:15 +02:00
{ { - range . BpJarDeps } }
2018-08-30 01:17:55 +02:00
"{{.}}" ,
{ { - end } }
{ { - range . BpAarDeps } }
"{{.}}" ,
{ { - end } }
2019-04-16 12:35:20 +02:00
{ { - range . BpExtraStaticLibs } }
"{{.}}" ,
{ { - end } }
] ,
{ { - if . BpExtraLibs } }
libs : [
{ { - range . BpExtraLibs } }
2018-08-30 01:17:55 +02:00
"{{.}}" ,
{ { - end } }
2018-04-16 22:52:10 +02:00
] ,
2021-09-13 19:12:28 +02:00
{ { - end } }
2021-08-31 22:00:52 +02:00
{ { - if . BpOptionalUsesLibs } }
optional_uses_libs : [
{ { - range . BpOptionalUsesLibs } }
"{{.}}" ,
{ { - end } }
] ,
2019-04-16 12:35:20 +02:00
{ { - end } }
2018-04-16 22:52:10 +02:00
java_version : "1.7" ,
}
` ) )
2021-09-20 16:14:22 +02:00
var bazelTemplate = template . Must ( template . New ( "bp" ) . Parse ( `
{ { . BazelImportTargetType } } (
name = "{{.BpName}}" ,
{ { . BazelImportProperty } } : { { - if not . IsAar } } [ { { - end } } "{{.ArtifactFile}}" { { - if not . IsAar } } ] { { - end } } ,
visibility = [ "//visibility:public" ] ,
{ { - if . IsAar } }
deps = [
{ { - range . BazelJarDeps } }
"{{.}}" ,
{ { - end } }
{ { - range . BazelAarDeps } }
"{{.}}" ,
{ { - end } }
{ { - range . BpExtraStaticLibs } }
"{{.}}" ,
{ { - end } }
{ { - range . BpExtraLibs } }
"{{.}}" ,
{ { - end } }
{ { - range . BpOptionalUsesLibs } }
"{{.}}" ,
{ { - end } }
] ,
{ { - end } }
)
` ) )
var bazelDepsTemplate = template . Must ( template . New ( "bp" ) . Parse ( `
{ { . BazelImportTargetType } } (
name = "{{.BpName}}" ,
{ { . BazelImportProperty } } = { { - if not . IsAar } } [ { { - end } } "{{.ArtifactFile}}" { { - if not . IsAar } } ] { { - end } } ,
visibility = [ "//visibility:public" ] ,
exports = [
{ { - range . BazelJarDeps } }
"{{.}}" ,
{ { - end } }
{ { - range . BazelAarDeps } }
"{{.}}" ,
{ { - end } }
{ { - range . BpExtraStaticLibs } }
"{{.}}" ,
{ { - end } }
{ { - range . BpExtraLibs } }
"{{.}}" ,
{ { - end } }
{ { - range . BpOptionalUsesLibs } }
"{{.}}" ,
{ { - end } }
] ,
)
` ) )
2018-04-16 22:52:10 +02:00
func parse ( filename string ) ( * Pom , error ) {
data , err := ioutil . ReadFile ( filename )
if err != nil {
return nil , err
}
var pom Pom
err = xml . Unmarshal ( data , & pom )
if err != nil {
return nil , err
}
if useVersion != "" && pom . Version != useVersion {
return nil , nil
}
if pom . Packaging == "" {
pom . Packaging = "jar"
}
pom . PomFile = filename
pom . ArtifactFile = strings . TrimSuffix ( filename , ".pom" ) + "." + pom . Packaging
return & pom , nil
}
func rerunForRegen ( filename string ) error {
buf , err := ioutil . ReadFile ( filename )
if err != nil {
return err
}
scanner := bufio . NewScanner ( bytes . NewBuffer ( buf ) )
// Skip the first line in the file
for i := 0 ; i < 2 ; i ++ {
if ! scanner . Scan ( ) {
if scanner . Err ( ) != nil {
return scanner . Err ( )
} else {
return fmt . Errorf ( "unexpected EOF" )
}
}
}
// Extract the old args from the file
line := scanner . Text ( )
2021-09-20 16:14:22 +02:00
if strings . HasPrefix ( line , "// pom2bp " ) { // .bp file
2018-04-16 22:52:10 +02:00
line = strings . TrimPrefix ( line , "// pom2bp " )
2021-09-20 16:14:22 +02:00
} else if strings . HasPrefix ( line , "// pom2mk " ) { // .bp file converted from .mk file
2018-04-16 22:52:10 +02:00
line = strings . TrimPrefix ( line , "// pom2mk " )
2021-09-20 16:14:22 +02:00
} else if strings . HasPrefix ( line , "# pom2mk " ) { // .mk file
2018-04-16 22:52:10 +02:00
line = strings . TrimPrefix ( line , "# pom2mk " )
2021-09-20 16:14:22 +02:00
} else if strings . HasPrefix ( line , "# pom2bp " ) { // Bazel BUILD file
line = strings . TrimPrefix ( line , "# pom2bp " )
2018-04-16 22:52:10 +02:00
} else {
return fmt . Errorf ( "unexpected second line: %q" , line )
}
args := strings . Split ( line , " " )
lastArg := args [ len ( args ) - 1 ]
args = args [ : len ( args ) - 1 ]
// Append all current command line args except -regen <file> to the ones from the file
for i := 1 ; i < len ( os . Args ) ; i ++ {
2018-06-27 00:27:20 +02:00
if os . Args [ i ] == "-regen" || os . Args [ i ] == "--regen" {
2018-04-16 22:52:10 +02:00
i ++
} else {
args = append ( args , os . Args [ i ] )
}
}
args = append ( args , lastArg )
cmd := os . Args [ 0 ] + " " + strings . Join ( args , " " )
// Re-exec pom2bp with the new arguments
output , err := exec . Command ( "/bin/sh" , "-c" , cmd ) . Output ( )
if exitErr , _ := err . ( * exec . ExitError ) ; exitErr != nil {
return fmt . Errorf ( "failed to run %s\n%s" , cmd , string ( exitErr . Stderr ) )
} else if err != nil {
return err
}
// If the old file was a .mk file, replace it with a .bp file
if filepath . Ext ( filename ) == ".mk" {
os . Remove ( filename )
filename = strings . TrimSuffix ( filename , ".mk" ) + ".bp"
}
return ioutil . WriteFile ( filename , output , 0666 )
}
func main ( ) {
flag . Usage = func ( ) {
fmt . Fprintf ( os . Stderr , ` pom2bp , a tool to create Android . bp files from maven repos
The tool will extract the necessary information from * . pom files to create an Android . bp whose
aar libraries can be linked against when using AAPT2 .
2021-08-31 22:00:52 +02:00
Usage : % 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 > ]
2018-04-16 22:52:10 +02:00
- rewrite < regex >= < replace >
rewrite can be used to specify mappings between Maven projects and Android . bp modules . The - rewrite
option can be specified multiple times . When determining the Android . bp module for a given Maven
project , mappings are searched in the order they were specified . The first < regex > matching
either the Maven project ' s < groupId > : < artifactId > or < artifactId > will be used to generate
the Android . bp module name using < replace > . If no matches are found , < artifactId > is used .
- exclude < module >
Don ' t put the specified module in the Android . bp file .
2019-04-16 12:35:20 +02:00
- extra - static - libs < module >= < module > [ , < module > ]
Some Android . bp modules have transitive static dependencies that must be specified when they
are depended upon ( like android - support - v7 - mediarouter requires android - support - v7 - appcompat ) .
This may be specified multiple times to declare these dependencies .
- extra - libs < module >= < module > [ , < module > ]
Some Android . bp modules have transitive runtime dependencies that must be specified when they
are depended upon ( like androidx . test . rules requires android . test . base ) .
2018-04-16 22:52:10 +02:00
This may be specified multiple times to declare these dependencies .
2021-08-31 22:00:52 +02:00
- optional - uses - libs < module >= < module > [ , < module > ]
Some Android . bp modules have optional dependencies ( typically specified with < uses - library > in
the module ' s AndroidManifest . xml ) that must be specified when they are depended upon ( like
androidx . window : window optionally requires androidx . window : window - extensions ) .
This may be specified multiple times to declare these dependencies .
2018-04-16 22:52:10 +02:00
- sdk - version < version >
2019-04-21 06:47:14 +02:00
Sets sdk_version : "<version>" for all modules .
2021-03-29 17:10:06 +02:00
- default - min - sdk - version
The default min_sdk_version to use for a module if one cannot be mined from AndroidManifest . xml
2018-04-16 22:52:10 +02:00
- use - version < version >
If the maven directory contains multiple versions of artifacts and their pom files ,
- use - version can be used to only write Android . bp files for a specific version of those artifacts .
2022-09-19 18:26:00 +02:00
- write - cmd
Whether to write the command line arguments used to generate the build file as a comment at
the top of the build file itself .
2019-04-21 06:47:14 +02:00
- jetifier
Sets jetifier : true for all modules .
2018-04-16 22:52:10 +02:00
< dir >
The directory to search for * . pom files under .
The contents are written to stdout , to be put in the current directory ( often as Android . bp )
- regen < file >
Read arguments from < file > and overwrite it ( if it ends with . bp ) or move it to . bp ( if it
ends with . mk ) .
` , os . Args [ 0 ] )
}
var regen string
2021-09-20 16:14:22 +02:00
var pom2build bool
2022-08-11 16:59:12 +02:00
var prepend string
2018-04-16 22:52:10 +02:00
flag . Var ( & excludes , "exclude" , "Exclude module" )
2019-04-16 12:35:20 +02:00
flag . Var ( & extraStaticLibs , "extra-static-libs" , "Extra static dependencies needed when depending on a module" )
flag . Var ( & extraLibs , "extra-libs" , "Extra runtime dependencies needed when depending on a module" )
2021-08-31 22:00:52 +02:00
flag . Var ( & optionalUsesLibs , "optional-uses-libs" , "Extra optional dependencies needed when depending on a module" )
2018-04-16 22:52:10 +02:00
flag . Var ( & rewriteNames , "rewrite" , "Regex(es) to rewrite artifact names" )
2018-08-24 20:30:13 +02:00
flag . Var ( & hostModuleNames , "host" , "Specifies that the corresponding module (specified in the form 'module.group:module.artifact') is a host module" )
2019-07-18 22:36:44 +02:00
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." )
2019-04-21 06:47:14 +02:00
flag . StringVar ( & sdkVersion , "sdk-version" , "" , "What to write to sdk_version" )
2021-03-29 17:10:06 +02:00
flag . StringVar ( & defaultMinSdkVersion , "default-min-sdk-version" , "24" , "Default min_sdk_version to use, if one is not available from AndroidManifest.xml. Default: 24" )
2018-04-16 22:52:10 +02:00
flag . StringVar ( & useVersion , "use-version" , "" , "Only read artifacts of a specific version" )
2019-04-22 06:37:39 +02:00
flag . BoolVar ( & staticDeps , "static-deps" , false , "Statically include direct dependencies" )
2022-09-19 18:26:00 +02:00
flag . BoolVar ( & writeCmd , "write-cmd" , true , "Write command line arguments as a comment" )
2019-04-21 06:47:14 +02:00
flag . BoolVar ( & jetifier , "jetifier" , false , "Sets jetifier: true on all modules" )
2018-04-16 22:52:10 +02:00
flag . StringVar ( & regen , "regen" , "" , "Rewrite specified file" )
2021-09-20 16:14:22 +02:00
flag . BoolVar ( & pom2build , "pom2build" , false , "If true, will generate a Bazel BUILD file *instead* of a .bp file" )
2022-08-11 16:59:12 +02:00
flag . StringVar ( & prepend , "prepend" , "" , "Path to a file containing text to insert at the beginning of the generated build file" )
2018-04-16 22:52:10 +02:00
flag . Parse ( )
if regen != "" {
err := rerunForRegen ( regen )
if err != nil {
fmt . Fprintln ( os . Stderr , err )
os . Exit ( 1 )
}
os . Exit ( 0 )
}
if flag . NArg ( ) == 0 {
fmt . Fprintln ( os . Stderr , "Directory argument is required" )
os . Exit ( 1 )
} else if flag . NArg ( ) > 1 {
fmt . Fprintln ( os . Stderr , "Multiple directories provided:" , strings . Join ( flag . Args ( ) , " " ) )
os . Exit ( 1 )
}
dir := flag . Arg ( 0 )
absDir , err := filepath . Abs ( dir )
if err != nil {
fmt . Fprintln ( os . Stderr , "Failed to get absolute directory:" , err )
os . Exit ( 1 )
}
var filenames [ ] string
err = filepath . Walk ( absDir , func ( path string , info os . FileInfo , err error ) error {
if err != nil {
return err
}
name := info . Name ( )
if info . IsDir ( ) {
if strings . HasPrefix ( name , "." ) {
return filepath . SkipDir
}
return nil
}
if strings . HasPrefix ( name , "." ) {
return nil
}
if strings . HasSuffix ( name , ".pom" ) {
path , err = filepath . Rel ( absDir , path )
if err != nil {
return err
}
filenames = append ( filenames , filepath . Join ( dir , path ) )
}
return nil
} )
if err != nil {
fmt . Fprintln ( os . Stderr , "Error walking files:" , err )
os . Exit ( 1 )
}
if len ( filenames ) == 0 {
fmt . Fprintln ( os . Stderr , "Error: no *.pom files found under" , dir )
os . Exit ( 1 )
}
sort . Strings ( filenames )
poms := [ ] * Pom { }
modules := make ( map [ string ] * Pom )
duplicate := false
for _ , filename := range filenames {
pom , err := parse ( filename )
if err != nil {
fmt . Fprintln ( os . Stderr , "Error converting" , filename , err )
os . Exit ( 1 )
}
if pom != nil {
key := pom . BpName ( )
if excludes [ key ] {
continue
}
if old , ok := modules [ key ] ; ok {
fmt . Fprintln ( os . Stderr , "Module" , key , "defined twice:" , old . PomFile , pom . PomFile )
duplicate = true
}
poms = append ( poms , pom )
modules [ key ] = pom
}
}
if duplicate {
os . Exit ( 1 )
}
2021-09-20 16:14:22 +02:00
if pom2build {
if err := InitRefreshMod ( poms ) ; err != nil {
fmt . Fprintf ( os . Stderr , "Error in refreshmod: %s" , err )
os . Exit ( 1 )
}
BazelifyExtraDeps ( extraStaticLibs , modules )
BazelifyExtraDeps ( extraLibs , modules )
BazelifyExtraDeps ( optionalUsesLibs , modules )
}
2018-04-16 22:52:10 +02:00
for _ , pom := range poms {
2018-06-27 00:27:20 +02:00
if pom . IsAar ( ) {
err := pom . ExtractMinSdkVersion ( )
if err != nil {
2018-07-14 06:25:15 +02:00
fmt . Fprintf ( os . Stderr , "Error reading manifest for %s: %s" , pom . ArtifactFile , err )
2018-06-27 00:27:20 +02:00
os . Exit ( 1 )
}
}
2018-04-16 22:52:10 +02:00
pom . FixDeps ( modules )
2021-09-20 16:14:22 +02:00
if pom2build {
pom . GetBazelDepNames ( modules )
}
2018-04-16 22:52:10 +02:00
}
buf := & bytes . Buffer { }
2021-09-20 16:14:22 +02:00
commentString := "//"
if pom2build {
commentString = "#"
}
2022-09-19 18:26:00 +02:00
fmt . Fprintln ( buf , commentString , "This is a generated file. Do not modify directly." )
if writeCmd {
fmt . Fprintln ( buf , commentString , "Automatically generated with:" )
fmt . Fprintln ( buf , commentString , "pom2bp" , strings . Join ( proptools . ShellEscapeList ( os . Args [ 1 : ] ) , " " ) )
}
2018-04-16 22:52:10 +02:00
2022-08-11 16:59:12 +02:00
if prepend != "" {
contents , err := ioutil . ReadFile ( prepend )
if err != nil {
fmt . Fprintln ( os . Stderr , "Error reading" , prepend , err )
os . Exit ( 1 )
}
fmt . Fprintln ( buf , string ( contents ) )
}
2021-09-20 16:14:22 +02:00
depsTemplate := bpDepsTemplate
template := bpTemplate
if pom2build {
depsTemplate = bazelDepsTemplate
template = bazelTemplate
}
2018-04-16 22:52:10 +02:00
for _ , pom := range poms {
var err error
2022-10-12 21:12:02 +02:00
if staticDeps && ! pom . IsApk ( ) {
2021-09-20 16:14:22 +02:00
err = depsTemplate . Execute ( buf , pom )
2019-04-22 06:37:39 +02:00
} else {
2021-09-20 16:14:22 +02:00
err = template . Execute ( buf , pom )
2019-04-22 06:37:39 +02:00
}
2018-04-16 22:52:10 +02:00
if err != nil {
fmt . Fprintln ( os . Stderr , "Error writing" , pom . PomFile , pom . BpName ( ) , err )
os . Exit ( 1 )
}
}
2021-09-20 16:14:22 +02:00
if pom2build {
os . Stdout . WriteString ( buf . String ( ) )
} else {
out , err := bpfix . Reformat ( buf . String ( ) )
if err != nil {
fmt . Fprintln ( os . Stderr , "Error formatting output" , err )
os . Exit ( 1 )
}
os . Stdout . WriteString ( out )
2018-04-16 22:52:10 +02:00
}
}