repo2docker/repo2docker/buildpacks/julia/julia_require.py

185 wiersze
7.0 KiB
Python

"""Generates a Dockerfile based on an input matrix with REQUIRE for legacy Julia"""
import os
from ...semver import parse_version as V
from ..python import PythonBuildPack
class JuliaRequireBuildPack(PythonBuildPack):
"""
Julia build pack which uses conda and REQUIRE.
"""
minor_julias = {"0.6": "0.6.4", "0.7": "0.7.0", "1.0": "1.0.4", "1.1": "1.1.1"}
major_julias = {"1": "1.1.1"}
@property
def python_version(self):
# IJulia doesn't build on julia 0.6
# due to old incompatibilities with Jupyter-core >= 4.5,
# so use the similarly-old Python 3.5 base environment
if V(self.julia_version) < V("0.7"):
return "3.5"
else:
return super().python_version
@property
def julia_version(self):
require = self.binder_path("REQUIRE")
try:
with open(require) as f:
julia_version_line = (
f.readline().strip()
) # First line is optionally a julia version
except FileNotFoundError:
julia_version_line = ""
if not julia_version_line.startswith("julia "):
# not a Julia version line.
# use the default Julia.
self._julia_version = self.minor_julias["0.6"]
return self._julia_version
julia_version_info = julia_version_line.split(" ", 1)[1].split(".")
julia_version = ""
if len(julia_version_info) == 1:
julia_version = self.major_julias[julia_version_info[0]]
elif len(julia_version_info) == 2:
# get major.minor
julia_version = self.minor_julias[".".join(julia_version_info)]
else:
# use supplied julia version
julia_version = ".".join(julia_version_info)
self._julia_version = julia_version
return self._julia_version
def get_build_env(self):
"""Get additional environment settings for Julia and Jupyter
Returns:
an ordered list of environment setting tuples
The tuples contain a string of the environment variable name and
a string of the environment setting:
- `JULIA_PATH`: base path where all Julia Binaries and libraries
will be installed
- `JULIA_HOME`: path where all Julia Binaries will be installed
- `JULIA_PKGDIR`: path where all Julia libraries will be installed
- `JULIA_DEPOT_PATH`: path where Julia libraries are installed.
Similar to JULIA_PKGDIR, used in 1.x.
- `JULIA_VERSION`: default version of julia to be installed
- `JUPYTER`: environment variable required by IJulia to point to
the `jupyter` executable
For example, a tuple may be `('JULIA_VERSION', '0.6.0')`.
"""
return super().get_build_env() + [
("JULIA_PATH", "${APP_BASE}/julia"),
("JULIA_HOME", "${JULIA_PATH}/bin"), # julia <= 0.6
("JULIA_BINDIR", "${JULIA_HOME}"), # julia >= 0.7
("JULIA_PKGDIR", "${JULIA_PATH}/pkg"),
("JULIA_DEPOT_PATH", "${JULIA_PKGDIR}"), # julia >= 0.7
("JULIA_VERSION", self.julia_version),
("JUPYTER", "${NB_PYTHON_PREFIX}/bin/jupyter"),
]
def get_path(self):
"""Adds path to Julia binaries to user's PATH.
Returns:
an ordered list of path strings. The path to the Julia
executable is added to the list.
"""
return super().get_path() + ["${JULIA_HOME}"]
def get_build_scripts(self):
"""
Return series of build-steps common to "ALL" Julia repositories
All scripts found here should be independent of contents of a
particular repository.
This creates a directory with permissions for installing julia packages
(from get_assemble_scripts).
"""
return super().get_build_scripts() + [
(
"root",
r"""
mkdir -p ${JULIA_PATH} && \
curl -sSL "https://julialang-s3.julialang.org/bin/linux/x64/${JULIA_VERSION%[.-]*}/julia-${JULIA_VERSION}-linux-x86_64.tar.gz" | tar -xz -C ${JULIA_PATH} --strip-components 1
""",
),
(
"root",
r"""
mkdir -p ${JULIA_PKGDIR} && \
chown ${NB_USER}:${NB_USER} ${JULIA_PKGDIR}
""",
),
(
"${NB_USER}",
# HACK: Can't seem to tell IJulia to install in sys-prefix
# FIXME: Find way to get it to install under /srv and not $HOME?
r"""
julia -e 'if (VERSION > v"0.7-") using Pkg; else Pkg.init(); end; Pkg.add("IJulia"); using IJulia;' && \
mv ${HOME}/.local/share/jupyter/kernels/julia-${JULIA_VERSION%[.-]*} ${NB_PYTHON_PREFIX}/share/jupyter/kernels/julia-${JULIA_VERSION%[.-]*}
""",
),
]
def get_assemble_scripts(self):
"""
Return series of build-steps specific to "this" Julia repository
Precompile all Julia libraries found in the repository's REQUIRE
file. The parent, CondaBuildPack, will add the build steps for
any needed Python packages found in environment.yml.
"""
require = self.binder_path("REQUIRE")
return super().get_assemble_scripts() + [
(
"${NB_USER}",
# Install and pre-compile all libraries if they've opted into it.
# In v0.6, Pkg.resolve() installs all the packages, but in v0.7+, we
# have to manually Pkg.add() each of them (since the REQUIRES file
# format is deprecated).
# The precompliation is done via `using {libraryname}`.
r"""
julia /tmp/install-repo-dependencies.jl "%(require)s"
"""
% {"require": require}
# TODO: For some reason, `rm`ing the file fails with permission denied.
# && rm /tmp/install-repo-dependencies.jl
)
]
def get_build_script_files(self):
files = {
"julia/install-repo-dependencies.jl": "/tmp/install-repo-dependencies.jl"
}
files.update(super().get_build_script_files())
return files
def detect(self):
"""
Check if current repo should be built with the Julia Legacy Build pack
super().detect() is not called in this function - it would return
false unless an `environment.yml` is present and we do not want to
require the presence of a `environment.yml` to use Julia.
Instead we just check if the path to `REQUIRE` exists and that there is
no julia 1.0 style environment
"""
return os.path.exists(self.binder_path("REQUIRE")) and not (
os.path.exists(self.binder_path("Project.toml"))
or os.path.exists(self.binder_path("JuliaProject.toml"))
)