Can I remove dependency on Pkg from my package using xxx_jll?

For some reason, I want to compile my package into binaries with PackageCompiler. It generally works fine, but compiled directories always include something like libgit2, which should be of no use to my package.
After some digging, I find the main reason to be the dependency on Pkg, which comes from many xxx_jll packages.

Let’s first have a look at how many dependencies Pkg with introduce:

[[deps.Pkg]]
deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"]
uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"

It can be understood that seems I want to compile my package into binaries to deliver, dependencies like Downloads (which then depends on LibCURL), LibGit2, REPL, p7zip_jll and so on are generally useless.
Besides, these dependencies will introduce vulnerabilities, like I have reported last year in this post. LibCURL and LibGit2 will both introduce vulnerabilities, like CVE-2021-22945, CVE-2018-25032, and so on. Yes, the latter is a zlib bug, but libgit2 depends on that. These vulnerabilities sometimes can be solved by simply remove the related files, but why we ever have them, when we don’t need them at all?

Then, the reason for this.
It seems that these packages are created by JLLWrappers, (through Yggdrasil, I suppose?) and are relevant with following codes:

macro generate_main_file_header(src_name)
    return excat(
        # Declare this module as interpreted
        generate_compiler_options(src_name),
        # import Artifacts module
        generate_imports(src_name),
    )
end
macro generate_main_file(src_name, pkg_uuid)
    return excat(
        # `is_available()` forward declaration, `PATH_list` and `LIBPATH_list` forward definitions,
        # `find_artifact_dir()` definition.
        generate_toplevel_definitions(src_name, __source__),
        # Select and load the best wrapper file
        generate_wrapper_load(src_name, pkg_uuid, __source__),
    )
end

Digging further, I find the following:

function generate_imports(src_name)
    # We lie a bit in the registry that JLL packages are usable on Julia 1.0-1.2.
    # This is to allow packages that might want to support Julia 1.0 to get the
    # benefits of a JLL package on 1.3 (requiring them to declare a dependence on
    # the JLL package in their Project.toml) but engage in heroic hacks to do
    # something other than actually use a JLL package on 1.0-1.2.  By allowing
    # this package to be installed (but not loaded) on 1.0-1.2, we enable users
    # to avoid splitting their package versions into pre-1.3 and post-1.3 branches
    # if they are willing to engage in the kinds of hoop-jumping they might need
    # to in order to install binaries in a JLL-compatible way on 1.0-1.2. One
    # example of this hoop-jumping being to express a dependency on this JLL
    # package, then import it within a `VERSION >= v"1.3"` conditional, and use
    # the deprecated `build.jl` mechanism to download the binaries through e.g.
    # `BinaryProvider.jl`.  This should work well for the simplest packages, and
    # require greater and greater heroics for more and more complex packages.
    @static if VERSION < v"1.3.0-rc4"
        return quote
            error("Unable to use $($(src_name))_jll on Julia versions older than 1.3!")
        end
    elseif VERSION < v"1.6.0-DEV"
        # Use slow Pkg-based Artifacts
        return quote
            using Libdl, Pkg, Pkg.BinaryPlatforms, Pkg.Artifacts
            using Pkg.Artifacts: load_artifacts_toml, unpack_platform
            using Pkg.BinaryPlatforms: triplet, select_platform
            HostPlatform() = platform_key_abi()
        end
    else
        # Use fast stdlib-based Artifacts + Preferences
        return quote
            using Libdl, Artifacts, JLLWrappers.Preferences, Base.BinaryPlatforms
            using Artifacts: load_artifacts_toml, unpack_platform
            using Base.BinaryPlatforms: triplet, select_platform
        end
    end
end

Thus, Pkg seems to be required for pre-1.6.0 versions only? However, because of the considerations of backward compatibility, we will always have Pkg as a dependency, at least before we move to julia 2.x?

Besides, some xxx_jll packages need LazyArtifacts, which in turn depends on Pkg. This also is of no meaning for compiled binaries, I suppose?

1 Like

Is there a way to define conditional dependencies (say, for julia<1.6), like in Python? I tried to check but couldn’t find anything.

Neat! So after this patch has been released, and probably after the jlls in question have been regenerated with that new version, we can expect smaller packagecompiler results? :grinning:

1 Like

Yes. Note that this is already in use in Yggdrasil, so you only need to wait for more and more packages to be updated.

1 Like

I just tried updating one of my projects, and indeed I could observe that some jll Manifest entries lost their Pkg dependency, nice!
I went from ~85 to 81 instances of this, so there is still a ways to go. :sweat_smile: