Skip to content

Instantly share code, notes, and snippets.

@Zetten
Last active November 3, 2020 23:54
Show Gist options
  • Save Zetten/8cf873195f1680ba7278dfd22257301d to your computer and use it in GitHub Desktop.
Save Zetten/8cf873195f1680ba7278dfd22257301d to your computer and use it in GitHub Desktop.
Bazel workspace generation script
plugins {
id "com.github.ben-manes.versions" version "0.17.0"
id "io.spring.dependency-management" version "1.0.5.RELEASE"
id "com.github.jk1.dependency-license-report" version '1.0'
}
apply plugin: 'base'
group = 'com.example'
version = '0.0.0'
description = """
Dependency management and generation of java dependency configuration for Bazel project.
"""
repositories {
maven {
url "https://jcenter.bintray.com"
}
maven {
url "https://repo.spring.io/libs-milestone"
}
maven {
url "http://download.osgeo.org/webdav/geotools"
}
}
configurations {
generate
}
configurations.generate {
resolutionStrategy {
dependencySubstitution {
// Work around missing jar for jai_core in Maven Central
substitute module('javax.media:jai_core:1.1.3') with module('javax.media:jai-core:1.1.3')
}
}
}
ext['awaitility.version'] = '3.1.0'
ext['commons-net.version'] = '3.6'
ext['cucumber-jvm.version'] = '3.0.2'
ext['geotools.version'] = '19.1'
ext['grpc-spring-boot-starter.version'] = '2.3.2'
ext['guava.version'] = '25.1-jre'
ext['fabric8-kubernetes.version'] = '3.1.12'
ext['jfairy.version'] = '0.5.9'
ext['jackson.version'] = '2.9.5'
ext['jimfs.version'] = '1.1'
ext['jool.version'] = '0.9.13'
ext['lombok.version'] = '1.18.0'
ext['okhttp3.version'] = '3.10.0'
ext['MockFtpServer.version'] = '2.7.1'
ext['postgresql.version'] = '42.2.2'
ext['protobuf-java.version'] = '3.5.1'
ext['spring-boot.version'] = '2.0.2.RELEASE'
ext['spring-cloud.version'] = 'Finchley.RC2'
ext['truth.version'] = '0.40'
dependencyManagement {
imports {
mavenBom 'org.springframework.cloud:spring-cloud-dependencies:' + ext['spring-cloud.version']
mavenBom 'org.springframework.boot:spring-boot-dependencies:' + ext['spring-boot.version']
}
dependencies {
dependency group: 'com.google.guava', name: 'guava', version: ext['guava.version']
dependency group: 'io.fabric8', name: 'kubernetes-client', version: ext['fabric8-kubernetes.version']
dependency group: 'com.google.jimfs', name: 'jimfs', version: ext['jimfs.version']
dependency group: 'com.google.protobuf', name: 'protobuf-java', version: ext['protobuf-java.version']
dependency group: 'com.google.truth', name: 'truth', version: ext['truth.version']
dependency group: 'com.google.truth.extensions', name: 'truth-java8-extension', version: ext['truth.version']
dependencySet(group: 'com.squareup.okhttp3', version: ext['okhttp3.version']) {
entry 'logging-interceptor'
entry 'mockwebserver'
entry 'okhttp'
}
dependency group: 'commons-net', name: 'commons-net', version: ext['commons-net.version']
dependency group: 'io.codearte.jfairy', name: 'jfairy', version: ext['jfairy.version']
dependencySet(group: 'io.cucumber', version: ext['cucumber-jvm.version']) {
entry 'cucumber-java8'
entry 'cucumber-junit'
entry 'cucumber-picocontainer'
}
dependency group: 'org.awaitility', name: 'awaitility', version: ext['awaitility.version']
dependencySet(group: 'org.geotools', version: ext['geotools.version']) {
entry 'gt-geojson'
entry 'gt-opengis'
}
dependencySet(group: 'org.jooq', version: ext['jool.version']) {
entry 'jool'
entry 'jool-java-8'
}
dependency group: 'org.lognet', name: 'grpc-spring-boot-starter', version: ext['grpc-spring-boot-starter.version']
dependency group: 'org.mockftpserver', name: 'MockFtpServer', version: ext['MockFtpServer.version']
dependencySet(group: 'org.springframework.boot', version: ext['spring-boot.version']) {
entry('spring-boot-starter') {
exclude group: 'org.springframework.boot', name: 'spring-boot-starter-logging'
}
entry 'spring-boot-loader-tools'
}
}
}
dependencies {
generate 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml'
generate 'com.fasterxml.jackson.datatype:jackson-datatype-guava'
generate 'com.fasterxml.jackson.datatype:jackson-datatype-hibernate5'
generate 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8'
generate 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
generate 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml'
generate 'com.google.guava:guava'
generate 'com.google.jimfs:jimfs'
generate 'com.google.protobuf:protobuf-java'
generate 'com.google.truth:truth'
generate 'com.google.truth.extensions:truth-java8-extension'
generate 'com.h2database:h2'
generate 'com.querydsl:querydsl-apt'
generate 'com.querydsl:querydsl-jpa'
generate 'com.squareup.okhttp3:logging-interceptor'
generate 'com.squareup.okhttp3:mockwebserver'
generate 'com.squareup.okhttp3:okhttp'
generate 'commons-net:commons-net'
generate 'io.codearte.jfairy:jfairy'
generate 'io.cucumber:cucumber-java8'
generate 'io.cucumber:cucumber-junit'
generate 'io.cucumber:cucumber-picocontainer'
generate 'io.fabric8:kubernetes-client'
generate 'javax.servlet:javax.servlet-api'
generate 'org.awaitility:awaitility'
generate 'org.geotools:gt-geojson'
generate 'org.geotools:gt-opengis'
generate 'org.jooq:jool-java-8'
generate 'org.lognet:grpc-spring-boot-starter'
generate 'org.mockftpserver:MockFtpServer'
generate 'org.postgresql:postgresql'
generate 'org.projectlombok:lombok'
generate 'org.springframework.batch:spring-batch-test'
generate 'org.springframework.boot:spring-boot-configuration-processor'
generate 'org.springframework.boot:spring-boot-loader'
generate 'org.springframework.boot:spring-boot-loader-tools'
generate 'org.springframework.boot:spring-boot-properties-migrator'
generate 'org.springframework.boot:spring-boot-starter'
generate 'org.springframework.boot:spring-boot-starter-actuator'
generate 'org.springframework.boot:spring-boot-starter-batch'
generate 'org.springframework.boot:spring-boot-starter-data-jpa'
generate 'org.springframework.boot:spring-boot-starter-data-rest'
generate 'org.springframework.boot:spring-boot-starter-hateoas'
generate 'org.springframework.boot:spring-boot-starter-log4j2'
generate 'org.springframework.boot:spring-boot-starter-security'
generate 'org.springframework.boot:spring-boot-starter-test'
generate 'org.springframework.boot:spring-boot-starter-web'
generate 'org.springframework.boot:spring-boot-starter-websocket'
generate 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
generate 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server'
generate 'org.springframework.cloud:spring-cloud-starter-gateway'
generate 'org.springframework.data:spring-data-rest-hal-browser'
}
Set<ProjectDependency> projectDependencies = new HashSet<>()
task generateWorkspace {
description 'Generates java_import_external Bazel targets for dependencies'
doLast {
com.github.jk1.license.ConfigurationData configurationData = new com.github.jk1.license.reader.ConfigurationReader().read(project, configurations.generate)
// Collect all dependency info
configurations.generate.resolvedConfiguration.firstLevelModuleDependencies.each {
parseDependencyTree(it, projectDependencies, configurationData)
}
generateJavaRepositoriesFile(projectDependencies.stream()
.flatMap { collectDependencies(it) }
.collect(java.util.stream.Collectors.toCollection { new TreeSet() }))
}
}
ProjectDependency parseDependencyTree(ResolvedDependency dep, Set<ProjectDependency> projectDependencies, com.github.jk1.license.ConfigurationData configurationData) {
ProjectDependency existing = projectDependencies.find { it.id == dep.module.id }
if (existing) return existing
ProjectDependency thisDep = new ProjectDependency()
thisDep.id = dep.module.id
thisDep.dependencies = dep.children.collect {
parseDependencyTree(it, projectDependencies, configurationData)
}.toSet()
thisDep.jarSha256 = sha256(dep.moduleArtifacts.first().file)
thisDep.licenseTypes = getLicenseTypes(configurationData.dependencies.find { moduleData ->
(moduleData.group == dep.moduleGroup && moduleData.name == dep.moduleName && moduleData.version == dep.moduleVersion)
})
thisDep.urls = findArtifactUrls(dep.module.id.toString())
Set<ResolvedArtifactResult> sourcesArtifacts = dependencies.createArtifactResolutionQuery()
.forModule(dep.moduleGroup, dep.moduleName, dep.moduleVersion)
.withArtifacts(JvmLibrary, SourcesArtifact)
.execute()
.resolvedComponents
.collectMany { it.getArtifacts(SourcesArtifact) }
.toSet()
if (sourcesArtifacts.size() == 1) {
thisDep.srcjarSha256 = sha256(sourcesArtifacts.first().file)
thisDep.srcjarUrls = findArtifactUrls(dep.module.id.toString() + ":sources")
} else if (sourcesArtifacts.size() > 1) {
println("Artifact had multiple sources artifacts! ${it.moduleVersion.id}")
}
projectDependencies.add(thisDep)
return thisDep
}
private static List<String> getLicenseTypes(com.github.jk1.license.ModuleData moduleData) {
return moduleData.poms.collectMany { pom -> pom.licenses }
.collect { guessLicenseType([name: it.name, url: it.url]) }
}
private static String sha256(file) {
java.security.MessageDigest md = java.security.MessageDigest.getInstance("SHA-256");
file.eachByte 4096, { bytes, size -> md.update(bytes, 0, size) }
return md.digest().collect { String.format "%02x", it }.join()
}
private List<String> findArtifactUrls(String artifactId) {
return repositories.findResults {
def url = getArtifactUrl(artifactId, it.url) + ".jar"
int code = new URL(url).openConnection().with {
requestMethod = 'HEAD'
connect()
responseCode
}
return (code >= 100 && code < 400) ? url : null
}
}
private static String getArtifactUrl(artifactId, repoUrl) {
def group, artifact, version, classifier, file_version
String[] parts = artifactId.split(':')
if (parts.length == 4) {
(group, artifact, version, classifier) = parts
file_version = version + '-' + classifier
} else {
(group, artifact, version) = parts
file_version = version
}
return [repoUrl,
group.replace('.', '/'),
artifact,
version,
artifact + '-' + file_version].join('/')
}
// Ref: https://bazel.build/versions/master/docs/be/functions.html#licenses_args
// Implementation from https://github.com/jart/bazel/blob/ef0328910fd61a7e77f4c56443d87365eeac7174/tools/build_defs/repo/maven_config_generator/index.html
private static String guessLicenseType(license) {
if (license['url'].indexOf('gnu.org/licenses/agpl') != -1 ||
license['url'].indexOf('wtfpl.net') != -1 ||
license['name'].indexOf('WTFPL') != -1 ||
license['name'].indexOf('AGPL') != -1 ||
license['name'].indexOf('Affero') != -1 ||
license['name'].indexOf('affero') != -1 ||
license['name'].indexOf('Fuck') != -1 ||
license['name'].indexOf('fuck') != -1) {
return 'by_exception_only';
}
if (license['url'].indexOf('apache.org/licenses/LICENSE-2.0') != -1 ||
license['url'].indexOf('opensource.org/licenses/mit') != -1 ||
license['url'].indexOf('opensource.org/licenses/bsd') != -1 ||
license['url'].indexOf('opensource.org/licenses/BSD') != -1 ||
license['url'].indexOf('opensource.org/licenses/ISC') != -1 ||
license['url'].indexOf('opensource.org/licenses/zpl') != -1 ||
license['url'].indexOf('opensource.org/licenses/zlib') != -1 ||
license['url'].indexOf('unicode.org/copyright') != -1 ||
license['url'].indexOf('json.org/license') != -1 ||
license['url'].indexOf('gwtproject.org/terms') != -1) {
return 'notice';
}
if (license['url'].indexOf('www.gnu.org') != -1 ||
license['url'].indexOf('www.oracle.com/technetwork/java/javase/terms/license/index.html') != -1 ||
license['url'].indexOf('opensource.org/licenses/sleepycat.php') != -1 ||
license['url'].indexOf('opensource.org/licenses/osl') != -1 ||
license['url'].indexOf('opensource.org/licenses/qtpl') != -1 ||
license['url'].indexOf('opensource.org/licenses/sleepycat') != -1 ||
license['url'].indexOf('cr.yp.to/qmail') != -1 ||
license['url'].indexOf('MPL/NPL/') != -1) {
return 'restricted';
}
if (license['url'].indexOf('opensource.org/licenses/cpl') != -1 ||
license['url'].indexOf('opensource.org/licenses/eclipse') != -1 ||
license['url'].indexOf('opensource.org/licenses/apsl') != -1 ||
license['url'].indexOf('opensource.org/licenses/ibmpl') != -1 ||
license['url'].indexOf('eclipse.org/org/documents/epl') != -1 ||
license['url'].indexOf('eclipse.org/legal/epl') != -1) {
return 'reciprocal';
}
if (license['url'].indexOf('creativecommons.org/licenses/publicdomain') != -1 ||
license['url'].indexOf('unlicense.org') != -1) {
return 'unencumbered';
}
if (license['name'] == 'gpl' ||
license['name'] == 'lgpl' ||
license['name'].indexOf('GPL') != -1 ||
license['name'].indexOf('BCL') != -1 ||
license['name'].indexOf('Oracle Binary Code License') != -1 ||
license['name'].indexOf('Attribution-ShareAlike') != -1 ||
license['name'].indexOf('CC BY-SA') != -1 ||
license['name'].indexOf('Attribution-NoDerivs') != -1 ||
license['name'].indexOf('CC BY-ND') != -1 ||
license['name'].indexOf('Sleepycat') != -1) {
return 'restricted';
}
if (license['name'].indexOf('MPL') != -1 ||
license['name'].indexOf('Mozilla Public License') != -1 ||
license['name'].indexOf('Common Public License') != -1 ||
license['name'].indexOf('CDDL') != -1 ||
license['name'].indexOf('Common Development and Distribution License') != -1 ||
license['name'].indexOf('EPL') != -1 ||
license['name'].indexOf('Eclipse Public License') != -1 ||
license['name'].indexOf('APSL') != -1 ||
license['name'].indexOf('Apple Public Source License') != -1) {
return 'reciprocal';
}
if (license['name'].indexOf('BSD') != -1 ||
license['name'].indexOf('MIT') != -1 ||
license['name'].indexOf('X11') != -1 ||
license['name'].indexOf('Apache') != -1 ||
license['name'].indexOf('Artistic') != -1 ||
license['name'].indexOf('ISC') != -1 ||
license['name'].indexOf('ICU') != -1 ||
license['name'].indexOf('JSON License') != -1) {
return 'notice';
}
if (license['name'].indexOf('Beerware') != -1 ||
license['name'].indexOf('beerware') != -1 ||
license['name'].indexOf('Google App Engine Terms of Service') != -1) {
return 'permissive';
}
if (license['name'] == 'Public Domain' && license['url'] == '') {
return 'unencumbered'; // e.g. aopalliance
}
return 'TODO';
}
java.util.stream.Stream<ProjectDependency> collectDependencies(ProjectDependency projectDependency) {
return java.util.stream.Stream.concat(java.util.stream.Stream.of(projectDependency),
projectDependency.dependencies.stream().flatMap { collectDependencies(it) })
}
void generateJavaRepositoriesFile(SortedSet<ProjectDependency> projectDependencies) {
def javaRepositories = file("java_repositories.bzl")
List<String> javaRepositoriesContent = new ArrayList<>()
javaRepositoriesContent.add('load("@bazel_tools//tools/build_defs/repo:java.bzl", "java_import_external")')
javaRepositoriesContent.add("")
javaRepositoriesContent.add("")
javaRepositoriesContent.add('def java_repositories():')
projectDependencies.each { dep ->
javaRepositoriesContent.add(" java_import_external(")
javaRepositoriesContent.add(" name = \"${dep.bazelIdentifier}\",")
javaRepositoriesContent.add(" licenses = [\"${getMostRestrictiveLicense(dep)}\"],")
javaRepositoriesContent.add(" jar_urls = [")
dep.urls.each {
javaRepositoriesContent.add(" \"${it}\",")
}
javaRepositoriesContent.add(" ],")
javaRepositoriesContent.add(" jar_sha256 = \"${dep.jarSha256}\",")
if (dep.srcjarSha256) {
javaRepositoriesContent.add(" srcjar_urls = [")
dep.srcjarUrls.each {
javaRepositoriesContent.add(" \"${it}\",")
}
javaRepositoriesContent.add(" ],")
javaRepositoriesContent.add(" srcjar_sha256 = \"${dep.srcjarSha256}\",")
}
if (!dep.dependencies.isEmpty()) {
javaRepositoriesContent.add(" runtime_deps = [")
dep.dependencies.toSorted().each {
javaRepositoriesContent.add(" \"@${it.bazelIdentifier}\",")
}
javaRepositoriesContent.add(" ],")
}
javaRepositoriesContent.add(" )")
javaRepositoriesContent.add("")
}
javaRepositories.withWriter{ out ->
javaRepositoriesContent.each { out.println it }
}
}
String getMostRestrictiveLicense(ProjectDependency dep) {
for (LicenseType type : LicenseType.values()) {
for (String license : dep.licenseTypes) {
if (type.toString().equals(license.toUpperCase())) {
return license;
}
}
}
println "WARNING: No Bazel-registered license detected for ${dep.id}"
return "none"
}
enum LicenseType {
BY_EXCEPTION_ONLY,
RESTRICTED,
RESTRICTED_IF_STATICALLY_LINKED,
RECIPROCAL,
NOTICE,
PERMISSIVE,
UNENCUMBERED,
NONE
}
class ProjectDependency implements Comparable<ProjectDependency> {
ModuleVersionIdentifier id
Set<ProjectDependency> dependencies
String jarSha256
List<String> licenseTypes
List<String> urls
String srcjarSha256
List<String> srcjarUrls
@Override
int compareTo(ProjectDependency o) {
return getBazelIdentifier() <=> o.getBazelIdentifier()
}
String getBazelIdentifier() {
id.group.replaceAll("[^\\w]", "_") + "_" + id.name.replaceAll("[^\\w]", "_")
}
@Override
String toString() {
return id.toString()
}
boolean equals(o) {
if (this.is(o)) return true
if (getClass() != o.class) return false
ProjectDependency that = (ProjectDependency) o
if (dependencies != that.dependencies) return false
if (id != that.id) return false
if (jarSha256 != that.jarSha256) return false
if (licenseTypes != that.licenseTypes) return false
if (srcjarSha256 != that.srcjarSha256) return false
if (srcjarUrls != that.srcjarUrls) return false
if (urls != that.urls) return false
return true
}
int hashCode() {
int result
result = (id != null ? id.hashCode() : 0)
result = 31 * result + (dependencies != null ? dependencies.hashCode() : 0)
result = 31 * result + (jarSha256 != null ? jarSha256.hashCode() : 0)
result = 31 * result + (licenseTypes != null ? licenseTypes.hashCode() : 0)
result = 31 * result + (urls != null ? urls.hashCode() : 0)
result = 31 * result + (srcjarSha256 != null ? srcjarSha256.hashCode() : 0)
result = 31 * result + (srcjarUrls != null ? srcjarUrls.hashCode() : 0)
return result
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment