Skip to content

Commit

Permalink
allow startup arguments to be passed + add custom wine version option…
Browse files Browse the repository at this point in the history
… to config
  • Loading branch information
Jelmerro committed Jun 9, 2020
1 parent 9e88fa8 commit 3a32a6e
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 34 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2018 Jelmer van Arnhem
Copyright (c) 2018-2020 Jelmer van Arnhem

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
47 changes: 27 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ carafe is a tiny management tool for wine ~~bottles~~ carafes.

- A tiny command-line script to create bottles/carafes for different programs
- Automatically manages different wine prefixes
- Always uses system wine, but never changes the default .wine configuration
- Never changes the default .wine configuration
- Configure settings for each carafe separately
- Create shortcuts for installed programs and/or easily start them from the carafe CLI
- All configuration and carafes are neatly stored in a single folder (and are auto-deleted when all carafes are deleted)
Expand All @@ -16,7 +16,7 @@ carafe is a tiny management tool for wine ~~bottles~~ carafes.

## Simple usage, get going fast

There are two example provided here,
There are two examples provided here,
both of which assume you have the setup stored inside the `~/Downloads` folder.
It's recommended to read both examples before starting,
to get a good idea of the different ways to configure carafe.
Expand Down Expand Up @@ -86,6 +86,21 @@ the most important ones are listed here:
- Remove the rufus carafe completely with: `./carafe rufus remove`
- Copy a carafe to a new location (as a backup for example): `./carafe steam copy steam-backup`

### Dependencies

carafe is pure python and only uses native imports, some of them are python 3.5+.

Wine is the only dependency of carafe and it can even do some management tasks without wine installed (such as remove, info and list).
Creating and starting the carafes is done by wine, and won't work without it installed.

carafe will show a warning when the 'wine' command is not found,
and offer instructions to resolve the problem.
For some installation methods an alias might be needed,
or you can configure the wine location in the config file.
You can manually edit the `~/.carafe/config.json` to change the default wine command location.
It might be needed to create the config file, as it normally will only be stored when links or special arch types are used.
The config file also accepts a 'winetricks' field for setting the winetricks location/path separately.

## Advanced usage

carafe is a single script which can be started from any location.
Expand All @@ -95,8 +110,7 @@ Most testing was done on Linux, but I'm open to pull requests to broaden OS supp
### Wine versions

carafe will need wine as the most important dependency.
Unlike other management tools for wine bottles, carafe does not allow the user to switch between versions.
Instead it uses the system wine for all carafes.
By default, the system wine is used for all carafes.
The following advantages are a direct consequence of this decision:

- All carafes will automatically use the latest/greatest version of wine
Expand All @@ -110,6 +124,14 @@ There is one trade-off to all these advantages:
The [wine wiki](https://wiki.winehq.org/FAQ#Which_version_of_Wine_should_I_use.3F) recommends to use a recent or even the latest version of wine,
which is automatically configured by using carafe.

#### Changing a wine version

In the rare cases where you want to use a custom wine version,
you can manually update the carafe config.
A toplevel "wine" key can be used as the global wine location,
while a carafe specific "wine" key can be used to override that per carafe.
The value of the configuration should be an absolute path to the wine executable.

### Manage carafe

After running `./carafe` a list of the supported options will shown.
Expand All @@ -118,7 +140,7 @@ All of them are listed in the output as shown here:
```
usage: carafe {<carafe_name>,list} <sub_command>
Welcome to carafe 1.0.0
Welcome to carafe 1.1.0
carafe is a tiny management tool for wine bottles/carafes.
optional arguments:
Expand Down Expand Up @@ -521,21 +543,6 @@ which is something that the carafe command will handle for you.
In short, carafe is aimed to ease the configuration of wine bottles/carafes,
without introducing any magic or changing the wine prefix system.

### Dependencies

carafe is pure python and only uses native imports, some of them are python 3.5+.

Wine is the only dependency of carafe and it can even do some management tasks without wine installed (such as remove, info and list).
Creating and starting the carafes is done by wine, and won't work without it installed.

carafe will show a warning when the 'wine' command is not found,
and offer instructions to resolve the problem.
For some installation methods an alias might be needed,
or you can configure the wine location in the config file.
You can manually edit the `~/.carafe/config.json` to change the default wine command location.
It might be needed to create the config file, as it normally will only be stored when links or special arch types are used.
The config file also accepts a 'winetricks' field for setting the winetricks location/path separately.

### Logging

No wine commands executed by carafe will show any output in the terminal.
Expand Down
46 changes: 33 additions & 13 deletions carafe
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ __author__ = "Jelmer van Arnhem"
# See README.md for more details and usage instructions
__license__ = "MIT"
# See LICENSE for more details and exact terms
__version__ = "1.0.0"
__version__ = "1.1.0"
# See https://github.com/jelmerro/carafe for repo and updates

import argparse
Expand All @@ -23,7 +23,6 @@ CONFIG_FOLDER = os.path.join(os.path.expanduser("~"), ".carafe")

# CONFIG FILE LOCATION
# It's recommended to leave this path as is and only change the folder location
# Read and write function assume the file is directly inside the config folder
CONFIG_FILE = os.path.join(CONFIG_FOLDER, "config.json")


Expand Down Expand Up @@ -123,6 +122,7 @@ class Carafe:
self.prefix = os.path.join(CONFIG_FOLDER, self.name)
self.arch = self.read_arch()
self.link_location = self.read_link()
self.wine = self.read_wine()

# Linked functions directly called from the parser

Expand All @@ -137,7 +137,7 @@ class Carafe:
remove_config(self.name)
if self.arch:
modify_config(self.name, "arch", self.arch)
self.run_command(f"{WINE} wineboot --init")
self.run_command(f"{self.wine} wineboot --init")

def install(self, args):
self.exists()
Expand All @@ -155,9 +155,9 @@ class Carafe:
print("The specified executable could not be found")
sys.exit(1)
if executable.endswith(".msi"):
self.run_command(f"{WINE} msiexec /i \"{executable}\"")
self.run_command(f"{self.wine} msiexec /i \"{executable}\"")
else:
self.run_command(f"{WINE} \"{executable}\"")
self.run_command(f"{self.wine} \"{executable}\"")

def start(self, args):
self.exists()
Expand All @@ -176,16 +176,21 @@ class Carafe:
start = self.link_location
self.arch = self.read_arch()
path = os.path.join(self.prefix, "drive_c", start)
arg_string = " "
for arg in args.arguments:
arg_string += f"{arg} "
if args.keep_log:
self.run_command(f"{WINE} \"{path}\"", os.path.dirname(path))
self.run_command(
f"{self.wine} \"{path}\" {arg_string}",
os.path.dirname(path))
else:
env = os.environ
env["WINEPREFIX"] = self.prefix
if self.arch:
env["WINEARCH"] = self.arch
env["WINEDEBUG"] = "-all"
subprocess.run(
f"{WINE} \"{path}\"", shell=True,
f"{self.wine} \"{path}\" {arg_string}", shell=True,
stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
cwd=os.path.dirname(path), env=env)

Expand Down Expand Up @@ -295,11 +300,11 @@ class Carafe:

def regedit(self, args):
self.exists()
self.run_command(f"{WINE} regedit")
self.run_command(f"{self.wine} regedit")

def winecfg(self, args):
self.exists()
self.run_command(f"{WINE} winecfg")
self.run_command(f"{self.wine} winecfg")

def winetricks(self, args):
self.exists()
Expand All @@ -326,6 +331,13 @@ class Carafe:
return config[self.name]["link"]
return None

def read_wine(self):
config = read_config()
if self.name in config:
if "wine" in config[self.name]:
return config[self.name]["wine"]
return WINE

def read_arch(self):
config = read_config()
if self.name in config:
Expand Down Expand Up @@ -362,7 +374,8 @@ class Carafe:
windows = os.path.join(drive_c, "windows")
exe_pattern = os.path.join(drive_c, "**", "*.exe")
executables = []
for exe in glob.glob(exe_pattern, recursive=True):
exe_files = sorted(glob.glob(exe_pattern, recursive=True))
for exe in exe_files:
if not exe.startswith(windows):
exe = exe.replace(drive_c, "", 1)
if exe.startswith("/"):
Expand Down Expand Up @@ -418,7 +431,7 @@ class Carafe:
command = f"env WINEPREFIX=\"{self.prefix}\""
if self.arch:
command += " WINEARCH=\"{self.arch}\""
command += f" {WINE} \"C:/{loc}\""
command += f" {self.wine} \"C:/{loc}\""
path = os.path.dirname(os.path.join(self.prefix, "drive_c", loc))
return "#!/usr/bin/env xdg-open\n" \
"[Desktop Entry]\n" \
Expand All @@ -428,8 +441,7 @@ class Carafe:
f"Path={path}\n"


# Main startup steps
if __name__ == "__main__":
def main():
# Prepare the main parser
description = f"Welcome to carafe {__version__}\n" \
"carafe is a tiny management tool for wine bottles/carafes.\n"
Expand Down Expand Up @@ -474,6 +486,9 @@ if __name__ == "__main__":
sub_start.add_argument(
"-l", "--location",
help="Location of the executable inside the carafe to start")
sub_start.add_argument(
"arguments", nargs=argparse.REMAINDER,
help="Any arguments will directly be passed to the started executable")
# Rename
sub_rename = sub.add_parser(
"rename", help="rename an existing carafe",
Expand Down Expand Up @@ -557,3 +572,8 @@ if __name__ == "__main__":
# Call the correct subcommand on the Carafe class
carafe = globals()["Carafe"](carafe_name)
getattr(carafe, subargs.sub)(subargs)


# Main startup steps
if __name__ == "__main__":
main()

0 comments on commit 3a32a6e

Please sign in to comment.