Skip to content

Commit

Permalink
make it possible to specify compatibility for the project
Browse files Browse the repository at this point in the history
  • Loading branch information
KristofferC committed Jun 12, 2018
1 parent 1e83aaa commit 8de8474
Show file tree
Hide file tree
Showing 7 changed files with 470 additions and 268 deletions.
73 changes: 64 additions & 9 deletions stdlib/Pkg/docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,9 +329,9 @@ Let’s say we found a bug in one of our dependencies, e.g. `JSON` that we want
...
```

By default, the package get cloned to the `~/.julia/dev` folder but can also be set by the `JULIA_PKG_DEVDIR` environment variable.
By default, the package gets cloned to the `~/.julia/dev` folder but can also be set by the `JULIA_PKG_DEVDIR` environment variable.
When we have fixed the bug and checked that `JSON` now works correctly with our project, we can make a PR to the `JSON` repository.
When the PR has been merged we can go over to track the master branch and finally, when a new release of `JSON` is made, we can go back to using the versioned `JSON` using the command `free` and `update` (see next section):
When the PR has been merged we can go over to track the master branch and finally when a new release of `JSON` is made, we can go back to using the versioned `JSON` using the command `free` and `update` (see next section):

```
(HelloWorld) pkg> free JSON
Expand All @@ -344,11 +344,11 @@ When the PR has been merged we can go over to track the master branch and finall

It is also possible to give a local path as the argument to `develop` which will not clone anything but simply use that directory for the package.

Overriding the dependency path for a non registered package is done by giving the git-repo url as an argument to `develop`.
Overriding the dependency path for a non-registered package is done by giving the git-repo url as an argument to `develop`.

## Updating dependencies

When new versions of packages the project is using are released, it is a good idea to update. Simply calling `up` will try to update *all* the dependencies of the project. Sometimes this is not what you want. You can specify a subset of the dependencies to upgrade by giving them as arguments to `up`, e.g:
When new versions of packages the project is using are released, it is a good idea to update. Simply calling `up` will try to update *all* the dependencies of the project. Sometimes this is not what you want. You can specify a subset of the dependencies to upgrade by giving them as arguments to `up`, e.g:

```
(HelloWorld) pkg> up JSON
Expand All @@ -363,7 +363,7 @@ The version of all other direct dependencies will stay the same. If you only wan
Packages that track a branch are not updated when a minor upgrade is done.
Developed packages are never touched by the package manager.

If you just want install the packages that are given by the current `Manifest.toml` use
If you just want to install the packages that are given by the current `Manifest.toml` use

```
(HelloWorld) pkg> instantiate
Expand Down Expand Up @@ -394,18 +394,73 @@ or
(HelloWorld) pkg> preview up
```

will show you the effects adding `Plots`, or doing a full upgrade, respectively, would have on your project.
will show you the effects of adding `Plots`, or doing a full upgrade, respectively, would have on your project.
However, nothing would be installed and your `Project.toml` and `Manfiest.toml` are untouched.

## Using someone elses project
## Using someone else's project

Simple clone their project using e.g. `git clone`, `cd` to the project directory and call

```
(SomeProject) pkg> instantiate
```

If the project contains a manifest, this will install the packages at the same state that is given by that manifest.
Otherwise it will resolve the latest versions of the dependencies compatible with the project.
If the project contains a manifest, this will install the packages in the same state that is given by that manifest.
Otherwise, it will resolve the latest versions of the dependencies compatible with the project.

## Compatibility

Compatibility refers to the ability to restrict what version of the dependencies that your project is compatible with.
If the compatibility for a dependency is not given, the project is assumed to be compatible with all versions of that dependency.

Compatibility for a dependency is entered in the `Project.toml` file as for example:

```toml
[compatibility]
Example = "0.4.3"
```

After a compatibility entry is put into the project file, `up` can be used to apply it.

The format of the version specifier is described in detail below.

!!! info
There is currently no way to give compatibility from the Pkg REPL mode so for now, one has to manually edit the project file.

### Version specifier format

Similar to other package managers, the Julia package manager respects [semantic versioning](https://semver.org/) (semver).
As an example, a version specifier is given as e.g. `1.2.3` is therefore assumed to be compatible with the versions `[1.2.3 - 2.0.0)` where `)` is a non-inclusive upper bound.
More specifically, a version specifier is either given as a **caret specifier**, e.g. `~1.2.3` or a **tilde specifier** `^1.2.3`.
Caret specifiers are the default and hence `1.2.3 == ~1.2.3`. The difference between a caret and tilde is described in the next section.

#### Caret specifiers

A caret specifier allows upgrade that would be compatible according to semver.
An updated dependency is considered compatible if the new version does not modify the left-most non zero digit in the version specifier.

Some examples are shown below.

```
^1.2.3 = [1.2.3, 2.0.0)
^1.2 = [1.2.0, 2.0.0)
^1 = [1.0.0, 2.0.0)
^0.2.3 = [0.2.3, 0.3.0)
^0.0.3 = [0.0.3, 0.0.4)
^0.0 = [0.0.0, 0.1.0)
^0 = [0.0.0, 1.0.0)
```

While the semver specification says that all versions with a major version of 0 are incompatible with each other, we have made that choice that
a version given as `0.a.b` is considered compatible with `0.a.c` if `a != 0` and `c >= b`.

#### Tilde specifiers

A tilde specifier provides more limited upgrade possibilities. With a caret, only the last specified digit is allowed to increment by one.
This gives the following example.

```
~1.2.3 = [1.2.3, 1.3.0)
~1.2 = [1.2.0, 1.3.0)
~1 = [1.0.0, 2.0.0)
```
8 changes: 4 additions & 4 deletions stdlib/Pkg/ext/TOML/src/print.jl
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ function printvalue(io::IO, value; sorted=false)
end
end

function _print(io::IO, a::AbstractDict, ks=String[]; sorted=false)
function _print(io::IO, a::AbstractDict, ks=String[]; sorted=false, by=identity)
akeys = keys(a)
if sorted
akeys = sort!(collect(akeys))
akeys = sort!(collect(akeys), by=by)
end
first_block = true

Expand Down Expand Up @@ -95,5 +95,5 @@ function _print(io::IO, a::AbstractDict, ks=String[]; sorted=false)
end
end

print(io::IO, a::AbstractDict; sorted=false) = _print(io, a, sorted=sorted)
print(a::AbstractDict; sorted=false) = print(stdout, a, sorted=sorted)
print(io::IO, a::AbstractDict; kwargs...) = _print(io, a; kwargs...)
print(a::AbstractDict; kwargs...) = print(stdout, a; kwargs...)
29 changes: 20 additions & 9 deletions stdlib/Pkg/src/Operations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,9 @@ function collect_project!(ctx::Context, pkg::PackageSpec, path::String, fix_deps
fix_deps_map[pkg.uuid] = valtype(fix_deps_map)()
!isfile(project_file) && return false
project = read_project(project_file)
compat = get(project, "compatibility", Dict())
for (deppkg_name, uuid) in project["deps"]
vspec = VersionSpec() # TODO: Update with compatibility from Project
vspec = haskey(compat, deppkg_name) ? Types.semver_spec