corking, and a major refactor to accomodate it

This commit is contained in:
Nicholas O'Connor 2017-03-28 12:41:25 -07:00
parent f140b9b68c
commit 91526bfe5b
13 changed files with 409 additions and 1622 deletions

View File

@ -3,10 +3,10 @@ project(cellar CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_FLAGS -pipe)
set(CMAKE_CXX_FLAGS_DEBUG -O0 -g)
set(CMAKE_CXX_FLAGS_RELEASE -O2)
set(CMAKE_CXX_FLAGS_RELWITHDBGINFO ${CMAKE_CXX_FLAGS_RELEASE} -g)
set(CMAKE_CXX_FLAGS "-pipe")
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -Wall")
set(CMAKE_CXX_FLAGS_RELEASE "-O2")
set(CMAKE_CXX_FLAGS_RELWITHDBGINFO "${CMAKE_CXX_FLAGS_RELEASE} -g")
if(NOT CMAKE_BUILD_TYPE)
message(STATUS "Assuming this is a release build. -DCMAKE_BUILD_TYPE=Debug otherwise.")
@ -75,8 +75,22 @@ function(get_sources globtarget output)
endfunction(get_sources)
set(cellar_LIBRARIES)
function(cellar_library target)
get_sources("src/${target}/*.cpp" targetsources)
function(cellar_library)
set(multiValueArgs SUBDIRS)
set(oneValueArgs TARGET)
cmake_parse_arguments(cellar_library "" "${oneValueArgs}"
"${multiValueArgs}" ${ARGN})
set(target ${cellar_library_TARGET})
set(targetsources)
foreach(subdir ${cellar_library_SUBDIRS})
get_sources("src/${subdir}/*.cpp" subdirsources)
foreach(source ${subdirsources})
set(targetsources ${targetsources} ${source})
endforeach(source)
endforeach(subdir)
add_library(${target} SHARED ${targetsources})
add_dependencies(${target} cog)
@ -84,8 +98,7 @@ function(cellar_library target)
set(cellar_LIBRARIES ${cellar_LIBRARIES} ${target} PARENT_SCOPE)
endfunction(cellar_library)
cellar_library(bottles)
cellar_library(launch)
cellar_library(TARGET cellarlib SUBDIRS bottles launch)
file(GLOB coresources RELATIVE "${CMAKE_SOURCE_DIR}"
"${CMAKE_SOURCE_DIR}/src/*.cpp")

View File

@ -38,7 +38,15 @@ namespace cellar {
string get_config(string);
bool set_config(string, string);
};
extern Bottle active_bottle;
extern map<string, Bottle> get_bottles();
extern string resolve_bottle(string);
extern void cork(string, bool);
extern void press(string, vector<string>, bool);
extern void uncork(string);
}
}

View File

@ -11,8 +11,6 @@ using namespace std;
namespace cellar {
extern void print_header();
extern bottles::Bottle active_bottle;
extern bool dryrun;
extern bool verbose;
}

View File

@ -13,6 +13,7 @@ using namespace cellar::commands;
namespace cellar {
namespace launch {
extern void popen(string);
extern void popen(vector<string>);
/*[[[cog
import cog

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,6 @@
#include "bottles.hpp"
#include "internal/bottles.hpp"
#include "dll.hpp"
#include "fs.hpp"
#include "output.hpp"
@ -56,7 +55,7 @@ Bottle::Bottle(string patharg) {
}
}
DLL_PUBLIC map<string, Bottle> cellar::bottles::get_bottles() {
map<string, Bottle> cellar::bottles::get_bottles() {
map<string, Bottle> result;
string homepath = getenv("HOME");
@ -73,6 +72,31 @@ DLL_PUBLIC map<string, Bottle> cellar::bottles::get_bottles() {
return result;
}
string cellar::bottles::resolve_bottle(string bottlechoice) {
string result;
if (bottlechoice.substr(0,1) == "/" || bottlechoice.substr(0,1) == ".") { // absolute or relative path
result = bottlechoice;
} else if (bottlechoice.substr(0,1) == "~") { // "absolute" path in home directory, not expanded by the shell for some reason (i've seen some shit)
// this is a naive replacement and will fail if the user tries something like ~nick/.wine
// i'm figuring at that point if you're doing that, you'll also recognize if your shell
// isn't actually expanding your path...
bottlechoice.replace(0,1,getenv("HOME"));
// or at least you'll think to use verbose mode to make sure it's loading the right directory
output::warning("your shell didn't expand your given path properly, doing a naive replacement", true);
result = bottlechoice;
} else {
if (bottlechoice.substr(0,6) == ".wine.") {
output::statement("tip: cellar can add the \".wine.\" prefix automatically");
bottlechoice.replace(0,6,"");
}
string homepath = getenv("HOME");
string fullbottlepath = homepath + "/.wine." + bottlechoice;
result = fullbottlepath;
}
return result;
}
void cellar::bottles::print_bottles(int argc, vector<string> argv) {
map<string, Bottle> bottles = get_bottles();

View File

@ -4,3 +4,6 @@ activate switch_active_bottle Switch the active WINE bottle.
config config_command Change configuration options.
create create_bottle Create a new WINE bottle.
remove remove_bottle Remove a WINE bottle.
cork cork_command Cork a bottle, to be "uncorked" later.
press press_command
uncork uncork_command Uncork a bottle.

View File

@ -45,6 +45,12 @@ bool Bottle::save_config() {
ofstream configstream(jsonpath);
configstream << this->config.dump(4);
configstream.close();
if (fs::exists(jsonpath + ".old")) {
// at this point it should be safe to remove the old config
fs::remove(jsonpath + ".old");
}
return true;
}

98
src/bottles/cork.cpp Normal file
View File

@ -0,0 +1,98 @@
#include <cstdlib>
#include <string>
#include <vector>
#include <boost/algorithm/string/join.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include "json.hpp"
#include "bottles.hpp"
#include "internal/bottles.hpp"
#include "launch.hpp"
#include "internal/launch.hpp"
#include "fs.hpp"
#include "output.hpp"
using namespace std;
using namespace cellar;
using namespace cellar::bottles;
using json = nlohmann::json;
void cellar::bottles::cork(string bottlearg, bool remove) {
string bottlepath = resolve_bottle(bottlearg);
// ensure bottle is corkable
if (!boost::filesystem::exists(bottlepath + "/cellar.json")) {
output::error("cannot cork anonymous (or nonexistent) wine bottles");
return;
}
// ensure corked bottle directory exists
string homedir = getenv("HOME");
string datadir = homedir + "/.local/share/cellar";
string corkdir = datadir + "/corked";
string corkpath = corkdir + "/" + bottlearg + ".json";
if (!boost::filesystem::exists(datadir)) { boost::filesystem::create_directory(datadir); }
if (!boost::filesystem::exists(corkdir)) { boost::filesystem::create_directory(corkdir); }
if (boost::filesystem::exists(corkpath)) {
output::error("bottle " + bottlearg + " already corked");
return;
}
boost::filesystem::copy(bottlepath + "/cellar.json", datadir + "/corked/" + bottlearg + ".json");
if (remove) { fs::recursive_remove(bottlepath); }
}
void cellar::bottles::cork_command(int argc, vector<string> argv) {
if (argv[1] == "-k" || argv[1] == "--keep") { cork(argv[2], false); }
else { cork(argv[1], true); }
}
void cellar::bottles::uncork(string bottlearg) {
string homedir = getenv("HOME");
string datadir = homedir + "/.local/share/cellar";
if (!boost::filesystem::exists(datadir + "/corked/" + bottlearg + ".json")) {
output::error("no bottle named " + bottlearg + " to uncork");
return;
}
string bottlepath = resolve_bottle(bottlearg);
if (boost::filesystem::exists(bottlepath)) {
output::error("refusing to clobber existing bottle " + bottlearg);
return;
}
output::statement("creating wine prefix at " + bottlepath, true);
setenv("WINEPREFIX", bottlepath.c_str(), 1);
vector<string> createargs = {"cellar create", bottlearg};
create_bottle(2, createargs);
boost::filesystem::copy(datadir + "/corked/" + bottlearg + ".json", bottlepath + "/cellar.json");
active_bottle = Bottle(bottlepath);
if (active_bottle.config.find("winetricks") != active_bottle.config.end()) {
vector<string> winetrickery = active_bottle.config.at("winetricks");
output::statement("running winetricks with args: " + boost::algorithm::join(winetrickery, " "));
launch::winetricks(winetrickery.size(), winetrickery);
}
if (active_bottle.config.find("pressed") != active_bottle.config.end()) {
auto presseddir = boost::filesystem::path(datadir + "/pressed");
auto pressed = active_bottle.config.at("pressed");
for (auto pressed_iter = pressed.begin(); pressed_iter != pressed.end(); pressed_iter++) {
string exec = pressed_iter.key();
vector<string> args = pressed_iter.value();
output::statement("running pressed installer " + exec + " with arguments: " + boost::algorithm::join(args, " "), true);
vector<string> subargv;
subargv.push_back("wine");
subargv.push_back((presseddir / exec).native());
for (string arg : args) { subargv.push_back(arg); }
launch::popen(subargv);
}
}
}
void cellar::bottles::uncork_command(int argc, vector<string> argv) { uncork(argv[1]); }

70
src/bottles/press.cpp Normal file
View File

@ -0,0 +1,70 @@
#include <cstdlib>
#include <string>
#include <vector>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include "json.hpp"
#include "bottles.hpp"
#include "internal/bottles.hpp"
#include "launch.hpp"
#include "internal/launch.hpp"
#include "fs.hpp"
#include "output.hpp"
using namespace std;
using namespace cellar;
using namespace cellar::bottles;
using json = nlohmann::json;
void cellar::bottles::press(string exec, vector<string> args, bool noexec) {
// ensure pressed installer directory exists
string homedir = getenv("HOME");
string datadir = homedir + "/.local/share/cellar";
string presseddir = datadir + "/pressed";
if (!boost::filesystem::exists(datadir)) { boost::filesystem::create_directory(datadir); }
if (!boost::filesystem::exists(presseddir)) { boost::filesystem::create_directory(presseddir); }
string filename = boost::filesystem::path(exec).filename().native();
auto pressedpath = boost::filesystem::path(presseddir);
string pressedfile = (pressedpath / filename).native();
if (boost::filesystem::exists(pressedfile)) {
output::warning("clobbering existing version of " + filename);
boost::filesystem::remove(pressedfile);
}
boost::filesystem::copy(exec, pressedfile);
vector<string> argv;
argv.push_back("wine");
argv.push_back(pressedfile);
if (!noexec) { launch::popen(argv); }
if (active_bottle.config.find("pressed") == active_bottle.config.end()) {
json pressed;
active_bottle.config["pressed"] = pressed;
}
active_bottle.config["pressed"].emplace(filename, args);
active_bottle.save_config();
}
void cellar::bottles::press_command(int argc, vector<string> argv) {
bool noexec = false;
string exec;
vector<string> subargv;
int startarg = 1;
if (argv[1] == "-n") {
noexec = true;
startarg = 2;
}
for (int curarg = startarg; curarg < argc; curarg++) {
if (curarg == startarg) { exec = argv[curarg]; }
else { subargv.push_back(argv[curarg]); }
}
press(exec, subargv, noexec);
}

View File

@ -31,7 +31,7 @@ using json = nlohmann::json;
bool cellar::dryrun;
bool cellar::verbose;
bottles::Bottle cellar::active_bottle;
bottles::Bottle cellar::bottles::active_bottle;
void cellar::print_header() {
output::statement("cellar - bottle management tool for WINE connoisseurs");
@ -90,18 +90,18 @@ int main(int argc, char* argv[]) {
string env_wineprefix = getenv("WINEPREFIX");
output::warning("cellar was designed to handle WINEPREFIX for you with the -b argument");
output::warning("WINEPREFIX will be respected for consistency");
active_bottle = bottles::Bottle(env_wineprefix);
bottles::active_bottle = bottles::Bottle(env_wineprefix);
} else {
string homepath = getenv("HOME");
string fullbottlepath = homepath + "/.wine";
active_bottle = bottles::Bottle(fullbottlepath);
bottles::active_bottle = bottles::Bottle(fullbottlepath);
}
set_environment = false;
} else {
string bottlechoice = bottlearg.getValue();
if (bottlechoice.substr(0,1) == "/" || bottlechoice.substr(0,1) == ".") { // absolute or relative path
active_bottle = bottles::Bottle(bottlechoice);
bottles::active_bottle = bottles::Bottle(bottlechoice);
} else if (bottlechoice.substr(0,1) == "~") { // "absolute" path in home directory, not expanded by the shell for some reason (i've seen some shit)
// this is a naive replacement and will fail if the user tries something like ~nick/.wine
// i'm figuring at that point if you're doing that, you'll also recognize if your shell
@ -109,7 +109,7 @@ int main(int argc, char* argv[]) {
bottlechoice.replace(0,1,getenv("HOME"));
// or at least you'll think to use verbose mode to make sure it's loading the right directory
output::warning("your shell didn't expand your given path properly, doing a naive replacement", true);
active_bottle = bottles::Bottle(bottlechoice);
bottles::active_bottle = bottles::Bottle(bottlechoice);
} else {
if (bottlechoice.substr(0,6) == ".wine.") {
output::statement("tip: cellar can add the \".wine.\" prefix automatically");
@ -118,15 +118,15 @@ int main(int argc, char* argv[]) {
string homepath = getenv("HOME");
string fullbottlepath = homepath + "/.wine." + bottlechoice;
active_bottle = bottles::Bottle(fullbottlepath);
bottles::active_bottle = bottles::Bottle(fullbottlepath);
}
}
active_bottle.load_config();
bottles::active_bottle.load_config();
if (set_environment) {
output::statement("WINEPREFIX=" + active_bottle.path, true);
setenv("WINEPREFIX", active_bottle.path.c_str(), 1);
output::statement("WINEPREFIX=" + bottles::active_bottle.path, true);
setenv("WINEPREFIX", bottles::active_bottle.path.c_str(), 1);
}
string usercmd = command.getValue();

View File

@ -1,8 +1,9 @@
#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string.hpp>
#include "subprocess.hpp"
#include "launch.hpp"
@ -28,8 +29,26 @@ void cellar::launch::launch_command(int argc, vector<string> args) {
launch::launch_program(args);
}
// BULLSHIT: subprocess.hpp throws linker errors if included in multiple files
void cellar::launch::popen(string argv) {
auto wine = subprocess::Popen(argv);
wine.wait();
vector<string> argvsplit;
boost::algorithm::split(argvsplit, argv, boost::is_any_of(" "));
string exec = argvsplit[0];
vector<string> subargv;
for (int curarg = 1; curarg < argvsplit.size(); curarg++) {
subargv.push_back(argvsplit[curarg]);
}
auto subproc = subprocess::popen(exec, subargv);
cout << subproc.stdout().rdbuf();
cerr << subproc.stderr().rdbuf();
}
void cellar::launch::popen(vector<string> argv) {
string exec = argv[0];
vector<string> subargv;
for (int curarg = 1; curarg < argv.size(); curarg++) {
subargv.push_back(argv[curarg]);
}
auto subproc = subprocess::popen(exec, subargv);
cout << subproc.stdout().rdbuf();
cerr << subproc.stderr().rdbuf();
}

View File

@ -6,7 +6,6 @@
//#include "subprocess.hpp"
#include "bottles.hpp"
#include "cellar.hpp"
#include "launch.hpp"
#include "internal/launch.hpp"
#include "output.hpp"
@ -26,14 +25,14 @@ void cellar::launch::winetricks(int argc, vector<string> argv) {
//output::statement(winetricks_str);
launch::popen(winetricks_str);
if (cellar::active_bottle.config.find("winetricks") == cellar::active_bottle.config.end()) {
cellar::active_bottle.config.emplace("winetricks", vector<string>());
if (bottles::active_bottle.config.find("winetricks") == bottles::active_bottle.config.end()) {
bottles::active_bottle.config.emplace("winetricks", vector<string>());
}
for (string winetrick : winetricks_argv) {
if (winetrick == "winetricks") { continue; }
else if (winetrick.substr(0,1) == "-") { continue; } // opts don't get saved
else { cellar::active_bottle.config["winetricks"].push_back(winetrick); }
else { bottles::active_bottle.config["winetricks"].push_back(winetrick); }
}
cellar::active_bottle.save_config();
bottles::active_bottle.save_config();
}