Offline installation of Julia packages

Hello,

I need to install a bunch of packages on an offline computer ( it must be disconnected ).

I didn’t found any guide that provide a clean way for installing packages offline and maybe someone could help me with that.

I tried this :

  1. For each packages, download its github repo ( not with git clone ) and store it somewhere at local/path/pkg_n
  2. Add the local/path/pkg_n to these packages via Pkg

But when adding these local path, Pkg return this :

(v1.1) pkg> add local\path\pkg_1
Updating registry at ~\.julia\registries\General
Updating git-repo https://github.com/JuliaRegistries/General.git
Cloning git-repo local\path\pkg_1\
ERROR: Git repository not found at ‘local\path\pkg_1’

So the local path must be a git repo.

Then I tried with dev since it will create automatically a repo and update julia environnement Manifest.toml and Project.toml files.

For packages with a lot of dependencies, I could do this on all the packages in the dependency graph that are not already installed on hope for the best, but I haven’t tried yet.

Does anyone have any idea on how to do this properly ?

Thank you in advance !

1 Like

I think currently there is no way of doing that, see


However, I think you can try installing in another computer, and then copy the ~/.julia directory to the offline computer. Sometimes it gives problem by differences in the systems, but it is the simpler way. I suggest you to test it.

Well with this solution the problem is that I can only use packages built on a windows OS, and since the offline computer is using debian I think it is going to be tricky .

I might get around that by using Pkg.develop and configuring myself the dependency like I would do for a private package.

It is going to be cumbersome but I guess I don’t have another choice.

Use a docker container.

3 Likes

@gdestouet You might look at Julia Team https://juliacomputing.com/products/juliateam.html

Thank you for your response.
So if ever want to add or upgrade a package, I will have to build a docker image and reinstall it ? It seems a bit cumbersome at the beginning but I will give it a try.

Did you do this using ‘git clone’ ? Please give us an example of the package you wish to install and the command you used to create the clone. Thanks!

1 Like

You can automate it. Also, offline environments are already cumbersome, the best you can do is to come up with a workflow that mitigates this to some extent.

1 Like

For a simple test I tried with Unitful.jl, I wasn’t using git clone for this but only the decompressed archive.

With git clone it works well when using Pkg.add, I am going to do this for any packages I need.

I will do this with a package with many dependencies and come back with what I did.

Unfortunately I can’t use Docker Images and I would prefer to build packages on the offline machine, so here is what I did :

Let say that I have a project on a computer connected to internet that I want to export and install on an offline computer along with its dependencies ( outside Julia’s stdlib ).

For this project I have the canonical structure :

path/to/Project_test
│   Manifest.toml
│   Project.toml
└───src/
        Project_Test.jl
        *.jl

I make sure that all dependencies of Project_Test are installed and present in Manifest.toml and that it is running properly. And that both computers have the same project with the same dependencies.

Instead of copying all the packages from the default depot path (.julia/packages/...) to julia depot path on the offline computer. The following function ( a flattened and pruned version of Pkg.Types.add_or_develop and Pkg.Operates.add_or_develop combined ) will download all the packages needed and store it on the external_device at path_to_external . It will keep the structure used in the default depot path ( version-dependent ).It will also copy the registry to avoid Pkg.Types.manifest_info to call for a remote clone of the default registry while installing packages on the offline computer.

import Pkg.Types: PackageSpec,GitRepo,Context,project_deps_resolve!,registry_resolve!,stdlib_resolve!,ensure_resolved,manifest_info,write_env,update_registries,pkgerror,printpkgstyle
import Pkg.activate
import Pkg.Operations:resolve_versions!,version_data!,install_archive
import Pkg.BinaryProvider
using UUIDs
import LibGit2

function export_packages(path_to_project,path_to_external)
    activate(path_to_project)
    ctx=Context()
    project = ctx.env.project

    pkgs = [ PackageSpec(k,v) for (k,v) in project.deps ]

    project_deps_resolve!(ctx.env,pkgs)
    registry_resolve!(ctx.env,pkgs)
    stdlib_resolve!(ctx,pkgs)
    ensure_resolved(ctx.env,pkgs)
    for pkg in pkgs
        ctx.env.project.deps[pkg.name] = pkg.uuid
    end

    for (name::String, uuid::UUID) in ctx.env.project.deps
        entry = manifest_info(ctx.env, uuid)
        entry !== nothing && entry.version !== nothing || continue
        version = VersionNumber(entry.version)
        for pkg in pkgs
            pkg.uuid == uuid && version ∈ pkg.version || continue
            pkg.version = version
        end
    end

    resolve_versions!(ctx, pkgs)
    hashes, urls = version_data!(ctx, pkgs)

    for pkg in pkgs
        pkg.uuid in keys(ctx.stdlibs) && continue
        pkg.repo = GitRepo(urls[pkg.uuid][1],nothing,nothing)
    end

    BinaryProvider.probe_platform_engines!()
    pkgs_to_install = Tuple{PackageSpec, String}[]
    for pkg in pkgs
        pkg.uuid in keys(ctx.stdlibs) && continue
        path = joinpath(path_to_external,pkg.name,Base.version_slug(pkg.uuid,hashes[pkg.uuid]))
        push!(pkgs_to_install, (pkg, path))
    end

    widths = [textwidth(pkg.name) for (pkg, _) in pkgs_to_install]
    max_name = length(widths) == 0 ? 0 : maximum(widths)


    results = Channel(length(pkgs));
    i=1
    for (pkg, path) in pkgs_to_install
        print("$i over $(length(pkgs_to_install)) downloaded packages\r")
        try
            success = install_archive(urls[pkg.uuid], hashes[pkg.uuid], path)
            if ctx.use_only_tarballs_for_downloads && !success
                pkgerror("failed to get tarball from $(urls[pkg.uuid])")
            end
            put!(results, (pkg, success, path))
        catch err
            put!(results, (pkg, err, catch_backtrace()))
        end
        i+=1
    end

    missed_packages = Tuple{PackageSpec, String}[]
    for i in 1:length(pkgs_to_install)
        pkg, exc_or_success, bt_or_path = take!(results)
        exc_or_success isa Exception && pkgerror("Error when installing package $(pkg.name):\n",
                                                 sprint(Base.showerror, exc_or_success, bt_or_path))
        success, path = exc_or_success, bt_or_path
        if success
            vstr = pkg.version != nothing ? "v$(pkg.version)" : "[$h]"
            printpkgstyle(ctx, :Installed, string(rpad(pkg.name * " ", max_name + 2, "─"), " ", vstr))
        else
            push!(missed_packages, (pkg, path))
        end
    end

    for (pkg, path) in missed_packages
        uuid = pkg.uuid
        if !ctx.preview
            install_git(ctx, pkg.uuid, pkg.name, hashes[uuid], urls[uuid], pkg.version::VersionNumber, path)
        end
        vstr = pkg.version != nothing ? "v$(pkg.version)" : "[$h]"
        @info "Installed $(rpad(pkg.name * " ", max_name + 2, "─")) $vstr"
    end

end

Once the packages are stored on the external device. I used the following script to install and build them on the offline computer.

import Pkg:activate,depots1
import Pkg.Types:Context
import Pkg.Operations:build_versions

function add_local_packagesv11(pkgs_path,project_path)
    activate(project_path)
    ctx=Context()
    uuids_to_build=collect(keys(ctx.env.manifest))
    files=Set(readdir(pkgs_path))
    registry_path = joinpath(depots1(),"registries")
    Base.cp(joinpath(pkgs_path,pop!(files,"registries")),registry_path,force=true)
    default_path=joinpath(depots1(),"packages")
    for file in files
        path=joinpath(pkgs_path,file)
        if isdir(path)
            Base.cp(path,joinpath(default_path,file),force=true)
        end
    end
    build_versions(ctx,uuids_to_build)
end

project_path = "/path/to/Project_Test"
pkgs_path= "/path/to/external_device/pkgs"
add_local_packagesv11(pkgs_path,project_path)

Works well if the packages used doesn’t require internet to build, in which case you will have to configure the deps/build.jl scripts.
For example, if I want to build DSP on the offline computer I will have to modify its build.jl and download the specific FFTW library it needs.
Whereas, Conda builds well offline.

Tested with a project with ~40 dependencies on Julia 1.1.0.

3 Likes

I have created a Manifest.toml file and a Project.toml file using

pkg> add SQLite JLD Printf

and then I was able to use your export_packages function to create a directory of all the files and dependencies. Unfortunately the function add_local_packagesv11 errors on the line

Base.cp(joinpath(pkgs_path,pop!(files,"registries")),registry_path,force=true)

I never created a registries file and am not sure how. Can you provide some help on what i’m missing?

Thanks,

I might have done some modifications. I will put here the two scripts I use :

The first one for exporting a zip file containing all the packages useful for a given project

import Pkg.Types: PackageSpec,GitRepo,Context,project_deps_resolve!,registry_resolve!,stdlib_resolve!,ensure_resolved,manifest_info,write_env,update_registries,pkgerror,printpkgstyle
import Pkg.activate
import Pkg.Operations:resolve_versions!,version_data!,install_archive
import Pkg.BinaryProvider
import Pkg:depots1
using UUIDs
import LibGit2

function main(path_to_project,path_to_external)
    !isdir(path_to_external) ? mkdir(path_to_external) : nothing
    activate(path_to_project)
    ctx=Context()
    println("Project Path : " * path_to_project)
    println("Using : " * ctx.env.manifest_file * "\n"
           *"        " * ctx.env.project_file)
    println("Copying these files to " * path_to_external)
    Base.cp(ctx.env.project_file,joinpath(path_to_external,basename(ctx.env.project_file)),force=true)
    Base.cp(ctx.env.manifest_file,joinpath(path_to_external,basename(ctx.env.manifest_file)),force=true)
    project = ctx.env.project
    registry_path = joinpath(depots1(),"registries")
    registry_path_extern = joinpath(path_to_external,"registries")
    mkpath(registry_path_extern)
    Base.cp(registry_path,registry_path_extern,force=true)

    pkgs = [ PackageSpec(k,v) for (k,v) in project.deps ]

    project_deps_resolve!(ctx.env,pkgs)
    registry_resolve!(ctx.env,pkgs)
    stdlib_resolve!(ctx,pkgs)
    ensure_resolved(ctx.env,pkgs)
    for pkg in pkgs
        ctx.env.project.deps[pkg.name] = pkg.uuid
    end

    for (name::String, uuid::UUID) in ctx.env.project.deps
        entry = manifest_info(ctx.env, uuid)
        entry !== nothing && entry.version !== nothing || continue
        version = VersionNumber(entry.version)
        for pkg in pkgs
            pkg.uuid == uuid && version ∈ pkg.version || continue
            pkg.version = version
        end
    end

    resolve_versions!(ctx, pkgs)
    hashes, urls = version_data!(ctx, pkgs)
    for pkg in pkgs
        pkg.uuid in keys(ctx.stdlibs) && continue
        pkg.repo = GitRepo(urls[pkg.uuid][1],nothing,nothing)
    end

    BinaryProvider.probe_platform_engines!()
    pkgs_to_install = Tuple{PackageSpec, String}[]
    for pkg in pkgs
        pkg.uuid in keys(ctx.stdlibs) && continue
        path = joinpath(path_to_external,pkg.name,Base.version_slug(pkg.uuid,hashes[pkg.uuid]))
		if !isdir(path)
			push!(pkgs_to_install, (pkg, path))
		end
    end

    widths = [textwidth(pkg.name) for (pkg, _) in pkgs_to_install]
    max_name = length(widths) == 0 ? 0 : maximum(widths)

    results = Channel(length(pkgs));
    i=1
    for (pkg, path) in pkgs_to_install
        print("     $i over $(length(pkgs_to_install)) downloaded packages\n
                    Currently processing $(pkg)\r")
        try
            success = install_archive(urls[pkg.uuid], hashes[pkg.uuid], path)
            if ctx.use_only_tarballs_for_downloads && !success
                pkgerror("failed to get tarball from $(urls[pkg.uuid])")
            end
            put!(results, (pkg, success, path))
        catch err
            put!(results, (pkg, err, catch_backtrace()))
        end
        i+=1
    end

    missed_packages = Tuple{PackageSpec, String}[]
    for i in 1:length(pkgs_to_install)
        pkg, exc_or_success, bt_or_path = take!(results)
        exc_or_success isa Exception && pkgerror("Error when installing package $(pkg.name):\n",
                                                 sprint(Base.showerror, exc_or_success, bt_or_path))
        success, path = exc_or_success, bt_or_path
        if success
            vstr = pkg.version != nothing ? "v$(pkg.version)" : "[$h]"
            printpkgstyle(ctx, :Installed, string(rpad(pkg.name * " ", max_name + 2, "─"), " ", vstr))
        else
            push!(missed_packages, (pkg, path))
        end
    end

    for (pkg, path) in missed_packages
        uuid = pkg.uuid
        if !ctx.preview
            install_git(ctx, pkg.uuid, pkg.name, hashes[uuid], urls[uuid], pkg.version::VersionNumber, path)
        end
        vstr = pkg.version != nothing ? "v$(pkg.version)" : "[$h]"
        @info "Installed $(rpad(pkg.name * " ", max_name + 2, "─")) $vstr"
    end

end

path_to_project = "your_project_path"
path_to_external = "where_to_export"
main(path_to_project,path_to_external)
## Optional, create a .zip with all the packages
zip_exe = "path_to_zip_exe"
zip_name = abspath(joinpath(path_to_external,"../julia_packages.zip"))
command = `cmd /c $(zip_exe) u -tzip  $(zip_name) $(path_to_project) $(path_to_external)`
run(command)

Now that you have your .zip file or the folder with all the packages, you can put this on the offline computer and make the installation with the following script

import Pkg:activate,depots1
import Pkg.Types:Context
import Pkg.Operations:build_versions

function add_local_packagesv11(pkgs_path,project_path)
    activate(project_path)
    ctx=Context()
    uuids_to_build=collect(keys(ctx.env.manifest))
    files=Set(readdir(pkgs_path))
    pop!(files,"Manifest.toml")
    pop!(files,"Project.toml")
    registry_path = joinpath(depots1(),"registries")
    Base.cp(joinpath(pkgs_path,pop!(files,"registries")),registry_path,force=true)
    default_path=joinpath(depots1(),"packages")
    for file in files
        	path = joinpath(pkgs_path,file)
	        path_project = joinpath(default_path,file)
        	versions_pkgs = Set(readdir(path))
	        versions_project = isdir(path_project) ? Set(readdir(path_project)) : Set()
		for version in setdiff(versions_pkgs,versions_project)
			path_version = joinpath(path_project,version)
			path_pkg_version = joinpath(path,version)
			mkpath(path_version)
			Base.cp(path_pkg_version,path_version,force=true)
		end
    end
    build_versions(ctx,uuids_to_build)
end

pkgs_path = "where_the_exported_packages_are"
project_path = "path_to_project"
pkgs_path= joinpath(pkgs_path,project_path)
add_local_packagesv11(pkgs_path,project_path)

This script only install new packages or newer versions, if you want to override a package you can manually delete it or slightly modify this script with options.

Try with these scripts and let me know if you still have problems. Also, some packages need to be built with libraries, you will need to look in build.jl files for download links and put libraries under pkg_being_built/deps/usr/downloads ( or sometimes pkg_being_built/deps/usr ) and make sure that during build the build.jl doesnt delete the library you just manually installed ( you can pass a cat build.jl | grep rm to check this ).

Hope this help.

3 Likes

Hello !

A repo on github is available here : JuliaOffline
Currently, three scripts are available :

  1. For downloading packages source files
  2. One that parses build.jl files generated by BinaryProvider and download the right binaries according to the architecture of the offline computer
  3. the last one automatically installs packages from step 1 and builds the acquired binaries from step 2 on the offline computer

It stills needs some testing and improvements so feel free to propose your own code.

5 Likes

Thanks for sharing this code, this is very useful! I am in the situation where I don’t have internet access on the system that generates the depot. Building on your code I wrote a function to create a depot for the packages in a given environment. This allows you to derive a small depot for a specific application from a larger depot. It also has the benefit to include binary dependencies.


using Pkg.Types: Context,project_deps_resolve!,registry_resolve!,stdlib_resolve!,ensure_resolved,manifest_info,write_env,update_registries,pkgerror,printpkgstyle
using Pkg
using Pkg.TOML
using Pkg.Operations:resolve_versions!,version_data!
using UUIDs

"""
    copypackages(ctx,pkgs,dest_depot_path) -> allpkgs

Copy packages `pkgs` to `dest_depot_path`. Returns `allpkgs`, which
contains the original packages and all it dependecies, as copied.
"""
function copypackages(ctx,pkgs,dest_depot_path)
  pkgs = copy(pkgs)

  # Resolve dependencies and version
  project_deps_resolve!(ctx.env,pkgs)
  registry_resolve!(ctx.env,pkgs)
  stdlib_resolve!(ctx,pkgs)
  ensure_resolved(ctx.env,pkgs)
  resolve_versions!(ctx, pkgs)

  # Copy to destination
  max_name = maximum(textwidth(pkg.name) for pkg in pkgs)
  src_package_dir = joinpath(Pkg.depots1(),"packages")
  dest_package_dir = joinpath(dest_depot_path,"packages")
  hashes,_ = version_data!(ctx, pkgs)
  for pkg in pkgs
    pkg.uuid ∈ keys(ctx.stdlibs) && continue
    slug = Base.version_slug(pkg.uuid,hashes[pkg.uuid])
    srcdir = joinpath(Pkg.depots1(),"packages",pkg.name,slug)
    dstdir = joinpath(dest_depot_path,"packages",pkg.name,slug)
    mkpath(dstdir)
    cp(srcdir,dstdir,force=true)
    vstr = isnothing(pkg.version) ? "[-]" : "v$(pkg.version)" 
    printpkgstyle(ctx, :Packaged, string(rpad(pkg.name * " ", max_name + 2, "─"), " ", vstr))
  end
  pkgs
end

"""
    packagedepot(env_path,dest_depot_path;startup="",registry_repo_only=false) -> zipfile

Generate depot out of environment `env_path` and store in depot
directory `dest_depot_path`.
`startup` may contain a startup script. If `registry_repo_only` is `true`, only
store the registry depot `registries/General/.git`. This considerably accelerates the (un)zip,
but requires git to be present on the system where the depot is deployed. 
"""
function packagedepot(env_path,dest_depot_path;startup="",registry_repo_only=false)
  printstyled("Creating depot from environment\n$env_path\n",color=:blue)

  isdir(dest_depot_path) || mkpath(dest_depot_path)
  Pkg.activate(env_path)

  ctx = Context()

  # Packages
  pkgs = [PackageSpec(k,v) for (k,v) in ctx.env.project.deps]
  copypackages(ctx,pkgs,dest_depot_path)
  printstyled("✓ packages\n",color=:blue)

  # Project.toml and Manifest.toml
  v = VERSION
  env_dest_path = joinpath(dest_depot_path,"environments","v$(v.major).$(v.minor)")
  mkpath(env_dest_path)
  cp(ctx.env.project_file,joinpath(env_dest_path,basename(ctx.env.project_file)),force=true)
  cp(ctx.env.manifest_file,joinpath(env_dest_path,basename(ctx.env.manifest_file)),force=true)
  printstyled("✓ Project.toml/Manifest.toml\n",color=:blue)

  # Registry
  registry_path = joinpath(Pkg.depots1(),"registries")
  dest_registry_path = joinpath(dest_depot_path,"registries")
  if registry_repo_only
    registry_path,dest_registry_path = joinpath.([registry_path,dest_registry_path],"General",".git")
  end
  mkpath(dest_registry_path)
  cp(registry_path,dest_registry_path,force=true)
  msg = "✓ registry"
  registry_repo_only && (msg *= " - git repo only")
  printstyled(msg*"\n",color=:blue)

  # Add config/startup.jl
  if !isempty(startup)
    configdir = joinpath(dest_depot_path,"config")
    mkpath(configdir)
    write(joinpath(configdir,"startup.jl"),startup)
    printstyled("✓ startup.jl\n",color=:blue)
  end

  # Archive to zip file
  zipfile_fullpath = dest_depot_path*".zip"
  rm(zipfile_fullpath,force=true)
  zipfile = basename(zipfile_fullpath)
  command = `zip -r -q -o $zipfile .`
  cd(dest_depot_path) do   
    run(command)
    mv(zipfile,joinpath("..",zipfile))
  end
  # @time run(pipeline(command,stdout=Pipe()))
  printstyled("✓ zip file\n$zipfile_fullpath\n",color=:blue)

  zipfile
end
1 Like

With the introduction of Artifacts in Pkg for Julia v1.3+, this simple script does the work, by calling julia with julia --project=/path/to/project

using Pkg
# On the host machine, call the following line to get the right 'machine' string corresponding to the host (here a Debian linux machine)
#machine=string(Sys.MACHINE,BinaryPlatforms.compiler_abi_str(BinaryPlatforms.detect_compiler_abi(platform_key_abi(Sys.MACHINE))))

machine="x86_64-pc-linux-gnu-libgfortran4-cxx11" # See above
platform = Pkg.BinaryPlatforms.platform_key_abi(machine)

path_to_project = # Fill this
path_to_external = # Fill this
ispath(path_to_external) || mkpath(path_to_external)
empty!(DEPOT_PATH)
push!(DEPOT_PATH,path_to_external)

function main(path_to_project,path_to_external,platform)
    Pkg.activate(path_to_project)
    ctx=Pkg.Types.Context()
    println("Project Path : " * path_to_project)
    println("Using : " * ctx.env.manifest_file * "\n"
           *"        " * ctx.env.project_file)
    println("Installing to " * path_to_external)
    println("Platform $platform")
    Pkg.instantiate(ctx;platform=platform,verbose=true)
end


main(path_to_project,path_to_external,platform)

It creates a new depot at path_to_external which can then be exported on the host machine.
On the host machine, you have to run JULIA_DEPOT_PATH=/path/to/exported/depot julia and launch using Pkg; Pkg.build().

You might have some problems with not found libraries, but don’t worry it is generally just a symlink/renaming problem.

Note that if your project use Conda/PyCall, it won’t install a proper python distribution corresponding to your machine, you have to install it manually and specify the CONDA_JL_HOME environment variable.

For Plots.jl with GR (unfortunately not using Pkg.Artifacts for the moment) you have to directly download the library at https://github.com/sciapp/gr/releases, install it in GR/###/deps/downloads and remove this line in GR/###/deps/build.jl

5 Likes

I used your script succesfully and moved the extracted files from the computer with internet access to the offline computer.

But when I tried to build the packages on the offline computer it still wants to check online:

julia> using Pkg; Pkg.build()
   Updating registry at `C:\Julia_Depot\registries\General`
┌ Warning: could not download https://pkg.julialang.org/registries
└ @ Pkg.Types C:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.5\Pkg\src\Types.jl:951
No Changes to `C:\Julia_Depot\environments\v1.5\Project.toml`
No Changes to `C:\Julia_Depot\environments\v1.5\Manifest.toml`

I don’t know how to go further. Do you have a suggestion to what I could try?


I think it works now. Thank you wulpuqu and ericphanson! I was just extracting the wrong library. Also the Pkg.offline() (JULIA_PKG_OFFLINE set to true in envionment variables) helped a lot, so I was not looking for the wrong problem.

One thing I need to do, was to copy Manifest.toml and Project.toml from the source library to environments/1.5/ (in “<user_folder>/.julia”).

You can use Pkg.offline(); I can’t find it in the Pkg manual, but you can see the code and docstring here: https://github.com/JuliaLang/Pkg.jl/blob/2029bee7e94140aa0614f7ec4077042c419a2fe9/src/Pkg.jl#L441

3 Likes

Thanks for your script! It’s exactly what i have been looking for.

It seems there have been some changes to BinaryPlatforms though.
On the host machine do the following

julia -e 'println(Base.BinaryPlatforms.triplet(Base.BinaryPlatforms.HostPlatform()))'

and copy the platform string that is returned.

The script from above then becomes

using Pkg

# copy platform string of the target machine from command above
platform_str = "x86_64-linux-gnu-libgfortran4-cxx11-libstdcxx26-julia_version+1.6.1"
platform = Base.BinaryPlatforms.parse( Base.BinaryPlatforms.Platform, platform_str )

path_to_project = # Fill this
path_to_external = # Fill this
ispath(path_to_external) || mkpath(path_to_external)
empty!(DEPOT_PATH)
push!(DEPOT_PATH,path_to_external)

function main(path_to_project,path_to_external,platform)
    Pkg.activate(path_to_project)
    ctx=Pkg.Types.Context()
    println("Project Path : " * path_to_project)
    println("Using : " * ctx.env.manifest_file * "\n"
           *"        " * ctx.env.project_file)
    println("Installing to " * path_to_external)
    println("Platform $platform")
    Pkg.instantiate(ctx;platform=platform,verbose=true)
end


main(path_to_project,path_to_external,platform)

Edit: In the above script, you can also use this function

function add_package(name)
    Pkg.activate(tempname())
    ctx=Pkg.Types.Context()
    parsed_name = Pkg.REPLMode.parse_package_identifier(name; add_or_develop = true)
    Pkg.add(ctx, [parsed_name,]; platform=platform)
end

to download a package and its dependencies on the host machine into the DEPOT_PATH.
It works with git urls, too.

3 Likes

Is there any particular reason to activate the environment already from the command line with ˋjulia --project …ˋ, or wouldn’t a later ˋPkg> activate …ˋ serve the same purpose?

They serve the same purpose. The choice is entirely up to what you find most convenient. For my part I usually have the current directory of the shell at the project when I work with something and then activating it is just julia --project. If I restart Julia I just need to do arrow up and return in the shell to get the project activated again.