Skip to content

Commit

Permalink
App: fix issue 0002956
Browse files Browse the repository at this point in the history
  • Loading branch information
wwmayer committed Nov 11, 2021
1 parent c62239d commit e7e2994
Showing 1 changed file with 72 additions and 83 deletions.
155 changes: 72 additions & 83 deletions src/App/Application.cpp
Expand Up @@ -1073,8 +1073,7 @@ std::string Application::getUserAppDataDir()

std::string Application::getUserMacroDir()
{
std::string path("Macro/");
return mConfig["UserAppData"] + path;
return mConfig["UserMacroPath"];
}

std::string Application::getResourceDir()
Expand Down Expand Up @@ -2887,40 +2886,40 @@ QString getOldGenericDataLocation(QString home)
}

/*!
* \brief getNewDataLocation
* \brief getSubDirectories
* To a given path it adds the sub-directories where to store application specific files.
*/
void getNewDataLocation(std::map<std::string,std::string>& mConfig, boost::filesystem::path& appData)
void getSubDirectories(std::map<std::string,std::string>& mConfig, std::vector<std::string>& appData)
{
// If 'AppDataSkipVendor' is defined, the value of 'ExeVendor' must not be part of
// the path.
if (mConfig.find("AppDataSkipVendor") == mConfig.end()) {
appData /= mConfig["ExeVendor"];
appData.push_back(mConfig["ExeVendor"]);
}
appData /= mConfig["ExeName"];
appData.push_back(mConfig["ExeName"]);
}

/*!
* \brief getOldDataLocation
* To a given path it adds the sub-directories where to store application specific files.
* On Linux or BSD a hidden directory (i.e. starting with a dot) is added.
*/
void getOldDataLocation(std::map<std::string,std::string>& mConfig, boost::filesystem::path& appData)
void getOldDataLocation(std::map<std::string,std::string>& mConfig, std::vector<std::string>& appData)
{
// Actually the name of the directory where the parameters are stored should be the name of
// the application due to branding reasons.
#if defined(FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_BSD)
// If 'AppDataSkipVendor' is defined, the value of 'ExeVendor' must not be part of
// the path.
if (mConfig.find("AppDataSkipVendor") == mConfig.end()) {
appData /= "." + mConfig["ExeVendor"];
appData /= mConfig["ExeName"];
appData.push_back(std::string(".") + mConfig["ExeVendor"]);
appData.push_back(mConfig["ExeName"]);
} else {
appData /= "." + mConfig["ExeName"];
appData.push_back(std::string(".") + mConfig["ExeName"]);
}

#elif defined(FC_OS_MACOSX) || defined(FC_OS_WIN32)
getNewDataLocation(mConfig, appData);
getSubDirectories(mConfig, appData);
#endif
}

Expand All @@ -2940,71 +2939,37 @@ QString findUserHomePath(const QString& userHome)
}

/*!
* \brief findOldUserDataPath
* Returns the path where to store application specific files to.
* If \a userDate is not empty it will be used, otherwise a path starting from \a userHome will be used.
* \brief findPath
* Returns the path where to store application files to.
* If \a customHome is not empty it will be used, otherwise a path starting from \a stdHome will be used.
*/
boost::filesystem::path findOldUserDataPath(std::map<std::string,std::string>& mConfig, const QString& userHome, const QString& userData)
boost::filesystem::path findPath(const QString& stdHome, const QString& customHome,
const std::vector<std::string>& paths, bool create)
{
QString dataPath = userData;
QString dataPath = customHome;
if (dataPath.isEmpty()) {
dataPath = getOldGenericDataLocation(userHome);
dataPath = stdHome;
}

boost::filesystem::path appData(stringToPath(dataPath.toStdString()));

if (!boost::filesystem::exists(appData)) {
// This should never ever happen
throw Base::FileSystemError("Application data directory " + appData.string() + " does not exist!");
}

// In the second step we want the directory where user settings of the application can be
// kept. There we create a directory with name of the vendor and a sub-directory with name
// of the application.
//
// If a custom user data path is given then don't modify it
if (userData.isEmpty())
getOldDataLocation(mConfig, appData);

// In order to write to our data path, we must create some directories, first.
if (!boost::filesystem::exists(appData) && !Py_IsInitialized()) {
try {
boost::filesystem::create_directories(appData);
} catch (const boost::filesystem::filesystem_error& e) {
throw Base::FileSystemError("Could not create app data directories. Failed with: " + e.code().message());
}
}

return appData;
}

/*!
* \brief findCachePath
* Returns the path where to store application specific cached files to.
* If \a userTemp is not empty it will be used, otherwise a path starting from \a cacheHome will be used.
*/
boost::filesystem::path findCachePath(std::map<std::string,std::string>& mConfig, const QString& cacheHome, const QString& userTemp)
{
QString dataPath = userTemp;
if (dataPath.isEmpty()) {
dataPath = cacheHome;
}

boost::filesystem::path appData(stringToPath(dataPath.toStdString()));

#if !defined(FC_OS_WIN32)
// If a custom user temp path is given then don't modify it
if (userTemp.isEmpty()) {
getNewDataLocation(mConfig, appData);
appData /= "Cache";
// If a custom user home path is given then don't modify it
if (customHome.isEmpty()) {
for (const auto& it : paths)
appData = appData / it;
}
#endif

// In order to write to our data path, we must create some directories, first.
if (!boost::filesystem::exists(appData)) {
if (create && !boost::filesystem::exists(appData) && !Py_IsInitialized()) {
try {
boost::filesystem::create_directories(appData);
} catch (const boost::filesystem::filesystem_error& e) {
throw Base::FileSystemError("Could not create cache directories. Failed with: " + e.code().message());
throw Base::FileSystemError("Could not create directories. Failed with: " + e.code().message());
}
}

Expand Down Expand Up @@ -3077,7 +3042,8 @@ std::tuple<QString, QString, QString> getStandardPaths()

// Keep the old behaviour
#if defined(FC_OS_WIN32)
dataHome = getOldGenericDataLocation(QString());
configHome = getOldGenericDataLocation(QString());
dataHome = configHome;
cacheHome = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
#endif

Expand All @@ -3087,55 +3053,78 @@ std::tuple<QString, QString, QString> getStandardPaths()

void Application::ExtractUserPath()
{
bool keepDeprecatedPaths = false;

// std paths
mConfig["BinPath"] = mConfig["AppHomePath"] + "bin" + PATHSEP;
mConfig["DocPath"] = mConfig["AppHomePath"] + "doc" + PATHSEP;

// this is to support a portable version of FreeCAD
auto paths = getCustomPaths();
QString userHome = std::get<0>(paths);
QString userData = std::get<1>(paths);
QString userTemp = std::get<2>(paths);
QString customHome = std::get<0>(paths);
QString customData = std::get<1>(paths);
QString customTemp = std::get<2>(paths);

// get the system standard paths
auto xdgPaths = getStandardPaths();
//QString configHome = std::get<0>(xdgPaths);
//QString dataHome = std::get<1>(xdgPaths);
QString cacheHome = std::get<2>(xdgPaths);
auto stdPaths = getStandardPaths();
QString configHome = std::get<0>(stdPaths);
QString dataHome = std::get<1>(stdPaths);
QString cacheHome = std::get<2>(stdPaths);

// User home path
//
userHome = findUserHomePath(userHome);
mConfig["UserHomePath"] = userHome.toUtf8().data();
QString homePath = findUserHomePath(customHome);
mConfig["UserHomePath"] = homePath.toUtf8().data();

// the old path name to save config and data files
std::vector<std::string> subdirs;
if (keepDeprecatedPaths) {
configHome = homePath;
dataHome = homePath;
cacheHome = homePath;
getOldDataLocation(mConfig, subdirs);
}
else {
getSubDirectories(mConfig, subdirs);
}

// User data path
//
boost::filesystem::path appData = findOldUserDataPath(mConfig, userHome, userData);
mConfig["UserAppData"] = pathToString(appData) + PATHSEP;
boost::filesystem::path data = findPath(dataHome, customData, subdirs, true);
mConfig["UserAppData"] = pathToString(data) + PATHSEP;


// User config path (for now equal to UserAppData but will be changed to be XDG compliant)
// User config path
//
mConfig["UserConfigPath"] = mConfig["UserAppData"];
boost::filesystem::path config = findPath(configHome, customHome, subdirs, true);
mConfig["UserConfigPath"] = pathToString(config) + PATHSEP;

std::vector<std::string> oldsubdirs;
getOldDataLocation(mConfig, oldsubdirs);
boost::filesystem::path appData = findPath(getOldGenericDataLocation(homePath), customData, oldsubdirs, false);

// If in new location user.cfg doesn't exist but in the old location then copy it
boost::filesystem::path oldUsercfg = appData / "user.cfg";
boost::filesystem::path newUsercfg = config / "user.cfg";
if (boost::filesystem::exists(oldUsercfg) && !boost::filesystem::exists(newUsercfg)) {
boost::filesystem::copy(oldUsercfg, newUsercfg);
}


// Set application tmp. directory
//
boost::filesystem::path cache = findCachePath(mConfig, cacheHome, userTemp);
std::vector<std::string> cachedirs = subdirs;
cachedirs.emplace_back("Cache");
boost::filesystem::path cache = findPath(cacheHome, customTemp, cachedirs, true);
mConfig["AppTempPath"] = pathToString(cache) + PATHSEP;


// Create the default macro directory
// Set the default macro directory
//
boost::filesystem::path macroDir = stringToPath(getUserMacroDir());
if (!boost::filesystem::exists(macroDir) && !Py_IsInitialized()) {
try {
boost::filesystem::create_directories(macroDir);
} catch (const boost::filesystem::filesystem_error& e) {
throw Base::FileSystemError("Could not create macro directory. Failed with: " + e.code().message());
}
}
std::vector<std::string> macrodirs = subdirs;
macrodirs.emplace_back("Macro");
boost::filesystem::path macro = findPath(dataHome, customData, macrodirs, true);
mConfig["UserMacroPath"] = pathToString(macro) + PATHSEP;
}

#if defined (FC_OS_LINUX) || defined(FC_OS_CYGWIN) || defined(FC_OS_BSD)
Expand Down

2 comments on commit e7e2994

@chennes
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this is breaking the CI unit tests over on Gitlab: the error is

Initialization of FreeCAD failed:
While initializing FreeCAD the following exception occurred: 'Application data directory /root/.local/share does not exist!'

I don't know if this is something for @berndhahnebach to fix, or if there is a problem with the new XDG code in FreeCAD.

@wwmayer
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this is breaking the CI unit tests over on Gitlab: the error is

I have discovered the reason right now, too. On a normal system the XDG root paths usually already exist but apparently it's not something that we should rely on. So, for now I have commented out the code that throws the exception and we will see if the creation of these directories works: 6da7749

Please sign in to comment.