Skip to content

Instantly share code, notes, and snippets.

@peteristhegreat
Created June 4, 2019 16:12
Show Gist options
  • Save peteristhegreat/f4f727a12df900109485536efba9ee5a to your computer and use it in GitHub Desktop.
Save peteristhegreat/f4f727a12df900109485536efba9ee5a to your computer and use it in GitHub Desktop.
Julia Precompiled sys.so sys.dll sys.dylib PackageCompiler gotchas

Basic Setup

add PackageCompiler@0.5.1

If you are trying different julia versions, you need to move the folder where sys.dll ends up in so it does it brand new.

Every deps.jl in any dependent package that has a built library, such as MbedTLS.jl or Rmath.jl or SpecialFunctions.jl all need to be modified to not use absolute paths on the harddrive to make deployment managable.

Change them from the generated code that came out of BinaryDependancy.jl

libxxxxx = joinpath(currentpath_for_deps, "usr","lib","libxxx.dll")

to

libxxxxx = "libxxx.dll"

Now you will need to copy dlls into the runpath or dll search path for each place you are planning on running from.

The package will get built into the root_modules but won't get listed properly. To enable proper listing as a baked in package, you can either look it up using the UUID at runtime with something like:

MyCustomPackage = Base.root_module(Base.PkgId(Base.UUID("abcdef0123-xxxx-xxxx-xxxx-xxxxxxxxxxxx"), "MyCustomPackage"))

or you can spoof into the StdLibs. This feels more elegant for the end use of the function but is a little hacky up front. You have to put in a fake StdLib folder for your module, with an empty src folder, and make a simple project file.

A generic way to do that follows:

function make_package_placeholder_in_stdlib(pkg_name)
    stdlib_path = joinpath(dirname(Base.julia_cmd().exec[1]), "..", "share", "julia", "stdlib", "v"*string(VERSION)[1:3])
    # make the destination folder structure
    new_dir = joinpath(stdlib_path, pkg_name, "src")
    @show new_dir
    mkpath(new_dir)
    @assert isdir(new_dir)

    # write an empty MyPackage.jl file under src
    open(io->write(io, ""), joinpath(stdlib_path, pkg_name, "src", pkg_name * ".jl"), "w")
    # write a TOML file with just the package name and the uuid
    pkg_uuid = try
        # string(Base.module_keys[findfirst(x-> x.name == pkg_name, Base.module_keys)].uuid)
        Pkg.TOML.parsefile(joinpath(Pkg.API.dir(pkg_name),"Project.toml"))["uuid"]
    catch e
        @show findfirst(x-> x.name == pkg_name, Base.module_keys)
        error("Failed to find $pkg_name 's uuid in its Project.toml")
    end

    stdlib_project_toml_file = joinpath(stdlib_path, pkg_name, "Project.toml")

    # open(io->Pkg.TOML.print(io, Dict("name"=>pkg_name, "uuid"=>pkg_uuid)), stdlib_project_toml_file, "w")
    open(stdlib_project_toml_file, "w") do io
        Pkg.TOML.print(io, Dict("name"=>pkg_name, "uuid"=>pkg_uuid))
    end

    @show stdlib_project_toml_file
    @assert isfile(stdlib_project_toml_file)
end

BUT using this function will gimp julia/loading.jl later. So when you are done compiling, you should clean up and remove this spoofed folder.

You can now build a custom julia sys library using

PackageCompiler.compile_package("MyCustomPackage"; force = false, reuse = false, cpu_target = "x86-64")

Troubleshooting

If you fail to remove the spoofed folder in StdLib you end up with the following error when using Pkg.test

(MyCustomPackage) pkg> test MyCustomPackage
   Testing MyCustomPackage
 Resolving package versions...
WARNING: --output requested, but no modules defined during run
┌ Warning: The call to compilecache failed to create a usable precompiled cache file for MyCustomPackage [abcdef0123-xxxx-xxxx-xxxx-xxxxxxxxxxxx]
│   exception = ArgumentError: Invalid header in cache file /Users/username/.julia/compiled/v1.0/MyCustomPackage/xGNPz.ji.
└ @ Base loading.jl:969
ERROR: LoadError: KeyError: key MyCustomPackage [abcdef0123-xxxx-xxxx-xxxx-xxxxxxxxxxxx] not found
Stacktrace:
 [1] getindex at ./dict.jl:478 [inlined]
 [2] root_module at ./loading.jl:898 [inlined]
 [3] require(::Base.PkgId) at ./loading.jl:864
 [4] require(::Module, ::Symbol) at ./loading.jl:853
 [5] include at ./boot.jl:317 [inlined]
 [6] include_relative(::Module, ::String) at ./loading.jl:1044
 [7] include(::Module, ::String) at ./sysimg.jl:29
 [8] include(::String) at ./client.jl:392
 [9] top-level scope at none:0
in expression starting at /Users/username/development/packages/MyCustomPackage/test/runtests.jl:5
ERROR: Package MyCustomPackage errored during testing
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment