-
-
Notifications
You must be signed in to change notification settings - Fork 397
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
Generating a command-line executable without the GUI features #556
Comments
Thanks for the suggestion! Supporting command line apps is definitely something we'd like to do; and conceptually, there's nothing about Briefcase that is inherently "GUI" about what it generates. The sticking point is working out how to wrap the Python app so that it can be accessed as a command line app. Briefcase specifically doesn't use the "Single file executable" model, because of problems I've historically seen with that approach. A lot of Python code in the wild includes implicit assumptions about how it is deployed - that's why, for example, the egg format includes the 'zip-safe' flag. If you're particularly enthused about the single file executable model, there are tools (like pyinstaller) that can support that. You could probably even write a Briefcase platform backend that would serve as a wrapper of that tool using Briefcase's plugin system. However, we're unlikely to merge a single-file executable model into Briefcase itself. |
After thinking about this some more, I think supporting command-line apps may not be as hard as I first thought. Linux apps are now command-line compatible. System packages install are installed into So - this leaves macOS and Windows. On these platforms, the actual binaries would work fine - if only they could be put on the path. If you manually invoke I think we may be able to do this with installers. On Windows, we already have an MSI installer; modifying On macOS, we'd need to introduce a formal installer, rather than
There may be some other internal changes that would be advisable; this may warrant using an entirely different template for both macOS app and Xcode builds. In both the Windows and macOS case, these changes could be enabled by an app-level Lastly, it would be worth auditing the command line stubs to ensure that they are fully isolated from the runtime environment. We need to ensure that if the command line app is run inside an active virtualenv, the packages in the virtualenv (or |
Forgetting a single file executable for now, briefcase should be able to use the zip package format (for distribution), then the user can unpack it wherever they like and just run the executable manually from the command line. I did that (uncompressed the zip and run the .exe executable from the command line) but it "didn't work". My use case is to quickly package something up for a small number of users (maybe 1 or 2) to quickly test, get some results, and that's basically it. There might be some quick turn-around of versions to try - the installer part is just a pain for this use case. NOTE: this use case is also applicable to an app with a GUI. i.e. my use case is to produce a "portable/stand-alone" distribution, whether it's zip (or single-file exe), that can be placed anywhere an executed, without "installation". |
Hello BeeWare team & @freakboy3742 ! I have a repository that demonstrates universal python packaging and I've created a wix template that accomplishes the CLI features required. It assumes PyInstaller (directory, not one file) but I think that could be worked around. For Linux I agree with the assessment above. I was using FPM for packaging to deb and rpm and simply add a "after-install" script. Seems a hack, but hey, Linux? Afraid I don't know much about MacOS! I was sorta hoping it would "just work". Speaking of which, will briefcase build me universal binaries on the GitHub macos runner? In addition to adding to PATH, I think another feature that's helpful for Windows users is having a double-clickable version of the CLI that simply launches a terminal that's running the program. This is accomplished by having Wix create a shortcut: It works because the CLI app has a flag I'm working on a series of articles about python application distribution with a focus on security and I'd like to include the briefcase environment as I'm working through it, especially for GUI apps. I experimented with pyoxidizer and got it working but abandoned it due to bloated sizes, poor/wrong documentation, and it seems to be abandoned. PyInstaller is "OK"... but you know. 🤣 Everything flags it as a virus. Which I suppose will happen to briefcase as well once it takes off unless it's mitigated somehow by executing signed python interpreters? https://dev.to/jphutchins/building-a-universally-portable-python-app-2gng |
Ah - that's a lot easier than adding WiX features usually ends up being :-) That should be a relatively straightforward change to add to the I think Windows may need one more change in addition to this WiX configuration. The stub binary that Briefcase uses bakes in some assumptions about the app being a GUI app; those assumptions need to be relaxed if running from the command line. As an example, package an app where the Python code deliberately includes a syntax or import error; the app will pop up a dialog, rather than outputting the stack trace to the console. Strictly, I guess that change isn't essential, but I suspect most console-based Windows apps would prefer console-based behavior.
I think this might be an artefact of your usage of FPM, rather than Briefcase. Briefcase is able to generate DEB, RPM and PKG files; in my testing, installing a briefcase-generated package results in a binary in
It "just works" for a GUI app, but not for a command line app; as with Windows, the issue is putting an item on the PATH. The complication is that a macOS App doesn't have an installer - it's a self-contained executable. As described above, the fix will be to create .pkg installer, which would allow for a post-install script that adds the binary to the user's path.
Yes. The binaries produced by macOS are always universal binaries (unless explicitly disabled with
I can see how that could be useful. If this were to be added to Briefcase, I'd suggest it should be controllable with an option flag so it can be opt in/out.
Any publicity is good publicity :-)
Briefcase includes binary and installer signing for Windows, so this shouldn't be an issue (provided you've got a certificate that is trusted by Windows itself). If you find any evidence to the contrary, let us know. |
I am in touch with the Azure Code Signing team: https://techcommunity.microsoft.com/t5/security-compliance-and-identity/azure-code-signing-democratizing-trust-for-developers-and/ba-p/3604669 Generally, FOSS isn’t thrilled to pay for MS’s certificates. And FOSS should not be distributed without a means of authentication. Hoping that MS steps up to the plate here. I will investigate the wix system in briefcase and see what works. Agreed re: popups and opt-in for a shortcut. |
Good luck with that :-) I'd be happy to be proven wrong, but personally, I wouldn't hold my breath on Microsoft changing their mind or approach on this. I'd posit that an approach much more likely to succeed would be a Let's Encrypt-style community project that decides to offer a usable certificates at no cost to FLOSS projects. |
@freakboy3742 So far I have no luck getting I see this entry: self.tools.subprocess.run(
[
self.tools.rcedit.rcedit_path,
self.binary_path(app).relative_to(self.bundle_path(app)),
"--set-version-string",
"CompanyName",
app.author,
# Although "FileDescription" sounds like it should be a... description,
# this is the label that appears as a grouping in the Task Manager
# when the application runs.
"--set-version-string",
"FileDescription",
app.formal_name,
"--set-version-string",
"FileVersion",
app.version,
"--set-version-string",
"InternalName",
app.module_name,
"--set-version-string",
"OriginalFilename",
self.binary_path(app).name,
"--set-version-string",
"ProductName",
app.formal_name,
"--set-version-string",
"ProductVersion",
app.version,
"--set-icon",
"icon.ico",
],
check=True,
cwd=self.bundle_path(app),
) but I do not see where the exe is created. The Long story short, I'm looking for the entry point and it feels like there's some Windows magic happening that I'm missing! Like, it's executing the application detached from the shell that it was called from. Thanks, |
There are two backends that you can use for Briefcase on Windows- the So - if you want to tinker with the actual executable, you probably want to use the VisualStudio backend, by invoking The https://github.com/beeware/briefcase-windows-app-template is what is used by default if you run |
Excellent, I see |
@JPHutchins, when I originally evaluated sending AllocConsole();
FILE* fDummy;
freopen_s(&fDummy, "CONOUT$", "w", stdout);
freopen_s(&fDummy, "CONOUT$", "w", stderr);
freopen_s(&fDummy, "CONIN$", "r", stdin); This replaces the |
Interesting, lots of boiler plate, but that's the whole point I suppose! Feels like it was already considered: https://github.com/beeware/briefcase-windows-VisualStudio-template/blob/5e349f51e8b5f2d1c1a98806934e541182a04ffa/%7B%7B%20cookiecutter.format%20%7D%7D/%7B%7B%20cookiecutter.formal_name%20%7D%7D/Main.cpp#L52-L54 Or is the "default" at the Visual Studio template not what was used to create the stubs at https://github.com/beeware/briefcase-windows-app-template? |
Where is the source for the stub binaries located here https://github.com/beeware/briefcase-windows-app-template/tree/main/%7B%7B%20cookiecutter.format%20%7D%7D/src? |
Those stub binaries are the ones generated from the Main.cpp you linked to. |
The binaries in the |
Interesting, the |
Hello, World!
It looks like the main switch will be changing the subsystem from "Window" to "Console": JPHutchins/briefcase-windows-VisualStudio-template@a4c1c27 It's already a template so that's easy enough. After further testing I can look into creating stubs since "installing Visual Studio" could generate some friction for users 🤣 |
To be clear - if we make a change to the Visual Studio template, we pretty much automatically re-tag the app template as well to generate new stubs. If it's a change that will enable a fix to this ticket, we'll definitely re-tag the binary. Having the visual studio template is mostly a convenience for people working on the stub, with a very minor additional benefit that if you want to link in complex third-party libraries, you've got a way to do that. |
Sorry, I wasn't clear! None of this is intended to modify the existing system. New stubs would be added, postfixed with "-cli" for example, to provide the config that a terminal app needs. |
As a guide - I'd vastly prefer to not have a separate stub binary for CLI apps. I'd much rather work out a way to make the two use cases co-exist in a single binary. A related piece of work is the removal of binaries from the templates discussed as part of #1523. Binaries don't fit well into the templates; if introducing a second binary is unavoidable, that might accelerate the need to do the 'binary-ectomy" part of that ticket. |
Does #1523 suggest that once the templates are consolidated, then it would be desirable for the stub sources to get their own repos with tagged release artifacts? I can state that it would have simplified development. Potentially simplifies unit testing for the stub binaries, though I have not dug into the test systems yet. |
I might backtrack on another point. If a developer is building the Windows executable locally, it is reasonable for them to install Visual Studio. If they develop from some other OS, then they can build their binaries in a GitHub workflow. Perhaps the concern about binary variants is a moot point and the user should be forced to use the “windows VisualStudio” build. |
#1523 is still an open discussion, especially the extent to which "consolidation" actually happens. If I had to use my crystal ball, I'd say that extracting the binaries as a separate artefact is the most likely, and possibly the only part of that ticket that will actually happen. I wouldn't expect to see a separate repo, though. What I'd expect to see is that the binary artefact that is built out of the visual studio template being published to an S3 bucket, rather than a GitHub commit. The issue that exists is entirely about using Git as a distribution channel for binaries - a task for which it isn't well suited, because all historical binaries are included in the commit history. We're unlikely to ever require visual studio to package a Windows binary, except possibly as a stop-gap measure to enable CLI-based apps before we've had a chance to split out binaries from the template. |
Wouldn't it be better to use a GitHub release artifact, like we do for cpython-apple-source-deps? Keeping everything on one site would make it easier to automate and manage. |
So - once upon a time, we did use GitHub artefacts. But then we managed to hit download limits during a PyCon sprint when we had 25 or so developers simultaneously downloading the same artefact. Moving to S3 hosting avoided this problem. That doesn't impact the cpython-source-deps because it's not downloaded by many people, whereas the support packages are downloaded by every user of Briefcase. As a side effect, using S3 means we're also in the position to track download metrics, which GitHub doesn't give us. That said - I haven't revisited this in several years, so it's possible (even probable) that the download limits have been lifted. The download location ultimately doesn't really matter, as long as it's reliable. |
Well, things ended up being a bit trickier than I expected - like, learning that C++/CLI exists - but I finally have an MVP. You can clone this repo to build a briefcase CLI app for Windows: https://github.com/JPHutchins/test-briefcase-cli It relies on Visual Studio building this fork of the Windows template: https://github.com/JPHutchins/briefcase-windows-VisualStudio-template/blob/main/%7B%7B%20cookiecutter.format%20%7D%7D/%7B%7B%20cookiecutter.formal_name%20%7D%7D/Main.cpp Most of the complexity is introduced by challenges in capturing stdout/stderr. As far as I understand it, the briefcase application has 3 phases:
So, I think this is accomplished but of course needs testing in the field. I've tested three scenarios:
Problems
The needs around console vs log output are different enough from a GUI app that I think it's justified for this to be its own template. Even if there was a way around the subsystem:windows vs subsystem:console thing. Once we've decided on the approach I'd like to help draft a roadmap for this feature. |
🎉
I'm not sure this is entirely correct. You're correct that there's 3 different "phases" of output - pre-startup, runtime, and exit. The pre-startup output is entirely debugging content, mostly useful to the developers of briefcase to ensure that PYTHONPATH etc has been configured correctly. It's not part of "normal" app output; it could be considered vaguely analogous to the output you get with The "runtime" output is anything that is usually printed to stdout and stderr. The exit output will be nothing in a well behaved app; but if the python code crashes, a stack trace needs to be displayed. The underlying problem here is that a GUI application, when started from the start menu or an icon, doesn't have a stdout that is visible. As a result, if there's a crash, or some other issue with app startup, it's completely invisible to the user - they click on a link, and it does nothing. For these cases, it's essential that errors are surfaced graphically. It's also helpful if "normal" runtime output is written to a log for diagnostic purposes - if an app crashes, you can ask the user to send you the log file. When you're running a GUI application from inside Briefcase (or, I guess, starting a GUI app from a command shell), you do have a console, and it's lot more helpful to see console output in the actual console, rather than needing to wrangle a log file. The "verbose pre-start" output isn't a problem, but isn't really needed either. Briefcase currently differentiating between these two cases by checking for the existence of an attached console, and disabling log handling if the attached console exists. You're adding a third case - a pure console app. For this case, console output is preferred in all cases, because GUI popups are problematic. Log files are possibly nice to have, but are essentially superfluous because you could also generate them by redirecting console output to a file. The "verbose pre-start" output is a distraction that you don't want to see unless you're actually debugging.
I'm glad I'm not the only person who had fun wrestling this :-) However, from what I can make out, a lot of the changes you've made to the template revolve around finding a clean abstraction of logging so that log file output is preserved. Unless I'm missing something, adding handling for logging in addition to console output isn't really needed here - you can get the same logs by capturing the console output into a file. Have I misunderstood the nature of the changes you've made here?
Templates can contain logic, so switching between However, the use of a different subsystem suggests that a different binary will be needed in the app template - if that's the case, I'd vastly prefer to extract the binary from the template as described by #1523, and then use that same capability for the macOS app template as well. |
In my limited testing, a briefcase app started from a windows console will not attach to that console. As I understand it, the reason is the use of the subsystem:window instead of subsystem:console. A GUI app using subsystem:console will attach to the console if launched from one. I am not sure what happens when it's double-clicked - we wouldn't want it to open a background terminal.
CLI applications that I work on typically have a log file in addition to console output. It's really no different than a website or native application. Storing verbose log files allows for a clean UX + clues for helping users.
IMO, "meta messages from the briefcase wrapper" can 1) go to a log file or 2) be removed from "release builds" entirely. Developers of CLI applications expect that the UX of their application is identical during development and release. Briefcase should be transparent to users; that is, regardless of whether the user installs the application via pip, pipx, clones the repo, or uses a briefcase package, the experience should be the same. Though I am a big fan of how briefcase removes the full paths from the back trace 😎!
Agreed - the crash dialog abstraction is replaced by an in-console crash dialog.
I still don't understand why I can't get it to work. 😢
This is accurate, but I would summarize the choices as 1) use a log file for briefcase wrapper "meta logs" or 2) suppress logging from the briefcase wrapper entirely. In order to preserve the developers intended UX.
It's out of scope, but I think that templates are not the right tool in this case. I like CMake for managing cross-platform C/C++ build systems. Well, "like" might not be the right word... 🤣 but it is the lingua franca. One approach would be to define an Operating System Abstraction Layer, "OSAL", header which would be implemented by source files for each OS. In this way, one common "main.c" could be used for Windows/MacOS/Linux. The Python C API seems to be OS agnostic, so no issue there 🤞.
Agreed! But I think we might be headed towards the GUI apps using subsystem:console if the current behavior is not intended? |
I'm not sure if we're using different terminology here (or if I'm massively mis-remembering what happens here), but that contradicts my understanding of what the existing stub app does. AFAIR, the reason the current console management code exists is so that
It's been a long time since I've worked on this code - but it was my understanding that this was the reason the windows subsystem is selected - if you use the console subsystem, the app will create a console if there isn't one visible. We should definitely confirm this, though.
Ok - this might be a Windows vs Unix discrepancy. I haven't used Windows as a daily driver since the early 2000s, and even then it was essentially being used as a mechanism to start cygwin, so my understanding of "expected" Windows idiom may be off. From the Unix experience, I wouldn't expect a console-based app to generate a log - if you want the logs, you tee/concat the console output.
Completely agreed - My original thought about this was that the stub app would honour a BRIEFCASE_VERBOSE environment variable that would enable/disable this pre-start output; Briefcase can easily turn this option on by default, but it won't be there for a normal user.
Yeah - that was a bit of basic developer ergonomics. On macOS in particular, the paths become unmanageable really quickly; and given that Python interpreter is isolated, you're not gaining any extra information by replicating the app prefix as part of stack traces etc.
The problem is that the Python C API is literally the only part that is OS agnostic. The rest of the stub apps for iOS, Android, macOS, Windows and GTK are all radically different. There's almost nothing re-usable, other than the raw Python startup code. Even then, there's differences in language and data types - C, Objective C, and Managed C++ al lhave very different representations of String and wide-char data types, so there's very little that can be shared beyond the basic "shape" of the stub app. The use of templates also overlaps with a piece of intended functionality - we present a "native" IDE experience for projects that actually want to develop a complex app that integrates with the rest of the platform developer ecosystem. Think an app developer that wants to drop a complex macOS framework or Windows assembly into their app so that their Python code can access it. It might be possible to use CMake to do this, but it won't be a "native" developer experience. Lastly, CMake isn't an option at all for Android or iOS - they're pretty much forced into Gradle and Xcode, respectively.
I'm always up for simplification - if we can use a single stub app, that would be great. However, we need to confirm whether we can use the console subsystem for Windows apps first. |
I think that you'll need to test this - it sounds like it may be behaving as intended and I am confused by differences between
I think it must be a "me" thing. I like logs, but I don't like cluttering up UX. Briefcase shouldn't care since the developer can choose where/how to implement application logs. For a CLI app, I would support dropping all "briefcase meta logs" other than stderr. It would vastly simplify the code. Describing why it would be hard to make generic source code for the OS templates is a great reminder of the utility of projects like Briefcase! And yes I am scared off from attempting anything, thanks for helping me help myself 😭. |
I've just done an audit of the current behavior. The existing template (using the Windows subsystem), unmodified, will:
Of these, case (2) is a bit of an edge case we don't really care about for GUI apps. The real use cases are (1) (debugging in Briefcase) and (3) (a packaged and deployed app); and those are currently behaving as intended. (There's maybe a bug to be resolved with the final exception content being printed twice - I think that's crash_dialog() and PyErr_Print() double handling the crash condition - but I'd call that a cosmetic issue - the log information is there, it's just duplicated) However, for a CLI app, cases (1) and (2) are what we care about. In those cases:
If you modify the existing template to just use the console subsystem, the behavior is the same for all three:
However, if you make the following three changes to the template:
I think you get the desired behavior, with the "no log file" option. Those changes could easily be accommodated with I also tried using your template to get a comparison. There's a bug in the main.cpp that is hard coding Python 3.12; but once I fixed that, in every mode of execution (briefcase, command line, icon):
That works, but means the log file isn't really all that useful, as it only ever contains the preamble, and just the crash exception (but not any logging context leading up to that crash).
Agreed - this is effectively the case (2) with the use of the Windows subsystem from above.
I think the important differentiation here is "Briefcase meta logs" and "normal operation logs". I agree that the briefcase meta logs shouldn't be visible. The fact that they're visible is not an issue with GUI apps because you're either debugging (running from Briefcase) where it's not a problem to see them; or you're activating from a GUI, where you can't see them because there's no console, but they're in the log file where they might be useful diagnostic detail. The console app case makes it slightly more difficult, because it might be helpful to see them if you're debugging a problem with the stub, but you definitely don't want to see them in the console during normal operation. I'd suggest that outputting any meta content is something that should be controlled by an environment variable; if you set BRIEFCASE_VERBOSE=1, you see the meta content (and Briefcase can set that value as part of starting the app in Beyond that, I agree that the developer can choose what to do with logs. The stub's logging behaviour is only required because for GUI apps, |
This seems like the way to go! |
So briefcase will support console app for windows? @freakboy3742 |
@Jzhenli It doesn't currently; this ticket is open because someone has requested this feature. Recent comments on this thread seem to have resolved many of the technical issues with a Windows implementation of this feature, but someone still needs to implement the feature. |
ok, got it, thanks for your effort on this feature.@freakboy3742 @JPHutchins |
a relevant datapoint I hit today.... 😞
source: https://github.com/beeware/briefcase/actions/runs/8928248580/job/24523637866?pr=1768 |
Oh good... a rate limit on obtaining GitHub Actions. That's not going to cause problems at all... |
Is your feature request related to a problem? Please describe.
The Beeware tutorials only mention how to build and distribute a GUI app. It's unclear how to generate a command-line program.
Describe the solution you'd like
I wonder if I could opt out of the GUI requirements and build a standalone CLI executable that includes all the implicit Python dependencies, i.e., a single-file executable. Then with the
package
command this executable can be included in an installer.The text was updated successfully, but these errors were encountered: