Skip to content

Commit 4469d64

Browse files
authored
Better build system robustness (#87)
* add dev_tools.jl. Start to enforce proper cuda toolkit usage. In progress * add CUDA_SDK_jll as a direct dependency allows for correct paths * add a workspaces and ./dev for CUDA_SDK_jll * update cuda tool pathing * dev override functionality * update to support cunumeric * mimic cunumeric's ci runs into branches like develop * modify set_jll_artifact_override * guard docs on wrapper changes * update version semantics to major.minor
1 parent 682ebb9 commit 4469d64

14 files changed

Lines changed: 378 additions & 140 deletions

File tree

.github/workflows/ci.yml

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ on:
1616
- 'src/**'
1717
- 'scripts/**'
1818
- 'deps/build.jl'
19+
- 'deps/buildtools/**'
1920
- 'Project.toml'
2021
- 'lib/LegatePreferences/src/**'
2122
- '.github/workflows/ci.yml'
@@ -29,17 +30,30 @@ on:
2930
- 'src/**'
3031
- 'scripts/**'
3132
- 'deps/build.jl'
33+
- 'deps/buildtools/**'
3234
- 'Project.toml'
3335
- 'lib/LegatePreferences/src/**'
3436
- '.github/workflows/ci.yml'
3537
- 'Dockerfile' # container.yml depends on this
3638
jobs:
37-
pkg_resolve:
38-
uses: ./.github/workflows/pkg_resolve.yml
39+
check_changes:
40+
name: Check for wrapper changes
41+
runs-on: ubuntu-latest
42+
outputs:
43+
wrapper_changed: ${{ steps.filter.outputs.wrapper }}
44+
steps:
45+
- uses: actions/checkout@v4
46+
- uses: dorny/paths-filter@v3
47+
id: filter
48+
with:
49+
filters: |
50+
wrapper:
51+
- 'lib/legate_jl_wrapper/**'
3952
4053
test:
4154
name: Julia ${{ matrix.julia }} - ${{ matrix.os }}
42-
needs: pkg_resolve
55+
needs: check_changes
56+
if: ${{ github.base_ref == 'main' || needs.check_changes.outputs.wrapper_changed != 'true' }}
4357
runs-on: ${{ matrix.os }}
4458
strategy:
4559
fail-fast: false

.github/workflows/developer.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ on:
1818
- 'src/**'
1919
- 'scripts/**'
2020
- 'deps/build.jl'
21+
- 'deps/buildtools/**'
2122
- 'Project.toml'
2223
- 'lib/legate_jl_wrapper/src/**'
2324
- 'lib/legate_jl_wrapper/include/**'
@@ -32,6 +33,7 @@ on:
3233
- 'src/**'
3334
- 'scripts/**'
3435
- 'deps/build.jl'
36+
- 'deps/buildtools/**'
3537
- 'Project.toml'
3638
- 'lib/legate_jl_wrapper/src/**'
3739
- 'lib/legate_jl_wrapper/include/**'
@@ -59,7 +61,7 @@ jobs:
5961
shell: bash
6062
env:
6163
LEGATE_AUTO_CONFIG: 0
62-
NO_CUDA: ON
64+
LEGATE_WRAPPER_ENABLE_CUDA: OFF
6365
steps:
6466
- name: Check out the repo
6567
uses: actions/checkout@v4

.github/workflows/docs.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,24 @@ on:
2323
- 'README.md'
2424
- 'lib/LegatePreferences/src*'
2525
jobs:
26+
check_changes:
27+
name: Check for wrapper changes
28+
runs-on: ubuntu-latest
29+
outputs:
30+
wrapper_changed: ${{ steps.filter.outputs.wrapper }}
31+
steps:
32+
- uses: actions/checkout@v4
33+
- uses: dorny/paths-filter@v3
34+
id: filter
35+
with:
36+
filters: |
37+
wrapper:
38+
- 'lib/legate_jl_wrapper/**'
39+
2640
docs:
2741
name : Documentation
42+
needs: check_changes
43+
if: ${{ github.base_ref == 'main' || needs.check_changes.outputs.wrapper_changed != 'true' }}
2844
permissions:
2945
actions: write
3046
contents: write

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ LocalPreferences.toml
2525
# Files generated by the build process
2626
libcxxwrap-julia*
2727
build*
28+
!deps/buildtools/
29+
!deps/buildtools/**
2830
wrapper/build
2931

3032
legate_wrapper_install
@@ -55,5 +57,8 @@ docs/site/
5557
# environment.
5658
Manifest.toml
5759

60+
# Generated by build.jl — do not commit
61+
dev/Project.toml
62+
dev/Manifest.toml
5863

5964
test_crash.sh

Project.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ name = "Legate"
22
uuid = "1238f2cf-6593-4d60-9aca-2f5364e49909"
33
version = "0.1.2"
44

5+
[workspace]
6+
projects = ["test", "dev"]
7+
58
[deps]
69
CxxWrap = "1f15a43c-97ca-5a2a-ae31-89f07a497df4"
710
FunctionWrappers = "069b7b12-0de2-55c6-9aab-29f3d0a68a2e"
@@ -26,7 +29,7 @@ CxxWrap = "0.17"
2629
FunctionWrappers = "1.1.3"
2730
JuliaFormatter = "2.2.0"
2831
LegatePreferences = "0.1.6"
32+
Preferences = "1"
2933
julia = "1.10"
3034
legate_jl_wrapper_jll = "25.10.4"
3135
legate_jll = "25.10.2"
32-
Preferences = "1"

deps/build.jl

Lines changed: 30 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -16,138 +16,52 @@
1616
* Author(s): David Krasowska <krasow@u.northwestern.edu>
1717
* Ethan Meitz <emeitz@andrew.cmu.edu>
1818
=#
19+
using Pkg
1920
using Preferences
2021
using LegatePreferences
2122

23+
include("buildtools/dev_tools.jl")
2224
include("version.jl")
2325

24-
# Automatically pipes errors to new file
25-
# and appends stdout to build.log
26-
function run_sh(cmd::Cmd, filename::String)
27-
println(cmd)
28-
29-
build_log = joinpath(@__DIR__, "build.log")
30-
tmp_build_log = joinpath(@__DIR__, "$(filename).log")
31-
err_log = joinpath(@__DIR__, "$(filename).err")
32-
33-
if isfile(err_log)
34-
rm(err_log)
35-
end
36-
37-
if isfile(tmp_build_log)
38-
rm(tmp_build_log)
39-
end
40-
41-
try
42-
run(pipeline(cmd; stdout=tmp_build_log, stderr=err_log, append=false))
43-
contents = read(tmp_build_log, String)
44-
open(build_log, "a") do io
45-
println(contents)
46-
end
47-
catch e
48-
println("stderr log generated: ", err_log, '\n')
49-
contents = read(err_log, String)
50-
if !isempty(strip(contents))
51-
println("---- Begin stderr log ----")
52-
println(contents)
53-
println("---- End stderr log ----")
54-
end
55-
end
56-
end
57-
5826
# patch legion. The readme below talks about our compilation error
5927
# https://github.com/ejmeitz/cuNumeric.jl/blob/main/scripts/README.md
6028
function patch_legion(repo_root::String, legate_root::String)
6129
if !check_if_patch(legate_root)
6230
legion_patch = joinpath(repo_root, "scripts/patch_legion.sh")
6331
@info "Legate.jl: Running legion patch script: $legion_patch"
64-
run_sh(`bash $legion_patch $repo_root $legate_root`, "legion_patch")
65-
end
66-
end
67-
68-
function build_jlcxxwrap(repo_root, legate_root)
69-
build_libcxxwrap = joinpath(repo_root, "scripts/install_cxxwrap.sh")
70-
version_path = joinpath(DEPOT_PATH[1], "dev/libcxxwrap_julia_jll/override/LEGATE_INSTALL.txt")
71-
if isfile(version_path)
72-
version = VersionNumber(strip(read(version_path, String)))
73-
@info "libcxxwrap: Found Legate $version"
74-
if is_supported_version(version)
75-
@info "libcxxwrap: Found supported version built with Legate.jl: $version"
76-
return nothing
77-
else
78-
@info "libcxxwrap: Unsupported version found: $version. Rebuilding..."
79-
end
80-
else
81-
@info "libcxxwrap: No version file found. Starting build..."
82-
end
83-
84-
@info "libcxxwrap: Running build script: $build_libcxxwrap"
85-
run_sh(`bash $build_libcxxwrap $repo_root`, "libcxxwrap")
86-
open(version_path, "w") do io
87-
write(io, string(get_legate_version(legate_root)))
32+
BuildTools.run_sh(
33+
`bash $legion_patch $repo_root $legate_root`, "legion_patch"; log_dir=@__DIR__
34+
)
8835
end
8936
end
9037

91-
function build_cpp_wrapper(repo_root, legate_root, install_root)
38+
function build_cpp_wrapper(
39+
repo_root, legate_root, install_root; cuda_root=nothing, cuda_enabled=true
40+
)
9241
@info "liblegatewrapper: Building C++ Wrapper Library"
93-
if isdir(install_root)
94-
rm(install_root; recursive=true)
95-
mkdir(install_root)
96-
end
97-
98-
build_cpp_wrapper = joinpath(repo_root, "scripts/build_cpp_wrapper.sh")
99-
nthreads = Threads.nthreads()
100-
101-
bld_command = `$build_cpp_wrapper $repo_root $legate_root $install_root $nthreads`
102-
103-
# write out a bash script for debugging
104-
cmd_str = join(bld_command.exec, " ")
105-
wrapper_path = joinpath(repo_root, "build_wrapper.sh")
106-
open(wrapper_path, "w") do io
107-
println(io, "#!/bin/bash")
108-
println(io, "set -xe")
109-
println(io, cmd_str)
110-
end
111-
chmod(wrapper_path, 0o755)
112-
113-
@info "Running build command: $bld_command"
114-
run_sh(`bash $bld_command`, "cpp_wrapper")
115-
end
116-
117-
function _find_jll_artifact_dir(jll)
118-
eval(:(using $(jll)))
119-
jll_mod = getfield(Main, jll)
120-
root = jll_mod.artifact_dir
121-
return root
42+
isdir(install_root) && (rm(install_root; recursive=true); mkdir(install_root))
43+
bld_command = `$(joinpath(repo_root, "scripts/build_cpp_wrapper.sh")) $repo_root $legate_root $install_root $(Threads.nthreads())`
44+
BuildTools.run_build_wrapper_script(
45+
repo_root, bld_command; cuda_root, cuda_enabled, log_dir=@__DIR__
46+
)
12247
end
12348

124-
function _start_build()
125-
pkg_root = up_dir(@__DIR__)
126-
deps_dir = joinpath(@__DIR__)
127-
128-
build_log = joinpath(deps_dir, "build.log")
129-
open(build_log, "w") do io
130-
println(io, "=== Build started ===")
131-
end
132-
133-
@info "Legate.jl: Parsed Package Dir as: $(pkg_root)"
134-
return pkg_root
135-
end
136-
137-
"""
138-
build CxxWrap and legate_jl_wrapper
139-
"""
140-
function build_deps(pkg_root, legate_root)
49+
function build_deps(pkg_root, legate_root; cuda_root=nothing, cuda_enabled=true)
50+
BuildTools.check_cmake_version(MIN_CMAKE_VERSION)
14151
install_dir = joinpath(pkg_root, "lib", "legate_jl_wrapper", "build")
14252
if !legate_valid(legate_root)
14353
error(
14454
"Legate.jl: Unsupported Legate version at $(legate_root). " *
145-
"Installed version: $(installed_version) not in range supported: " *
55+
"Installed version: $(get_legate_version(legate_root)) not in range supported: " *
14656
"$(MIN_LEGATE_VERSION)-$(MAX_LEGATE_VERSION).",
14757
)
14858
end
149-
build_jlcxxwrap(pkg_root, legate_root) # $pkg_root/lib/libcxxwrap-julia
150-
build_cpp_wrapper(pkg_root, legate_root, install_dir) # $pkg_root/lib/legate_jl_wrapper
59+
BuildTools.build_jlcxxwrap(
60+
pkg_root, get_legate_version(legate_root);
61+
log_dir=@__DIR__, is_compatible=is_supported_version,
62+
)
63+
build_cpp_wrapper(pkg_root, legate_root, install_dir; cuda_root, cuda_enabled)
64+
BuildTools.set_jll_artifact_override(:legate_jl_wrapper_jll, install_dir)
15165
end
15266

15367
function build(::LegatePreferences.JLL)
@@ -157,7 +71,7 @@ end
15771

15872
function build(::LegatePreferences.Conda)
15973
@warn "Conda Build does not currently pass our CI. Proceed with caution."
160-
pkg_root = _start_build()
74+
pkg_root = BuildTools.start_build("Legate.jl", @__DIR__)
16175

16276
legate_root = load_preference(LegatePreferences, "legate_conda_env", nothing)
16377
if isnothing(legate_root)
@@ -170,19 +84,20 @@ function build(::LegatePreferences.Conda)
17084
end
17185

17286
function build(::LegatePreferences.Developer)
173-
pkg_root = _start_build()
87+
pkg_root = BuildTools.start_build("Legate.jl", @__DIR__)
17488

175-
# can be nothing so this errors if not set
17689
legate_root = load_preference(LegatePreferences, "legate_path", nothing)
17790
if isnothing(legate_root)
178-
# we are using legate_jll for legate
179-
legate_root = _find_jll_artifact_dir(:legate_jll)
91+
legate_root, cuda_root = BuildTools.setup_jll_build_env(pkg_root, BuildTools.LEGATE_JLL_DEP)
92+
cuda_enabled = !isnothing(cuda_root) # cuda_root resolving to nothing means there is no cuda
18093
else
181-
# this means we have a custom path set
18294
is_legate_installed(legate_root; throw_errors=true)
18395
patch_legion(pkg_root, legate_root)
96+
cuda_enabled, cuda_root = BuildTools.resolve_custom_cuda("legate") # cuda_root is nothing.
18497
end
185-
build_deps(pkg_root, legate_root)
98+
99+
build_deps(pkg_root, legate_root; cuda_root, cuda_enabled)
100+
set_preferences!(LegatePreferences, "LEGATE_LIBDIR" => joinpath(legate_root, "lib"); force=true)
186101
end
187102

188103
const mode_str = load_preference(LegatePreferences, "legate_mode", LegatePreferences.MODE_JLL)

deps/buildtools/cuda_tools.jl

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
function resolve_custom_cuda(pkg_name::String)
2+
cuda_enabled = Sys.which("nvcc") !== nothing
3+
if !cuda_enabled
4+
@warn "nvcc not found on PATH — building without CUDA. " *
5+
"If your $(pkg_name) was built with CUDA, add nvcc to PATH and rebuild."
6+
end
7+
return cuda_enabled, nothing
8+
end
9+
10+
function detect_jll_cuda_enabled(jll_mod)
11+
cuda_val = get(jll_mod.host_platform.tags, "cuda", nothing)
12+
return cuda_val !== nothing && cuda_val != "none"
13+
end
14+
15+
# try/catch because CUDA_SDK_jll is a weakdep — may not be in the environment.
16+
function try_get_cuda_sdk_jll_dir()
17+
try
18+
Core.eval(Main, :(using CUDA_SDK_jll))
19+
return joinpath(getfield(Main, :CUDA_SDK_jll).artifact_dir, "cuda")
20+
catch
21+
return nothing
22+
end
23+
end
24+
25+
function build_cuda_env(cuda_enabled::Bool, cuda_root)
26+
# user-set env vars override auto-detection
27+
if haskey(ENV, "LEGATE_WRAPPER_ENABLE_CUDA")
28+
user_enable = uppercase(ENV["LEGATE_WRAPPER_ENABLE_CUDA"]) == "ON"
29+
if user_enable && !cuda_enabled
30+
@warn "LEGATE_WRAPPER_ENABLE_CUDA=ON overrides auto-detected cuda_enabled=false. " *
31+
"The wrapper will link CUDA, but the underlying legate build may not — expect link/runtime errors if mismatched."
32+
end
33+
cuda_enabled = user_enable
34+
end
35+
if haskey(ENV, "CUDA_TOOLKIT_ROOT")
36+
cuda_root = ENV["CUDA_TOOLKIT_ROOT"]
37+
end
38+
39+
env = Dict{String,String}()
40+
!cuda_enabled && (env["LEGATE_WRAPPER_ENABLE_CUDA"] = "OFF")
41+
cuda_root !== nothing && (env["CUDA_TOOLKIT_ROOT"] = cuda_root)
42+
return env
43+
end

0 commit comments

Comments
 (0)