-
Notifications
You must be signed in to change notification settings - Fork 704
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
new-run #4586
new-run #4586
Conversation
This reverts commit aa682f9.
ping @dmwit |
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.
Looks OK to me. Would definitely good to expand test coverage.
@@ -90,7 +121,8 @@ runAction (configFlags, configExFlags, installFlags, haddockFlags) | |||
baseCtx <- establishProjectBaseContext verbosity cliConfig | |||
|
|||
targetSelectors <- either (reportTargetSelectorProblems verbosity) return | |||
=<< readTargetSelectors (localPackages baseCtx) targetStrings | |||
=<< readTargetSelectors (localPackages baseCtx) | |||
(take 1 targetStrings) -- we drop the exe's args |
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.
Urk. Wouldn't it be better to split targetStrings first before doing anything with it? See (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.
See (A).
pkg | ||
exe | ||
</> exe | ||
let args = drop 1 targetStrings |
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.
(A) Then it wouldn't be necessary to subsequently drop it here!
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 did this because (see B1 and B2) we don't know anything about the targets at the beginning of the function, and an empty targetStrings could have a meaning. If i wrote a match for (command:args) and [] then I'd have to duplicate most of the code or put everything in an helper function.
the take/drop approach handles well the absence of a target component.
But now that i think of it, what if we do
new-run <no explicit exe here, there is only one in the package> args
? the first arg will be interpreted as a target and the command will fail.
But I don't think there is a way to handle this without ambiguities.
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.
How about new-run -- args
for "run whatever executable is the default, with arguments arg
s" and new-run args
for "search for an executable named args
and run it"? Separating arguments for a subprocess with --
is a pretty common idiom. (e.g. old-style run
supports this.)
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.
Good idea.
Cabal's flags are filtered from the targetStrings already, so it should simply be a matter of checking the first targetString
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.
No it isn't.
The first --
is stripped from the targetStrings as well, probably because it marks the end of cabal flags and the beginning of the targets.
But if we document it appropriately we could use two of them, the first separating flags and targets, the second targets and targets' args
(actually its only use will be as a "placeholder" for the target when args are also needed, like @dmwit says)
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, -- --
is ugly and prevents future modification of the syntax, so I'll leave this as it is for now (args only when the exe/package is specified)
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, #3638 may be the origin 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.
Apparently even new-run target -exearg --exearg
doesn't work, as all -
-prefixed args are interpreted as cabal args, while new-run target -- --exearg
works.
Fixing this requires some modifications to the args parsing, but I don't think this is needed, as this was also the behaviour of old-run (except #4600).
edit: also run <empty> args
isn't supported in old-run, so I'll leave new-run as is for 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.
Keep in mind: modeling our behavior after old-style run
is good to a point (because it lets us leverage folks' familiarity with the old UI); but as we're making lots of breaking UI changes here, this is also a good time to fix warts. So I think any behavior we choose should either be the right behavior or have a clear forward-compatibility story with the right behavior; backward-compatibility with the old, wrong behavior is less of a concern.
- T4477-1.0 (exe:foo) (first run) | ||
Configuring executable 'foo' for T4477-1.0.. | ||
Preprocessing executable 'foo' for T4477-1.0.. | ||
Building executable 'foo' for T4477-1.0.. |
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.
Maybe not for this PR, but we should think carefully about whether or new-run should output this sort of information. In favor: it's good to be able to see when your executable is being built. Against: you won't be able to use new-run to run executables that you want to run as part of a pipeline, since there will be extra Cabal build progress goo in your stderr. Perhaps this is related to the desire for a mode, "please run this as quickly as possible, don't rebuild at all!"
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.
The question of whether new-run should output its own information is a good one. My stance on "build or not" is that new-run should always rebuild the appropriate files, and new-exec should never rebuild anything (but should ensure that all the project's executables would be on the PATH if they were built). This makes the divide nice and clean, and supports both needs.
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.
We also have -v0
-> FilePath | ||
binDirectoryFor layout config package exe = case elabBuildStyle package of | ||
BuildAndInstall -> installedBinDirectory package | ||
BuildInplaceOnly -> inplaceBinRoot layout config package </> exe |
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.
This logic is duplicated with inplace_bin_dir
in Distribution/Client/ProjectPlanning.hs
. It would be good to have this in only one place.
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.
Will merge.
matchingExecutable p = atMostOne | ||
$ filter (\x -> Just x == componentString | ||
|| isNothing componentString) | ||
$ executablesOfPackage p |
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.
You don't have to change this, but my preference here would have been to case on p here for the package versus component case, rather than put everything through the "get the list of components it defines, and now check if anything in that list matches". My primary reasoning here is that the component case is more reflective of what the "good" code looks like, but the way things are wired up now it makes it seem like the package case (where multiple components may exist) is more important.
-> ElaboratedInstallPlan -- ^ a plan in with to search for matching exes | ||
-> [(ElaboratedConfiguredPackage, UnqualComponentName)] -- ^ the matching package and the exe name | ||
extractMatchingElaboratedConfiguredPackages | ||
pkgId component = nubBy equalPackageIdAndExe |
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 is a nub necessary here? If the same package and component show up multiple times, isn't that a problem?
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.
If the there are multiple exes, then for each exe there will be an ElaboratedConfiguredPackage
in the ElaboratedInstallPlan
.
For each ElaboratedConfiguredPackage
then, when elabPkgOrComp
(see executablesOfPackage
) is ElabComponent
then there will be one exe, the matching one (it's a Maybe
), but if it's an ElabPackage
(which happens in custom builds), then that field is Nothing and we have to look at the executables
field, which contains every exe in the package, hence the duplication.
I can't dedup before that point because i need both the package and the component name to do so (what if two packages have the seme exe name?).
Maybe I should add a comment about this. Or maybe it's a bug? Or should I add another field to ElaboratedConfiguredPackage
/ElaboratedInstallPlan
?
@@ -90,7 +121,8 @@ runAction (configFlags, configExFlags, installFlags, haddockFlags) | |||
baseCtx <- establishProjectBaseContext verbosity cliConfig | |||
|
|||
targetSelectors <- either (reportTargetSelectorProblems verbosity) return | |||
=<< readTargetSelectors (localPackages baseCtx) targetStrings | |||
=<< readTargetSelectors (localPackages baseCtx) |
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.
It is too bad there is no singular version of readTargetSelectors, since it seems like that is what you want.
of [x] -> return x | ||
[ ] -> die' | ||
verbosity | ||
"No targets given" |
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.
Won't this error message occur when I type cabal new-run
? I think you can give a more informative message here.
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.
(B2) No arguments means "treat the package in the cwd/project as a target, if there is one" (look at the comment above extractMatchingElaboratedConfiguredPackages). But this is also handled before, so same as (B1).
This will work after #4583 is merged.
I'll add a test for it too.
"No targets given" | ||
_ -> die' | ||
verbosity | ||
"Multiple targets given" |
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.
This cannot happen, by virtue of the take 1
, correct? In that case, better to mark it as an internal error.
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.
(B1) Not by virtue of the take 1. For example if we specify a package with 2 exes in the targetStrings there will be two targets. But this is handled by lines 138-149 already, so you are correct. Should iI use an assert?
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.
Wait, did i just duplicate resolveTargets
' functionality? >.<
edit: Nvm, it doesn't return enough information. I think I already checked it some time ago.
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 think "multiple targets" is a bit misleading. It's more like a single, ambiguous target. Perhaps the error could be improved by mentioning longer target strings the user might want to try to disambiguate between the possibilities, e.g. "Ambiguous target my-package; could match my-package:my-exe1 or my-package:my-exe2" or "Ambiguous target my-exe; could match my-package1:my-exe or my-package2:my-exe". (Or am I misunderstanding when we would hit this branch?)
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.
let args = drop 1 targetStrings | ||
runProgramInvocation | ||
verbosity | ||
(simpleProgramInvocation exePath args) |
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 don't think you want runProgramInvocation here; it doesn't propagate the exit code. Use rawSystemExitWithEnv
instead, see Distribution/Client/Run.hs
for how it is previously done. You may also want to consider adding LD_LIBRARY_PATH
if it is necessary.
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.
Will do.
Isn't LD_LIBRARY_PATH automated by the os or something?
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 checked and it does propagate it, as it uses rawSystemExit
under the hood.
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.
Do we have a test case that checks that new-run
propagates the exit code?
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.
Now we have one
Note that you can also do |
And by the way, can you please format your commit messages in the standard way? See this article: https://chris.beams.io/posts/git-commit/ for an explanation of what that means. No need to reword commit messages in this PR, but something to keep in mind for the future. |
@23Skidoo |
We test both for single and multiple exe/package. `new-run` must halt on ambiguities or no compatible exes, and must run the exe if one can be uniquely selected between packages and components.
Tests ready! |
# cabal new-run | ||
Up to date | ||
# cabal new-run | ||
cabal: The run command is for running a single executable at once. The target '' refers to the package MultipleExes-1.0 which includes the executables foo and bar. |
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.
And here's the need for #4583
-> Bool | ||
matchPackage pkgId pkg = | ||
pkgId == Just (elabPkgSourceId pkg) | ||
|| isNothing pkgId --if the package is unspecified (Nothing), all packages 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.
Slight bikeshed: matchPackage pkgId pkg = maybe True (elabPkgSourceId pkg ==) pkgId
seems to say what you want more concisely. Might be worth pulling out
maybeMatches :: Eq a => a -> Maybe a -> Bool
maybeMatches x = maybe True (x==)
since matchComponent
has very similar code.
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.
Looks ok, but I'd prefer to flip the arguments (so the "thing to search/filter for" is first as usual).
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'm also tempted to change Maybe with something like
data Match a = Anything | Exactly a
but losing all the functions in Data.Maybe probably isn't worth it.
$ executablesOfPackage p | ||
componentString = componentNameString =<< component | ||
atMostOne [x] = Just x | ||
atMostOne _ = Nothing |
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.
exactlyOne?
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.
or safeHead, or headMaybe...
yeah, that probably wasn't the best name :-P
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.
Left some minor comments.
Cabal/doc/nix-local-build.rst
Outdated
target, which can be a component, a package or can be left blank, as | ||
long as it can uniquely identify an executable within the project. | ||
|
||
See new-build for the target syntax. |
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/new-build/new-build
section/
Can you also add a hyperlink here?
cabal-install/changelog
Outdated
@@ -5,6 +5,9 @@ | |||
roll back the index to an earlier state. | |||
* 'cabal new-configure' now backs up the old 'cabal.project.local' | |||
file if it exists (#4460). | |||
* Completeded the 'new-run' command (#4477). The functionality is 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.
Typo: completeded.
selectedComponent | ||
elaboratedPlan | ||
|
||
-- the names to match. used only for user feedback, as |
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.
Please use proper punctuation in comments (i.e. start sentences with capital letters, end them with full stops, and so on).
let args = drop 1 targetStrings | ||
runProgramInvocation | ||
verbosity | ||
(simpleProgramInvocation exePath args) |
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.
Do we have a test case that checks that new-run
propagates the exit code?
Would be nice if @dcoutts could take a look too. |
Once I fix those minor problems there are two issues I'd like to address before merging: The args issue. I thought about it, and i think the old-run command solved the problem well. I often add a cabal flag after the exe name, and the Returning the target from But in the only function we pass to it we can only alter the
I think @dcoutts has some more info about this. This last issue is only an implementation problem and does not affect the behaviour of new-run, so if you are all ok with this I'll open another issue for this and finally merge this one (once I've fixed those typos and tests >.<) so we can have a working |
All done here, I'm merging this by tomorrow. I'll also open another pr for the duplication issue. |
Fixes #4477.
The ci should be green now. Changelog, docs, and more tests incoming.
Can someone review it in the meantime?