Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support rich versions (strictly & prefer) #471

Open
LouisCAD opened this issue Dec 19, 2021 · 6 comments
Open

Support rich versions (strictly & prefer) #471

LouisCAD opened this issue Dec 19, 2021 · 6 comments
Assignees
Milestone

Comments

@LouisCAD
Copy link
Member

No description provided.

@LouisCAD LouisCAD created this issue from a note in Priorities 📝 (Priorities) Dec 19, 2021
@LouisCAD LouisCAD added this to the v1.0.0 milestone Dec 19, 2021
@LouisCAD LouisCAD self-assigned this Feb 6, 2022
@LouisCAD LouisCAD changed the title Support rich versions (strictly…), and maybe support reject Support rich versions (strictly & prefer) Feb 6, 2022
@LouisCAD
Copy link
Member Author

LouisCAD commented Feb 6, 2022

Implementation should be quite easy, setting the version happens in a single place, where we have the withDependencies { … } block.

However, there are some design decisions to tackle first because there are multiple ways to implement this:

  1. We can have a variant of the version placeholder, and we could add a corresponding function/property in the dependency notations API. However, this requires checking the other places that work with the version placeholder to ensure we don't break anything.
  2. We can support a special string value to be passed in the version { … } block.
  3. We can support a special value to be passed in the version { … } block to compare via reference.
  4. We can add parameter-less overloads of the strictly() and prefer() functions, hijacking the Gradle package.

For now, I'm biased towards option 4, but maybe someone has an objection to that, or an idea for the 3 other options, or a different option mind?

@LouisCAD
Copy link
Member Author

LouisCAD commented Feb 6, 2022

I started implementing option4, but there's something I'm not super happy with.

While it reads okay(ish?) for strictly:

dependencies {
    api(Something.whatever) {
        version { strictly() }
    }
}

it reads less nicely when the version parameter is omitted for the prefer function:

dependencies {
    api(Something.whatever) {
        version { prefer() }
    }
}

Instead of an extension for MutableVersionConstraint, we can have extensions on ExternalModuleDependency (the receiver of the block passed to api in the snippet above), or on Dependency? (before casting to ExternalModuleDependency), which is the type returned by the api function as well as its "friends" like implementation, testRuntimeOnly, etc.

Here are a few examples of how these could look like…
as extensions for Dependency?:

dependencies { // Let's call it option 5.
    api(Something.whatever).strictVersion()
    api(Something.whatever).preferDeclaredVersion()
    api(Something.whatever).preferNoTransitiveUpgrade()
}

as extensions for ExternalModuleDependency:

dependencies { // and this is option 6.
    api(Something.whatever) { strictVersion() }
    api(Something.whatever) { preferDeclaredVersion() }
    api(Something.whatever) { preferNoTransitiveUpgrade() }
}

@LouisCAD
Copy link
Member Author

LouisCAD commented Feb 6, 2022

For option 1, where we would use variants of the version placeholder, I have been thinking that we could use _ for require, as it's already the case, and use _! for prefer, and _!! for strictly. I find it intuitive, but also not very explicit at the same time, and not very discoverable, so I'm not sure about that, though we can have related functions for the DependencyNotation type, that would make it discoverable for built-in dependency notations.

@LouisCAD
Copy link
Member Author

LouisCAD commented Feb 6, 2022

I just discovered that option 5 cannot work.
However, here's a variant that can work when coupled with option 1:

dependencies { // This is option 1
    api(Something.whatever.strictVersion())
    api(Something.whatever.preferDeclaredVersion())
    api("com.example.something:something:_") // Require (default, allows transitive upgrades)
    api("com.example.something:something:_!") // Prefer (avoids upgrades if possible)
    api("com.example.something:something:_!!") // Strictly
}
dependencies { // This is option 7
    api(Something.whatever) {
        version { strictVersion() } // Variant A
    }
    api(Something.whatever) {
        version { strictlyDeclaredVersion() } // Variant B
    }
    api(Something.whatever) {
        version {
            preferDeclaredVersion()
            reject("1.0.0")
        }
    }
    api("com.example.something:something:_") 
    api("com.example.something:something:_") {
        version { preferDeclaredVersion() }
    }
    api("com.example.something:something:_") {
        version { strictVersion() } // Variant A
    }
    api("com.example.something:something:_") {
        version { strictlyDeclaredVersion() } // Variant B
    }
}

@LouisCAD
Copy link
Member Author

LouisCAD commented Feb 7, 2022

I was considering implementing option 1, but while Gradle let us do what we want with _! as a version placeholder, when we put _!!, it eats both exclamation marks… 😭

@LouisCAD
Copy link
Member Author

LouisCAD commented Feb 7, 2022

It turns out using _? and _! can work, so I'll probably end up using those.

That said, I'm concerned about how these fit in regarding version ranges, like when defining a range for require, and a value for prefer. What I started to work on doesn't support these use cases for now, I'll have to think about what we can do there to make for an intuitive support.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Priorities 📝
  
Priorities
Development

No branches or pull requests

2 participants