diff --git a/eng/bomgenerator/buildAndRun.cmd b/eng/bomgenerator/buildAndRun.cmd new file mode 100644 index 0000000000000..fd26bd8a1b556 --- /dev/null +++ b/eng/bomgenerator/buildAndRun.cmd @@ -0,0 +1,4 @@ +set "versioningClientFileLocation=%~dp0..\versioning\version_client.txt" +set "bomPomFileLocation=%~dp0..\..\sdk\boms\azure-sdk-bom\pom.xml" +set "outputFileLocation=%~dp0..\..\sdk\boms\azure-sdk-bom\newpom.xml" +mvn clean install && mvn exec:java -Dexec.args="-inputFile=%versioningClientFileLocation% -outputFile=%outputFileLocation% -pomFile=%bomPomFileLocation%" \ No newline at end of file diff --git a/eng/bomgenerator/generatedpom.xml b/eng/bomgenerator/generatedpom.xml new file mode 100644 index 0000000000000..100a97c076f87 --- /dev/null +++ b/eng/bomgenerator/generatedpom.xml @@ -0,0 +1,272 @@ + + + 4.0.0 + com.azure + azure-sdk-bom + 1.0.4-beta.1 + pom + Azure Java SDK BOM (Bill of Materials) + Azure Java SDK BOM (Bill of Materials) + https://github.com/azure/azure-sdk-for-java + + + The MIT License (MIT) + http://opensource.org/licenses/MIT + repo + + + + + microsoft + Microsoft Corporation + + + + scm:git:git://github.com/azure/azure-sdk-for-java + scm:git:git://github.com/azure/azure-sdk-for-java + https://github.com/azure/azure-sdk-for-java + + + GitHub + https://github.com/azure/azure-sdk-for-java/issues + + + UTF-8 + ${project.build.directory} + + + + + com.azure + azure-ai-formrecognizer + 3.1.2 + + + com.azure + azure-ai-metricsadvisor + 1.0.0 + + + com.azure + azure-ai-textanalytics + 5.1.0 + + + com.azure + azure-core + 1.18.0 + + + com.azure + azure-core-amqp + 2.3.0 + + + com.azure + azure-core-http-netty + 1.10.1 + + + com.azure + azure-core-http-okhttp + 1.7.1 + + + com.azure + azure-core-management + 1.3.1 + + + com.azure + azure-core-serializer-json-gson + 1.1.4 + + + com.azure + azure-core-serializer-json-jackson + 1.2.5 + + + com.azure + azure-data-appconfiguration + 1.2.0 + + + com.azure + azure-data-tables + 12.1.0 + + + com.azure + azure-identity + 1.3.3 + + + com.azure + azure-messaging-eventhubs + 5.9.0 + + + com.azure + azure-messaging-servicebus + 7.3.0 + + + com.azure + azure-security-keyvault-administration + 4.0.1 + + + com.azure + azure-security-keyvault-certificates + 4.2.1 + + + com.azure + azure-security-keyvault-jca + 1.0.1 + + + com.azure + azure-security-keyvault-keys + 4.3.1 + + + com.azure + azure-security-keyvault-secrets + 4.3.1 + + + com.fasterxml.jackson + jackson-bom + 2.12.3 + pom + import + + + com.google.code.gson + gson + 2.8.6 + + + com.microsoft.azure + msal4j + 1.10.1 + + + com.microsoft.azure + msal4j-persistence-extension + 1.1.0 + + + com.microsoft.azure + qpid-proton-j-extensions + 1.2.4 + + + com.squareup.okhttp3 + okhttp + 4.8.1 + + + io.netty + netty-bom + 4.1.65.Final + pom + import + + + io.netty + netty-tcnative-boringssl-static + 2.0.39.Final + + + io.projectreactor + reactor-bom + 2020.0.7 + pom + import + + + net.java.dev.jna + jna-platform + 5.6.0 + + + net.minidev + json-smart + 2.4.7 + + + org.apache.qpid + proton-j + 0.33.8 + + + org.linguafranca.pwdb + KeePassJava2 + 2.1.4 + + + org.slf4j + slf4j-api + 1.7.30 + + + + + + + maven-antrun-plugin + 1.8 + + + copy + package + + run + + + + + + + + + + + + + + dependency-checker + + + dependency-checker + + + + + + net.jonathangiles.tools + dependencyChecker-maven-plugin + 1.0.6 + + + package + + check + + + html + true + true + + + + + + + + + diff --git a/eng/bomgenerator/pom.xml b/eng/bomgenerator/pom.xml new file mode 100644 index 0000000000000..c91ee5b68ad28 --- /dev/null +++ b/eng/bomgenerator/pom.xml @@ -0,0 +1,63 @@ + + + 4.0.0 + + com.azure.tools + bomGenerator + 1.0.0-beta.1 + + + 11 + 11 + + + + + org.jboss.shrinkwrap.resolver + shrinkwrap-resolver-bom + 3.1.4 + pom + import + + + + + + + org.jboss.shrinkwrap.resolver + shrinkwrap-resolver-impl-maven + + + org.apache.maven + maven-core + 3.5.4 + + + org.slf4j + slf4j-log4j12 + 1.7.5 + + + + + + org.codehaus.mojo + exec-maven-plugin + 1.6.0 + + + + java + + + + + com.azure.tools.bomgenerator.Main + false + + + + + diff --git a/eng/bomgenerator/readme.md b/eng/bomgenerator/readme.md new file mode 100644 index 0000000000000..2194e93f4e027 --- /dev/null +++ b/eng/bomgenerator/readme.md @@ -0,0 +1,4 @@ +This tool generate the azure-sdk-bom. + +To run the tool provide the following arguments +-inputFile= -outputFile= -pomFile= \ No newline at end of file diff --git a/eng/bomgenerator/report.log b/eng/bomgenerator/report.log new file mode 100644 index 0000000000000..8ba34008fb9fa --- /dev/null +++ b/eng/bomgenerator/report.log @@ -0,0 +1,60 @@ +Dropped dependency com.azure:azure-storage-blob-cryptography:12.12.0. + Includes dependency com.azure:azure-core-http-netty:1.10.0. Expected dependency com.azure:azure-core-http-netty:1.10.1 + Includes dependency com.azure:azure-core:1.17.0. Expected dependency com.azure:azure-core:1.18.0 +Dropped dependency com.azure:azure-search-documents:11.4.0. + Includes dependency com.azure:azure-core-serializer-json-jackson:1.2.4. Expected dependency com.azure:azure-core-serializer-json-jackson:1.2.5 + Includes dependency com.azure:azure-core-http-netty:1.10.0. Expected dependency com.azure:azure-core-http-netty:1.10.1 + Includes dependency com.azure:azure-core:1.17.0. Expected dependency com.azure:azure-core:1.18.0 +Dropped dependency com.azure:azure-storage-file-datalake:12.6.0. + Includes dependency com.azure:azure-core-http-netty:1.10.0. Expected dependency com.azure:azure-core-http-netty:1.10.1 + Includes dependency com.azure:azure-core:1.17.0. Expected dependency com.azure:azure-core:1.18.0 +Dropped dependency com.azure:azure-communication-chat:1.0.0. + Includes dependency com.azure:azure-core:1.14.1. Expected dependency com.azure:azure-core:1.18.0 + Includes dependency com.azure:azure-communication-common:1.0.0. Expected dependency com.azure:azure-communication-common:1.0.3 +Dropped dependency com.azure:azure-storage-internal-avro:12.0.5. + Includes dependency com.azure:azure-core-http-netty:1.10.0. Expected dependency com.azure:azure-core-http-netty:1.10.1 + Includes dependency com.azure:azure-core:1.17.0. Expected dependency com.azure:azure-core:1.18.0 +Dropped dependency com.azure:azure-storage-common:12.12.0. + Includes dependency com.azure:azure-core-http-netty:1.10.0. Expected dependency com.azure:azure-core-http-netty:1.10.1 + Includes dependency com.azure:azure-core:1.17.0. Expected dependency com.azure:azure-core:1.18.0 +Dropped dependency com.azure:azure-digitaltwins-core:1.1.1. + Includes dependency com.azure:azure-core-http-netty:1.9.2. Expected dependency com.azure:azure-core-http-netty:1.10.1 + Includes dependency com.azure:azure-core-serializer-json-jackson:1.2.3. Expected dependency com.azure:azure-core-serializer-json-jackson:1.2.5 + Includes dependency com.fasterxml.jackson.core:jackson-annotations:2.12.2. Expected dependency com.fasterxml.jackson.core:jackson-annotations:2.12.3 + Includes dependency com.azure:azure-core:1.16.0. Expected dependency com.azure:azure-core:1.18.0 +Dropped dependency com.azure:azure-storage-queue:12.10.0. + Includes dependency com.azure:azure-core-http-netty:1.10.0. Expected dependency com.azure:azure-core-http-netty:1.10.1 + Includes dependency com.azure:azure-core:1.17.0. Expected dependency com.azure:azure-core:1.18.0 +Dropped dependency com.azure:azure-communication-sms:1.0.3. + Includes dependency com.azure:azure-core:1.17.0. Expected dependency com.azure:azure-core:1.18.0 + Includes dependency com.azure:azure-communication-common:1.0.2. Expected dependency com.azure:azure-communication-common:1.0.3 +Dropped dependency com.azure:azure-communication-common:1.0.3. + Includes dependency com.azure:azure-core-http-netty:1.10.0. Expected dependency com.azure:azure-core-http-netty:1.10.1 + Includes dependency com.azure:azure-core:1.17.0. Expected dependency com.azure:azure-core:1.18.0 +Dropped dependency com.azure:azure-storage-blob:12.12.0. + Includes dependency com.azure:azure-core-http-netty:1.10.0. Expected dependency com.azure:azure-core-http-netty:1.10.1 + Includes dependency com.azure:azure-core:1.17.0. Expected dependency com.azure:azure-core:1.18.0 +Dropped dependency com.azure:azure-storage-blob-batch:12.10.0. + Includes dependency com.azure:azure-core-http-netty:1.10.0. Expected dependency com.azure:azure-core-http-netty:1.10.1 + Includes dependency com.azure:azure-core:1.17.0. Expected dependency com.azure:azure-core:1.18.0 +Dropped dependency com.azure:azure-storage-file-share:12.10.0. + Includes dependency com.azure:azure-core-http-netty:1.10.0. Expected dependency com.azure:azure-core-http-netty:1.10.1 + Includes dependency com.azure:azure-core:1.17.0. Expected dependency com.azure:azure-core:1.18.0 +Dropped dependency com.azure:azure-communication-phonenumbers:1.0.3. + Includes dependency com.azure:azure-core:1.17.0. Expected dependency com.azure:azure-core:1.18.0 + Includes dependency com.azure:azure-communication-common:1.0.2. Expected dependency com.azure:azure-communication-common:1.0.3 +Dropped dependency com.azure:azure-mixedreality-remoterendering:1.0.0. + Includes dependency com.azure:azure-core-http-netty:1.9.0. Expected dependency com.azure:azure-core-http-netty:1.10.1 + Includes dependency com.azure:azure-core:1.14.1. Expected dependency com.azure:azure-core:1.18.0 + Includes dependency com.azure:azure-core:1.13.0. Expected dependency com.azure:azure-core:1.18.0 +Dropped dependency com.azure:azure-messaging-eventhubs-checkpointstore-blob:1.8.1. + Includes dependency com.azure:azure-core-http-netty:1.10.0. Expected dependency com.azure:azure-core-http-netty:1.10.1 + Includes dependency com.azure:azure-core:1.17.0. Expected dependency com.azure:azure-core:1.18.0 +Dropped dependency com.azure:azure-mixedreality-authentication:1.0.0. + Includes dependency com.azure:azure-core-http-netty:1.9.0. Expected dependency com.azure:azure-core-http-netty:1.10.1 + Includes dependency com.azure:azure-core:1.14.1. Expected dependency com.azure:azure-core:1.18.0 +Dropped dependency com.azure:azure-cosmos:4.17.0. + Includes dependency org.slf4j:slf4j-api:1.7.31. Expected dependency org.slf4j:slf4j-api:1.7.30 +Dropped dependency com.azure:azure-communication-identity:1.1.1. + Includes dependency com.azure:azure-core:1.17.0. Expected dependency com.azure:azure-core:1.18.0 + Includes dependency com.azure:azure-communication-common:1.0.2. Expected dependency com.azure:azure-communication-common:1.0.3 diff --git a/eng/bomgenerator/samplereport.txt b/eng/bomgenerator/samplereport.txt new file mode 100644 index 0000000000000..c675e336972c3 --- /dev/null +++ b/eng/bomgenerator/samplereport.txt @@ -0,0 +1,21 @@ +[main] INFO com.azure.tools.bomgenerator.BomGenerator - We dropped the following dependencies from the input list. +[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-communication-chat:1.0.0, Reason com.azure:azure-core:1.14.1 +[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-communication-common:1.0.3, Reason com.azure:azure-core-http-netty:1.10.0 +[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-communication-identity:1.1.1, Reason com.azure:azure-core:1.17.0 +[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-communication-phonenumbers:1.0.3, Reason com.azure:azure-core:1.17.0 +[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-communication-sms:1.0.3, Reason com.azure:azure-core:1.17.0 +[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-cosmos:4.17.0, Reason org.slf4j:slf4j-api:1.7.31 +[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-digitaltwins-core:1.1.1, Reason com.fasterxml.jackson.core:jackson-annotations:2.12.2 +[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-messaging-eventgrid:4.4.0, Reason com.azure:azure-core-http-netty:1.10.0 +[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-messaging-eventhubs-checkpointstore-blob:1.8.1, Reason com.azure:azure-core-http-netty:1.10.0 +[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-mixedreality-authentication:1.0.0, Reason com.azure:azure-core-http-netty:1.9.0 +[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-mixedreality-remoterendering:1.0.0, Reason com.azure:azure-core-http-netty:1.9.0 +[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-search-documents:11.4.0, Reason com.azure:azure-core-serializer-json-jackson:1.2.4 +[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-storage-blob-batch:12.10.0, Reason com.azure:azure-core-http-netty:1.10.0 +[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-storage-blob-cryptography:12.12.0, Reason com.azure:azure-core-http-netty:1.10.0 +[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-storage-blob:12.12.0, Reason com.azure:azure-core-http-netty:1.10.0 +[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-storage-common:12.12.0, Reason com.azure:azure-core-http-netty:1.10.0 +[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-storage-file-datalake:12.6.0, Reason com.azure:azure-core-http-netty:1.10.0 +[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-storage-file-share:12.10.0, Reason com.azure:azure-core-http-netty:1.10.0 +[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-storage-internal-avro:12.0.5, Reason com.azure:azure-core-http-netty:1.10.0 +[main] INFO com.azure.tools.bomgenerator.BomGenerator - Dependency com.azure:azure-storage-queue:12.10.0, Reason com.azure:azure-core-http-netty:1.10.0 \ No newline at end of file diff --git a/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/BomGenerator.java b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/BomGenerator.java new file mode 100644 index 0000000000000..3f76d24ab8f78 --- /dev/null +++ b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/BomGenerator.java @@ -0,0 +1,183 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.tools.bomgenerator; + +import com.azure.tools.bomgenerator.models.BomDependency; +import org.apache.maven.model.Dependency; +import org.apache.maven.model.DependencyManagement; +import org.apache.maven.model.Model; +import org.apache.maven.model.io.xpp3.MavenXpp3Reader; +import org.apache.maven.model.io.xpp3.MavenXpp3Writer; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.regex.Matcher; +import java.util.stream.Collectors; + +import static com.azure.tools.bomgenerator.Utils.BASE_AZURE_GROUPID; +import static com.azure.tools.bomgenerator.Utils.AZURE_PERF_LIBRARY_IDENTIFIER; +import static com.azure.tools.bomgenerator.Utils.AZURE_TEST_LIBRARY_IDENTIFIER; +import static com.azure.tools.bomgenerator.Utils.EXCLUSION_LIST; +import static com.azure.tools.bomgenerator.Utils.POM_TYPE; +import static com.azure.tools.bomgenerator.Utils.SDK_DEPENDENCY_PATTERN; + +public class BomGenerator { + private String outputFileName; + private String inputFileName; + private String pomFileName; + + private static Logger logger = LoggerFactory.getLogger(BomGenerator.class); + + BomGenerator(String inputFileName, String outputFileName, String pomFileName) { + this.inputFileName = inputFileName; + this.outputFileName = outputFileName; + this.pomFileName = pomFileName; + } + + public void generate() { + List inputDependencies = scan(); + List externalDependencies = resolveExternalDependencies(); + + // 1. Create the initial tree and reduce conflicts. + // 2. And pick only those dependencies. that were in the input set, since they become the new roots of n-ary tree. + DependencyAnalyzer analyzer = new DependencyAnalyzer(inputDependencies, externalDependencies); + analyzer.reduce(); + Collection outputDependencies = analyzer.getBomEligibleDependencies(); + + // 2. Create the new tree for the BOM. + analyzer = new DependencyAnalyzer(outputDependencies, externalDependencies); + boolean validationFailed = analyzer.validate(); + outputDependencies = analyzer.getBomEligibleDependencies(); + + // 4. Create the new BOM file. + if(!validationFailed) { + // Rewrite the existing BOM to have the dependencies in the order in which we insert them, making the diff PR easier to review. + rewriteExistingBomFile(); + writeBom(outputDependencies); + } + else { + logger.trace("Validation for the BOM failed. Exiting..."); + } + } + + private List scan() { + List inputDependencies = new ArrayList<>(); + + try { + for (String line : Files.readAllLines(Paths.get(inputFileName))) { + BomDependency dependency = scanDependency(line); + + if(dependency != null) { + inputDependencies.add(dependency); + } + } + } catch (IOException exception) { + logger.error("Input file parsing failed. Exception{}", exception.toString()); + } + + return inputDependencies; + } + + private BomDependency scanDependency(String line) { + Matcher matcher = SDK_DEPENDENCY_PATTERN.matcher(line); + if (!matcher.matches()) { + return null; + } + + if (matcher.groupCount() != 3) { + return null; + } + + String artifactId = matcher.group(1); + String version = matcher.group(2); + + if(version.contains("-")) { + // This is a non-GA library + return null; + } + + if (EXCLUSION_LIST.contains(artifactId) + || artifactId.contains(AZURE_PERF_LIBRARY_IDENTIFIER) + || (artifactId.contains(AZURE_TEST_LIBRARY_IDENTIFIER))) { + logger.trace("Skipping dependency {}:{}", BASE_AZURE_GROUPID, artifactId); + return null; + } + + return new BomDependency(BASE_AZURE_GROUPID, artifactId, version); + } + + private Model readModel() { + MavenXpp3Reader reader = new MavenXpp3Reader(); + try { + Model model = reader.read(new FileReader(this.pomFileName)); + return model; + } catch (XmlPullParserException | IOException e) { + logger.error("BOM reading failed with: {}", e.toString()); + } + + return null; + } + + private void writeModel(Model model) { + String pomFileName = this.pomFileName; + writeModel(pomFileName, model); + } + + private void writeModel(String fileName, Model model) { + MavenXpp3Writer writer = new MavenXpp3Writer(); + try { + writer.write(new FileWriter(fileName), model); + } catch (IOException exception) { + logger.error("BOM writing failed with: {}", exception.toString()); + } + } + + private List resolveExternalDependencies() { + List externalDependencies = new ArrayList<>(); + List externalBomDependencies = getExternalDependencies(); + externalDependencies.addAll(Utils.getExternalDependenciesContent(externalBomDependencies)); + return externalDependencies; + } + + private List getExternalDependencies() { + Model model = readModel(); + DependencyManagement management = model.getDependencyManagement(); + return management.getDependencies().stream().filter(dependency -> dependency.getType().equals(POM_TYPE)).collect(Collectors.toList()); + } + + private void rewriteExistingBomFile() { + Model model = readModel(); + DependencyManagement management = model.getDependencyManagement(); + List dependencies = management.getDependencies(); + dependencies.sort(new DependencyComparator()); + management.setDependencies(dependencies); + writeModel(model); + } + + private void writeBom(Collection bomDependencies) { + Model model = readModel(); + DependencyManagement management = model.getDependencyManagement(); + List externalBomDependencies = management.getDependencies().stream().filter(dependency -> dependency.getType().equals(POM_TYPE)).collect(Collectors.toList()); + List dependencies = bomDependencies.stream().map(bomDependency -> { + Dependency dependency = new Dependency(); + dependency.setGroupId(bomDependency.getGroupId()); + dependency.setArtifactId(bomDependency.getArtifactId()); + dependency.setVersion(bomDependency.getVersion()); + return dependency; + }).collect(Collectors.toList()); + dependencies.addAll(externalBomDependencies); + dependencies.sort(new DependencyComparator()); + management.setDependencies(dependencies); + writeModel(this.outputFileName, model); + } +} diff --git a/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/DependencyAnalyzer.java b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/DependencyAnalyzer.java new file mode 100644 index 0000000000000..795017e94cf45 --- /dev/null +++ b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/DependencyAnalyzer.java @@ -0,0 +1,274 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.tools.bomgenerator; + +import com.azure.tools.bomgenerator.models.BomDependency; +import com.azure.tools.bomgenerator.models.BomDependencyErrorInfo; +import com.azure.tools.bomgenerator.models.BomDependencyNoVersion; +import org.jboss.shrinkwrap.resolver.api.maven.Maven; +import org.jboss.shrinkwrap.resolver.api.maven.MavenFormatStage; +import org.jboss.shrinkwrap.resolver.api.maven.MavenResolvedArtifact; +import org.jboss.shrinkwrap.resolver.api.maven.MavenResolverSystemBase; +import org.jboss.shrinkwrap.resolver.api.maven.MavenStrategyStage; +import org.jboss.shrinkwrap.resolver.api.maven.PomEquippedResolveStage; +import org.jboss.shrinkwrap.resolver.api.maven.PomlessResolveStage; +import org.jboss.shrinkwrap.resolver.api.maven.ScopeType; +import org.jboss.shrinkwrap.resolver.api.maven.coordinate.MavenDependency; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.stream.Collectors; + +import static com.azure.tools.bomgenerator.Utils.RESOLVED_EXCLUSION_LIST; +import static com.azure.tools.bomgenerator.Utils.toBomDependencyNoVersion; + +public class DependencyAnalyzer { + private Set inputDependencies = new HashSet<>(); + private Set externalDependencies = new HashSet<>(); + private Set bomEligibleDependencies = new HashSet<>(); + private Set bomIneligibleDependencies = new HashSet<>(); + private Map coreDependencyNameToDependency = new HashMap<>(); + private Map errorInfo = new HashMap(); + + private Map>> nameToVersionToChildrenDependencyTree = new TreeMap<>(new Comparator() { + @Override + public int compare(BomDependencyNoVersion o1, BomDependencyNoVersion o2) { + return (o1.getGroupId() + o1.getArtifactId()).compareTo(o1.getGroupId() + o2.getArtifactId()); + } + }); + private static Logger logger = LoggerFactory.getLogger(BomGenerator.class); + + DependencyAnalyzer(Collection inputDependencies, Collection externalDependencies) { + if (inputDependencies != null) { + this.inputDependencies.addAll(inputDependencies); + } + if (externalDependencies != null) { + this.externalDependencies.addAll(externalDependencies); + } + } + + public Collection getBomEligibleDependencies() { + return this.bomEligibleDependencies; + } + + public void reduce() { + analyze(); + generateReport(); + this.bomEligibleDependencies.retainAll(this.inputDependencies); + } + + public boolean validate() { + analyze(); + return nameToVersionToChildrenDependencyTree.values().stream().anyMatch(value -> value.size() > 1); + } + + private void analyze() { + pickCoreDependencyRoots(); + resolveTree(); + resolveConflicts(); + filterConflicts(); + } + + private void generateReport() { + // From the input assemblies find the ones that have been dropped, along with why? + Set droppedDependencies = inputDependencies.stream().filter(dependency -> bomIneligibleDependencies.contains(dependency)).collect(Collectors.toSet()); + if (droppedDependencies.size() == 0) { + return; + } + + if (errorInfo.size() > 0) { + errorInfo.keySet().stream().forEach(key -> { + if (droppedDependencies.contains(key)) { + var conflictingDependencies = errorInfo.get(key).getConflictingDependencies(); + var expectedDependency = errorInfo.get(key).getExpectedDependency(); + if (expectedDependency != null) { + logger.info("Dropped dependency {}.", key.toString(), expectedDependency); + } + + conflictingDependencies.stream().forEach(conflictingDependency -> + logger.info("\t\tIncludes dependency {}. Expected dependency {}", conflictingDependency.getActualDependency(), conflictingDependency.getExpectedDependency())); + } + }); + } + } + + private BomDependency getAzureCoreDependencyFromInput() { + return inputDependencies.stream().filter(dependency -> dependency.getArtifactId().equals("azure-core")).findFirst().get(); + } + + private void pickCoreDependencyRoots() { + BomDependency coreDependency = getAzureCoreDependencyFromInput(); + + // Get all the dependencies of the azure-core dependency and put them in the coreDependencyMap. + var coreDependencies = getDependencies(coreDependency); + coreDependencyNameToDependency.put(toBomDependencyNoVersion(coreDependency), coreDependency); + coreDependencies.forEach(dependency -> coreDependencyNameToDependency.put(toBomDependencyNoVersion(dependency), dependency)); + + // Put all the core dependencies which are not external dependencies in the bomEligible list. + for(var dependency : coreDependencyNameToDependency.values()) { + if(!externalDependencies.contains(dependency)) { + // If this is not an external dependency, it needs to be in the BOM. + bomEligibleDependencies.add(dependency); + } + } + } + + + /* Create a tree map of all the input binaries into the following map. + * {groupId_artifactId}: {v1} : {all ancestors that include this binary.} + * : {v2} : {all ancestors that include this binary.} + * : {v3} : {all ancestors that include this binary.} + */ + private void resolveTree() { + for (MavenDependency gaLibrary : inputDependencies) { + try { + + BomDependency parentDependency = new BomDependency(gaLibrary.getGroupId(), gaLibrary.getArtifactId(), gaLibrary.getVersion()); + addDependencyToDependencyTree(parentDependency, null, nameToVersionToChildrenDependencyTree); + + List dependencies = getDependencies(gaLibrary); + for (BomDependency dependency : dependencies) { + if (dependency.getScope() == ScopeType.TEST) { + continue; + } + if (RESOLVED_EXCLUSION_LIST.contains(dependency.getArtifactId())) { + continue; + } + + BomDependency childDependency = new BomDependency(dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion()); + addDependencyToDependencyTree(childDependency, parentDependency, nameToVersionToChildrenDependencyTree); + } + } catch (Exception ex) { + System.out.println(ex); + } + } + } + + private static List getDependencies(MavenDependency dependency) { + try { + + MavenResolvedArtifact mavenResolvedArtifact = null; + + mavenResolvedArtifact = getMavenResolver() + .addDependency(dependency) + .resolve() + .withoutTransitivity() + .asSingleResolvedArtifact(); + + return Arrays.stream(mavenResolvedArtifact.getDependencies()).map(mavenDependency -> + new BomDependency(mavenDependency.getCoordinate().getGroupId(), + mavenDependency.getCoordinate().getArtifactId(), + mavenDependency.getCoordinate().getVersion(), + mavenDependency.getScope())).collect(Collectors.toList()); + } catch (Exception ex) { + logger.error(ex.toString()); + } + + return new ArrayList<>(); + } + + private static MavenResolverSystemBase getMavenResolver() { + return Maven.configureResolver().withMavenCentralRepo(true); + } + + private static void addDependencyToDependencyTree(BomDependency dependency, BomDependency parentDependency, Map>> dependencyTree) { + dependencyTree.computeIfAbsent(new BomDependencyNoVersion(dependency.getGroupId(), dependency.getArtifactId()), key -> new HashMap<>()); + + var value = dependencyTree.get(dependency).computeIfAbsent(dependency.getVersion(), key -> new ArrayList<>()); + if(parentDependency != null) { + value.add(parentDependency); + } + } + + private void updateErrorInfo(BomDependency droppedDependency, String expectedVersion) { + if (!errorInfo.containsKey(droppedDependency)) { + errorInfo.put(droppedDependency, new BomDependencyErrorInfo(new BomDependency(droppedDependency.getGroupId(), droppedDependency.getArtifactId(), expectedVersion))); + } + } + + private void updateErrorInfo(BomDependency droppedDependency, BomDependency actualDependency, String expectedVersion) { + updateErrorInfo(droppedDependency, expectedVersion); + errorInfo.get(droppedDependency).addConflictingDependency(actualDependency, new BomDependency(actualDependency.getGroupId(), actualDependency.getArtifactId(), expectedVersion)); + } + + + private void makeDependencyInEligible(BomDependency dependency, BomDependency dependencyReason, String expectedVersion) { + if (nameToVersionToChildrenDependencyTree.containsKey(dependency)) { + HashMap> versionToDependency = nameToVersionToChildrenDependencyTree.get(dependency); + bomIneligibleDependencies.add(dependency); + if (dependencyReason == null) { + dependencyReason = dependency; + updateErrorInfo(dependency, expectedVersion); + } else { + updateErrorInfo(dependency, dependencyReason, expectedVersion); + } + + // Make all the dependencies that include these also ineligible. + BomDependency finalDependencyReason = dependencyReason; + versionToDependency.get(dependency.getVersion()).forEach(parent -> makeDependencyInEligible(parent, finalDependencyReason, expectedVersion)); + } + } + + private void resolveConflict(BomDependencyNoVersion dependencyNoVersion) { + Map> versionToDependency = nameToVersionToChildrenDependencyTree.get(dependencyNoVersion); + if (versionToDependency.size() > 1) { + List versionList = versionToDependency.keySet().stream().sorted(new DependencyVersionComparator()).collect(Collectors.toList()); + String eligibleVersion; + + logger.trace("Multiple version of the dependency {} included", dependencyNoVersion); + + // 1. We give preference to the dependency version that is included by azure-core. + if (coreDependencyNameToDependency.containsKey(dependencyNoVersion)) { + eligibleVersion = coreDependencyNameToDependency.get(dependencyNoVersion).getVersion(); + logger.trace(String.format("\tPicking the version used by azure-core - %s:%s", dependencyNoVersion, eligibleVersion)); + } else { + eligibleVersion = versionList.get(versionList.size() - 1); + logger.trace(String.format("\tPicking the latest version %s:%s", dependencyNoVersion, eligibleVersion)); + } + + BomDependency dependency = new BomDependency(dependencyNoVersion.getGroupId(), dependencyNoVersion.getArtifactId(), eligibleVersion); + if (!externalDependencies.contains(dependency)) { + bomEligibleDependencies.add(dependency); + } + + // All the other versions of this library are made ineligible. + for (String version : versionList) { + if (!version.equals(eligibleVersion)) { + makeDependencyInEligible(new BomDependency(dependency.getGroupId(), dependency.getArtifactId(), version), null, eligibleVersion); + } + } + } + } + + private void resolveConflicts() { + nameToVersionToChildrenDependencyTree.keySet().stream().forEach(this::resolveConflict); + bomEligibleDependencies.removeAll(bomIneligibleDependencies); + } + + private void filterConflicts() { + nameToVersionToChildrenDependencyTree.keySet().stream().forEach( + key -> { + HashMap> versionToDependency = nameToVersionToChildrenDependencyTree.get(key); + + if (versionToDependency.size() == 1) { + BomDependency dependency = new BomDependency(key.getGroupId(), key.getArtifactId(), versionToDependency.keySet().stream().findFirst().get()); + if (!bomIneligibleDependencies.contains(dependency) + && !externalDependencies.contains(dependency)) { + // No conflict, the library can be added to the list. + bomEligibleDependencies.add(dependency); + } + } + }); + } +} diff --git a/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/DependencyComparator.java b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/DependencyComparator.java new file mode 100644 index 0000000000000..71f2c0208c26c --- /dev/null +++ b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/DependencyComparator.java @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.tools.bomgenerator; + +import org.apache.maven.model.Dependency; + +import java.util.Comparator; + +public class DependencyComparator implements Comparator { + @Override + public int compare(Object o1, Object o2) { + Dependency dependency1 = (Dependency) o1; + Dependency dependency2 = (Dependency) o2; + + return dependency1.toString().compareTo(dependency2.toString()); + } +} diff --git a/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/DependencyVersionComparator.java b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/DependencyVersionComparator.java new file mode 100644 index 0000000000000..8c5300ba515af --- /dev/null +++ b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/DependencyVersionComparator.java @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.tools.bomgenerator; + +import org.apache.maven.artifact.versioning.ComparableVersion; + +import java.util.Comparator; + +public class DependencyVersionComparator implements Comparator { + + @Override + public int compare(String o1, String o2) { + ComparableVersion version1 = new ComparableVersion(o1); + ComparableVersion version2 = new ComparableVersion(o2); + + return version1.compareTo(version2); + } +} diff --git a/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/Main.java b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/Main.java new file mode 100644 index 0000000000000..68589c34cca7f --- /dev/null +++ b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/Main.java @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.tools.bomgenerator; + +import java.util.regex.Matcher; + +import static com.azure.tools.bomgenerator.Utils.BASE_AZURE_GROUPID; +import static com.azure.tools.bomgenerator.Utils.COMMANDLINE_EXTERNALDEPENDENCIES; +import static com.azure.tools.bomgenerator.Utils.COMMANDLINE_GROUPID; +import static com.azure.tools.bomgenerator.Utils.COMMANDLINE_INPUTFILE; +import static com.azure.tools.bomgenerator.Utils.COMMANDLINE_OUTPUTFILE; +import static com.azure.tools.bomgenerator.Utils.COMMANDLINE_POMFILE; + +public class Main { + + public static void main(String[] args) { + BomGenerator generator = parseCommandLine(args); + generator.generate(); + } + + private static BomGenerator parseCommandLine(String[] args) { + String inputFile = null, outputFile = null, pomFile = null; + for (String arg : args) { + Matcher matcher = Utils.COMMANDLINE_REGEX.matcher(arg); + if (matcher.matches()) { + if (matcher.groupCount() == 2) { + String argName = matcher.group(1); + String argValue = matcher.group(2); + + switch (argName.toLowerCase()) { + case COMMANDLINE_INPUTFILE: + inputFile = argValue; + break; + + case COMMANDLINE_OUTPUTFILE: + outputFile = argValue; + break; + + case COMMANDLINE_POMFILE: + pomFile = argValue; + break; + } + } + } + } + + // validate that each of these are present. + validateInputs(inputFile, outputFile, pomFile); + return new BomGenerator(inputFile, outputFile, pomFile); + } + + private static void validateInputs(String inputFile, String outputFile, String pomFile) { + validateInput(inputFile, COMMANDLINE_INPUTFILE); + validateInput(outputFile, COMMANDLINE_OUTPUTFILE); + validateInput(pomFile, COMMANDLINE_POMFILE); + } + + private static void validateInput(String argName, String argValue) { + if(argValue == null || argValue.isEmpty()) { + throw new NullPointerException(String.format("%s can't be null", argName)); + } + } +} diff --git a/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/Utils.java b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/Utils.java new file mode 100644 index 0000000000000..0433ac49d470c --- /dev/null +++ b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/Utils.java @@ -0,0 +1,139 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.tools.bomgenerator; + +import com.azure.tools.bomgenerator.models.BomDependency; +import com.azure.tools.bomgenerator.models.BomDependencyNoVersion; +import org.apache.maven.model.Dependency; +import org.apache.maven.model.DependencyManagement; +import org.apache.maven.model.Model; +import org.apache.maven.model.io.xpp3.MavenXpp3Reader; +import org.apache.maven.model.io.xpp3.MavenXpp3Writer; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +public class Utils { + public static final String COMMANDLINE_INPUTFILE = "inputfile"; + public static final String COMMANDLINE_OUTPUTFILE = "outputfile"; + public static final String COMMANDLINE_POMFILE = "pomfile"; + public static final String COMMANDLINE_EXTERNALDEPENDENCIES = "externalDependencies"; + public static final String COMMANDLINE_GROUPID = "groupid"; + public static final Pattern COMMANDLINE_REGEX = Pattern.compile("-(.*)=(.*)"); + public static final List EXCLUSION_LIST = Arrays.asList("azure-spring-data-cosmos", "azure-spring-data-cosmos-test", "azure-core-test", "azure-sdk-all", "azure-sdk-parent", "azure-client-sdk-parent"); + public static final Pattern SDK_DEPENDENCY_PATTERN = Pattern.compile("com.azure:(.+);(.+);(.+)"); + public static final String BASE_AZURE_GROUPID = "com.azure"; + public static final String AZURE_TEST_LIBRARY_IDENTIFIER = "-test"; + public static final String AZURE_PERF_LIBRARY_IDENTIFIER = "-perf"; + public static final HttpClient HTTP_CLIENT = HttpClient.newHttpClient(); + public static final Pattern STRING_SPLIT_BY_DOT = Pattern.compile("[.]"); + + public static final HashSet RESOLVED_EXCLUSION_LIST = new HashSet<>(Arrays.asList( + "junit-jupiter-api" + )); + + public static final String POM_TYPE = "pom"; + private static Logger logger = LoggerFactory.getLogger(Utils.class); + + public static List getExternalDependenciesContent(List dependencies) { + List allResolvedDependencies = new ArrayList<>(); + + for (Dependency dependency : dependencies) { + List resolvedDependencies = getPomFileContent(dependency); + + if (resolvedDependencies != null) { + allResolvedDependencies.addAll(resolvedDependencies); + } + } + + return allResolvedDependencies; + } + + public static List getPomFileContent(Dependency dependency) { + String[] groups = STRING_SPLIT_BY_DOT.split(dependency.getGroupId()); + String url = null; + if(groups.length == 2) { + url = "https://repo1.maven.org/maven2" + "/" + groups[0] + "/" + groups[1] + "/" + dependency.getArtifactId() + "/" + dependency.getVersion() + "/" + dependency.getArtifactId() + "-" + dependency.getVersion() + ".pom"; + } + else if (groups.length == 3) { + url = "https://repo1.maven.org/maven2" + "/" + groups[0] + "/" + groups[1] + "/" + groups[2] + "/" + dependency.getArtifactId() + "/" + dependency.getVersion() + "/" + dependency.getArtifactId() + "-" + dependency.getVersion() + ".pom"; + } + else { + throw new UnsupportedOperationException("Can't parse the external BOM file."); + } + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .GET() + .header("accept", "application/xml") + .timeout(Duration.ofMillis(5000)) + .build(); + + return HTTP_CLIENT.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()) + .thenApply(response -> { + if(response.statusCode() == 200) { + return Utils.parsePomFileContent(response.body()); + } + + return null; + }).join(); + } + + public static BomDependencyNoVersion toBomDependencyNoVersion(BomDependency bomDependency) { + return new BomDependencyNoVersion(bomDependency.getGroupId(), bomDependency.getArtifactId()); + } + + private static List parsePomFileContent(InputStream responseStream) { + MavenXpp3Reader reader = new MavenXpp3Reader(); + try { + Model model = reader.read(responseStream); + DependencyManagement management = model.getDependencyManagement(); + + return management.getDependencies().stream().map(dep -> { + String version = getPropertyName(dep.getVersion()); + + while(model.getProperties().getProperty(version) != null) { + version = getPropertyName(model.getProperties().getProperty(version)); + } + + if(version == null) { + version = dep.getVersion(); + } + + BomDependency bomDependency = new BomDependency(dep.getGroupId(), dep.getArtifactId(), version); + return bomDependency; + }).collect(Collectors.toList()); + } catch (IOException exception) { + exception.printStackTrace(); + } catch (XmlPullParserException e) { + e.printStackTrace(); + } + + return null; + } + + private static String getPropertyName(String propertyValue) { + if(propertyValue.startsWith("${")) { + return propertyValue.substring(2, propertyValue.length() - 1); + } + + return propertyValue; + } +} diff --git a/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/models/BomDependency.java b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/models/BomDependency.java new file mode 100644 index 0000000000000..898377b3df084 --- /dev/null +++ b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/models/BomDependency.java @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.tools.bomgenerator.models; + +import org.jboss.shrinkwrap.resolver.api.maven.ScopeType; + +public class BomDependency extends BomDependencyNoVersion { + private String version; + private ScopeType scope; + + public BomDependency(String groupId, String artifactId, String version) { + super(groupId, artifactId); + this.version = version; + this.scope = null; + } + + public BomDependency(String groupId, String artifactId, String version, ScopeType scope) { + this(groupId, artifactId, version); + this.scope = scope; + } + + @Override + public String getVersion() { + return this.version; + } + + public ScopeType getScope() { + return this.scope; + } + + @Override + public String toString() { + return this.getGroupId() + ":" + this.getArtifactId() + ":" + this.getVersion(); + } + + @Override + public boolean equals(Object o) { + if(o == null) { + return false; + } + + if (o instanceof BomDependency) { + BomDependency bomSource = this; + BomDependency bomTarget = (BomDependency) o; + + return bomSource.toString().equals(bomTarget.toString()); + } + + return false; + } + + @Override + public int hashCode() { + return toString().hashCode(); + } +} diff --git a/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/models/BomDependencyErrorInfo.java b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/models/BomDependencyErrorInfo.java new file mode 100644 index 0000000000000..2aa7e33b5cbfc --- /dev/null +++ b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/models/BomDependencyErrorInfo.java @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.tools.bomgenerator.models; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +public class BomDependencyErrorInfo { + private BomDependency expectedDependency; + private Set conflictingDependencies; + + public BomDependencyErrorInfo(BomDependency expectedDependency) { + this.expectedDependency = expectedDependency; + conflictingDependencies = new HashSet<>(); + } + + public void addConflictingDependency(BomDependency actualDependency, BomDependency expectedDependency) { + conflictingDependencies.add(new ConflictingDependency(actualDependency, expectedDependency)); + } + + public Set getConflictingDependencies() { + return Collections.unmodifiableSet(this.conflictingDependencies); + } + + public BomDependency getExpectedDependency() { + return this.expectedDependency; + } +} + + diff --git a/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/models/BomDependencyNoVersion.java b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/models/BomDependencyNoVersion.java new file mode 100644 index 0000000000000..2da94b98efc9e --- /dev/null +++ b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/models/BomDependencyNoVersion.java @@ -0,0 +1,98 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.tools.bomgenerator.models; + +import org.jboss.shrinkwrap.resolver.api.maven.PackagingType; +import org.jboss.shrinkwrap.resolver.api.maven.ScopeType; +import org.jboss.shrinkwrap.resolver.api.maven.coordinate.MavenCoordinate; +import org.jboss.shrinkwrap.resolver.api.maven.coordinate.MavenDependency; +import org.jboss.shrinkwrap.resolver.api.maven.coordinate.MavenDependencyExclusion; + +import java.util.HashSet; +import java.util.Set; + +public class BomDependencyNoVersion implements MavenDependency { + private String groupId; + private String artifactId; + + public BomDependencyNoVersion(String groupId, String artifactId) { + this.groupId = groupId; + this.artifactId = artifactId; + } + + public BomDependencyNoVersion(MavenCoordinate coordinate) { + this.artifactId = coordinate.getArtifactId(); + this.groupId = coordinate.getGroupId(); + } + + @Override + public Set getExclusions() { + return new HashSet<>(); + } + + @Override + public ScopeType getScope() { + return ScopeType.RUNTIME; + } + + @Override + public boolean isOptional() { + return false; + } + + @Override + public PackagingType getPackaging() { + return PackagingType.JAR; + } + + @Override + public PackagingType getType() { + return PackagingType.JAR; + } + + @Override + public String getClassifier() { + return null; + } + + @Override + public String getVersion() { + return null; + } + + @Override + public String getGroupId() { + return groupId; + } + + @Override + public String getArtifactId() { + return artifactId; + } + + @Override + public String toCanonicalForm() { + return null; + } + + @Override + public String toString() { + return this.getGroupId() + ":" + this.getArtifactId(); + } + + @Override + public boolean equals(Object o) { + if(o instanceof BomDependencyNoVersion) { + BomDependencyNoVersion bomDependency = (BomDependencyNoVersion) o; + return bomDependency.toString().equals(this.toString()); + } + + return false; + } + + @Override + public int hashCode() { + return toString().hashCode(); + } +} diff --git a/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/models/ConflictingDependency.java b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/models/ConflictingDependency.java new file mode 100644 index 0000000000000..5f45714607974 --- /dev/null +++ b/eng/bomgenerator/src/main/java/com/azure/tools/bomgenerator/models/ConflictingDependency.java @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.tools.bomgenerator.models; + +public class ConflictingDependency { + private BomDependency expectedDependency; + private BomDependency actualDependency; + + ConflictingDependency(BomDependency actualDependency, BomDependency expectedDependency) { + this.expectedDependency = expectedDependency; + this.actualDependency = actualDependency; + } + + ConflictingDependency(BomDependency actualDependency) { + this.actualDependency = actualDependency; + } + + public BomDependency getExpectedDependency() { + return this.expectedDependency; + } + + public BomDependency getActualDependency() { + return this.actualDependency; + } + + @Override + public String toString() { + return this.actualDependency.toString() + this.expectedDependency.toString(); + } + + @Override + public boolean equals(Object o) { + if(o == null) { + return false; + } + + if (o instanceof ConflictingDependency) { + ConflictingDependency bomSource = this; + ConflictingDependency bomTarget = (ConflictingDependency) o; + + return bomSource.toString().equals(bomTarget.toString()); + } + + return false; + } + + @Override + public int hashCode() { + return toString().hashCode(); + } +} diff --git a/eng/bomgenerator/src/main/resources/log4j.properties b/eng/bomgenerator/src/main/resources/log4j.properties new file mode 100644 index 0000000000000..6f3e271cbf56e --- /dev/null +++ b/eng/bomgenerator/src/main/resources/log4j.properties @@ -0,0 +1,17 @@ +# Define the root logger with appender file +log4j.rootLogger = INFO, FILE + +log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender +log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout +log4j.appender.CONSOLE.Target=System.out + +# Define the file appender +log4j.appender.FILE=org.apache.log4j.FileAppender +log4j.appender.FILE.File=report.log + +# Set the append to false, overwrite +log4j.appender.FILE.Append=false + +# Define the layout for file appender +log4j.appender.FILE.layout=org.apache.log4j.PatternLayout +log4j.appender.FILE.layout.conversionPattern=%m%n