1 /* 2 * Copyright 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tools.build.jetifier.standalone 18 19 import com.android.tools.build.jetifier.processor.archive.Archive 20 import com.android.tools.build.jetifier.processor.archive.ArchiveFile 21 import com.android.tools.build.jetifier.processor.archive.ArchiveItemVisitor 22 import com.android.tools.build.jetifier.processor.transform.pom.PomDocument 23 import java.io.File 24 import java.nio.file.Paths 25 import java.security.MessageDigest 26 27 /** 28 * Utility to rebuild de-jetified zipped maven repo. 29 */ 30 class TopOfTreeBuilder { 31 32 companion object { 33 const val DIR_PREFIX = "m2repository" 34 } 35 36 fun rebuildFrom(inputZip: File, outputZip: File) { 37 val archive = Archive.Builder.extract(inputZip, recursive = false) 38 39 // Find poms 40 val pomFilter = FileFilter({ it.isPomFile() }) 41 archive.accept(pomFilter) 42 val pomFiles = pomFilter.files 43 44 // Find archives 45 val archivesFilter = FileFilter({ 46 return@FileFilter it.fileName.endsWith(".aar") 47 || (it.fileName.endsWith("jar") 48 && !it.fileName.contains("sources") && !it.fileName.contains("javadoc")) 49 }) 50 archive.accept(archivesFilter) 51 val libFiles = archivesFilter.files 52 53 // Process 54 val newFiles = mutableSetOf<ArchiveFile>() 55 pomFiles.forEach { 56 pomFile -> run { 57 val name = pomFile.relativePath.toFile().nameWithoutExtension 58 val nameAar = name + ".aar" 59 val nameJar = name + ".jar" 60 val artifactFile = libFiles.first { 61 it.fileName == nameAar || it.fileName == nameJar 62 } 63 64 process(pomFile, artifactFile, newFiles) 65 } 66 } 67 68 // Write the result 69 val finalArchive = Archive(outputZip.toPath(), newFiles.toList()) 70 finalArchive.writeSelf() 71 } 72 73 private fun process( 74 pomFile: ArchiveFile, 75 artifactFile: ArchiveFile, 76 resultSet: MutableSet<ArchiveFile> 77 ) { 78 val pomDep = PomDocument.loadFrom(pomFile).getAsPomDependency() 79 80 val groupAsPath = pomDep.groupId!!.replace('.', '/') 81 82 val packaging = artifactFile.relativePath.toFile().extension 83 val baseFileName = "${pomDep.artifactId}-${pomDep.version}" 84 85 val artifactDir = Paths.get(DIR_PREFIX, groupAsPath, pomDep.artifactId, pomDep.version!!) 86 val newLibFilePath = Paths.get(artifactDir.toString(), "$baseFileName.$packaging") 87 val newPomFilePath = Paths.get(artifactDir.toString(), "$baseFileName.pom") 88 89 val newArtifactFile = ArchiveFile(newLibFilePath, artifactFile.data) 90 val newPomFile = ArchiveFile(newPomFilePath, pomFile.data) 91 92 resultSet.add(newArtifactFile) 93 resultSet.add(getHashFileOf(newArtifactFile, "MD5")) 94 resultSet.add(getHashFileOf(newArtifactFile, "SHA1")) 95 96 resultSet.add(newPomFile) 97 resultSet.add(getHashFileOf(newPomFile, "MD5")) 98 resultSet.add(getHashFileOf(newPomFile, "SHA1")) 99 } 100 101 private fun getHashFileOf(file: ArchiveFile, hashType: String): ArchiveFile { 102 val md = MessageDigest.getInstance(hashType) 103 val result = md.digest(file.data) 104 return ArchiveFile(Paths.get( 105 file.relativePath.toString() + "." + hashType.toLowerCase()), result) 106 } 107 108 private class FileFilter(private val filter: (ArchiveFile) -> Boolean) : ArchiveItemVisitor { 109 110 val files = mutableSetOf<ArchiveFile>() 111 112 override fun visit(archiveFile: ArchiveFile) { 113 if (filter(archiveFile)) { 114 files.add(archiveFile) 115 } 116 } 117 118 override fun visit(archive: Archive) { 119 archive.files.forEach { it.accept(this) } 120 } 121 } 122 }