-
Notifications
You must be signed in to change notification settings - Fork 256
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
Enable packaging Dafny as a .NET tool #2051
Conversation
What about the target language runtimes and supporting the |
That's a good question. I think the idea is that all the target language runtimes would be available through the packaging system for the respective languages (and the C# runtime is prepared to be available through NuGet, so C# compilation and execution should work smoothly). Alternatively, I expect the source files for the target language runtimes could be included as data files in the Dafny package, and spit out alongside the generated code (as I think would be necessary for C++?). They're at least not platform-dependent, whereas Z3 binaries are. |
This is the approach we should use for now, including the I'm assuming that |
Commented-out code also exists to upload them, but we need to configure an API key first.
#- name: Upload package to NuGet | ||
# run: dotnet nuget push "Binaries/Dafny*.nupkg" -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json |
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.
#- name: Upload package to NuGet | |
# run: dotnet nuget push "Binaries/Dafny*.nupkg" -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json | |
- name: Upload package to NuGet | |
run: dotnet nuget push "Binaries/Dafny*.nupkg" -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json |
Source/Dafny/DafnyPipeline.csproj
Outdated
<EmbeddedResource Include="..\DafnyRuntime\DafnyRuntime.csproj"> | ||
<LogicalName>DafnyRuntime.csproj</LogicalName> | ||
</EmbeddedResource> |
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 this also work if done in DafnyRuntime
?
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 DafnyPipeline
project doesn't depend on the DafnyRuntime
project, so I don't think we'd be able to get the files into the executable assembly that way. The DafnyRuntime
project is, as I understand it, something that generated C# code can depend on, but not something the compiler includes in its code base 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.
Would adding that dependency be so bad?
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 seems like it would be sort of a strange thing to do, to me. The DafnyRuntime
project is a C# library intended for use by generated C# code. It doesn't make sense to me for it include, for instance, DafnyRuntime.js
as an embedded resource. Said differently, it makes more sense, in my view, for the compiler to bake in the code for the various runtime systems, rather than having the runtime system for one language include the runtime systems for other languages.
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 why do you need the csproj
file itself inside the DLL? What is reading that exactly?
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 we should either get rid of the csproj
entirely, or use it as much as possible.
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.
Another way to look at it: if we embed those files in DafnyRuntime.csproj
, then the DafnyRuntime.dll
file that users download from NuGet to link with their generated C# code will also include the text of the various other language runtimes. It wouldn't cause problems to do that, but it seems awkward to me.
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 we should have separate projects for each back-end, and each of those should include their own runtime, and then , not DafnyPipeline
but DafnyDriver.csproj
should reference those. That way, consumers of Dafny as a library will consume DafnyPipeline but not necessarily all the back-ends and runtimes, while Dafny tool consumers do get all the back-ends. One example we have of a Dafny as a library consumer is DafnyLanguageServer. In the proposed setup, you can build DafnyLanguageServer without having Java instead because there's no dependency on DafnyRuntime.csproj.
Keeping DafnyRuntime.csproj as is, the above proposal turns into DafnyDriver.csproj
referencing DafnyRuntime.csproj
.
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.
Yeah, I like the main proposal you're making, @keyboardDrummer. Each back-end should be a separate project.
Given the current state of the code, though, the reason for embedding those resources in DafnyPipeline
for the moment is so that the compilers can get a reference to them through GetExecutingAssembly
. I originally put them in DafnyDriver
. It would be possible to move them back into DafnyDriver
and instead use GetEntryAssembly
.
I had previously only seen methods in the Assembly
class to load by type, and types in DafnyRuntime
aren't available in the compilers, which are currently in DafnyPipeline
, unless we make DafnyRuntime
a dependency of DafnyPipeline
, which is pretty much the opposite of what you're proposing. We could potentially use Load
to load DafnyRuntime
by name. I prefer something that doesn't require a name as a string, but not strongly.
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 see, sorry I didn't see the GetExecutingAssembly
call. In that case I think adding DafnyRuntime as a dependency to DafnyPipeline is OK 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.
How did we deal with z3?
The current plan is to have it be an implicit dependency that must be installed separately for the moment, but perhaps to add functionality to automatically download it at some point in the future. |
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.
Today is a great day for Dafny. :)
Mostly just questions, but one other blocker: we need a copy of release-downloads.yml
that installs the .NET tool version and runs the same smoke test. I don't see an easy way of triggering that automatically, so we should just make it a manual step of the release process for now.
RELEASE_NOTES.md
Outdated
@@ -1,6 +1,7 @@ | |||
# Upcoming | |||
|
|||
- feat: The IDE will show verification errors for a method immediately after that method has been verified, instead of after all methods are verified. | |||
- feat: Dafny is now available on NuGet, installable with `dotnet tool install Dafny`. |
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.
- feat: Dafny is now available on NuGet, installable with `dotnet tool install Dafny`. | |
- feat: Dafny is now available on NuGet, installable with `dotnet tool install --global Dafny`. |
Debatable perhaps, but it seems like the most common use case by far is putting it on your PATH so you can just run dafny
on the command line.
I would also be more specific about the set of packages we're publishing than just "Dafny" :)
var rt = wr.NewFile("dafny/dafny.go"); | ||
ReadRuntimeSystem("DafnyRuntime.go", rt); |
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 did this have to move?
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.
Because it now depends on program
which isn't in scope in EmitBuiltinDecls
but is in EmitHeader
. And changing the parameters of EmitBuiltinDecls
would require changes to every compiler. The other compilers read the runtimes in EmitHeader
, too, so it's more consistent that way.
You could argue that EmitBuiltinDecls
is the right place for them all to emit the runtime system, though.
x.y.z.40112). Edit the internal version number in the following | ||
places: | ||
|
||
* `Source/version.cs` |
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 add a comment to this file noting that the other two instances have to be kept in sync, just in case.
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 thought.
Source/Dafny/DafnyPipeline.csproj
Outdated
<EmbeddedResource Include="..\DafnyRuntime\DafnyRuntime.csproj"> | ||
<LogicalName>DafnyRuntime.csproj</LogicalName> | ||
</EmbeddedResource> |
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 why do you need the csproj
file itself inside the DLL? What is reading that exactly?
@@ -8,6 +8,7 @@ | |||
<RootNamespace>Microsoft.Dafny.LanguageServer</RootNamespace> | |||
<OutputPath>..\..\Binaries\</OutputPath> | |||
<ValidateExecutableReferencesMatchSelfContained>false</ValidateExecutableReferencesMatchSelfContained> | |||
<PackageVersion>3.6.0.40511</PackageVersion> |
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's the use of publishing this as a package? I think it could be useful as a too, either a separate tool or part of the one dafny tool (preferred), so that the VSCode extension can install it using dotnet install tool dafny
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 you know whether it's possible to set it up so that the language server gets distributed along with the main CLI? And, if so, how to do it? I think that'd be ideal, but wasn't sure if it would be possible, at least without significant refactoring.
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 sure it possible, and I think we should, but we should spend a little more time discussing whether we want it. For now, maybe just not publish DafnyLanguageServer, unless you see a reason why it would be useful to consume for other .NET projects.
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.
Sounds reasonable.
@@ -34,4 +34,19 @@ | |||
<ItemGroup> |
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.
Shouldn't DafnyPipeline
and DafnyRuntime
be published as packages for Dafny as a library use-cases?
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 so, but I was thinking that we'd make the modifications you mention here before doing that. If you think the library would be useful as-is then, yeah, let's include it.
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 we should publish them now. Customers can always copy things from DafnyDriver if they're missing something that's only there.
@@ -34,4 +34,19 @@ | |||
<ItemGroup> | |||
<Content Include="DafnyPrelude.bpl" CopyToOutputDirectory="PreserveNewest" /> | |||
</ItemGroup> | |||
|
|||
<ItemGroup> |
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.
For reasons I outlined here, I don't think DafnyPipeline should reference DafnyRuntime in any way. Can we move these references to DafnyDriver?
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.
Yeah, I think that's where they belong (and where I originally put them). Now that I understand how to make that work, I'm going to do it.
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 changed my mind on this yet again. The issue I came across is that the DafnyPipeline
project, which we're making available as a library, includes the compilers. If you invoke those compilers and their assembly (or, as an option, possibly some other one that's available at runtime) doesn't contain the runtime files, they'll crash. So the runtime files can't reasonably go in DafnyDriver
. They ultimately should go in language-specific compiler packages, as I think we all agree. But I still feel like it'd be messy to put them in the DafnyRuntime
assembly (even though the source code is in that package). So that leaves DafnyPipeline
, for now, until we split off the individual compilers.
This is currently failing due to a failed LSP test, |
z3 -version | ||
dafny -version | ||
- name: Check | ||
run: dafny/dafny /compileVerbose:0 /compile:0 a.dfy |
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'l have to replace dafny/dafny
with dafny
everywhere, since dotnet tool install --global
will put it directly on the PATH.
`RELEASE_NOTES.md`, and add a new "Upcoming" header above it. | ||
|
||
Push and cut a PR, get it approved, and squash and merge those | ||
changes to the `master` 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.
You need a step after #7 to manually trigger the new release-downloads-nuget.yml
workflow too.
lastdot = lines[2].rindex('.',qstart) | ||
v1 = lines[2][qstart+1:lastdot] | ||
v2 = lines[2][lastdot+1:qend] | ||
verline = lines[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.
Yuck - there has to be a better way of tracking the version number in the future :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.
Yeah, I agree. :)
@@ -16,6 +16,7 @@ | |||
using System.Diagnostics; | |||
using System.Text.RegularExpressions; | |||
using System.Reflection; | |||
using Microsoft.Win32.SafeHandles; |
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 used somehow? Hopefully not?
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.
Ah, IDEs! I was exploring various APIs to use to emit DafnyRuntime.jar
, and my IDE must have inserted that while I had something temporary in place that I removed as soon as I read the docstring. :)
@@ -2195,6 +2196,19 @@ private class GenericArrayElementLvalue : ILvalue { | |||
} | |||
} | |||
|
|||
private void EmitRuntimeJar(string targetDirectory) { |
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 is definitely "weird", but not a lot weirder than the old way. :)
It's at least more consistent with the behavior of the other languages.
@@ -2207,8 +2221,12 @@ private class GenericArrayElementLvalue : ILvalue { | |||
return false; | |||
} | |||
} | |||
|
|||
var targetDirectory = Path.GetDirectoryName(targetFilename); | |||
EmitRuntimeJar(targetDirectory); |
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.
Hmm...this should probably only happen if DafnyOptions.O.UseRuntimeLib
is false
. If it's true
, the assumption is that you'll add DafnyRuntime.jar
to the classpath yourself when you compile/run later. How hard is it to extract DafnyRuntime.jar
from the nuget package after you've installed it?
I don't think this is blocking since existing codebases since we're still publishing the zip files as well, but we should probably make sure we start publishing DafnyRuntime.jar
as well before we STOP publishing the zip files.
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.
That's an easy check to add, so I'll do it even if it's not immediately critical.
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.
Oh, and in response to your question, I don't know of any way to extract the embedded files from an assembly other than through the .NET API, but there probably is one.
This makes the first steps toward packaging Dafny as a .NET CLI tool.
Mostly fixes #1779, but doesn't yet auto-install Z3. We can do that as part of a later PR.
By submitting this pull request, I confirm that my contribution is made under the terms of the MIT license.