Skip to content

Commit 78f4587

Browse files
quinnjKristofferC
authored andcommitted
Support a [sources] section in Project.toml for specifying relative path and repo locations for dependencies
1 parent 1a2ea0d commit 78f4587

File tree

14 files changed

+270
-35
lines changed

14 files changed

+270
-35
lines changed

Diff for: docs/src/toml-files.md

+14-1
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,19 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
9191
Typically it is not needed to manually add entries to the `[deps]` section; this is instead
9292
handled by Pkg operations such as `add`.
9393

94+
### The `[sources]` section
95+
96+
Specifiying a path or repo (+ branch) for a dependency is done in the `[sources]` section.
97+
These are especially useful for controlling unregistered dependencies without having to bundle a
98+
corresponding manifest file.
99+
100+
```toml
101+
[sources]
102+
Example = {url = "https://github.com/JuliaLang/Example.jl", rev = "custom_branch"}
103+
SomeDependency = {path = "deps/SomeDependency.jl"}
104+
```
105+
106+
Note that this information is only used when this environment is active, i.e. it is not used if this project is a package that is being used as a dependency.
94107

95108
### The `[compat]` section
96109

@@ -135,7 +148,7 @@ Julia will then preferentially use the version-specific manifest file if availab
135148
For example, if both `Manifest-v1.11.toml` and `Manifest.toml` exist, Julia 1.11 will prioritize using `Manifest-v1.11.toml`.
136149
However, Julia versions 1.10, 1.12, and all others will default to using `Manifest.toml`.
137150
This feature allows for easier management of different instantiated versions of dependencies for various Julia versions.
138-
Note that there can only be one `Project.toml` file. While `Manifest-v{major}.{minor}.toml` files are not automatically
151+
Note that there can only be one `Project.toml` file. While `Manifest-v{major}.{minor}.toml` files are not automatically
139152
created by Pkg, users can manually rename a `Manifest.toml` file to match
140153
the versioned format, and Pkg will subsequently maintain it through its operations.
141154

Diff for: src/API.jl

+33-2
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,31 @@ for f in (:develop, :add, :rm, :up, :pin, :free, :test, :build, :status, :why, :
181181
end
182182
end
183183

184+
function update_source_if_set(project, pkg)
185+
source = get(project.sources, pkg.name, nothing)
186+
source === nothing && return
187+
# This should probably not modify the dicts directly...
188+
if pkg.repo.source !== nothing
189+
source["url"] = pkg.repo.source
190+
end
191+
if pkg.repo.rev !== nothing
192+
source["rev"] = pkg.repo.rev
193+
end
194+
if pkg.path !== nothing
195+
source["path"] = pkg.path
196+
end
197+
path, repo = get_path_repo(project, pkg.name)
198+
if path !== nothing
199+
pkg.path = path
200+
end
201+
if repo.source !== nothing
202+
pkg.repo.source = repo.source
203+
end
204+
if repo.rev !== nothing
205+
pkg.repo.rev = repo.rev
206+
end
207+
end
208+
184209
function develop(ctx::Context, pkgs::Vector{PackageSpec}; shared::Bool=true,
185210
preserve::PreserveLevel=Operations.default_preserve(), platform::AbstractPlatform=HostPlatform(), kwargs...)
186211
require_not_empty(pkgs, :develop)
@@ -212,13 +237,15 @@ function develop(ctx::Context, pkgs::Vector{PackageSpec}; shared::Bool=true,
212237

213238
new_git = handle_repos_develop!(ctx, pkgs, shared)
214239

240+
215241
for pkg in pkgs
216242
if Types.collides_with_project(ctx.env, pkg)
217243
pkgerror("package $(err_rep(pkg)) has the same name or UUID as the active project")
218244
end
219245
if length(findall(x -> x.uuid == pkg.uuid, pkgs)) > 1
220246
pkgerror("it is invalid to specify multiple packages with the same UUID: $(err_rep(pkg))")
221247
end
248+
update_source_if_set(ctx.env.project, pkg)
222249
end
223250

224251
Operations.develop(ctx, pkgs, new_git; preserve=preserve, platform=platform)
@@ -272,6 +299,7 @@ function add(ctx::Context, pkgs::Vector{PackageSpec}; preserve::PreserveLevel=Op
272299
if length(findall(x -> x.uuid == pkg.uuid, pkgs)) > 1
273300
pkgerror("it is invalid to specify multiple packages with the same UUID: $(err_rep(pkg))")
274301
end
302+
update_source_if_set(ctx.env.project, pkg)
275303
end
276304

277305
Operations.add(ctx, pkgs, new_git; preserve, platform, target)
@@ -311,12 +339,14 @@ end
311339
function append_all_pkgs!(pkgs, ctx, mode)
312340
if mode == PKGMODE_PROJECT || mode == PKGMODE_COMBINED
313341
for (name::String, uuid::UUID) in ctx.env.project.deps
314-
push!(pkgs, PackageSpec(name=name, uuid=uuid))
342+
path, repo = get_path_repo(ctx.env.project, name)
343+
push!(pkgs, PackageSpec(name=name, uuid=uuid, path=path, repo=repo))
315344
end
316345
end
317346
if mode == PKGMODE_MANIFEST || mode == PKGMODE_COMBINED
318347
for (uuid, entry) in ctx.env.manifest
319-
push!(pkgs, PackageSpec(name=entry.name, uuid=uuid))
348+
path, repo = get_path_repo(ctx.env.project, entry.name)
349+
push!(pkgs, PackageSpec(name=entry.name, uuid=uuid, path=path, repo=repo))
320350
end
321351
end
322352
return
@@ -347,6 +377,7 @@ function up(ctx::Context, pkgs::Vector{PackageSpec};
347377
manifest_resolve!(ctx.env.manifest, pkgs)
348378
ensure_resolved(ctx, ctx.env.manifest, pkgs)
349379
end
380+
350381
Operations.up(ctx, pkgs, level; skip_writing_project, preserve)
351382
return
352383
end

Diff for: src/Operations.jl

+78-22
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,15 @@ function load_direct_deps(env::EnvCache, pkgs::Vector{PackageSpec}=PackageSpec[]
7171
pkgs = copy(pkgs)
7272
for (name::String, uuid::UUID) in env.project.deps
7373
findfirst(pkg -> pkg.uuid == uuid, pkgs) === nothing || continue # do not duplicate packages
74+
path, repo = get_path_repo(env.project, name)
7475
entry = manifest_info(env.manifest, uuid)
7576
push!(pkgs, entry === nothing ?
76-
PackageSpec(;uuid=uuid, name=name) :
77+
PackageSpec(;uuid=uuid, name=name, path=path, repo=repo) :
7778
PackageSpec(;
7879
uuid = uuid,
7980
name = name,
80-
path = entry.path,
81-
repo = entry.repo,
81+
path = path === nothing ? entry.path : path,
82+
repo = repo == GitRepo() ? entry.repo : repo,
8283
pinned = entry.pinned,
8384
tree_hash = entry.tree_hash, # TODO should tree_hash be changed too?
8485
version = load_version(entry.version, isfixed(entry), preserve),
@@ -108,6 +109,19 @@ end
108109
function load_all_deps(env::EnvCache, pkgs::Vector{PackageSpec}=PackageSpec[];
109110
preserve::PreserveLevel=PRESERVE_ALL)
110111
pkgs = load_manifest_deps(env.manifest, pkgs; preserve=preserve)
112+
# Sources takes presedence over the manifest...
113+
for pkg in pkgs
114+
path, repo = get_path_repo(env.project, pkg.name)
115+
if path !== nothing
116+
pkg.path = path
117+
end
118+
if repo.source !== nothing
119+
pkg.repo.source = repo.source
120+
end
121+
if repo.rev !== nothing
122+
pkg.repo.rev = repo.rev
123+
end
124+
end
111125
return load_direct_deps(env, pkgs; preserve=preserve)
112126
end
113127

@@ -244,8 +258,9 @@ function collect_project(pkg::PackageSpec, path::String)
244258
pkgerror("julia version requirement from Project.toml's compat section not satisfied for package $(err_rep(pkg)) at `$path`")
245259
end
246260
for (name, uuid) in project.deps
261+
path, repo = get_path_repo(project, name)
247262
vspec = get_compat(project, name)
248-
push!(deps, PackageSpec(name, uuid, vspec))
263+
push!(deps, PackageSpec(name=name, uuid=uuid, version=vspec, path=path, repo=repo))
249264
end
250265
for (name, uuid) in project.weakdeps
251266
vspec = get_compat(project, name)
@@ -302,6 +317,11 @@ function collect_fixed!(env::EnvCache, pkgs::Vector{PackageSpec}, names::Dict{UU
302317
names[pkg.uuid] = pkg.name
303318
end
304319
for pkg in pkgs
320+
# add repo package if necessary
321+
if (pkg.repo.rev !== nothing || pkg.repo.source !== nothing) && pkg.tree_hash === nothing
322+
# ensure revved package is installed
323+
Types.handle_repo_add!(Types.Context(env=env), pkg)
324+
end
305325
path = project_rel_path(env, source_path(env.manifest_file, pkg))
306326
if !isdir(path)
307327
pkgerror("expected package $(err_rep(pkg)) to exist at path `$path`")
@@ -1134,7 +1154,7 @@ function build_versions(ctx::Context, uuids::Set{UUID}; verbose=false)
11341154
fancyprint && show_progress(ctx.io, bar)
11351155

11361156
let log_file=log_file
1137-
sandbox(ctx, pkg, source_path, builddir(source_path), build_project_override; preferences=build_project_preferences) do
1157+
sandbox(ctx, pkg, builddir(source_path), build_project_override; preferences=build_project_preferences) do
11381158
flush(ctx.io)
11391159
ok = open(log_file, "w") do log
11401160
std = verbose ? ctx.io : log
@@ -1225,6 +1245,9 @@ function rm(ctx::Context, pkgs::Vector{PackageSpec}; mode::PackageMode)
12251245
filter!(ctx.env.project.compat) do (name, _)
12261246
name == "julia" || name in keys(ctx.env.project.deps) || name in keys(ctx.env.project.extras) || name in keys(ctx.env.project.weakdeps)
12271247
end
1248+
filter!(ctx.env.project.sources) do (name, _)
1249+
name in keys(ctx.env.project.deps) || name in keys(ctx.env.project.extras)
1250+
end
12281251
deps_names = union(keys(ctx.env.project.deps), keys(ctx.env.project.extras))
12291252
filter!(ctx.env.project.targets) do (target, deps)
12301253
!isempty(filter!(in(deps_names), deps))
@@ -1237,8 +1260,8 @@ function rm(ctx::Context, pkgs::Vector{PackageSpec}; mode::PackageMode)
12371260
show_update(ctx.env, ctx.registries; io=ctx.io)
12381261
end
12391262

1240-
update_package_add(ctx::Context, pkg::PackageSpec, ::Nothing, is_dep::Bool) = pkg
1241-
function update_package_add(ctx::Context, pkg::PackageSpec, entry::PackageEntry, is_dep::Bool)
1263+
update_package_add(ctx::Context, pkg::PackageSpec, ::Nothing, source_path, source_repo, is_dep::Bool) = pkg
1264+
function update_package_add(ctx::Context, pkg::PackageSpec, entry::PackageEntry, source_path, source_repo, is_dep::Bool)
12421265
if entry.pinned
12431266
if pkg.version == VersionSpec()
12441267
println(ctx.io, "`$(pkg.name)` is pinned at `v$(entry.version)`: maintaining pinned version")
@@ -1381,7 +1404,8 @@ function add(ctx::Context, pkgs::Vector{PackageSpec}, new_git=Set{UUID}();
13811404
for (i, pkg) in pairs(pkgs)
13821405
entry = manifest_info(ctx.env.manifest, pkg.uuid)
13831406
is_dep = any(uuid -> uuid == pkg.uuid, [uuid for (name, uuid) in ctx.env.project.deps])
1384-
pkgs[i] = update_package_add(ctx, pkg, entry, is_dep)
1407+
source_path, source_repo = get_path_repo(ctx.env.project, pkg.name)
1408+
pkgs[i] = update_package_add(ctx, pkg, entry, source_path, source_repo, is_dep)
13851409
end
13861410

13871411
names = (p.name for p in pkgs)
@@ -1455,21 +1479,27 @@ end
14551479

14561480
# load version constraint
14571481
# if version isa VersionNumber -> set tree_hash too
1458-
up_load_versions!(ctx::Context, pkg::PackageSpec, ::Nothing, level::UpgradeLevel) = false
1459-
function up_load_versions!(ctx::Context, pkg::PackageSpec, entry::PackageEntry, level::UpgradeLevel)
1482+
up_load_versions!(ctx::Context, pkg::PackageSpec, ::Nothing, source_path, source_repo, level::UpgradeLevel) = false
1483+
function up_load_versions!(ctx::Context, pkg::PackageSpec, entry::PackageEntry, source_path, source_repo, level::UpgradeLevel)
1484+
# With [sources], `pkg` can have a path or repo here
14601485
entry.version !== nothing || return false # no version to set
14611486
if entry.pinned || level == UPLEVEL_FIXED
14621487
pkg.version = entry.version
14631488
pkg.tree_hash = entry.tree_hash
1464-
elseif entry.repo.source !== nothing # repo packages have a version but are treated special
1465-
pkg.repo = entry.repo
1489+
elseif entry.repo.source !== nothing || source_repo.source !== nothing # repo packages have a version but are treated specially
1490+
if source_repo.source !== nothing
1491+
pkg.repo = source_repo
1492+
else
1493+
pkg.repo = entry.repo
1494+
end
14661495
if level == UPLEVEL_MAJOR
14671496
# Updating a repo package is equivalent to adding it
14681497
new = Types.handle_repo_add!(ctx, pkg)
14691498
pkg.version = entry.version
14701499
if pkg.tree_hash != entry.tree_hash
14711500
# TODO parse find_installed and set new version
14721501
end
1502+
14731503
return new
14741504
else
14751505
pkg.version = entry.version
@@ -1489,8 +1519,12 @@ end
14891519
up_load_manifest_info!(pkg::PackageSpec, ::Nothing) = nothing
14901520
function up_load_manifest_info!(pkg::PackageSpec, entry::PackageEntry)
14911521
pkg.name = entry.name # TODO check name is same
1492-
pkg.repo = entry.repo # TODO check that repo is same
1493-
pkg.path = entry.path
1522+
if pkg.repo == GitRepo()
1523+
pkg.repo = entry.repo # TODO check that repo is same
1524+
end
1525+
if pkg.path === nothing
1526+
pkg.path = entry.path
1527+
end
14941528
pkg.pinned = entry.pinned
14951529
# `pkg.version` and `pkg.tree_hash` is set by `up_load_versions!`
14961530
end
@@ -1558,12 +1592,15 @@ function up(ctx::Context, pkgs::Vector{PackageSpec}, level::UpgradeLevel;
15581592
# TODO check all pkg.version == VersionSpec()
15591593
# set version constraints according to `level`
15601594
for pkg in pkgs
1561-
new = up_load_versions!(ctx, pkg, manifest_info(ctx.env.manifest, pkg.uuid), level)
1595+
source_path, source_repo = get_path_repo(ctx.env.project, pkg.name)
1596+
entry = manifest_info(ctx.env.manifest, pkg.uuid)
1597+
new = up_load_versions!(ctx, pkg, entry, source_path, source_repo, level)
15621598
new && push!(new_git, pkg.uuid) #TODO put download + push! in utility function
15631599
end
15641600
# load rest of manifest data (except for version info)
15651601
for pkg in pkgs
1566-
up_load_manifest_info!(pkg, manifest_info(ctx.env.manifest, pkg.uuid))
1602+
entry = manifest_info(ctx.env.manifest, pkg.uuid)
1603+
up_load_manifest_info!(pkg, entry)
15671604
end
15681605
if preserve !== nothing
15691606
pkgs, deps_map = targeted_resolve_up(ctx.env, ctx.registries, pkgs, preserve, ctx.julia_version)
@@ -1653,7 +1690,11 @@ end
16531690
# TODO: this is two technically different operations with the same name
16541691
# split into two subfunctions ...
16551692
function free(ctx::Context, pkgs::Vector{PackageSpec}; err_if_free=true)
1656-
foreach(pkg -> update_package_free!(ctx.registries, pkg, manifest_info(ctx.env.manifest, pkg.uuid), err_if_free), pkgs)
1693+
for pkg in pkgs
1694+
entry = manifest_info(ctx.env.manifest, pkg.uuid)
1695+
delete!(ctx.env.project.sources, pkg.name)
1696+
update_package_free!(ctx.registries, pkg, entry, err_if_free)
1697+
end
16571698

16581699
if any(pkg -> pkg.version == VersionSpec(), pkgs)
16591700
pkgs = load_direct_deps(ctx.env, pkgs)
@@ -1744,8 +1785,9 @@ function sandbox_preserve(env::EnvCache, target::PackageSpec, test_project::Stri
17441785
env.manifest.manifest_format = v"2.0"
17451786
end
17461787
# preserve important nodes
1788+
project = read_project(test_project)
17471789
keep = [target.uuid]
1748-
append!(keep, collect(values(read_project(test_project).deps)))
1790+
append!(keep, collect(values(project.deps)))
17491791
record_project_hash(env)
17501792
# prune and return
17511793
return prune_manifest(env.manifest, keep)
@@ -1760,8 +1802,17 @@ function abspath!(env::EnvCache, manifest::Manifest)
17601802
return manifest
17611803
end
17621804

1805+
function abspath!(env::EnvCache, project::Project)
1806+
for (key, entry) in project.sources
1807+
if haskey(entry, "path")
1808+
entry["path"] = project_rel_path(env, entry["path"])
1809+
end
1810+
end
1811+
return project
1812+
end
1813+
17631814
# ctx + pkg used to compute parent dep graph
1764-
function sandbox(fn::Function, ctx::Context, target::PackageSpec, target_path::String,
1815+
function sandbox(fn::Function, ctx::Context, target::PackageSpec,
17651816
sandbox_path::String, sandbox_project_override;
17661817
preferences::Union{Nothing,Dict{String,Any}} = nothing,
17671818
force_latest_compatible_version::Bool=false,
@@ -1782,16 +1833,20 @@ function sandbox(fn::Function, ctx::Context, target::PackageSpec, target_path::S
17821833
sandbox_project_override = Project()
17831834
end
17841835
end
1836+
abspath!(ctx.env, sandbox_project_override)
17851837
Types.write_project(sandbox_project_override, tmp_project)
17861838

17871839
# create merged manifest
17881840
# - copy over active subgraph
17891841
# - abspath! to maintain location of all deved nodes
1790-
working_manifest = abspath!(ctx.env, sandbox_preserve(ctx.env, target, tmp_project))
1842+
working_manifest = sandbox_preserve(ctx.env, target, tmp_project)
1843+
abspath!(ctx.env, working_manifest)
1844+
17911845
# - copy over fixed subgraphs from test subgraph
17921846
# really only need to copy over "special" nodes
17931847
sandbox_env = Types.EnvCache(projectfile_path(sandbox_path))
17941848
abspath!(sandbox_env, sandbox_env.manifest)
1849+
abspath!(sandbox_env, sandbox_env.project)
17951850
for (uuid, entry) in sandbox_env.manifest.deps
17961851
entry_working = get(working_manifest, uuid, nothing)
17971852
if entry_working === nothing
@@ -1896,6 +1951,7 @@ function gen_target_project(ctx::Context, pkg::PackageSpec, source_path::String,
18961951
source_env = EnvCache(projectfile_path(source_path))
18971952
# collect regular dependencies
18981953
test_project.deps = source_env.project.deps
1954+
test_project.sources = source_env.project.sources
18991955
# collect test dependencies
19001956
for name in get(source_env.project.targets, target, String[])
19011957
uuid = nothing
@@ -1975,11 +2031,11 @@ function test(ctx::Context, pkgs::Vector{PackageSpec};
19752031
end
19762032
# now we sandbox
19772033
printpkgstyle(ctx.io, :Testing, pkg.name)
1978-
sandbox(ctx, pkg, source_path, testdir(source_path), test_project_override; preferences=test_project_preferences, force_latest_compatible_version, allow_earlier_backwards_compatible_versions, allow_reresolve) do
2034+
sandbox(ctx, pkg, testdir(source_path), test_project_override; preferences=test_project_preferences, force_latest_compatible_version, allow_earlier_backwards_compatible_versions, allow_reresolve) do
19792035
test_fn !== nothing && test_fn()
19802036
sandbox_ctx = Context(;io=ctx.io)
19812037
status(sandbox_ctx.env, sandbox_ctx.registries; mode=PKGMODE_COMBINED, io=sandbox_ctx.io, ignore_indent = false, show_usagetips = false)
1982-
flags = gen_subprocess_flags(source_path; coverage, julia_args)
2038+
flags = gen_subprocess_flags(source_path; coverage,julia_args)
19832039

19842040
if should_autoprecompile()
19852041
cacheflags = Base.CacheFlags(parse(UInt8, read(`$(Base.julia_cmd()) $(flags) --eval 'show(ccall(:jl_cache_flags, UInt8, ()))'`, String)))

0 commit comments

Comments
 (0)