Skip to content

Commit

Permalink
native
Browse files Browse the repository at this point in the history
  • Loading branch information
Him188 committed Dec 26, 2024
1 parent ab1e7ac commit b31f280
Show file tree
Hide file tree
Showing 88 changed files with 212 additions and 8,718 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.takeWhile
import kotlinx.coroutines.withContext
import kotlinx.io.IOException
import org.openani.anitorrent.api.TorrentSession
import org.openani.anitorrent.api.pieces.Piece
import org.openani.anitorrent.api.pieces.PieceList
import kotlinx.io.files.Path
import me.him188.ani.utils.io.SeekableInput
import me.him188.ani.utils.io.SystemPath
import me.him188.ani.utils.io.absolutePath
Expand All @@ -35,6 +33,9 @@ import me.him188.ani.utils.io.length
import me.him188.ani.utils.io.resolve
import me.him188.ani.utils.logging.info
import me.him188.ani.utils.logging.thisLogger
import org.openani.anitorrent.api.TorrentSession
import org.openani.anitorrent.api.pieces.Piece
import org.openani.anitorrent.api.pieces.PieceList
import kotlin.concurrent.Volatile
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
Expand Down Expand Up @@ -113,10 +114,10 @@ interface TorrentFileEntry { // 实现提示, 无 test mock
/**
* 绝对路径. 挂起直到文件路径可用 (即有任意一个 piece 下载完成时)
*/
suspend fun resolveFile(): SystemPath
suspend fun resolveFile(): Path

@Throws(IOException::class)
fun resolveFileMaybeEmptyOrNull(): SystemPath?
fun resolveFileMaybeEmptyOrNull(): Path?

/**
* Opens the downloaded file as a [SeekableInput].
Expand Down Expand Up @@ -238,7 +239,7 @@ abstract class AbstractTorrentFileEntry(

protected abstract fun updatePriority()

override suspend fun resolveFile(): SystemPath = resolveDownloadingFile()
override suspend fun resolveFile(): Path = resolveDownloadingFile()

protected suspend fun resolveDownloadingFile(): SystemPath {
while (true) {
Expand All @@ -258,7 +259,7 @@ abstract class AbstractTorrentFileEntry(
}

@Throws(IOException::class)
override fun resolveFileMaybeEmptyOrNull(): SystemPath? =
override fun resolveFileMaybeEmptyOrNull(): Path? =
saveDirectory.resolve(relativePath).takeIf { it.isRegularFile() }

override fun toString(): String {
Expand Down
220 changes: 202 additions & 18 deletions anitorrent-native/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,30 +1,24 @@
import com.vanniktech.maven.publish.JavaLibrary
import com.vanniktech.maven.publish.JavadocJar
import com.vanniktech.maven.publish.SonatypeHost

/*
* Ani
* Copyright (C) 2022-2024 Him188
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* Copyright (C) 2024 OpenAni and contributors.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license, which can be found at the following link.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
* https://github.com/open-ani/ani/blob/main/LICENSE
*/

plugins {
kotlin("jvm")
java
idea
alias(libs.plugins.vanniktech.mavenPublish)
}

sourceSets.main {
java.setSrcDirs(listOf("gen/java"))
kotlin.setSrcDirs(listOf<String>())
}

/// ANITORRENT
Expand All @@ -36,6 +30,11 @@ val generateSwigImpl = tasks.register("generateSwigImpl", Exec::class.java) {
group = "anitorrent"

val swig = System.getenv("SWIG") ?: "swig"
// swig -java -c++ \
// -o ./src/anitorrent_wrap.cpp \
// -outdir ./java/me/him188/ani/app/torrent/anitorrent/binding \
// -package me.him188.ani.app.torrent.anitorrent.binding \
// ./anitorrent.i

val swigI = anitorrentRootDir.resolve("anitorrent.i")
inputs.file(swigI)
Expand All @@ -44,13 +43,13 @@ val generateSwigImpl = tasks.register("generateSwigImpl", Exec::class.java) {
outputs.dir(anitorrentRootDir.resolve("gen/java"))

val cppDir = anitorrentRootDir.resolve("gen/cpp")
val javaDir = anitorrentRootDir.resolve("gen/java/org/openani/anitorrent/binding")
val javaDir = anitorrentRootDir.resolve("gen/java/me/him188/ani/app/torrent/anitorrent/binding")
commandLine = listOf(
swig,
"-java", "-c++", "-directors", "-cppext", "cpp", "-addextern",
"-o", cppDir.resolve("anitorrent_wrap.cpp").absolutePath,
"-outdir", javaDir.absolutePath,
"-package", "org.openani.anitorrent.anitorrent.binding",
"-package", "me.him188.ani.app.torrent.anitorrent.binding",
swigI.absolutePath,
)
doFirst {
Expand All @@ -69,7 +68,9 @@ val patchGeneratedSwig = tasks.register("patchGeneratedSwig") {
gen.walk().forEach {
if (it.extension in listOf("cpp", "h", "java")) {
if (!it.readText().contains("@formatter"))
it.writeText("//@formatter:off\n" + it.readText() + "\n//@formatter:on")
it.writeText("//@formatter:off\n" + it.readText() +
"\n// @formatter:on",
)
}
}
}
Expand Down Expand Up @@ -198,6 +199,140 @@ val buildAnitorrent = tasks.register("buildAnitorrent", Exec::class.java) {
)
}

val copyNativeFiles by tasks.registering {
dependsOn(buildAnitorrent)
group = "anitorrent"
description = "Copy anitrorent native files and dependencies to build/native-files"

val cmakeCache = anitorrentBuildDir.resolve("CMakeCache.txt")
if (cmakeCache.exists()) {
inputs.file(cmakeCache)
}

val targetDir = layout.buildDirectory.dir("native-files")
outputs.dir(targetDir)

val buildType = getPropertyOrNull("CMAKE_BUILD_TYPE") ?: "Debug"
inputs.property("buildType", buildType)

val anitorrentBuildDir = anitorrentBuildDir

val os = getOs()
inputs.property("os", os)
doLast {
class Dep(
val path: File,
val overrideName: String?
)

fun getAnitorrentNativeFiles(): List<Dep> {
return buildList {
fun add(file: File) {
add(Dep(file, null))
}

fun addIfExist(file: File) {
if (file.exists()) {
add(file)
}
}

when (getOs()) {
Os.Windows -> {
add(anitorrentBuildDir.resolve("$buildType/anitorrent.dll"))
add(anitorrentBuildDir.resolve("_deps/libtorrent-build/$buildType/torrent-rasterbar.dll"))
addIfExist(anitorrentBuildDir.resolve("_deps/libtorrent-build/$buildType/libssl-3-x64.dll"))
addIfExist(anitorrentBuildDir.resolve("_deps/libtorrent-build/$buildType/libcrypto-3-x64.dll"))
}

Os.MacOS -> {
add(anitorrentBuildDir.resolve("libanitorrent.dylib"))
}

Os.Unknown, Os.Linux -> {
add(anitorrentBuildDir.resolve("libanitorrent.so"))
add(anitorrentBuildDir.resolve("_deps/libtorrent-build/libtorrent-rasterbar.2.0.10.so"))
}
}
}
}

fun parseCMakeCache(cmakeCache: File): Map<String, String> {
return cmakeCache.readText().lines().filterNot { it.startsWith("#") }.mapNotNull {
val parts = it.split("=", limit = 2)
if (parts.size != 2) return@mapNotNull null
parts[0].trim() to parts[1].trim()
}.toMap()
}

val map = parseCMakeCache(cmakeCache)

fun Map<String, String>.getOrFail(key: String): String {
return this[key] ?: error("Key $key not found in CMakeCache")
}

val dependencies = buildMap {
map["OPENSSL_CRYPTO_LIBRARY:FILEPATH"]?.let {
put("OPENSSL_CRYPTO_LIBRARY", File(it))
}
map["OPENSSL_SSL_LIBRARY:FILEPATH"]?.let {
put("OPENSSL_SSL_LIBRARY", File(it))
}

if (os == Os.Windows) {
// LIB_EAY_RELEASE:FILEPATH=C:/vcpkg/installed/x64-windows/lib/libcrypto.lib
// SSL_EAY_RELEASE:FILEPATH=C:/vcpkg/installed/x64-windows/lib/libssl.lib
fun findDll(libFile: File): List<File> {
val matched = libFile.parentFile.parentFile.resolve("bin")
.listFiles().orEmpty()
.filter { it.extension == "dll" && it.nameWithoutExtension.startsWith(libFile.nameWithoutExtension) }
return matched
}

fun findSystemDll(filename: String): File? {
val systemDir = File("C:/Windows/System32")
val systemDll = systemDir.resolve(filename)
if (systemDll.exists()) {
return systemDll
}
return null
}
findSystemDll("vcruntime140.dll")?.let {
put("vcruntime140", it)
}
findSystemDll("vcruntime140_1.dll")?.let {
put("vcruntime140_1", it)
}
findSystemDll("msvcp140.dll")?.let {
put("MSVCP140", it)
}
map["LIB_EAY_RELEASE:FILEPATH"]?.let {
findDll(File(it)).forEachIndexed { index, file ->
put("LIB_EAY_RELEASE_${index}", file)
}
}
map["SSL_EAY_RELEASE:FILEPATH"]?.let {
findDll(File(it)).forEachIndexed { index, file ->
put("SSL_EAY_RELEASE_${index}", file)
}
}
}
}

(dependencies.values.map { Dep(it, null) } + getAnitorrentNativeFiles()).forEach {
val target = targetDir.get().file(it.overrideName ?: it.path.name)
it.path.copyTo(target.asFile, overwrite = true)
}
}
}

sourceSets.main {
resources.srcDir(copyNativeFiles.map { it.outputs.files.singleFile })
}

tasks.compileJava {
dependsOn(copyNativeFiles)
}

idea {
module {
Expand All @@ -210,3 +345,52 @@ idea {
tasks.named("compileJava") {
mustRunAfter(generateSwigImpl)
}

mavenPublishing {
configure(JavaLibrary(JavadocJar.Empty(), true))
publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL)
signAllPublications()

pom {
name = "Anitorrent Native"
description = "Anitorrent Native"
url = "https://github.com/open-ani/anitorrent"

licenses {
license {
name = "GNU General Public License, Version 3"
url = "https://github.com/open-ani/anitorrent/blob/main/LICENSE"
distribution = "https://www.gnu.org/licenses/gpl-3.0.txt"
}
}

developers {
developer {
id = "openani"
name = "OpenAni and contributors"
email = "[email protected]"
}
}

scm {
connection = "scm:git:https://github.com/open-ani/anitorrent.git"
developerConnection = "scm:git:[email protected]:open-ani/anitorrent.git"
url = "https://github.com/open-ani/anitorrent"
}
}
}

val nativeJar = tasks.register("nativeJar", Jar::class.java) {
dependsOn(copyNativeFiles)
group = "anitorrent"
archiveClassifier.set(getOsTriple())
from(copyNativeFiles.map { it.outputs.files.singleFile })
}

publishing {
publications {
getByName("maven", MavenPublication::class) {
artifact(nativeJar)
}
}
}
Loading

0 comments on commit b31f280

Please sign in to comment.