Skip to content

Commit 8f71332

Browse files
authored
Merge pull request #216 from mkitti/mk/conda_jl_conda_exe
Introduce CONDA_JL_CONDA_EXE environment variable
2 parents f169018 + 84c662c commit 8f71332

File tree

5 files changed

+141
-46
lines changed

5 files changed

+141
-46
lines changed

Project.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "Conda"
22
uuid = "8f4d0f93-b110-5947-807f-2305c1781a2d"
3-
version = "1.6"
3+
version = "1.7"
44

55
[deps]
66
Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6"

README.md

+15-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
[![Build Status](https://github.com/JuliaPy/Conda.jl/actions/workflows/CI.yml/badge.svg)](https://github.com/JuliaPy/Conda.jl/actions/workflows/CI.yml)
44

55
This package allows one to use [conda](http://conda.pydata.org/) as a cross-platform binary provider for Julia for other Julia packages,
6-
especially to install binaries that have complicated dependencies
7-
like Python.
6+
especially to install binaries that have complicated dependencies like Python.
87

98
`conda` is a package manager which started as the binary package manager for the
109
Anaconda Python distribution, but it also provides arbitrary packages. Instead
@@ -81,6 +80,20 @@ julia> ENV["CONDA_JL_HOME"] = "/path/to/miniconda/envs/conda_jl" # change this
8180
pkg> build Conda
8281
```
8382

83+
## Using a conda executable outside of the home environment
84+
To use a specific conda executable, set the `CONDA_JL_CONDA_EXE` environment
85+
variable to the location of the conda executable. This conda executable can
86+
exist outside of the environment set by `CONDA_JL_HOME`. To apply the settting,
87+
rebuild `Conda.jl`. In Julia, run:
88+
89+
```jl
90+
julia> ENV["CONDA_JL_CONDA_EXE"] = "/path/to/miniconda/bin/conda" # change this to the path of the conda executable
91+
92+
pkg> build Conda
93+
```
94+
95+
*The use of `CONDA_JL_CONDA_EXE` requires at least version 1.7 of Conda.jl.*
96+
8497
## Conda and pip
8598
As of [conda 4.6.0](https://docs.conda.io/projects/conda/en/latest/user-guide/configuration/pip-interoperability.html#improving-interoperability-with-pip) there is improved support for PyPi packages.
8699
**Conda is still the recommended installation method** however if there are packages that are only availible with `pip` one can do the following:

deps/build.jl

+42
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,19 @@ Using the Anaconda/defaults channel instead, which is free for non-commercial us
2424
if !isdefined(@__MODULE__, :USE_MINIFORGE)
2525
const USE_MINIFORGE = USE_MINIFORGE_DEFAULT
2626
end
27+
function default_conda_exe(ROOTENV)
28+
@static if Sys.iswindows()
29+
p = joinpath(ROOTENV, "Scripts")
30+
conda_bat = joinpath(p, "conda.bat")
31+
isfile(conda_bat) ? conda_bat : joinpath(p, "conda.exe")
32+
else
33+
joinpath(ROOTENV, "bin", "conda")
34+
end
35+
end
36+
37+
if !isdefined(@__MODULE__, :CONDA_EXE)
38+
const CONDA_EXE = default_conda_exe(ROOTENV)
39+
end
2740
end
2841

2942
MINICONDA_VERSION = get(ENV, "CONDA_JL_VERSION", DefaultDeps.MINICONDA_VERSION)
@@ -52,10 +65,39 @@ These will require rebuilding.
5265
""")
5366
end
5467

68+
CONDA_EXE = get(ENV, "CONDA_JL_CONDA_EXE") do
69+
if ROOTENV == DefaultDeps.ROOTENV
70+
DefaultDeps.CONDA_EXE
71+
else
72+
DefaultDeps.default_conda_exe(ROOTENV)
73+
end
74+
end
75+
76+
if haskey(ENV, "CONDA_JL_CONDA_EXE")
77+
# Check to see if CONDA_EXE is an executable file
78+
if isfile(CONDA_EXE)
79+
if Sys.isexecutable(CONDA_EXE)
80+
@info "Executable conda located." CONDA_EXE
81+
else
82+
error("CONDA_JL_CONDA_EXE, $CONDA_EXE, cannot be executed by the current user.")
83+
end
84+
else
85+
error("CONDA_JL_CONDA_EXE, $CONDA_EXE, does not exist.")
86+
end
87+
else
88+
if !isfile(CONDA_EXE)
89+
# An old CONDA_EXE has gone missing, revert to default in ROOTENV
90+
@info "CONDA_EXE not found. Reverting to default in ROOTENV" CONDA_EXE ROOTENV
91+
CONDA_EXE = DefaultDeps.default_conda_exe(ROOTENV)
92+
end
93+
end
94+
95+
5596
deps = """
5697
const ROOTENV = "$(escape_string(ROOTENV))"
5798
const MINICONDA_VERSION = "$(escape_string(MINICONDA_VERSION))"
5899
const USE_MINIFORGE = $USE_MINIFORGE
100+
const CONDA_EXE = "$(escape_string(CONDA_EXE))"
59101
"""
60102

61103
mkpath(condadir)

src/Conda.jl

+11-8
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,12 @@ function python_dir(env::Environment)
7070
end
7171
const PYTHONDIR = python_dir(ROOTENV)
7272

73-
# note: the same conda program is used for all environments
74-
const conda = if Sys.iswindows()
75-
p = script_dir(ROOTENV)
76-
conda_bat = joinpath(p, "conda.bat")
77-
isfile(conda_bat) ? conda_bat : joinpath(p, "conda.exe")
78-
else
79-
joinpath(bin_dir(ROOTENV), "conda")
73+
if ! @isdefined(CONDA_EXE)
74+
# We have an oudated deps.jl file that does not define CONDA_EXE
75+
error("CONDA_EXE not defined in $deps_file.\nPlease rebuild Conda.jl via `using Pkg; pkg\"build Conda\";`")
8076
end
77+
# note: the same conda program is used for all environments
78+
const conda = CONDA_EXE
8179

8280
"Path to the condarc file"
8381
function conda_rc(env::Environment)
@@ -191,6 +189,7 @@ _quiet() = get(ENV, "CI", "false") == "true" ? `-q` : ``
191189
"Install miniconda if it hasn't been installed yet; _install_conda(true) installs Conda even if it has already been installed."
192190
function _install_conda(env::Environment, force::Bool=false)
193191
if force || !isfile(Conda.conda)
192+
@assert startswith(abspath(Conda.conda), abspath(PREFIX)) "CONDA_EXE, $(conda), does not exist within $PREFIX"
194193
@info("Downloading miniconda installer ...")
195194
if Sys.isunix()
196195
installer = joinpath(PREFIX, "installer.sh")
@@ -211,7 +210,7 @@ function _install_conda(env::Environment, force::Bool=false)
211210
end
212211
end
213212
if !isdir(prefix(env))
214-
runconda(`create $(_quiet()) -y -p $(prefix(env))`)
213+
create(env)
215214
end
216215
end
217216

@@ -228,6 +227,10 @@ function rm(pkg::PkgOrPkgs, env::Environment=ROOTENV)
228227
runconda(`remove $(_quiet()) -y $pkg`, env)
229228
end
230229

230+
function create(env::Environment)
231+
runconda(`create $(_quiet()) -y -p $(prefix(env))`)
232+
end
233+
231234
"Update all installed packages."
232235
function update(env::Environment=ROOTENV)
233236
if env == ROOTENV

test/runtests.jl

+72-35
Original file line numberDiff line numberDiff line change
@@ -70,36 +70,42 @@ Conda.rm_channel("foo", env)
7070
Conda.add("zlib", env; channel=alt_channel)
7171

7272
@testset "Batch install and uninstall" begin
73-
Conda.add(["affine", "ansi2html"], env)
74-
installed = Conda._installed_packages(env)
75-
@test "affine" installed
76-
@test "ansi2html" installed
77-
78-
Conda.rm(["affine", "ansi2html"], env)
79-
installed = Conda._installed_packages(env)
80-
@test "affine" installed
81-
@test "ansi2html" installed
73+
mktempdir() do env
74+
Conda.create(env)
75+
Conda.add(["affine", "ansi2html"], env)
76+
installed = Conda._installed_packages(env)
77+
@test "affine" installed
78+
@test "ansi2html" installed
79+
80+
Conda.rm(["affine", "ansi2html"], env)
81+
installed = Conda._installed_packages(env)
82+
@test "affine" installed
83+
@test "ansi2html" installed
84+
end
8285
end
8386

8487
# Run conda clean
8588
Conda.clean(; debug=true)
8689

8790
@testset "Exporting and creating environments" begin
88-
new_env = :test_conda_jl_2
89-
Conda.add("curl", env)
90-
Conda.export_list("conda-pkg.txt", env)
91-
92-
# Create a new environment
93-
rm(Conda.prefix(new_env); force=true, recursive=true)
94-
Conda.import_list(
95-
IOBuffer(read("conda-pkg.txt")), new_env; channels=["foo", alt_channel, default_channel]
96-
)
97-
98-
# Ensure that our new environment has our channels and package installed.
99-
@test Conda.channels(new_env) == ["foo", alt_channel, default_channel]
100-
installed = Conda._installed_packages(new_env)
101-
@test "curl" installed
102-
rm("conda-pkg.txt")
91+
mktempdir() do env
92+
new_env = :test_conda_jl_2
93+
Conda.create(env)
94+
Conda.add("curl", env)
95+
Conda.export_list("conda-pkg.txt", env)
96+
97+
# Create a new environment
98+
rm(Conda.prefix(new_env); force=true, recursive=true)
99+
Conda.import_list(
100+
IOBuffer(read("conda-pkg.txt")), new_env; channels=["foo", alt_channel, default_channel]
101+
)
102+
103+
# Ensure that our new environment has our channels and package installed.
104+
@test Conda.channels(new_env) == ["foo", alt_channel, default_channel]
105+
installed = Conda._installed_packages(new_env)
106+
@test "curl" installed
107+
rm("conda-pkg.txt")
108+
end
103109
end
104110

105111
@testset "Conda.pip_interop" begin
@@ -150,6 +156,17 @@ end
150156
end
151157
end
152158

159+
function default_conda_exe(ROOTENV)
160+
@static if Sys.iswindows()
161+
p = joinpath(ROOTENV, "Scripts")
162+
conda_bat = joinpath(p, "conda.bat")
163+
isfile(conda_bat) ? conda_bat : joinpath(p, "conda.exe")
164+
else
165+
joinpath(ROOTENV, "bin", "conda")
166+
end
167+
end
168+
169+
153170
if Sys.ARCH in [:x86, :i686]
154171
CONDA_JL_USE_MINIFORGE_DEFAULT = "false"
155172
else
@@ -162,12 +179,15 @@ end
162179
@test !isfile(depsfile)
163180
@test !isfile(joinpath(condadir, "deps.jl"))
164181

165-
withenv("CONDA_JL_VERSION" => nothing, "CONDA_JL_HOME" => nothing, "CONDA_JL_USE_MINIFORGE" => nothing) do
182+
withenv("CONDA_JL_VERSION" => nothing, "CONDA_JL_HOME" => nothing, "CONDA_JL_USE_MINIFORGE" => nothing, "CONDA_JL_CONDA_EXE" => nothing) do
166183
Pkg.build("Conda")
184+
local ROOTENV=joinpath(condadir, "3")
185+
local CONDA_EXE=default_conda_exe(ROOTENV)
167186
@test read(depsfile, String) == """
168-
const ROOTENV = "$(escape_string(joinpath(condadir, "3")))"
187+
const ROOTENV = "$(escape_string(ROOTENV))"
169188
const MINICONDA_VERSION = "3"
170189
const USE_MINIFORGE = $(CONDA_JL_USE_MINIFORGE_DEFAULT)
190+
const CONDA_EXE = "$(escape_string(CONDA_EXE))"
171191
"""
172192
end
173193
end
@@ -179,12 +199,15 @@ end
179199
@test !isfile(depsfile)
180200
@test !isfile(joinpath(condadir, "deps.jl"))
181201

182-
withenv("CONDA_JL_VERSION" => nothing, "CONDA_JL_HOME" => nothing, "CONDA_JL_USE_MINIFORGE" => "1") do
202+
withenv("CONDA_JL_VERSION" => nothing, "CONDA_JL_HOME" => nothing, "CONDA_JL_USE_MINIFORGE" => "1", "CONDA_JL_CONDA_EXE" => nothing) do
183203
Pkg.build("Conda")
204+
local ROOTENV=joinpath(condadir, "3")
205+
local CONDA_EXE=default_conda_exe(ROOTENV)
184206
@test read(depsfile, String) == """
185207
const ROOTENV = "$(escape_string(joinpath(condadir, "3")))"
186208
const MINICONDA_VERSION = "3"
187209
const USE_MINIFORGE = true
210+
const CONDA_EXE = "$(escape_string(CONDA_EXE))"
188211
"""
189212
end
190213
end
@@ -193,12 +216,15 @@ end
193216
@test !isfile(depsfile)
194217
@test !isfile(joinpath(condadir, "deps.jl"))
195218

196-
withenv("CONDA_JL_VERSION" => nothing, "CONDA_JL_HOME" => nothing, "CONDA_JL_USE_MINIFORGE" => "0") do
219+
withenv("CONDA_JL_VERSION" => nothing, "CONDA_JL_HOME" => nothing, "CONDA_JL_USE_MINIFORGE" => "0", "CONDA_JL_CONDA_EXE" => nothing) do
197220
Pkg.build("Conda")
221+
local ROOTENV=joinpath(condadir, "3")
222+
local CONDA_EXE=default_conda_exe(ROOTENV)
198223
@test read(depsfile, String) == """
199-
const ROOTENV = "$(escape_string(joinpath(condadir, "3")))"
224+
const ROOTENV = "$(escape_string(ROOTENV))"
200225
const MINICONDA_VERSION = "3"
201226
const USE_MINIFORGE = false
227+
const CONDA_EXE = "$(escape_string(CONDA_EXE))"
202228
"""
203229
end
204230
end
@@ -207,12 +233,14 @@ end
207233
@testset "custom home" begin
208234
preserve_build() do
209235
mktempdir() do dir
210-
withenv("CONDA_JL_VERSION" => "3", "CONDA_JL_HOME" => dir, "CONDA_JL_USE_MINIFORGE" => nothing) do
236+
withenv("CONDA_JL_VERSION" => "3", "CONDA_JL_HOME" => dir, "CONDA_JL_USE_MINIFORGE" => nothing, "CONDA_JL_CONDA_EXE" => nothing) do
211237
Pkg.build("Conda")
238+
local CONDA_EXE=default_conda_exe(dir)
212239
@test read(depsfile, String) == """
213240
const ROOTENV = "$(escape_string(dir))"
214241
const MINICONDA_VERSION = "3"
215242
const USE_MINIFORGE = $(CONDA_JL_USE_MINIFORGE_DEFAULT)
243+
const CONDA_EXE = "$(escape_string(CONDA_EXE))"
216244
"""
217245
end
218246
end
@@ -222,28 +250,37 @@ end
222250
@testset "version mismatch" begin
223251
preserve_build() do
224252
# Mismatch in written file
253+
local ROOTENV=joinpath(condadir, "3")
254+
local CONDA_EXE=default_conda_exe(ROOTENV)
225255
write(depsfile, """
226-
const ROOTENV = "$(escape_string(joinpath(condadir, "3")))"
256+
const ROOTENV = "$(escape_string(ROOTENV))"
227257
const MINICONDA_VERSION = "2"
228258
const USE_MINIFORGE = $(CONDA_JL_USE_MINIFORGE_DEFAULT)
259+
const CONDA_EXE = "$(escape_string(CONDA_EXE))"
229260
""")
230261

231-
withenv("CONDA_JL_VERSION" => nothing, "CONDA_JL_HOME" => nothing, "CONDA_JL_USE_MINIFORGE" => nothing) do
262+
withenv("CONDA_JL_VERSION" => nothing, "CONDA_JL_HOME" => nothing, "CONDA_JL_USE_MINIFORGE" => nothing, "CONDA_JL_CONDA_EXE" => nothing) do
232263
Pkg.build("Conda")
264+
local ROOTENV=joinpath(condadir, "2")
265+
local CONDA_EXE=default_conda_exe(ROOTENV)
233266
@test read(depsfile, String) == """
234-
const ROOTENV = "$(escape_string(joinpath(condadir, "2")))"
267+
const ROOTENV = "$(escape_string(ROOTENV))"
235268
const MINICONDA_VERSION = "2"
236269
const USE_MINIFORGE = $(CONDA_JL_USE_MINIFORGE_DEFAULT)
270+
const CONDA_EXE = "$(escape_string(CONDA_EXE))"
237271
"""
238272
end
239273

240274
# ROOTENV should be replaced since CONDA_JL_HOME wasn't explicitly set
241-
withenv("CONDA_JL_VERSION" => "3", "CONDA_JL_HOME" => nothing, "CONDA_JL_USE_MINIFORGE" => nothing) do
275+
withenv("CONDA_JL_VERSION" => "3", "CONDA_JL_HOME" => nothing, "CONDA_JL_USE_MINIFORGE" => nothing, "CONDA_JL_CONDA_EXE" => nothing) do
242276
Pkg.build("Conda")
277+
local ROOTENV=joinpath(condadir, "3")
278+
local CONDA_EXE=default_conda_exe(ROOTENV)
243279
@test read(depsfile, String) == """
244-
const ROOTENV = "$(escape_string(joinpath(condadir, "3")))"
280+
const ROOTENV = "$(escape_string(ROOTENV))"
245281
const MINICONDA_VERSION = "3"
246282
const USE_MINIFORGE = $(CONDA_JL_USE_MINIFORGE_DEFAULT)
283+
const CONDA_EXE = "$(escape_string(CONDA_EXE))"
247284
"""
248285
end
249286
end

0 commit comments

Comments
 (0)