diff --git a/plugins/package-managers/conan/src/main/kotlin/Conan.kt b/plugins/package-managers/conan/src/main/kotlin/Conan.kt index f9f401db87231..18c1a783050fb 100644 --- a/plugins/package-managers/conan/src/main/kotlin/Conan.kt +++ b/plugins/package-managers/conan/src/main/kotlin/Conan.kt @@ -20,6 +20,8 @@ package org.ossreviewtoolkit.plugins.packagemanagers.conan import com.charleskorn.kaml.Yaml +import com.charleskorn.kaml.YamlList +import com.charleskorn.kaml.YamlMap import com.charleskorn.kaml.YamlNode import com.charleskorn.kaml.YamlScalar import com.charleskorn.kaml.yamlList @@ -41,7 +43,6 @@ import org.ossreviewtoolkit.analyzer.parseAuthorString import org.ossreviewtoolkit.downloader.VcsHost import org.ossreviewtoolkit.downloader.VersionControlSystem import org.ossreviewtoolkit.model.Hash -import org.ossreviewtoolkit.model.HashAlgorithm import org.ossreviewtoolkit.model.Identifier import org.ossreviewtoolkit.model.Package import org.ossreviewtoolkit.model.PackageReference @@ -303,7 +304,7 @@ class Conan( val homepageUrl = pkgInfo.homepage.orEmpty() val id = parsePackageId(pkgInfo, workingDir) - val conanData = readConanData(id) + val conanData = readConanData(id.name, id.version, conanStoragePath) return Package( id = id, @@ -314,7 +315,7 @@ class Conan( binaryArtifact = RemoteArtifact.EMPTY, // TODO: implement me! sourceArtifact = parseSourceArtifact(conanData), vcs = processPackageVcs(VcsInfo.EMPTY, homepageUrl), - isModified = "patches" in conanData + isModified = conanData.hasPatches ) } @@ -372,46 +373,17 @@ class Conan( private fun parsePackageField(pkgInfo: PackageInfo, workingDir: File, field: String): String = inspectField(pkgInfo.displayName, workingDir, field) - /** - * Return the generic map of Conan data for the [id]. - */ - private fun readConanData(id: Identifier): Map { - val conanDataFile = conanStoragePath.resolve("${id.name}/${id.version}/_/_/export/conandata.yml") - - return runCatching { - val conanData = Yaml.default.parseToYamlNode(conanDataFile.readText()).yamlMap.entries.mapKeys { - it.key.content - } - - // Replace metadata for all version with metadata for this specific version for convenient access. - conanData.mapNotNull { (key, value) -> - key to when (key) { - "patches", "sources" -> value.yamlMap[id.version] ?: return@mapNotNull null - else -> value - } - }.toMap() - }.getOrDefault(emptyMap()) - } - /** * Return the source artifact contained in [conanData], or [RemoteArtifact.EMPTY] if no source artifact is * available. */ - private fun parseSourceArtifact(conanData: Map): RemoteArtifact = - runCatching { - val artifactEntry = conanData.getValue("sources").yamlMap - - val url = checkNotNull(artifactEntry.get("url")).let { urlNode -> - urlNode.takeIf { it is YamlScalar } ?: urlNode.yamlList.items.first() - }.yamlScalar.content + private fun parseSourceArtifact(conanData: Conandata): RemoteArtifact { + val url = conanData.url ?: return RemoteArtifact.EMPTY + val hashValue = conanData.sha256.orEmpty() + val hash = Hash.NONE.takeIf { hashValue.isEmpty() } ?: Hash.create(hashValue, url) - val hashValue = artifactEntry.get("sha256")?.content.orEmpty() - val hash = Hash.create(hashValue, HashAlgorithm.SHA256.name) - - RemoteArtifact(url, hash) - }.getOrElse { - RemoteArtifact.EMPTY - } + return RemoteArtifact(url, hash) + } /** * Return a [Package] containing project-level information from [pkgInfo] and [definitionFile] using the @@ -450,3 +422,28 @@ class Conan( private fun parseAuthors(pkgInfo: PackageInfo): Set = setOfNotNull(parseAuthorString(pkgInfo.author.orEmpty(), '<', '(')) } + +private data class Conandata( + val url: String?, + val sha256: String?, + val hasPatches: Boolean +) + +private fun readConanData(name: String, version: String, conanStorageDir: File): Conandata { + val conanDataFile = conanStorageDir.resolve("$name/$version/_/_/export/conandata.yml") + + val root = Yaml.default.parseToYamlNode(conanDataFile.readText()).yamlMap + val sourceForVersion = root.get("sources")?.get(version) + val patchesForVersion = root.get("patches")?.get(version) + + val sha256 = sourceForVersion?.get("sha256")?.content + val hasPatches = patchesForVersion?.items.orEmpty().isNotEmpty() + val url = sourceForVersion?.get("url")?.let { + when { + it is YamlList -> it.yamlList?.items?.first()?.yamlScalar?.content + else -> it.yamlScalar.content + } + } + + return Conandata(url, sha256, hasPatches) +}