Skip to content

Commit c91148e

Browse files
Add HostBuildDependency (#43)
* Add HostBuildDependency Main changes: * we now have two "prefixes", one for the target and one for the host. To implement this, * `setup_workspace` takes in input the platforms of the target and the host, and it creates the symlink `/workspace/destdir` -> `/workspace/TARGET_TRIPLET/destdir` * the user will call `setup_dependencies` twice, once for the host and once for the target * a new helper function `destdir(prefix, platform)` gives consistently the path to the destdir for the given platform within the given prefix * added relevant environment variables `host_*dir` in the runners, and add `host_bindir` to the `PATH` * added `is_host_dependency`, `is_target_dependency`, `is_build_dependency`, `is_runtime_dependency` functions which are _very_ useful to filter dependencies in different situations: as we add more and more of these `AbstractDependency` types, there are multiple types matching each trait and it's hard to consistently list all of them in all places * Apply suggestions from code review Co-authored-by: Elliot Saba <staticfloat@gmail.com> Co-authored-by: Elliot Saba <staticfloat@gmail.com>
1 parent b699dcd commit c91148e

File tree

5 files changed

+154
-36
lines changed

5 files changed

+154
-36
lines changed

src/Dependencies.jl

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using UUIDs
22

3-
export Dependency, BuildDependency
3+
export Dependency, BuildDependency, HostBuildDependency,
4+
is_host_dependency, is_target_dependency, is_build_dependency, is_runtime_dependency
5+
46

57
# Pkg.PackageSpec return different types in different Julia versions so...
68
const PkgSpec = typeof(Pkg.PackageSpec(name="dummy"))
@@ -15,9 +17,40 @@ Concrete subtypes of `AbstractDependency` are
1517
and to load the generated JLL package.
1618
* [`BuildDependency`](@ref): a JLL package that is necessary only to build the
1719
package. This will not be a dependency of the generated JLL package.
20+
* [`HostBuildDependency`](@ref): similar to `BuildDependency`, but it will
21+
install the artifact for the host platform, instead of that for the target
22+
platform.
1823
"""
1924
abstract type AbstractDependency end
2025

26+
"""
27+
is_host_dependency(dep::AbstractDependency) -> Bool
28+
29+
Return whether `dep` is a dependency of the host platform or not.
30+
"""
31+
is_host_dependency
32+
33+
"""
34+
is_target_dependency(dep::AbstractDependency) -> Bool
35+
36+
Return whether `dep` is a dependency of the target platform or not.
37+
"""
38+
is_target_dependency(dep::AbstractDependency) = !is_host_dependency(dep)
39+
40+
"""
41+
is_build_dependency(dep::AbstractDependency) -> Bool
42+
43+
Return whether `dep` is a build-time dependency or not.
44+
"""
45+
is_build_dependency
46+
47+
"""
48+
is_runtime_dependency(dep::AbstractDependency) -> Bool
49+
50+
Return whether `dep` is a runtime dependency or not.
51+
"""
52+
is_runtime_dependency
53+
2154
"""
2255
Dependency(dep::Union{PackageSpec,String})
2356
@@ -31,6 +64,9 @@ struct Dependency <: AbstractDependency
3164
Dependency(pkg::PkgSpec, build_version = nothing) = new(pkg, build_version)
3265
end
3366
Dependency(dep::AbstractString, build_version = nothing) = Dependency(PackageSpec(; name = dep), build_version)
67+
is_host_dependency(::Dependency) = false
68+
is_build_dependency(::Dependency) = true
69+
is_runtime_dependency(::Dependency) = true
3470

3571
"""
3672
BuildDependency(dep::Union{PackageSpec,String})
@@ -43,6 +79,27 @@ struct BuildDependency <: AbstractDependency
4379
pkg::PkgSpec
4480
end
4581
BuildDependency(dep::AbstractString) = BuildDependency(PackageSpec(; name = dep))
82+
is_host_dependency(::BuildDependency) = false
83+
is_build_dependency(::BuildDependency) = true
84+
is_runtime_dependency(::BuildDependency) = false
85+
86+
"""
87+
HostBuildDependency(dep::Union{PackageSpec,String})
88+
89+
Define a binary dependency that is necessary only to build the package.
90+
Different from the [`BuildDependency`](@ref), the artifact for the host
91+
platform will be installed, instead of that for the target platform.
92+
93+
The argument can be either a string with the name of the JLL package or a
94+
`Pkg.PackageSpec`.
95+
"""
96+
struct HostBuildDependency <: AbstractDependency
97+
pkg::PkgSpec
98+
end
99+
HostBuildDependency(dep::AbstractString) = HostBuildDependency(PackageSpec(; name = dep))
100+
is_host_dependency(::HostBuildDependency) = true
101+
is_build_dependency(::HostBuildDependency) = true
102+
is_runtime_dependency(::HostBuildDependency) = false
46103

47104
getpkg(d::AbstractDependency) = d.pkg
48105
function getpkg(d::Dependency)
@@ -124,7 +181,7 @@ __version(v::Pkg.Types.VersionSpec) = v.ranges[1].lower
124181
version(d::AbstractDependency) = __version(getpkg(d).version)
125182
version(d::Dependency) = __version(d.pkg.version)
126183

127-
for (type, type_descr) in ((Dependency, "dependency"), (BuildDependency, "builddependency"))
184+
for (type, type_descr) in ((Dependency, "dependency"), (BuildDependency, "builddependency"), (HostBuildDependency, "hostdependency"))
128185
JSON.lower(d::type) = Dict("type" => type_descr,
129186
"name" => d.pkg.name,
130187
"uuid" => string_or_nothing(d.pkg.uuid),
@@ -137,16 +194,18 @@ end
137194
# dictionaries. This function converts the dictionary back to the appropriate
138195
# AbstractDependency.
139196
function dependencify(d::Dict)
140-
if d["type"] == "dependency"
197+
if d["type"] in ("dependency", "builddependency", "hostdependency")
141198
uuid = isnothing(d["uuid"]) ? d["uuid"] : UUID(d["uuid"])
142199
version = VersionNumber(d["version-major"], d["version-minor"], d["version-patch"])
143200
version = version == v"0" ? nothing : version
144-
return Dependency(PackageSpec(; name = d["name"], uuid = uuid, version = version))
145-
elseif d["type"] == "builddependency"
146-
uuid = isnothing(d["uuid"]) ? d["uuid"] : UUID(d["uuid"])
147-
version = VersionNumber(d["version-major"], d["version-minor"], d["version-patch"])
148-
version = version == v"0" ? nothing : version
149-
return BuildDependency(PackageSpec(; name = d["name"], uuid = uuid, version = version))
201+
T = if d["type"] == "dependency"
202+
Dependency
203+
elseif d["type"] == "builddependency"
204+
BuildDependency
205+
elseif d["type"] == "hostdependency"
206+
HostBuildDependency
207+
end
208+
return T(PackageSpec(; name = d["name"], uuid = uuid, version = version))
150209
else
151210
error("Cannot convert to dependency")
152211
end

src/Prefix.jl

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,9 @@ function setup(source::PatchSource, targetdir, verbose)
321321
open(f->write(f, source.patch), joinpath(patches_dir, source.name), "w")
322322
end
323323

324+
destdir(prefix, platform::AbstractPlatform) =
325+
joinpath(prefix, triplet(platform), "destdir")
326+
324327
"""
325328
setup_workspace(build_path::String, sources::Vector{SetupSource};
326329
verbose::Bool = false)
@@ -332,7 +335,9 @@ the environment variables that will be defined within the sandbox environment.
332335
This method returns the `Prefix` to install things into, and the runner
333336
that can be used to launch commands within this workspace.
334337
"""
335-
function setup_workspace(build_path::AbstractString, sources::Vector;
338+
function setup_workspace(build_path::AbstractString, sources::Vector,
339+
target_platform::AbstractPlatform,
340+
host_platform::AbstractPlatform=default_host_platform;
336341
verbose::Bool = false)
337342
# Use a random nonce to make detection of paths in embedded binary easier
338343
nonce = randstring()
@@ -341,10 +346,14 @@ function setup_workspace(build_path::AbstractString, sources::Vector;
341346

342347
# We now set up two directories, one as a source dir, one as a dest dir
343348
srcdir = joinpath(workspace, "srcdir")
344-
destdir = joinpath(workspace, "destdir")
349+
target_destdir = destdir(workspace, target_platform)
350+
host_destdir = destdir(workspace, host_platform)
345351
metadir = joinpath(workspace, "metadir")
346-
wrapperdir = joinpath(workspace, "compiler_wrappers")
347-
mkdir.((srcdir, destdir, metadir))
352+
mkpath.((srcdir, target_destdir, host_destdir, metadir))
353+
# Create the symlink /workspace/destdir -> /workspace/TARGET_TRIPLET/destdir
354+
# Necessary for compatibility with recipes that hardcode `/workspace/destdir` in them,
355+
# as well as `.pc` files that contain absolute paths to `/workspace/destdir/...`
356+
symlink("$(triplet(target_platform))/destdir", joinpath(workspace, "destdir"))
348357

349358
# Setup all sources
350359
for source in sources
@@ -428,8 +437,8 @@ function setup_dependencies(prefix::Prefix, dependencies::Vector{PkgSpec}, platf
428437
# We're going to create a project and install all dependent packages within
429438
# it, then create symlinks from those installed products to our build prefix
430439

431-
mkpath(joinpath(prefix, "artifacts"))
432-
deps_project = joinpath(prefix, ".project")
440+
mkpath(joinpath(prefix, triplet(platform), "artifacts"))
441+
deps_project = joinpath(prefix, triplet(platform), ".project")
433442
Pkg.activate(deps_project) do
434443
# Update registry first, in case the jll packages we're looking for have just been registered/updated
435444
ctx = Pkg.Types.Context(;julia_version = julia_version)
@@ -491,7 +500,7 @@ function setup_dependencies(prefix::Prefix, dependencies::Vector{PkgSpec}, platf
491500

492501
# Copy the artifact from the global installation location into this build-specific artifacts collection
493502
src_path = Pkg.Artifacts.artifact_path(artifact_hash)
494-
dest_path = joinpath(prefix, "artifacts", basename(src_path))
503+
dest_path = joinpath(prefix, triplet(platform), "artifacts", basename(src_path))
495504
cp(src_path, dest_path)
496505

497506
# Keep track of our dep paths for later symlinking
@@ -501,7 +510,7 @@ function setup_dependencies(prefix::Prefix, dependencies::Vector{PkgSpec}, platf
501510

502511
# Symlink all the deps into the prefix
503512
for art_path in artifact_paths
504-
symlink_tree(art_path, joinpath(prefix, "destdir"))
513+
symlink_tree(art_path, destdir(prefix, platform))
505514
end
506515

507516
# Return the artifact_paths so that we can clean them up later
@@ -510,7 +519,13 @@ end
510519

511520
function cleanup_dependencies(prefix::Prefix, artifact_paths)
512521
for art_path in artifact_paths
513-
unsymlink_tree(art_path, joinpath(prefix, "destdir"))
522+
# Unsymlink all destdirs within the prefix
523+
for dir in readdir(prefix.path; join=true)
524+
destdir = joinpath(dir, "destdir")
525+
if isdir(destdir)
526+
unsymlink_tree(art_path, destdir)
527+
end
528+
end
514529
end
515530
end
516531

src/Runner.jl

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
import Base: strip
22
abstract type Runner; end
33

4+
export default_host_platform
5+
46
# Host platform _must_ match the C++ string ABI of the binaries we get from the
57
# repositories. Note: when preferred_gcc_version=v"4" we can't really build for
68
# that C++ string ABI :-(
9+
"""
10+
default_host_platform
11+
12+
The default host platform in the build environment.
13+
"""
714
const default_host_platform = Platform("x86_64", "linux"; libc="musl", cxxstring_abi="cxx11")
815

916
function nbits(p::AbstractPlatform)
@@ -741,11 +748,17 @@ function platform_envs(platform::AbstractPlatform, src_name::AbstractString;
741748

742749
# Prefix, libdir, etc...
743750
prefix = "/workspace/destdir"
751+
host_prefix = "/workspace/$(triplet(host_platform))/destdir"
744752
if Sys.iswindows(platform)
745753
libdir = "$(prefix)/bin"
746754
else
747755
libdir = "$(prefix)/lib"
748756
end
757+
if Sys.iswindows(host_platform)
758+
host_libdir = "$(host_prefix)/bin"
759+
else
760+
host_libdir = "$(host_prefix)/lib"
761+
end
749762

750763
if get(stdout, :color, false)
751764
PS1 = string(
@@ -795,6 +808,12 @@ function platform_envs(platform::AbstractPlatform, src_name::AbstractString;
795808
"libdir" => libdir,
796809
"includedir" => "$(prefix)/include",
797810

811+
# Set variables for the host prefix
812+
"host_prefix" => host_prefix,
813+
"host_bindir" => "$(host_prefix)/bin",
814+
"host_libdir" => host_libdir,
815+
"host_includedir" => "$(host_prefix)/include",
816+
798817
# Fancyness!
799818
"USER" => get(ENV, "USER", "julia"),
800819
# Docker filters out `PS1` so we route around it
@@ -862,8 +881,10 @@ function platform_envs(platform::AbstractPlatform, src_name::AbstractString;
862881
"/opt/$(host_target)/bin",
863882
# Default alpine PATH
864883
mapping["PATH"],
884+
# Host tools, installed as `HostBuildDependency`
885+
mapping["host_bindir"],
865886
# Finally, dependency tools
866-
"$(prefix)/bin",
887+
mapping["bindir"],
867888
), ":"),
868889

869890
"LD_LIBRARY_PATH" => join((

0 commit comments

Comments
 (0)