-
Notifications
You must be signed in to change notification settings - Fork 790
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
[WIP] - Build packagemanagement based on Steffen's Package manager design. #4042
Conversation
src/fsharp/FSComp.txt
Outdated
@@ -1422,3 +1422,6 @@ notAFunctionButMaybeIndexer,"This expression is not a function and cannot be app | |||
notAFunctionButMaybeDeclaration,"This value is not a function and cannot be applied. Did you forget to terminate a declaration?" | |||
3218,ArgumentsInSigAndImplMismatch,"The argument names in the signature '%s' and implementation '%s' do not match. The argument name from the signature file will be used. This may cause problems when debugging or profiling." | |||
3219,pickleUnexpectedNonZero,"An error occurred while reading the F# metadata of assembly '%s'. A reserved construct was utilized. You may need to upgrade your F# compiler or use an earlier version of the assembly that doesn't make use of a specific construct." | |||
3220,couldNotLoadDependencyManagerExtenstion,"The dependency manager extension %s could not be loaded. Message: %s" |
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 should be in single quotes here
try | ||
CustomAttributeExtensions.GetCustomAttributes(theAssembly) | ||
|> Seq.tryFind (fun a -> a.GetType().Name = attributeName) | ||
|> function | Some _ -> true | _ -> false |
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 can be replaced with: |> Seq.exists (fun a -> ...
// * to minimize footprint of integration in fsi/CompileOps | ||
|
||
/// hardcoded to net461 as we don't have fsi on netcore | ||
let targetFramework = "net461" |
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.
What is this actually used for? Can we not determine our current .Net Framework version instead of hardcoding?
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.
eventually yes. but not needed 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.
Well what if I’m on .Net 4.5?
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.
then fsi is still...
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.
Right but this is going to be passed to nuget/paket/whatever to determine which assembly to reference... the precise .Net Framework version is important.
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.
Hard coding net461 is wrong for FSharp.Compiler.Service.dll
(which can be used to build tools that run on other frameworks) and for editing environments. I'd rather see this passed down from the host tool, or otherwise configurable somehow.
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.
Of course hard coding is wrong, but it's a bit tricky since it needs to be the executing environment. Not necessarily the fcs environment. Consider we host the feature in fake
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.
Then ionide might run in net461 but fake process in netcore. Of course fake would restore a second time, but that would be a waste
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.
Then ionide might run in net461 but fake process in netcore. Of course fake would restore a second time, but that would be a waste
I see.
It is true that the "default assumed target framework" for the F# scripting model is net461. That is, an editing tool, or execution tool should process a script on the assumption it is targeting net461.
For FSI on .NET Core this will be netcoreapp20 (we should at least define this conditionally here?), but editing tools will still have to assume net461 by default.
Because of this, we will likely need to add a directive #framework "net451"
or #framework "netcoreapp20"
I suppose, to allow editing tools to know that the script is really designed for use on .NET Core.
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.
Specification and switching of an alternate target framework is TBD. The default target framework will either depend on the host or be netstandard2.0.
src/fsharp/fsi/fsi.fs
Outdated
tcConfigB.AddIncludePath(m,folder,"") | ||
|
||
match loadScript with | ||
| Some loadScript -> istate := fsiDynamicCompiler.EvalSourceFiles (ctok, !istate, m, [loadScript], lexResourceManager, errorLogger) |
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.
Instead of using a ref variable here, this can be restructured as a Seq.fold over tcConfigB.packageManagerLines
Thank you @KevinRansom! :) |
| _ -> | ||
match DependencyManagerIntegration.tryFindDependencyManagerByKey m packageManagerKey with | ||
| None -> | ||
errorR(DependencyManagerIntegration.createPackageManagerUnknownError packageManagerKey m) |
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.
Perhaps this should just be a warning. There will be a later error almost for sure - and if there isn't for some reason then it seems reasonable to proceed with compilation/execution
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.
Yes additionally, I think this would be a breaking change
|> Seq.tryFind (fun a -> a.GetType().Name = attributeName) | ||
with | _ -> None | ||
|
||
let getInstanceProperty<'treturn> (theType: Type) indexParameterTypes propertyName = |
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 believe indexParameterTypes is always empty in this PR, I think it's best to remove it.
let executingAssembly = | ||
typeof<IDependencyManagerProvider>.GetTypeInfo().Assembly | ||
yield Path.GetDirectoryName(executingAssembly.Location) | ||
#if FX_NO_APP_DOMAINS |
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.
Use #if !FX_NO_APP_DOMAINS
interface System.IDisposable with | ||
member __.Dispose() = () | ||
|
||
let registeredDependencyManagers = ref None |
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.
Does this have to be a static mutable global? Why not hold it in TcConfig or the TcImports (the one for the non-framework assemblies)?
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.
Looking good! I made a few comments above. Some more tests needed :)
@KevinRansom @cartermp We should add an RFC for this. Language/tooling suggestion is here: fsharp/fslang-suggestions#542 |
This is the one that will need reworking: https://github.com/fsharp/fslang-design/blob/master/tooling/FST-1027-fsi-references.md |
Surely you had an RFC when you did this original work? |
Ping :-) When do we plan to add this? End of 2018?(no trolling) |
When it's ready. |
@realvictorprm sorry for the lack of progress. This is on me, it's an expected deliverable, but stuff keeps coming up. Kevin |
Many thanks for the response. That's all what I wanted to know so far :-)
Am 26.01.2018 9:04 nachm. schrieb "Kevin Ransom (msft)" <
[email protected]>:
… @realvictorprm <https://github.com/realvictorprm> sorry for the lack of
progress. This is on me, it's an expected deliverable, but stuff keeps
coming up.
Sorry ...
Kevin
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#4042 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AYPM8HZpVGkYFo9m2gPaB8BgD0wbOB6dks5tOi_qgaJpZM4QxuqJ>
.
|
Would this also support plain old environment variables?
|
That would depend on the package manager. #r "pathToDll" doesn't, and won't be amended to enable. however: I'm guessing but if someone wrote a filesystem package manager: would, if someone chose to make a filesystem package manager. I hope this helps Kevin |
Yes, it does help. |
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.
Some questions/clarifications. Maybe some comments are required (or another signature for the interface)
| _ -> | ||
match DependencyManagerIntegration.tryFindDependencyManagerByKey m packageManagerKey with | ||
| None -> | ||
errorR(DependencyManagerIntegration.createPackageManagerUnknownError packageManagerKey m) |
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.
Yes additionally, I think this would be a breaking change
abstract Name : string | ||
abstract ToolName: string | ||
abstract Key: string | ||
abstract ResolveDependencies : string * string * string * string * string seq -> string option * string list |
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 I understand correctly that this returns F# code which itself is compiled and run?
Shouldn't we just have a list of implementation and reference assemblies or do we need that for fable somehow?
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.
@matthid Yes, the extension manager, returns a script to FSI that is executed. The interesting thing about this approach is it's very easy to reason about and debug, what is happening. For example we know what happens when you reference an already referenced assembly.
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.
Well I have to say that there is some technical elegance in this.
But is there particular use-case where we need this? Because on the other side it slows down things and might introduce a lot more error scenarios where a user might not realize what happens behind the scenes. (You might argue that performance is probably not a huge deal considering the package-manager might go on the network and I basically agree, on the other side stuff might be cached already and the package manager might return very fast)
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.
@matthid / @KevinRansom it actually returns location of a generated script, it means if any error surfaces, it will point to a temporary file which remains there for debug purpose.
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 but realistically what can a user do if there is an error in that script? Especially a newcomer will not know where to report the issue. While we can probably pass the context and improve the error message I don't think it is part of this PR (or the spec for that matter).
| None -> istate // error already reported | ||
| Some (loadScript, additionalIncludeFolders) -> | ||
for folder in additionalIncludeFolders do | ||
tcConfigB.AddIncludePath(m, folder, "") |
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 do we actually need include folders when we have the load-Script?
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.
@matthid AFAIR they affect the resolution of subsequently loaded .fsx as well as references.
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.
But what is the difference to the script containing #I
?
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 suspect the choice to return both the script location and additional include folders was taken in context of ScriptCs integration but don't remember for sure.
I'd need to check where that second value is used in context of FSI.
Other advantages (beside troubleshooting) of having a temporary script loaded is caching (based on a hash of the package manager directive contents), and making it easier to load a graph of things using relative path from the top level temporary script.
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.
Note that I'm not against a temporary script at all. I'm just trying to follow the reasoning, because the spec doesn't really say or list pros/cons or something like that. From my perspective it just makes things more complicated. Also I don't really understand the caching argument because the package manager can still cache a list of paths and so can the compiler?
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.
There is still room to adjust the PackageManager integration signatures, and actually document it better in the code (I see there are no comments about the return value).
One concern though was that the return value has to be plain .net types rather than F# specific or relying on extra assembly, to allow easier interop via reflection from reflection / tooling perspective.
Are you concerned the temporary script is going to be a concern?
This is @forki's original PR. With the paket specific stuff removed.
Discussion: #2483
So there will be a few minor tweaks to implement a package based extensibility mechanism, that will be aligned with Don's proposal for TypeProvider design time components. Other than that the basic idea remains unchanged. The package manger receives the text for a #r" it ensures that the packages are deployed, provides FSI a script fragment that references the correct dlls to be executed.
Packages can be specified and loaded during an interactive session.
new PackageManagers can be loaded whilst a sript is executing and from that point on in the session.
Todo: