Skip to content

Commit 499e7d3

Browse files
KristofferCKristofferCIanButterworthMoelfgdalle
authored
Backports for 1.12 (#4493)
Co-authored-by: KristofferC <[email protected]> Co-authored-by: Ian Butterworth <[email protected]> Co-authored-by: Jerry Ling <[email protected]> Co-authored-by: Guillaume Dalle <[email protected]>
2 parents f571edd + 4ff74e8 commit 499e7d3

File tree

14 files changed

+206
-96
lines changed

14 files changed

+206
-96
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ Markdown = "1.11"
4343
Printf = "1.11"
4444
Random = "1.11"
4545
REPL = "1.11"
46-
SHA = "0.7"
46+
SHA = "0.7, 1"
4747
TOML = "1"
4848
Tar = "1.10"
4949
UUIDs = "1.11"

docs/src/creating-packages.md

Lines changed: 71 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -275,79 +275,100 @@ test-specific dependencies, are available, see below.
275275

276276
### Test-specific dependencies
277277

278-
There are two ways of adding test-specific dependencies (dependencies that are not dependencies of the package but will still be available to
279-
load when the package is tested).
278+
Test-specific dependencies are dependencies that are not dependencies of the package itself but are available when the package is tested.
280279

281-
#### `target` based test specific dependencies
280+
#### Recommended approach: Using workspaces with `test/Project.toml`
282281

283-
Using this method of adding test-specific dependencies, the packages are added under an `[extras]` section and to a test target,
284-
e.g. to add `Markdown` and `Test` as test dependencies, add the following to the `Project.toml` file:
285-
286-
```toml
287-
[extras]
288-
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
289-
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
290-
291-
[targets]
292-
test = ["Markdown", "Test"]
293-
```
294-
295-
Note that the only supported targets are `test` and `build`, the latter of which (not recommended) can be used
296-
for any `deps/build.jl` scripts.
297-
298-
#### Alternative approach: `test/Project.toml` file test specific dependencies
282+
!!! compat
283+
Workspaces require Julia 1.12+. For older Julia versions, see the legacy approaches below.
299284

300-
!!! note
301-
The exact interaction between `Project.toml`, `test/Project.toml` and their corresponding
302-
`Manifest.toml`s are not fully worked out and may be subject to change in future versions.
303-
The older method of adding test-specific dependencies, described in the previous section,
304-
will therefore be supported throughout all Julia 1.X releases.
285+
The recommended way to add test-specific dependencies is to use workspaces. This is done by:
305286

306-
In Julia 1.2 and later test dependencies can be declared in `test/Project.toml`. When running
307-
tests, Pkg will automatically merge this and the package Projects to create the test environment.
287+
1. Adding a `[workspace]` section to your package's `Project.toml`:
308288

309-
!!! note
310-
If no `test/Project.toml` exists Pkg will use the `target` based test specific dependencies.
289+
```toml
290+
[workspace]
291+
projects = ["test"]
292+
```
311293

312-
To add a test-specific dependency, i.e. a dependency that is available only when testing,
313-
it is thus enough to add this dependency to the `test/Project.toml` project. This can be
314-
done from the Pkg REPL by activating this environment, and then use `add` as one normally
315-
does. Let's add the `Test` standard library as a test dependency:
294+
2. Creating a `test/Project.toml` file with your test dependencies:
316295

317296
```julia-repl
318297
(HelloWorld) pkg> activate ./test
319298
[ Info: activating environment at `~/HelloWorld/test/Project.toml`.
320299
321-
(test) pkg> add Test
300+
(HelloWorld/test) pkg> dev . # add current package to test dependencies using its path
301+
Resolving package versions...
302+
Updating `~/HelloWorld/test/Project.toml`
303+
[8dfed614] + HelloWorld v0.1.0 `..`
304+
305+
(HelloWorld/test) pkg> add Test # add other test dependencies
322306
Resolving package versions...
323307
Updating `~/HelloWorld/test/Project.toml`
324308
[8dfed614] + Test
325-
Updating `~/HelloWorld/test/Manifest.toml`
326-
[...]
327309
```
328310

329-
We can now use `Test` in the test script and we can see that it gets installed when testing:
311+
When using workspaces, the package manager resolves dependencies for all projects in the workspace together, and creates a single `Manifest.toml` next to the base `Project.toml`. This provides better dependency resolution and makes it easier to manage test-specific dependencies.
312+
313+
!!! warning
314+
Unlike some earlier test dependency workflows, this one explicitly requires adding `HelloWorld` (the parent package) to your `test/Project.toml`.
315+
316+
You can now use `Test` in the test script:
330317

331318
```julia-repl
332319
julia> write("test/runtests.jl",
333320
"""
334-
using Test
321+
using HelloWorld, Test
335322
@test 1 == 1
336323
""");
337324
338-
(test) pkg> activate .
325+
(HelloWorld/test) pkg> activate .
339326
340327
(HelloWorld) pkg> test
341328
Testing HelloWorld
342329
Resolving package versions...
343-
Updating `/var/folders/64/76tk_g152sg6c6t0b4nkn1vw0000gn/T/tmpPzUPPw/Project.toml`
344-
[d8327f2a] + HelloWorld v0.1.0 [`~/.julia/dev/Pkg/HelloWorld`]
345-
[8dfed614] + Test
346-
Updating `/var/folders/64/76tk_g152sg6c6t0b4nkn1vw0000gn/T/tmpPzUPPw/Manifest.toml`
347-
[d8327f2a] + HelloWorld v0.1.0 [`~/.julia/dev/Pkg/HelloWorld`]
348-
Testing HelloWorld tests passed```
330+
Testing HelloWorld tests passed
349331
```
350332

333+
Workspaces can also be used for other purposes, such as documentation or benchmarks, by adding additional projects to the workspace:
334+
335+
```toml
336+
[workspace]
337+
projects = ["test", "docs", "benchmarks"]
338+
```
339+
340+
See the section on [Workspaces](@ref) in the `Project.toml` documentation for more details.
341+
342+
#### Legacy approach: `target` based test specific dependencies
343+
344+
!!! warning
345+
This approach is legacy and maintained for compatibility. New packages should use workspaces instead.
346+
347+
Using this method, test-specific dependencies are added under an `[extras]` section and to a test target:
348+
349+
```toml
350+
[extras]
351+
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
352+
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
353+
354+
[targets]
355+
test = ["Markdown", "Test"]
356+
```
357+
358+
Note that the only supported targets are `test` and `build`, the latter of which (not recommended) can be used for any `deps/build.jl` scripts.
359+
360+
#### Legacy approach: `test/Project.toml` without workspace
361+
362+
!!! warning
363+
This approach is legacy and maintained for compatibility. New packages should use workspaces instead.
364+
365+
In Julia 1.2 and later, test dependencies can be declared in `test/Project.toml` without using a workspace. When running tests, Pkg will automatically merge the package and test projects to create the test environment.
366+
367+
!!! note
368+
If no `test/Project.toml` exists, Pkg will use the `target` based test specific dependencies.
369+
370+
This approach works similarly to the workspace approach, but without the workspace declaration in the main `Project.toml`.
371+
351372
## Compatibility on dependencies
352373

353374
Every dependency should in general have a compatibility constraint on it.
@@ -450,9 +471,7 @@ Extensions can have arbitrary names (here `ContourExt`), following the format of
450471
In `Pkg` output, extension names are always shown together with their parent package name.
451472

452473
!!! compat
453-
Often you will put the extension dependencies into the `test` target so they are loaded when running e.g. `Pkg.test()`. On earlier Julia versions
454-
this requires you to also put the package in the `[extras]` section. This is unfortunate but the project verifier on older Julia versions will
455-
complain if this is not done.
474+
Often you will want to load extension dependencies when testing your package. The recommended approach is to use workspaces and add the extension dependencies to your `test/Project.toml` (see [Test-specific dependencies](@ref adding-tests-to-packages)). For older Julia versions that don't support workspaces, you can put the extension dependencies into the `test` target, which requires you to also put the package in the `[extras]` section. The project verifier on older Julia versions will complain if this is not done.
456475

457476
!!! note
458477
If you use a manifest generated by a Julia version that does not know about extensions with a Julia version that does
@@ -557,12 +576,14 @@ This is done by making the following changes (using the example above):
557576

558577
In the case where one wants to use an extension (without worrying about the
559578
feature of the extension being available on older Julia versions) while still
560-
supporting older Julia versions the packages under `[weakdeps]` should be
579+
supporting older Julia versions without workspace support, the packages under `[weakdeps]` should be
561580
duplicated into `[extras]`. This is an unfortunate duplication, but without
562581
doing this the project verifier under older Julia versions will throw an error
563582
if it finds packages under `[compat]` that is not listed in `[extras]`.
564583

565-
## Package naming rules
584+
For Julia 1.12+, using workspaces is recommended and this duplication is not necessary.
585+
586+
## [Package naming guidelines](@id Package-naming-rules)
566587

567588
Package names should be sensible to most Julia users, *even to those who are not domain experts*.
568589
The following rules apply to the `General` registry but may be useful for other package
@@ -623,7 +644,7 @@ may fit your package better.
623644
9. Packages should follow the [Stylistic Conventions](https://docs.julialang.org/en/v1/manual/variables/#Stylistic-Conventions).
624645
* The package name begin with a capital letter and word separation is shown with upper camel case
625646
* Packages that provide the functionality of a project from another language should use the Julia convention
626-
* Packages that [provide pre-built libraries and executables](https://docs.binarybuilder.org/stable/jll/) can keep orignal name, but should get `_jll`as a suffix. For example `pandoc_jll` wraps pandoc. However, note that the generation and release of most JLL packages is handled by the [Yggdrasil](https://github.com/JuliaPackaging/Yggdrasil) system.
647+
* Packages that [provide pre-built libraries and executables](https://docs.binarybuilder.org/stable/jll/) can keep orignal name, but should get `_jll`as a suffix. For example `pandoc_jll` wraps pandoc. However, note that the generation and release of most JLL packages is handled by the [Yggdrasil](https://github.com/JuliaPackaging/Yggdrasil) system.
627648

628649
## Registering packages
629650

docs/src/toml-files.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ name = "Example"
3939
The name must be a valid [identifier](https://docs.julialang.org/en/v1/base/base/#Base.isidentifier)
4040
(a sequence of Unicode characters that does not start with a number and is neither `true` nor `false`).
4141
For packages, it is recommended to follow the
42-
[package naming rules](@ref Package-naming-rules). The `name` field is mandatory
42+
[package naming guidelines](@ref Package-naming-rules). The `name` field is mandatory
4343
for packages.
4444

4545

@@ -138,7 +138,7 @@ constraints in detail. It is also possible to list constraints on `julia` itself
138138
julia = "1.1"
139139
```
140140

141-
### The `[workspace]` section
141+
### [The `[workspace]` section](@id Workspaces)
142142

143143
A project file can define a workspace by giving a set of projects that is part of that workspace.
144144
Each project in a workspace can include their own dependencies, compatibility information, and even function as full packages.
@@ -152,7 +152,7 @@ A workspace is defined in the base project by giving a list of the projects in i
152152
projects = ["test", "docs", "benchmarks", "PrivatePackage"]
153153
```
154154

155-
This structure is particularly beneficial for developers using a monorepo approach, where a large number of unregistered packages may be involved. It's also useful for adding documentation or benchmarks to a package by including additional dependencies beyond those of the package itself.
155+
This structure is particularly beneficial for developers using a monorepo approach, where a large number of unregistered packages may be involved. It's also useful for adding test-specific dependencies to a package by including a `test` project in the workspace (see [Test-specific dependencies](@ref adding-tests-to-packages)), or for adding documentation or benchmarks with their own dependencies.
156156

157157
Workspace can be nested: a project that itself defines a workspace can also be part of another workspace.
158158
In this case, the workspaces are "merged" with a single manifest being stored alongside the "root project" (the project that doesn't have another workspace including it).

ext/REPLExt/precompile.jl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,12 @@ let
3636
end
3737

3838
if Base.generating_output()
39-
pkgreplmode_precompile()
39+
ccall(:jl_tag_newly_inferred_enable, Cvoid, ())
40+
try
41+
pkgreplmode_precompile()
42+
finally
43+
ccall(:jl_tag_newly_inferred_disable, Cvoid, ())
44+
end
4045
end
4146

4247
end # let

src/Apps/Apps.jl

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ using Pkg.Types: AppInfo, PackageSpec, Context, EnvCache, PackageEntry, Manifest
77
using Pkg.Operations: print_single, source_path, update_package_add
88
using Pkg.API: handle_package_input!
99
using TOML, UUIDs
10+
using Dates
1011
import Pkg.Registry
1112

1213
app_env_folder() = joinpath(first(DEPOT_PATH), "environments", "apps")
@@ -183,6 +184,9 @@ function add(pkg::PackageSpec)
183184
handle_package_input!(pkg)
184185

185186
ctx = app_context()
187+
188+
Pkg.Operations.update_registries(ctx; force = false, update_cooldown = Day(1))
189+
186190
manifest = ctx.env.manifest
187191
new = false
188192

@@ -211,6 +215,9 @@ function add(pkg::PackageSpec)
211215
manifest.deps[pkg.uuid] = entry
212216

213217
_resolve(manifest, pkg.name)
218+
if new === true || (new isa Set{UUID} && pkg.uuid in new)
219+
Pkg.Operations.build_versions(ctx, Set([pkg.uuid]); verbose = true)
220+
end
214221
precompile(pkg.name)
215222

216223
@info "For package: $(pkg.name) installed apps $(join(keys(project.apps), ","))"
@@ -247,8 +254,10 @@ function develop(pkg::PackageSpec)
247254
manifest = ctx.env.manifest
248255
manifest.deps[pkg.uuid] = entry
249256

250-
_resolve(manifest, pkg.name)
251-
precompile(pkg.name)
257+
# For dev, we don't create an app environment - just point shims directly to the dev'd project
258+
write_manifest(manifest, app_manifest_file())
259+
generate_shims_for_apps(pkg.name, project.apps, sourcepath, joinpath(Sys.BINDIR, "julia"))
260+
252261
@info "For package: $(pkg.name) installed apps: $(join(keys(project.apps), ","))"
253262
check_apps_in_path(project.apps)
254263
end

src/Pkg.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ Pkg.add(name="Example", version="0.3") # Specify version; latest release in the
177177
Pkg.add(name="Example", version="0.3.1") # Specify version; exact release
178178
Pkg.add(url="https://github.com/JuliaLang/Example.jl", rev="master") # From url to remote gitrepo
179179
Pkg.add(url="/remote/mycompany/juliapackages/OurPackage") # From path to local gitrepo
180-
Pkg.add(url="https://github.com/Company/MonoRepo", subdir="juliapkgs/Package.jl)") # With subdir
180+
Pkg.add(url="https://github.com/Company/MonoRepo", subdir="juliapkgs/Package.jl") # With subdir
181181
```
182182
183183
After the installation of new packages the project will be precompiled. See more at [Environment Precompilation](@ref).

src/REPLMode/argument_parsers.jl

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,46 @@ end
3434

3535
# Simple URL detection
3636
function looks_like_url(str::String)
37-
return startswith(str, "http://") || startswith(str, "https://") ||
38-
startswith(str, "git@") || startswith(str, "ssh://") ||
39-
contains(str, ".git")
37+
if startswith(str, "http://") || startswith(str, "https://") ||
38+
startswith(str, "git@") || startswith(str, "ssh://") ||
39+
contains(str, ".git")
40+
return true
41+
end
42+
43+
# Check for user@host:path pattern (SSH URL with user)
44+
# This handles cases like: [email protected]:PackageName.jl
45+
# The host part should not contain / or @ characters, and should come before the :
46+
at_pos = findfirst('@', str)
47+
if at_pos !== nothing
48+
colon_pos = findnext(':', str, nextind(str, at_pos))
49+
if colon_pos !== nothing
50+
host_part = str[nextind(str, at_pos):(prevind(str, colon_pos))]
51+
# Host should not contain / (which would suggest this is package@version:subdir syntax)
52+
# and should look like a hostname or IP address (no spaces, etc.)
53+
# Additionally, exclude things that look like version numbers (e.g., "1.0", "1.0.0")
54+
# by checking if the host contains only digits and dots (which would be a version or IP)
55+
# If it's all digits and dots, it must have at least 3 dots to be an IP (X.X.X.X)
56+
if !contains(host_part, '/') && !contains(host_part, ' ') && !isempty(host_part)
57+
# Check if this looks like a version number (e.g., "1.0", "1.0.0")
58+
# vs an IP address (e.g., "10.20.30.40") or hostname (e.g., "server.com")
59+
if all(c -> isdigit(c) || c == '.', host_part)
60+
# All digits and dots - could be version or IP
61+
# Count dots: version has 1-2 dots, IP has 3 dots
62+
dot_count = count(==('.'), host_part)
63+
if dot_count >= 3
64+
# Likely an IP address (X.X.X.X)
65+
return true
66+
end
67+
# else: likely a version number, not a URL
68+
else
69+
# Contains letters or other chars - likely a hostname
70+
return true
71+
end
72+
end
73+
end
74+
end
75+
76+
return false
4077
end
4178

4279
# Simple path detection
@@ -119,14 +156,13 @@ end
119156
function is_url_structure_colon(input::String, colon_pos::Int)
120157
after_colon = input[nextind(input, colon_pos):end]
121158

122-
# Check for git@host:path syntax
123-
if startswith(input, "git@")
124-
at_pos = findfirst('@', input)
125-
if at_pos !== nothing
126-
between_at_colon = input[nextind(input, at_pos):prevind(input, colon_pos)]
127-
if !contains(between_at_colon, '/')
128-
return true
129-
end
159+
# Check for user@host:path syntax (including git@host:path)
160+
at_pos = findfirst('@', input)
161+
if at_pos !== nothing && at_pos < colon_pos
162+
between_at_colon = input[nextind(input, at_pos):prevind(input, colon_pos)]
163+
# If there's no '/' between @ and :, this colon is part of the SSH URL structure
164+
if !contains(between_at_colon, '/')
165+
return true
130166
end
131167
end
132168

src/Resolve/graphtype.jl

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,6 +1078,11 @@ function propagate_constraints!(graph::Graph, sources::Set{Int} = Set{Int}(); lo
10781078

10791079
seen = copy(staged)
10801080

1081+
# Pre-allocate workspace for added constraints
1082+
max_spp = maximum(spp, init = 0)
1083+
added_constr1 = BitVector(undef, max_spp)
1084+
old_gconstr1 = BitVector(undef, max_spp)
1085+
10811086
while !isempty(staged)
10821087
staged_next = Set{Int}()
10831088
for p0 in staged
@@ -1091,16 +1096,28 @@ function propagate_constraints!(graph::Graph, sources::Set{Int} = Set{Int}(); lo
10911096
pkgs[p1] == uuid_julia && continue
10921097

10931098
msk = gmsk[p0][j1]
1094-
# consider the sub-mask with only allowed versions of p0
1095-
sub_msk = msk[:,gconstr0]
10961099
# if an entire row of the sub-mask is false, that version of p1
10971100
# is effectively forbidden
10981101
# (this is just like calling `any` row-wise)
1099-
added_constr1 = any!(BitVector(undef, spp[p1]), sub_msk)
1102+
# sub_msk = msk[:, gconstr0]
1103+
# added_constr1 = any!(BitVector(undef, spp[p1]), sub_msk)
1104+
# The code below is equivalent to the shorter code above, but avoids allocating
1105+
spp1 = spp[p1]
1106+
resize!(added_constr1, spp1)
1107+
fill!(added_constr1, false)
1108+
for v1 in 1:spp1
1109+
for v0 in 1:spp[p0]
1110+
if gconstr0[v0] && msk[v1, v0]
1111+
added_constr1[v1] = true
1112+
break
1113+
end
1114+
end
1115+
end
1116+
11001117
# apply the new constraints, checking for contradictions
11011118
# (keep the old ones for comparison)
11021119
gconstr1 = gconstr[p1]
1103-
old_gconstr1 = copy(gconstr1)
1120+
copy!(old_gconstr1, gconstr1)
11041121
gconstr1 .&= added_constr1
11051122
# if the new constraints are more restrictive than the
11061123
# previous ones, record it and propagate them next

0 commit comments

Comments
 (0)