-
Notifications
You must be signed in to change notification settings - Fork 698
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
Target package names #4889
Target package names #4889
Conversation
Instead of two constructors MatchExact and MatchInexact we now have one Match constructor with a field of data MatchClass = Inexact | Exact. This factoring helps simplify several other functions where we treat them uniformly or according to the Ord instance. The real ulterior motive however is that we'd like to introduce a third Match class value between Inexact and Exact. This refactoring is a prerequisite to that, to avoid a combinatorial explosion of cases.
Bundle up what we had been passing as separate args into a single KnownTargets type, and separate out some utils for constructing it.
To KnownPackage and KnownComponent. This is in preparation for extending KnownPackage by adding more constructors for known package names.
Previously the TargetSelector type had a type param for the type of the package that it referred to. In particular we used it with types like: type Matcher = ... -> Match (TargetSelector KnownPackage) type Renderer = TargetSelector PackageId -> ... However we are about to extend the TargetSelector so that it does not just refer to one form of package (e.g. KnownPackage) but can refer to packages via various different forms and partial information. So it no longer makes sense to have TargetSelector be paramaterised by the different states of the one kind of package it refers to, as there are now many kinds. So in preparation for that we simplify it so that it is equivalent to always using TargetSelector PackageId, and we remove the type paramater.
It is unnecessary now that we do not put the whole KnownPackage into the TargetSelector. It not being recursive also makes it possible to have a Show instance, which is handy.
That is, the TargetPackage instead of having a single PackageId contains a [PackageId]. Ultimately this will allow us to support multiple .cabal files in a single directory. But the real reason to do this generalisation now is that it helps with the TargetImplicitCwd case. For the implicit CWD case we need to be able to parse the target whether or not there is a package in the CWD. So the simplest solution is to pass in all the local CWD packages (though typically only 0 or 1) and put all of them in. Then at the end we can check if in fact there were 0 and fail. When we do want to support multiple .cabal files in a dir, we'll also need to adjust the project config code, and extend the syntax slightly so that we render as the package location for the case of multiple packages.
We are about to generalise KnownPackage in such a way that it can no longer be an instance of Package. The extra constructor will only hold a PackageName, not a full PackageId. In addition, whenever we get a KnownPackage, we do case analysis and record pattern matching to select the bits we need. This is in preparation for adding the second constructor. We switch to record pattern matching rather than field names as functions since those functions will become partial once we add the second constructor.
We'll need display of component kind both for component names (as now) and also directly for ComponentKind. Rename so the naming is sane.
Add TargetPackageNamed, like TargetPackage but for known packages within the project that are only specified by name. This includes the extra-packages from the @cabal.project@ file. It does not include indirect deps or other packages from hackage. That will be covered by a separate constructor. This replaces the previous TargetPackageName constructor which was part of a much more limited implementation of the same general idea.
Via the new KnownPackage and TargetPackageNamed stuff.
Easier to understand and document when it's a top level function.
The PackageId and ComponentName are already part of the existing AvailableTarget record argument. We need to eliminate this redundancy because for new kinds of target selectors we will not have the PackageId and ComponentName from the selector, only from the AvailableTarget selected.
Improve clarity and reduce code duplication a bit.
For local packages we have the full .cabal file so we know exactly what components are available. For packages named in extra-packages we do not know the components until after solving, which is after the target parsing stage. So this adds partial support parsing targets that refer to components of extra-packages, even though we do not yet know if the component exists.
This allows for referring to packages outside of the project, e.g. for cabal (new-)install to refer to packages from hackage. At the moment it still reports an error message that the package is not known, but this error is reported at an appropriate point where we could resolve the package name to a package from some wider context such as a hackage archive.
Travis still red... looks like a failure in the CmdRun test. |
Trivial change, but actually a wording improvement.
The failing one is #4644 again |
Is this a prior failing test? Is this one I need to fix? |
CI seems to be green now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM modulo some minor comments.
data Match a = NoMatch Confidence [MatchError] | ||
| ExactMatch Confidence [a] | ||
| InexactMatch Confidence [a] | ||
data Match a = NoMatch !Confidence [MatchError] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW, I noticed that we have exactly the same thingy in Cabal/Distribution/Simple/BuildTarget.hs
, perhaps these two can be merged?
data MaybeAmbiguous a = None [MatchError] | Unambiguous a | Ambiguous Bool [a] | ||
NoMatch _ msgs -> None msgs | ||
Match _ _ [x] -> Unambiguous x | ||
Match m d [] -> error $ "findMatch: impossible: " ++ show match' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May be worth using Data.List.NonEmpty
here, though we'll have to Distribution.Compat.
that module. Or we can just add a TODO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd vote for a TODO; this won't be the only place benefiting from NonEmpty
@@ -1560,6 +1548,15 @@ dispM = display | |||
-- Package and component info | |||
-- | |||
|
|||
data KnownTargets = KnownTargets { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be nice with a Haddock comment here.
syntaxForms :: [PackageInfo] -> [PackageInfo] -> Syntax | ||
syntaxForms ppinfo opinfo = | ||
syntaxForms :: KnownTargets -> Syntax | ||
syntaxForms KnownTargets { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we're using RecordWildcards
, this can be rewritten as KnownTargets {..} = ... knownPackagesAll ...
.
Left ((m, ms):_) -> Left (MatchingInternalError targetStr | ||
(fmap packageId m) | ||
(map (fmap (map (fmap packageId))) ms)) | ||
Right targets' -> Left (TargetSelectorAmbiguous targetStr targets') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
@@ -476,6 +476,12 @@ resolveTargets selectPackageTargets selectComponentTarget liftProblem | |||
| otherwise | |||
= Left (liftProblem (TargetProblemNoSuchPackage pkgid)) | |||
|
|||
checkTarget (TargetPackage _ _ _) | |||
= error "TODO: add support for multiple packages in a directory" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder how this use case will play together with the --cabal-file
option of configure
which allows using multiple .cabal
files for a single package.
orNoThingIn "component" (cinfoStrName c) $ do | ||
(filepath,_) <- matchComponentFile [c] str7 | ||
return (TargetComponent (packageId p) (cinfoName c) (FileTarget filepath)) | ||
case p of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use KnownPackage{pinfoId, pinfoComponents} <- matchPackage ...
? That'd make the diff a bit smaller.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I see, because of the addition of KnownPackageName
in a later commit.
@@ -149,19 +148,19 @@ data TargetSelector = | |||
-- | |||
TargetPackage TargetImplicitCwd [PackageId] (Maybe ComponentKindFilter) | |||
|
|||
-- | A package within the project speciied by name. This includes the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/speciied/specified/
@@ -1669,13 +1668,15 @@ getKnownTargets dirActions@DirActions{..} pkgs = do | |||
|
|||
|
|||
collectKnownPackageInfo :: (Applicative m, Monad m) => DirActions m | |||
-> SourcePackage (PackageLocation a) | |||
-> PackageSpecifier (SourcePackage (PackageLocation a)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re: commit message: TargetSpecifier
is not a thing, you meant either PackageSpecifier
or TargetSelector
.
@@ -1104,10 +1116,14 @@ syntaxForm2PackageComponent ps = | |||
return (TargetComponent pinfoId (cinfoName c) WholeComponent) | |||
--TODO: the error here ought to say there's no component by that name in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this TODO still valid?
@dcoutts Do you want to merge this as-is or do you want to write some additional tests before merging? The latter probably makes more sense once the feature is fully implemented. |
I'm merging this now, because the PR looks quite good on the whole and there's a chance it will start bitrotting. |
Much improved support for named packages in the target selector syntax and resolution. Instead of just supporting syntax of a single unadorned package name, all the existing syntax that allow package names are extended to support named packages.
This includes packages from the
extra-packages
but also indirect deps and packages from hackage. The resolution forextra-packages
and indirect deps should just work, while currently packages outside the project are reported as an error (but we're in a good position now to resolve to packages from hackage if we want).So this should be a better basis for the target handling for the new install code, since any command should now be able to refer to packages outside of the project (or at least we're a short step from tha now).
This passes the existing target selector integration tests. New tests need to be added to cover the
extra-packages
and indirect deps within the project, and packages outside the project.This does extend what the existing syntax can refer to so, docs & changelog will need to be updated.
BTW, this PR is designed to be read patch-by-patch, not all in one go. Each change is hopefully comprehensible on its own.
Please include the following checklist in your PR:
[ci skip]
is used to avoid triggering the build bots.Please also shortly describe how you tested your change. Bonus points for added tests!