Skip to content

Commit

Permalink
Merge pull request #3566 from Hannah-Sten/dockerized-texlive
Browse files Browse the repository at this point in the history
Support TeX Live docker image
  • Loading branch information
PHPirates authored Jun 1, 2024
2 parents 2f4b859 + 2f6fe21 commit 255525c
Show file tree
Hide file tree
Showing 11 changed files with 89 additions and 40 deletions.
6 changes: 6 additions & 0 deletions Writerside/topics/Run-configuration-settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -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_
Expand Down
79 changes: 52 additions & 27 deletions src/nl/hannahsten/texifyidea/run/compiler/LatexCompiler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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
Expand Down Expand Up @@ -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<String>) {
// 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<String>) {
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 }
Expand All @@ -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"))
parameterList += if (isMiktex) {
listOf(
"-v",
"miktex:/miktex/.miktex",
"-v",
"${mainFile.parent.path}:/miktex/work"
)
}
else {
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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,7 @@ class LatexRunConfiguration(
latexDistribution
}
else {
LatexSdkUtil.getLatexProjectSdkType(project)?.getLatexDistributionType() ?: LatexDistributionType.TEXLIVE
LatexSdkUtil.getLatexDistributionType(project) ?: LatexDistributionType.TEXLIVE
}
}

Expand Down
18 changes: 13 additions & 5 deletions src/nl/hannahsten/texifyidea/settings/sdk/DockerSdk.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> =
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/nl/hannahsten/texifyidea/settings/sdk/LatexSdk.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 7 additions & 1 deletion src/nl/hannahsten/texifyidea/settings/sdk/LatexSdkUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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 ->
Expand Down Expand Up @@ -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).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion src/nl/hannahsten/texifyidea/settings/sdk/TectonicSdk.kt
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/nl/hannahsten/texifyidea/settings/sdk/TexliveSdk.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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() }
Expand Down

0 comments on commit 255525c

Please sign in to comment.