Merge pull request #1247 from colobot/dev-basedir

Add support for binary relocation
modernize-cmake-1
Emxx52 2020-04-07 18:49:57 +02:00 committed by GitHub
commit 5815db4062
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 155 additions and 27 deletions

View File

@ -198,7 +198,7 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
add_definitions(-DNOEXCEPT= -DHAS_MSVC_EXCEPTION_BUG) add_definitions(-DNOEXCEPT= -DHAS_MSVC_EXCEPTION_BUG)
# Needed for Debug information (it's set to "No" by default for some reason) # Needed for Debug information (it's set to "No" by default for some reason)
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS} /DEBUG") set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS} /DEBUG /NODEFAULTLIB:MSVCRTD /NODEFAULTLIB:LIBCMT")
set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS} /DEBUG") set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS} /DEBUG")
else() else()
message(FATAL_ERROR "Your C++ compiler doesn't seem to be supported.") message(FATAL_ERROR "Your C++ compiler doesn't seem to be supported.")
@ -230,7 +230,10 @@ option(DEV_BUILD "Enable development build (enables some debugging tools, local
# PLEASE DO NOT USE ON UNOFFICIAL BUILDS. Thanks. # PLEASE DO NOT USE ON UNOFFICIAL BUILDS. Thanks.
option(OFFICIAL_COLOBOT_BUILD "Official build (changes crash screen text)" OFF) option(OFFICIAL_COLOBOT_BUILD "Official build (changes crash screen text)" OFF)
# Portable build - load all data from current directory # Hardcode relative paths instead of absolute paths
option(USE_RELATIVE_PATHS "Generate relative paths from absolute paths" OFF)
# Portable build - load all data from the base directory
option(PORTABLE "Portable build" OFF) option(PORTABLE "Portable build" OFF)
# Portable saves - suitable for e.g. putting the whole game on external storage and moving your saves with it # Portable saves - suitable for e.g. putting the whole game on external storage and moving your saves with it
@ -391,12 +394,12 @@ endif()
# Installation paths defined before compiling sources # Installation paths defined before compiling sources
if(PORTABLE OR (PLATFORM_WINDOWS AND MXE)) if(PORTABLE OR (PLATFORM_WINDOWS AND MXE))
# We need to use STRING because PATH doesn't accept relative paths set(COLOBOT_INSTALL_BIN_DIR ${CMAKE_INSTALL_PREFIX}/ CACHE PATH "Colobot binary directory")
set(COLOBOT_INSTALL_BIN_DIR ./ CACHE STRING "Colobot binary directory") set(COLOBOT_INSTALL_LIB_DIR ${CMAKE_INSTALL_PREFIX}/ CACHE PATH "Colobot libraries directory")
set(COLOBOT_INSTALL_LIB_DIR ./ CACHE STRING "Colobot libraries directory") set(COLOBOT_INSTALL_DATA_DIR ${CMAKE_INSTALL_PREFIX}/data CACHE PATH "Colobot shared data directory")
set(COLOBOT_INSTALL_DATA_DIR ./data CACHE STRING "Colobot shared data directory") set(COLOBOT_INSTALL_I18N_DIR ${CMAKE_INSTALL_PREFIX}/lang CACHE PATH "Colobot translations directory")
set(COLOBOT_INSTALL_I18N_DIR ./lang CACHE STRING "Colobot translations directory") set(COLOBOT_INSTALL_DOC_DIR ${CMAKE_INSTALL_PREFIX}/doc CACHE PATH "Colobot documentation directory")
set(COLOBOT_INSTALL_DOC_DIR ./doc CACHE STRING "Colobot documentation directory") set(USE_RELATIVE_PATHS ON)
elseif(PLATFORM_WINDOWS) elseif(PLATFORM_WINDOWS)
set(COLOBOT_INSTALL_BIN_DIR ${CMAKE_INSTALL_PREFIX}/ CACHE PATH "Colobot binary directory") set(COLOBOT_INSTALL_BIN_DIR ${CMAKE_INSTALL_PREFIX}/ CACHE PATH "Colobot binary directory")
set(COLOBOT_INSTALL_LIB_DIR ${CMAKE_INSTALL_PREFIX}/ CACHE PATH "Colobot libraries directory") set(COLOBOT_INSTALL_LIB_DIR ${CMAKE_INSTALL_PREFIX}/ CACHE PATH "Colobot libraries directory")
@ -417,6 +420,18 @@ else()
set(COLOBOT_INSTALL_DOC_DIR ${CMAKE_INSTALL_PREFIX}/share/doc/colobot CACHE PATH "Colobot documentation directory") set(COLOBOT_INSTALL_DOC_DIR ${CMAKE_INSTALL_PREFIX}/share/doc/colobot CACHE PATH "Colobot documentation directory")
endif() endif()
# Generate relative paths from absolute paths
if(USE_RELATIVE_PATHS)
message(STATUS "Generating relative paths")
file(RELATIVE_PATH COLOBOT_DATA_DIR ${COLOBOT_INSTALL_BIN_DIR} ${COLOBOT_INSTALL_DATA_DIR})
file(RELATIVE_PATH COLOBOT_I18N_DIR ${COLOBOT_INSTALL_BIN_DIR} ${COLOBOT_INSTALL_I18N_DIR})
add_definitions(-DUSE_RELATIVE_PATHS)
else()
set(COLOBOT_DATA_DIR ${COLOBOT_INSTALL_DATA_DIR})
set(COLOBOT_I18N_DIR ${COLOBOT_INSTALL_I18N_DIR})
endif()
# Subdirectory with sources # Subdirectory with sources
add_subdirectory(src) add_subdirectory(src)

2
Jenkinsfile vendored
View File

@ -27,6 +27,7 @@ pipeline {
dir('build/windows') { dir('build/windows') {
sh ''' sh '''
# FIXME: without -lsetupapi linking sdl2 fails # FIXME: without -lsetupapi linking sdl2 fails
rm -rf *
/opt/mxe/usr/bin/i686-w64-mingw32.static-cmake \ /opt/mxe/usr/bin/i686-w64-mingw32.static-cmake \
-DCMAKE_CXX_STANDARD_LIBRARIES="-lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32 -lsetupapi" \ -DCMAKE_CXX_STANDARD_LIBRARIES="-lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32 -lsetupapi" \
-DCMAKE_INSTALL_PREFIX=/install \ -DCMAKE_INSTALL_PREFIX=/install \
@ -53,6 +54,7 @@ pipeline {
sh 'mkdir -p build/linux' sh 'mkdir -p build/linux'
dir('build/linux') { dir('build/linux') {
sh ''' sh '''
rm -rf *
cmake \ cmake \
-DCMAKE_INSTALL_PREFIX=/install -DCMAKE_SKIP_INSTALL_RPATH=ON \ -DCMAKE_INSTALL_PREFIX=/install -DCMAKE_SKIP_INSTALL_RPATH=ON \
-DCMAKE_BUILD_TYPE=RelWithDebInfo -DDEV_BUILD=1 -DPORTABLE=1 -DTOOLS=1 -DTESTS=1 -DDESKTOP=1 ../.. -DCMAKE_BUILD_TYPE=RelWithDebInfo -DDEV_BUILD=1 -DPORTABLE=1 -DTOOLS=1 -DTESTS=1 -DDESKTOP=1 ../..

View File

@ -65,6 +65,8 @@ elseif(PLATFORM_WINDOWS)
find_library(TIFF_LIBRARY NAMES tiff.lib) find_library(TIFF_LIBRARY NAMES tiff.lib)
find_library(LZMA_LIBRARY NAMES lzma.lib) find_library(LZMA_LIBRARY NAMES lzma.lib)
find_library(FREETYPE_LIBRARY NAMES freetype.lib) find_library(FREETYPE_LIBRARY NAMES freetype.lib)
find_library(ICONV_LIBRARY NAMES libiconv.lib)
find_library(CHARSET_LIBRARY NAMES libcharset.lib)
set(MSVC_LIBS set(MSVC_LIBS
${LIBINTL_LIBRARY} ${LIBINTL_LIBRARY}
${OPENAL_MSVC_LIBS} ${OPENAL_MSVC_LIBS}
@ -73,6 +75,8 @@ elseif(PLATFORM_WINDOWS)
${BZ2_LIBRARY} ${BZ2_LIBRARY}
${LZMA_LIBRARY} ${LZMA_LIBRARY}
${FREETYPE_LIBRARY} ${FREETYPE_LIBRARY}
${ICONV_LIBRARY}
${CHARSET_LIBRARY}
winmm.lib winmm.lib
dxguid.lib dxguid.lib
imm32.lib imm32.lib
@ -81,6 +85,7 @@ elseif(PLATFORM_WINDOWS)
version.lib version.lib
wsock32.lib wsock32.lib
ws2_32.lib ws2_32.lib
setupapi.lib
) )
else(${MSVC_STATIC}) else(${MSVC_STATIC})
set(MSVC_LIBS ${LIBINTL_LIBRARY}) set(MSVC_LIBS ${LIBINTL_LIBRARY})

View File

@ -220,6 +220,30 @@ CSoundInterface* CApplication::GetSound()
return m_sound.get(); return m_sound.get();
} }
void CApplication::LoadEnvironmentVariables()
{
auto dataDir = m_systemUtils->GetEnvVar("COLOBOT_DATA_DIR");
if (!dataDir.empty())
{
m_pathManager->SetDataPath(dataDir);
GetLogger()->Info("Using data dir (based on environment variable): '%s'\n", dataDir.c_str());
}
auto langDir = m_systemUtils->GetEnvVar("COLOBOT_LANG_DIR");
if (!langDir.empty())
{
m_pathManager->SetLangPath(langDir);
GetLogger()->Info("Using lang dir (based on environment variable): '%s'\n", langDir.c_str());
}
auto saveDir = m_systemUtils->GetEnvVar("COLOBOT_SAVE_DIR");
if (!saveDir.empty())
{
m_pathManager->SetSavePath(saveDir);
GetLogger()->Info("Using save dir (based on environment variable): '%s'\n", saveDir.c_str());
}
}
ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[]) ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[])
{ {
enum OptionType enum OptionType
@ -286,15 +310,18 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[])
GetLogger()->Message("\n"); GetLogger()->Message("\n");
GetLogger()->Message("%s\n", COLOBOT_FULLNAME); GetLogger()->Message("%s\n", COLOBOT_FULLNAME);
GetLogger()->Message("\n"); GetLogger()->Message("\n");
GetLogger()->Message("List of available options:\n"); GetLogger()->Message("List of available options and environment variables:\n");
GetLogger()->Message(" -help this help\n"); GetLogger()->Message(" -help this help\n");
GetLogger()->Message(" -debug modes enable debug modes (more info printed in logs; see code for reference of modes)\n"); GetLogger()->Message(" -debug modes enable debug modes (more info printed in logs; see code for reference of modes)\n");
GetLogger()->Message(" -runscene sceneNNN run given scene on start\n"); GetLogger()->Message(" -runscene sceneNNN run given scene on start\n");
GetLogger()->Message(" -scenetest win every mission right after it's loaded\n"); GetLogger()->Message(" -scenetest win every mission right after it's loaded\n");
GetLogger()->Message(" -loglevel level set log level to level (one of: trace, debug, info, warn, error, none)\n"); GetLogger()->Message(" -loglevel level set log level to level (one of: trace, debug, info, warn, error, none)\n");
GetLogger()->Message(" -langdir path set custom language directory path\n"); GetLogger()->Message(" -langdir path set custom language directory path\n");
GetLogger()->Message(" environment variable: COLOBOT_LANG_DIR\n");
GetLogger()->Message(" -datadir path set custom data directory path\n"); GetLogger()->Message(" -datadir path set custom data directory path\n");
GetLogger()->Message(" environment variable: COLOBOT_DATA_DIR\n");
GetLogger()->Message(" -savedir path set custom save directory path (must be writable)\n"); GetLogger()->Message(" -savedir path set custom save directory path (must be writable)\n");
GetLogger()->Message(" environment variable: COLOBOT_SAVE_DIR\n");
GetLogger()->Message(" -mod path load datadir mod from given path\n"); GetLogger()->Message(" -mod path load datadir mod from given path\n");
GetLogger()->Message(" -resolution WxH set resolution\n"); GetLogger()->Message(" -resolution WxH set resolution\n");
GetLogger()->Message(" -headless headless mode - disables graphics, sound and user interaction\n"); GetLogger()->Message(" -headless headless mode - disables graphics, sound and user interaction\n");

View File

@ -164,7 +164,9 @@ public:
CSoundInterface* GetSound(); CSoundInterface* GetSound();
public: public:
//! Parses commandline arguments //! Loads some data from environment variables
void LoadEnvironmentVariables();
//! Parses commandline arguments (they take priority)
ParseArgsStatus ParseArguments(int argc, char *argv[]); ParseArgsStatus ParseArguments(int argc, char *argv[]);
//! Initializes the application //! Initializes the application
bool Create(); bool Create();

View File

@ -172,6 +172,8 @@ int main(int argc, char *argv[])
int code = 0; int code = 0;
CApplication app(systemUtils.get()); // single instance of the application CApplication app(systemUtils.get()); // single instance of the application
app.LoadEnvironmentVariables();
ParseArgsStatus status = app.ParseArguments(argc, argv); ParseArgsStatus status = app.ParseArguments(argc, argv);
if (status == PARSE_ARGS_FAIL) if (status == PARSE_ARGS_FAIL)
{ {

View File

@ -41,7 +41,7 @@ CPathManager::CPathManager(CSystemUtils* systemUtils)
: m_dataPath(systemUtils->GetDataPath()) : m_dataPath(systemUtils->GetDataPath())
, m_langPath(systemUtils->GetLangPath()) , m_langPath(systemUtils->GetLangPath())
, m_savePath(systemUtils->GetSaveDir()) , m_savePath(systemUtils->GetSaveDir())
, m_modAutoloadDir{ m_dataPath + "/mods", m_savePath + "/mods" } , m_modAutoloadDir{}
, m_mods{} , m_mods{}
{ {
} }
@ -130,6 +130,10 @@ void CPathManager::InitPaths()
{ {
GetLogger()->Info("Data path: %s\n", m_dataPath.c_str()); GetLogger()->Info("Data path: %s\n", m_dataPath.c_str());
GetLogger()->Info("Save path: %s\n", m_savePath.c_str()); GetLogger()->Info("Save path: %s\n", m_savePath.c_str());
m_modAutoloadDir.push_back(m_dataPath + "/mods");
m_modAutoloadDir.push_back(m_savePath + "/mods");
if (!m_modAutoloadDir.empty()) if (!m_modAutoloadDir.empty())
{ {
GetLogger()->Info("Mod autoload dirs:\n"); GetLogger()->Info("Mod autoload dirs:\n");

View File

@ -18,5 +18,5 @@
#cmakedefine PORTABLE_SAVES @PORTABLE_SAVES@ #cmakedefine PORTABLE_SAVES @PORTABLE_SAVES@
#define COLOBOT_DEFAULT_DATADIR "@COLOBOT_INSTALL_DATA_DIR@" #define COLOBOT_DEFAULT_DATADIR "@COLOBOT_DATA_DIR@"
#define COLOBOT_I18N_DIR "@COLOBOT_INSTALL_I18N_DIR@" #define COLOBOT_I18N_DIR "@COLOBOT_I18N_DIR@"

View File

@ -36,6 +36,7 @@
#include <iostream> #include <iostream>
#include <algorithm> #include <algorithm>
#include <SDL2/SDL.h>
std::unique_ptr<CSystemUtils> CSystemUtils::Create() std::unique_ptr<CSystemUtils> CSystemUtils::Create()
{ {
@ -176,17 +177,41 @@ float CSystemUtils::TimeStampDiff(SystemTimeStamp *before, SystemTimeStamp *afte
return result; return result;
} }
std::string CSystemUtils::GetBasePath()
{
if (m_basePath.empty())
{
auto* path = SDL_GetBasePath();
m_basePath = path;
SDL_free(path);
}
return m_basePath;
}
std::string CSystemUtils::GetDataPath() std::string CSystemUtils::GetDataPath()
{ {
#ifdef USE_RELATIVE_PATHS
return GetBasePath() + COLOBOT_DEFAULT_DATADIR;
#else
return COLOBOT_DEFAULT_DATADIR; return COLOBOT_DEFAULT_DATADIR;
#endif
} }
std::string CSystemUtils::GetLangPath() std::string CSystemUtils::GetLangPath()
{ {
#ifdef USE_RELATIVE_PATHS
return GetBasePath() + COLOBOT_I18N_DIR;
#else
return COLOBOT_I18N_DIR; return COLOBOT_I18N_DIR;
#endif
} }
std::string CSystemUtils::GetSaveDir() std::string CSystemUtils::GetSaveDir()
{ {
return "./saves"; return GetBasePath() + "saves";
}
std::string CSystemUtils::GetEnvVar(const std::string& name)
{
return "";
} }

View File

@ -127,6 +127,9 @@ public:
/** The difference is \a after - \a before. */ /** The difference is \a after - \a before. */
virtual long long TimeStampExactDiff(SystemTimeStamp *before, SystemTimeStamp *after) = 0; virtual long long TimeStampExactDiff(SystemTimeStamp *before, SystemTimeStamp *after) = 0;
//! Returns the path where the executable binary is located (ends with the path separator)
virtual std::string GetBasePath();
//! Returns the data path (containing textures, levels, helpfiles, etc) //! Returns the data path (containing textures, levels, helpfiles, etc)
virtual std::string GetDataPath(); virtual std::string GetDataPath();
@ -136,9 +139,13 @@ public:
//! Returns the save dir location //! Returns the save dir location
virtual std::string GetSaveDir(); virtual std::string GetSaveDir();
//! Returns the environment variable with the given name or an empty string if it does not exist
virtual std::string GetEnvVar(const std::string &name);
//! Sleep for given amount of microseconds //! Sleep for given amount of microseconds
virtual void Usleep(int usecs) = 0; virtual void Usleep(int usecs) = 0;
private: private:
std::string m_basePath;
std::vector<std::unique_ptr<SystemTimeStamp>> m_timeStamps; std::vector<std::unique_ptr<SystemTimeStamp>> m_timeStamps;
}; };

View File

@ -102,23 +102,23 @@ std::string CSystemUtilsLinux::GetSaveDir()
std::string savegameDir; std::string savegameDir;
// Determine savegame dir according to XDG Base Directory Specification // Determine savegame dir according to XDG Base Directory Specification
char *envXDG_DATA_HOME = getenv("XDG_DATA_HOME"); auto envXDG_DATA_HOME = GetEnvVar("XDG_DATA_HOME");
if (envXDG_DATA_HOME == nullptr) if (envXDG_DATA_HOME.empty())
{ {
char *envHOME = getenv("HOME"); auto envHOME = GetEnvVar("HOME");
if (envHOME == nullptr) if (envHOME.empty())
{ {
GetLogger()->Warn("Unable to find directory for saves - using current directory"); GetLogger()->Warn("Unable to find directory for saves - using default directory");
savegameDir = "./saves"; savegameDir = CSystemUtils::GetSaveDir();
} }
else else
{ {
savegameDir = std::string(envHOME) + "/.local/share/colobot"; savegameDir = envHOME + "/.local/share/colobot";
} }
} }
else else
{ {
savegameDir = std::string(envXDG_DATA_HOME) + "/colobot"; savegameDir = envXDG_DATA_HOME + "/colobot";
} }
GetLogger()->Trace("Saved game files are going to %s\n", savegameDir.c_str()); GetLogger()->Trace("Saved game files are going to %s\n", savegameDir.c_str());
@ -126,6 +126,17 @@ std::string CSystemUtilsLinux::GetSaveDir()
#endif #endif
} }
std::string CSystemUtilsLinux::GetEnvVar(const std::string& name)
{
char* envVar = getenv(name.c_str());
if (envVar != nullptr)
{
GetLogger()->Trace("Detected environment variable %s = %s\n", name.c_str(), envVar);
return std::string(envVar);
}
return "";
}
void CSystemUtilsLinux::Usleep(int usec) void CSystemUtilsLinux::Usleep(int usec)
{ {
usleep(usec); usleep(usec);

View File

@ -45,6 +45,8 @@ public:
std::string GetSaveDir() override; std::string GetSaveDir() override;
std::string GetEnvVar(const std::string& name) override;
void Usleep(int usec) override; void Usleep(int usec) override;
private: private:

View File

@ -113,6 +113,12 @@ std::string CSystemUtilsMacOSX::GetSaveDir()
#endif #endif
} }
std::string CSystemUtilsMacOSX::GetEnvVar(const std::string& str)
{
// TODO: I have no Mac
return std::string();
}
void CSystemUtilsMacOSX::Usleep(int usec) void CSystemUtilsMacOSX::Usleep(int usec)
{ {
usleep(usec); usleep(usec);

View File

@ -36,6 +36,8 @@ public:
std::string GetLangPath() override; std::string GetLangPath() override;
std::string GetSaveDir() override; std::string GetSaveDir() override;
std::string GetEnvVar(const std::string& name) override;
void Usleep(int usec) override; void Usleep(int usec) override;
private: private:

View File

@ -115,15 +115,15 @@ std::string CSystemUtilsWindows::GetSaveDir()
#else #else
std::string savegameDir; std::string savegameDir;
wchar_t* envUSERPROFILE = _wgetenv(L"USERPROFILE"); auto envUSERPROFILE = GetEnvVar("USERPROFILE");
if (envUSERPROFILE == nullptr) if (envUSERPROFILE.empty())
{ {
GetLogger()->Warn("Unable to find directory for saves - using current directory"); GetLogger()->Warn("Unable to find directory for saves - using default directory");
savegameDir = "./saves"; savegameDir = CSystemUtils::GetSaveDir();
} }
else else
{ {
savegameDir = UTF8_Encode(std::wstring(envUSERPROFILE)) + "\\colobot"; savegameDir = envUSERPROFILE + "\\colobot";
} }
GetLogger()->Trace("Saved game files are going to %s\n", savegameDir.c_str()); GetLogger()->Trace("Saved game files are going to %s\n", savegameDir.c_str());
@ -131,6 +131,22 @@ std::string CSystemUtilsWindows::GetSaveDir()
#endif #endif
} }
std::string CSystemUtilsWindows::GetEnvVar(const std::string& name)
{
std::wstring wname(name.begin(), name.end());
wchar_t* envVar = _wgetenv(wname.c_str());
if (envVar == nullptr)
{
return "";
}
else
{
std::string var = UTF8_Encode(std::wstring(envVar));
GetLogger()->Trace("Detected environment variable %s = %s\n", name.c_str(), var.c_str());
return var;
}
}
void CSystemUtilsWindows::Usleep(int usec) void CSystemUtilsWindows::Usleep(int usec)
{ {
LARGE_INTEGER ft; LARGE_INTEGER ft;

View File

@ -43,6 +43,8 @@ public:
std::string GetSaveDir() override; std::string GetSaveDir() override;
std::string GetEnvVar(const std::string& name) override;
void Usleep(int usec) override; void Usleep(int usec) override;
public: public: