From b6ae672819d080cbc668edafe257854ce3614e96 Mon Sep 17 00:00:00 2001 From: Thomas Schouten Date: Thu, 30 May 2024 20:02:36 +0200 Subject: [PATCH 1/3] Support TeX Live docker image --- .../texifyidea/run/compiler/LatexCompiler.kt | 79 ++++++++++++------- .../run/latex/LatexDistributionType.kt | 5 +- .../run/latex/LatexRunConfiguration.kt | 2 +- .../texifyidea/settings/sdk/DockerSdk.kt | 18 +++-- .../texifyidea/settings/sdk/LatexSdk.kt | 2 +- .../texifyidea/settings/sdk/LatexSdkUtil.kt | 8 +- .../texifyidea/settings/sdk/MiktexLinuxSdk.kt | 2 +- .../settings/sdk/MiktexWindowsSdk.kt | 2 +- .../texifyidea/settings/sdk/TectonicSdk.kt | 3 +- .../texifyidea/settings/sdk/TexliveSdk.kt | 2 +- 10 files changed, 83 insertions(+), 40 deletions(-) diff --git a/src/nl/hannahsten/texifyidea/run/compiler/LatexCompiler.kt b/src/nl/hannahsten/texifyidea/run/compiler/LatexCompiler.kt index 268279ea9..3faefa21c 100644 --- a/src/nl/hannahsten/texifyidea/run/compiler/LatexCompiler.kt +++ b/src/nl/hannahsten/texifyidea/run/compiler/LatexCompiler.kt @@ -292,28 +292,32 @@ enum class LatexCompiler(private val displayName: String, val executableName: St val moduleRoot = fileIndex.getContentRootForFile(mainFile) // For now we disable module roots with Docker // Could be improved by mounting them to the right directory - val moduleRoots = if (runConfig.getLatexDistributionType() != LatexDistributionType.DOCKER_MIKTEX) { - rootManager.contentSourceRoots + val moduleRoots = if (runConfig.getLatexDistributionType().isDocker()) { + emptyArray() } else { - emptyArray() + rootManager.contentSourceRoots } - // If we used /miktex/work/out, an out directory would appear in the src folder on the host system - val dockerOutputDir = "/miktex/out" - val dockerAuxilDir = "/miktex/auxil" - val outputPath = if (runConfig.getLatexDistributionType() != LatexDistributionType.DOCKER_MIKTEX) { - runConfig.outputPath.getAndCreatePath()?.path?.toPath(runConfig) + val outputPath = if (runConfig.getLatexDistributionType() == LatexDistributionType.DOCKER_MIKTEX) { + // If we used /miktex/work/out, an out directory would appear in the src folder on the host system + "/miktex/out" + } + else if (runConfig.getLatexDistributionType() == LatexDistributionType.DOCKER_TEXLIVE) { + "/out" } else { - dockerOutputDir + runConfig.outputPath.getAndCreatePath()?.path?.toPath(runConfig) } - val auxilPath = if (runConfig.getLatexDistributionType() != LatexDistributionType.DOCKER_MIKTEX) { - runConfig.auxilPath.getAndCreatePath()?.path?.toPath(runConfig) + val auxilPath = if (runConfig.getLatexDistributionType() == LatexDistributionType.DOCKER_MIKTEX) { + "/miktex/auxil" + } + else if (runConfig.getLatexDistributionType() == LatexDistributionType.DOCKER_TEXLIVE) { + null } else { - dockerAuxilDir + runConfig.auxilPath.getAndCreatePath()?.path?.toPath(runConfig) } val command = createCommand( @@ -338,8 +342,8 @@ enum class LatexCompiler(private val displayName: String, val executableName: St return mutableListOf("bash", "-ic", wslCommand) } - if (runConfig.getLatexDistributionType() == LatexDistributionType.DOCKER_MIKTEX) { - createDockerCommand(runConfig, dockerAuxilDir, dockerOutputDir, mainFile, command) + if (runConfig.getLatexDistributionType().isDocker()) { + createDockerCommand(runConfig, auxilPath, outputPath, mainFile, command) } // Custom compiler arguments specified by the user @@ -368,9 +372,13 @@ enum class LatexCompiler(private val displayName: String, val executableName: St } @Suppress("SameParameterValue") - private fun createDockerCommand(runConfig: LatexRunConfiguration, dockerAuxilDir: String, dockerOutputDir: String, mainFile: VirtualFile, command: MutableList) { - // See https://hub.docker.com/r/miktex/miktex - "docker volume create --name miktex".runCommand() + private fun createDockerCommand(runConfig: LatexRunConfiguration, dockerAuxilDir: String?, dockerOutputDir: String?, mainFile: VirtualFile, command: MutableList) { + val isMiktex = runConfig.getLatexDistributionType() == LatexDistributionType.MIKTEX + + if (isMiktex) { + // See https://hub.docker.com/r/miktex/miktex + "docker volume create --name miktex".runCommand() + } // Find the sdk corresponding to the type the user has selected in the run config val sdk = ProjectJdkTable.getInstance().allJdks.firstOrNull { it.sdkType is DockerSdk } @@ -379,20 +387,37 @@ enum class LatexCompiler(private val displayName: String, val executableName: St if (sdk == null) "docker" else (sdk.sdkType as DockerSdk).getExecutableName("docker", sdk.homePath!!), "run", "--rm", - "-v", - "miktex:/miktex/.miktex", - "-v", - "${mainFile.parent.path}:/miktex/work" ) - // Avoid mounting the mainfile parent also to /miktex/work/out, - // because there may be a good reason to make the output directory the same as the source directory - if (runConfig.outputPath.getAndCreatePath() != mainFile.parent) { - parameterList.addAll(listOf("-v", "${runConfig.outputPath.getAndCreatePath()?.path}:$dockerOutputDir")) + if (isMiktex) { + parameterList += listOf( + "-v", + "miktex:/miktex/.miktex", + "-v", + "${mainFile.parent.path}:/miktex/work" + ) + } + else { + parameterList += listOf( + "-v", + "${mainFile.parent.path}:/workdir" + ) + } + + if (dockerOutputDir != null) { + // Avoid mounting the mainfile parent also to /miktex/work/out, + // because there may be a good reason to make the output directory the same as the source directory + val outPath = runConfig.outputPath.getAndCreatePath() + if (outPath?.path != null && outPath != mainFile.parent) { + parameterList.addAll(listOf("-v", "${outPath.path}:$dockerOutputDir")) + } } - if (runConfig.auxilPath.getAndCreatePath() != mainFile.parent) { - parameterList.addAll(listOf("-v", "${runConfig.auxilPath.getAndCreatePath()?.path}:$dockerAuxilDir")) + if (dockerAuxilDir != null) { + val auxilPath = runConfig.auxilPath.getAndCreatePath() + if (auxilPath?.path != null && auxilPath != mainFile.parent) { + parameterList.addAll(listOf("-v", "${auxilPath.path}:$dockerAuxilDir")) + } } parameterList.add((sdk?.sdkAdditionalData as? DockerSdkAdditionalData)?.imageName ?: "miktex:latest") diff --git a/src/nl/hannahsten/texifyidea/run/latex/LatexDistributionType.kt b/src/nl/hannahsten/texifyidea/run/latex/LatexDistributionType.kt index 70ae5c2ee..bc79ecfc9 100644 --- a/src/nl/hannahsten/texifyidea/run/latex/LatexDistributionType.kt +++ b/src/nl/hannahsten/texifyidea/run/latex/LatexDistributionType.kt @@ -16,10 +16,13 @@ enum class LatexDistributionType(val displayName: String) { MIKTEX("MiKTeX"), WSL_TEXLIVE("TeX Live using WSL"), DOCKER_MIKTEX("Dockerized MiKTeX"), + DOCKER_TEXLIVE("Dockerized TeX Live"), PROJECT_SDK("Use project SDK"); private fun isMiktex() = this == MIKTEX || this == DOCKER_MIKTEX - fun isMiktex(project: Project) = this == MIKTEX || this == DOCKER_MIKTEX || (this == PROJECT_SDK && LatexSdkUtil.getLatexProjectSdkType(project)?.getLatexDistributionType()?.isMiktex() == true) + fun isMiktex(project: Project) = this == MIKTEX || this == DOCKER_MIKTEX || (this == PROJECT_SDK && LatexSdkUtil.getLatexDistributionType(project)?.isMiktex() == true) + + fun isDocker() = this == DOCKER_MIKTEX || this == DOCKER_TEXLIVE fun isAvailable(project: Project) = LatexSdkUtil.isAvailable(this, project) diff --git a/src/nl/hannahsten/texifyidea/run/latex/LatexRunConfiguration.kt b/src/nl/hannahsten/texifyidea/run/latex/LatexRunConfiguration.kt index 22d2145a5..704a4c9ab 100644 --- a/src/nl/hannahsten/texifyidea/run/latex/LatexRunConfiguration.kt +++ b/src/nl/hannahsten/texifyidea/run/latex/LatexRunConfiguration.kt @@ -541,7 +541,7 @@ class LatexRunConfiguration( latexDistribution } else { - LatexSdkUtil.getLatexProjectSdkType(project)?.getLatexDistributionType() ?: LatexDistributionType.TEXLIVE + LatexSdkUtil.getLatexDistributionType(project) ?: LatexDistributionType.TEXLIVE } } diff --git a/src/nl/hannahsten/texifyidea/settings/sdk/DockerSdk.kt b/src/nl/hannahsten/texifyidea/settings/sdk/DockerSdk.kt index 68ffa9a96..d55f7c5cf 100644 --- a/src/nl/hannahsten/texifyidea/settings/sdk/DockerSdk.kt +++ b/src/nl/hannahsten/texifyidea/settings/sdk/DockerSdk.kt @@ -8,18 +8,19 @@ import com.intellij.openapi.ui.DialogBuilder import com.intellij.openapi.util.SystemInfo import com.intellij.util.Consumer import nl.hannahsten.texifyidea.run.latex.LatexDistributionType +import nl.hannahsten.texifyidea.util.containsAny import nl.hannahsten.texifyidea.util.runCommand import org.jdom.Element import javax.swing.JComponent /** - * Currently, we only support MiKTeX Docker images, but it wouldn't be too difficult to extend for other images. + * Currently, we only support official MiKTeX/texlive Docker images, but it wouldn't be too difficult to extend for other images. */ class DockerSdk : LatexSdk("LaTeX Docker SDK") { object Availability { val isAvailable: Boolean by lazy { - getAvailableImages().any { it.contains("miktex") } + getAvailableImages().any { it.contains("miktex") || it.contains("texlive") } } fun getAvailableImages(): List = @@ -47,11 +48,18 @@ class DockerSdk : LatexSdk("LaTeX Docker SDK") { } override fun isValidSdkHome(path: String): Boolean { - // For now we only support miktex images - return "$path/docker image ls".runCommand()?.contains("miktex") ?: false + return "$path/docker image ls".runCommand()?.containsAny(setOf("miktex", "texlive")) == true } - override fun getLatexDistributionType() = LatexDistributionType.DOCKER_MIKTEX + override fun getLatexDistributionType(sdk: Sdk): LatexDistributionType { + val imageName = (sdk.sdkAdditionalData as? DockerSdkAdditionalData)?.imageName + return if (imageName?.contains("texlive") == true) { + LatexDistributionType.DOCKER_TEXLIVE + } + else { + LatexDistributionType.DOCKER_MIKTEX + } + } override fun getVersionString(sdk: Sdk): String? { val data = sdk.sdkAdditionalData as? DockerSdkAdditionalData diff --git a/src/nl/hannahsten/texifyidea/settings/sdk/LatexSdk.kt b/src/nl/hannahsten/texifyidea/settings/sdk/LatexSdk.kt index 5218f80bb..9f7f628f7 100644 --- a/src/nl/hannahsten/texifyidea/settings/sdk/LatexSdk.kt +++ b/src/nl/hannahsten/texifyidea/settings/sdk/LatexSdk.kt @@ -39,7 +39,7 @@ abstract class LatexSdk(name: String) : SdkType(name) { /** * Interface between this and [LatexDistributionType], which is used in the run configuration. */ - abstract fun getLatexDistributionType(): LatexDistributionType + abstract fun getLatexDistributionType(sdk: Sdk): LatexDistributionType /** * Construct a valid path to the executable, given the homepath. diff --git a/src/nl/hannahsten/texifyidea/settings/sdk/LatexSdkUtil.kt b/src/nl/hannahsten/texifyidea/settings/sdk/LatexSdkUtil.kt index a2e381309..d2bd9a324 100644 --- a/src/nl/hannahsten/texifyidea/settings/sdk/LatexSdkUtil.kt +++ b/src/nl/hannahsten/texifyidea/settings/sdk/LatexSdkUtil.kt @@ -73,6 +73,7 @@ object LatexSdkUtil { if (type == LatexDistributionType.MIKTEX && isMiktexAvailable) return true if (type == LatexDistributionType.TEXLIVE && TexliveSdk.Cache.isAvailable) return true if (type == LatexDistributionType.DOCKER_MIKTEX && DockerSdk.Availability.isAvailable) return true + if (type == LatexDistributionType.DOCKER_TEXLIVE && DockerSdk.Availability.isAvailable) return true if (type == LatexDistributionType.WSL_TEXLIVE && isWslTexliveAvailable) return true return false } @@ -135,7 +136,7 @@ object LatexSdkUtil { */ fun getExecutableName(executableName: String, project: Project, latexDistributionType: LatexDistributionType? = null): String { // Prefixing the LaTeX compiler is not relevant for Docker MiKTeX (perhaps the path to the docker executable) - if (latexDistributionType == LatexDistributionType.DOCKER_MIKTEX) return executableName + if (latexDistributionType?.isDocker() == true) return executableName // Give preference to the project SDK if a valid LaTeX SDK is selected getLatexProjectSdk(project)?.let { sdk -> @@ -189,6 +190,11 @@ object LatexSdkUtil { return getLatexProjectSdk(project)?.sdkType as? LatexSdk } + fun getLatexDistributionType(project: Project): LatexDistributionType? { + val sdk = getLatexProjectSdk(project) ?: return null + return (sdk.sdkType as? LatexSdk)?.getLatexDistributionType(sdk) + } + /** * Collect SDK source paths, so paths to texmf-dist/source/latex, based on Project SDK if available (combining the default * for the SDK type and any user-added source roots) and otherwise on a random guess (ok not really). diff --git a/src/nl/hannahsten/texifyidea/settings/sdk/MiktexLinuxSdk.kt b/src/nl/hannahsten/texifyidea/settings/sdk/MiktexLinuxSdk.kt index f8515f089..a0de6e731 100644 --- a/src/nl/hannahsten/texifyidea/settings/sdk/MiktexLinuxSdk.kt +++ b/src/nl/hannahsten/texifyidea/settings/sdk/MiktexLinuxSdk.kt @@ -21,7 +21,7 @@ class MiktexLinuxSdk : LatexSdk("MiKTeX Mac/Linux SDK") { var version: String? = null } - override fun getLatexDistributionType() = LatexDistributionType.MIKTEX + override fun getLatexDistributionType(sdk: Sdk) = LatexDistributionType.MIKTEX override fun getExecutableName(executable: String, homePath: String): String { return "$homePath/$executable" diff --git a/src/nl/hannahsten/texifyidea/settings/sdk/MiktexWindowsSdk.kt b/src/nl/hannahsten/texifyidea/settings/sdk/MiktexWindowsSdk.kt index d30aa2fe8..2dcdb9ca0 100644 --- a/src/nl/hannahsten/texifyidea/settings/sdk/MiktexWindowsSdk.kt +++ b/src/nl/hannahsten/texifyidea/settings/sdk/MiktexWindowsSdk.kt @@ -21,7 +21,7 @@ class MiktexWindowsSdk : LatexSdk("MiKTeX Windows SDK") { var version: DefaultArtifactVersion? = null } - override fun getLatexDistributionType() = LatexDistributionType.MIKTEX + override fun getLatexDistributionType(sdk: Sdk) = LatexDistributionType.MIKTEX override fun getExecutableName(executable: String, homePath: String): String { val path = LatexSdkUtil.getPdflatexParentPath(Paths.get(homePath, "miktex").toString()) ?: return executable diff --git a/src/nl/hannahsten/texifyidea/settings/sdk/TectonicSdk.kt b/src/nl/hannahsten/texifyidea/settings/sdk/TectonicSdk.kt index a6bb9e9a8..e4d3f82c7 100644 --- a/src/nl/hannahsten/texifyidea/settings/sdk/TectonicSdk.kt +++ b/src/nl/hannahsten/texifyidea/settings/sdk/TectonicSdk.kt @@ -1,5 +1,6 @@ package nl.hannahsten.texifyidea.settings.sdk +import com.intellij.openapi.projectRoots.Sdk import com.intellij.openapi.util.SystemInfo import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.openapi.vfs.VirtualFile @@ -46,7 +47,7 @@ class TectonicSdk : LatexSdk("Tectonic SDK") { return Cache.fileLocationCache?.get(name) ?: "" } - override fun getLatexDistributionType() = LatexDistributionType.TEXLIVE + override fun getLatexDistributionType(sdk: Sdk) = LatexDistributionType.TEXLIVE // We assume Tectonic is in PATH. override fun getExecutableName(executable: String, homePath: String) = executable diff --git a/src/nl/hannahsten/texifyidea/settings/sdk/TexliveSdk.kt b/src/nl/hannahsten/texifyidea/settings/sdk/TexliveSdk.kt index 0efa4cbac..d0d68f1e5 100644 --- a/src/nl/hannahsten/texifyidea/settings/sdk/TexliveSdk.kt +++ b/src/nl/hannahsten/texifyidea/settings/sdk/TexliveSdk.kt @@ -90,7 +90,7 @@ open class TexliveSdk(name: String = "TeX Live SDK") : LatexSdk(name) { override fun getInvalidHomeMessage(path: String) = "Could not find $path/bin/*/pdflatex" - override fun getLatexDistributionType() = LatexDistributionType.TEXLIVE + override fun getLatexDistributionType(sdk: Sdk) = LatexDistributionType.TEXLIVE override fun getVersionString(sdkHome: String): String { return "TeX Live " + sdkHome.split("/").lastOrNull { it.isNotBlank() } From 83d7890b4984f9e09f2ac3e952f3b59dc39b65d2 Mon Sep 17 00:00:00 2001 From: Thomas Schouten Date: Fri, 31 May 2024 20:45:49 +0200 Subject: [PATCH 2/3] formatting --- src/nl/hannahsten/texifyidea/run/compiler/LatexCompiler.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/nl/hannahsten/texifyidea/run/compiler/LatexCompiler.kt b/src/nl/hannahsten/texifyidea/run/compiler/LatexCompiler.kt index 3faefa21c..f51032d8a 100644 --- a/src/nl/hannahsten/texifyidea/run/compiler/LatexCompiler.kt +++ b/src/nl/hannahsten/texifyidea/run/compiler/LatexCompiler.kt @@ -389,8 +389,8 @@ enum class LatexCompiler(private val displayName: String, val executableName: St "--rm", ) - if (isMiktex) { - parameterList += listOf( + parameterList += if (isMiktex) { + listOf( "-v", "miktex:/miktex/.miktex", "-v", @@ -398,7 +398,7 @@ enum class LatexCompiler(private val displayName: String, val executableName: St ) } else { - parameterList += listOf( + listOf( "-v", "${mainFile.parent.path}:/workdir" ) From 2f6fe213edb75e218cdf152b048fdb8dcbbf9f19 Mon Sep 17 00:00:00 2001 From: Thomas Schouten Date: Sat, 1 Jun 2024 21:06:37 +0200 Subject: [PATCH 3/3] Update documentation --- Writerside/topics/Run-configuration-settings.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Writerside/topics/Run-configuration-settings.md b/Writerside/topics/Run-configuration-settings.md index 7e471d349..5fb897296 100644 --- a/Writerside/topics/Run-configuration-settings.md +++ b/Writerside/topics/Run-configuration-settings.md @@ -249,6 +249,12 @@ Custom output directories are supported. * You have to login to GitHub to use the Docker image: get a github token from [https://github.com/settings/tokens,](https://github.com/settings/tokens,) save it somewhere secure and run `echo my_token | docker login https://docker.pkg.github.com -u myusername --password-stdin` See [https://help.github.com/en/packages/using-github-packages-with-your-projects-ecosystem/configuring-docker-for-use-with-github-packages#authenticating-to-github-packages](https://help.github.com/en/packages/using-github-packages-with-your-projects-ecosystem/configuring-docker-for-use-with-github-packages#authenticating-to-github-packages) for more info. +### Dockerized TeX Live + +Similar to the MiKTeX docker image, you can also use a texlive docker image if you have it installed. +By default, the official `texlive/texlive` image is used. +If you use IntelliJ, you can select a different image name by creating a LaTeX Docker SDK, see [Project configuration](Project-configuration.md#sdks). + ### TeX Live from WSL _Since b0.6.10_