Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/ois_update/src/libraries/core/PathConfig.cc @ 7622

Last change on this file since 7622 was 7622, checked in by youngk, 13 years ago

Adjusted the executable path handling for Apple. Please test with other OSes.

  • Property svn:eol-style set to native
File size: 10.9 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Reto Grieder
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29#include "PathConfig.h"
30
31#include <cassert>
32#include <cstdlib>
33#include <cstdio>
34#include <vector>
35#include <boost/version.hpp>
36#include <boost/filesystem.hpp>
37
38#ifdef ORXONOX_PLATFORM_WINDOWS
39#  ifndef WIN32_LEAN_AND_MEAN
40#    define WIN32_LEAN_AND_MEAN
41#  endif
42#  include <windows.h>
43#  undef min
44#  undef max
45#elif defined(ORXONOX_PLATFORM_APPLE)
46#  include <sys/param.h>
47#  include <mach-o/dyld.h>
48#else /* Linux */
49#  include <sys/types.h>
50#  include <unistd.h>
51#endif
52
53#include "SpecialConfig.h"
54#include "util/Debug.h"
55#include "util/Exception.h"
56#include "CommandLineParser.h"
57
58// Boost 1.36 has some issues with deprecated functions that have been omitted
59#if (BOOST_VERSION == 103600)
60#  define BOOST_LEAF_FUNCTION filename
61#else
62#  define BOOST_LEAF_FUNCTION leaf
63#endif
64
65namespace orxonox
66{
67    namespace bf = boost::filesystem;
68
69    //! Static pointer to the singleton
70    PathConfig* PathConfig::singletonPtr_s  = 0;
71
72    SetCommandLineArgument(externalDataPath, "").information("Path to the external data files");
73    SetCommandLineOnlyArgument(writingPathSuffix, "").information("Additional subfolder for config and log files");
74
75    PathConfig::PathConfig()
76        : rootPath_(*(new bf::path()))
77        , executablePath_(*(new bf::path()))
78        , modulePath_(*(new bf::path()))
79        , dataPath_(*(new bf::path()))
80        , externalDataPath_(*(new bf::path()))
81        , configPath_(*(new bf::path()))
82        , logPath_(*(new bf::path()))
83        , bDevRun_(false)
84    {
85        //////////////////////////
86        // FIND EXECUTABLE PATH //
87        //////////////////////////
88
89#ifdef ORXONOX_PLATFORM_WINDOWS
90        // get executable module
91        TCHAR buffer[1024];
92        if (GetModuleFileName(NULL, buffer, 1024) == 0){
93            ThrowException(General, "Could not retrieve executable path.");
94                }
95               
96#elif defined(ORXONOX_PLATFORM_APPLE)
97        char buffer[1024];
98        uint32_t path_len = 1023;
99        if (_NSGetExecutablePath(buffer, &path_len))
100            ThrowException(General, "Could not retrieve executable path.");
101
102#else /* Linux */
103        /* written by Nicolai Haehnle <prefect_@gmx.net> */
104
105        /* Get our PID and build the name of the link in /proc */
106        char linkname[64]; /* /proc/<pid>/exe */
107        if (snprintf(linkname, sizeof(linkname), "/proc/%i/exe", getpid()) < 0)
108        {
109            /* This should only happen on large word systems. I'm not sure
110               what the proper response is here.
111               Since it really is an assert-like condition, aborting the
112               program seems to be in order. */
113            assert(false);
114        }
115
116        /* Now read the symbolic link */
117        char buffer[1024];
118        int ret;
119        ret = readlink(linkname, buffer, 1024);
120        /* In case of an error, leave the handling up to the caller */
121        if (ret == -1)
122            ThrowException(General, "Could not retrieve executable path.");
123
124        /* Ensure proper NUL termination */
125        buffer[ret] = 0;
126#endif
127
128        executablePath_ = bf::path(buffer);
129//#ifndef ORXONOX_PLATFORM_APPLE
130        executablePath_ = executablePath_.branch_path(); // remove executable name
131//#endif
132               
133        /////////////////////
134        // SET MODULE PATH //
135        /////////////////////
136               
137        if (bf::exists(executablePath_ / "orxonox_dev_build.keep_me"))
138        {
139            COUT(1) << "Running from the build tree." << std::endl;
140            PathConfig::bDevRun_ = true;
141            modulePath_ = specialConfig::moduleDevDirectory;
142        }
143        else
144        {
145
146#ifdef INSTALL_COPYABLE // --> relative paths
147
148            // Also set the root path
149            bf::path relativeExecutablePath(specialConfig::defaultRuntimePath);
150            rootPath_ = executablePath_;
151            while (!bf::equivalent(rootPath_ / relativeExecutablePath, executablePath_) && !rootPath_.empty())
152                rootPath_ = rootPath_.branch_path();
153            if (rootPath_.empty())
154                ThrowException(General, "Could not derive a root directory. Might the binary installation directory contain '..' when taken relative to the installation prefix path?");
155
156            // Module path is fixed as well
157            modulePath_ = rootPath_ / specialConfig::defaultModulePath;
158
159#else
160
161            // There is no root path, so don't set it at all
162            // Module path is fixed as well
163            modulePath_ = specialConfig::moduleInstallDirectory;
164
165#endif
166        }
167    }
168
169    PathConfig::~PathConfig()
170    {
171        delete &rootPath_;
172        delete &executablePath_;
173        delete &modulePath_;
174        delete &dataPath_;
175        delete &externalDataPath_;
176        delete &configPath_;
177        delete &logPath_;
178    }
179
180    void PathConfig::setConfigurablePaths()
181    {
182        if (bDevRun_)
183        {
184            dataPath_         = specialConfig::dataDevDirectory;
185            configPath_       = specialConfig::configDevDirectory;
186            logPath_          = specialConfig::logDevDirectory;
187
188            // Check for data path override by the command line
189            if (!CommandLineParser::getArgument("externalDataPath")->hasDefaultValue())
190                externalDataPath_ = CommandLineParser::getValue("externalDataPath").getString();
191            else
192                externalDataPath_ = specialConfig::externalDataDevDirectory;
193        }
194        else
195        {
196
197#ifdef INSTALL_COPYABLE // --> relative paths
198
199            // Using paths relative to the install prefix, complete them
200            dataPath_   = rootPath_ / specialConfig::defaultDataPath;
201            configPath_ = rootPath_ / specialConfig::defaultConfigPath;
202            logPath_    = rootPath_ / specialConfig::defaultLogPath;
203
204#else
205
206            dataPath_  = specialConfig::dataInstallDirectory;
207
208            // Get user directory
209#  ifdef ORXONOX_PLATFORM_UNIX /* Apple? */
210            char* userDataPathPtr(getenv("HOME"));
211#  else
212            char* userDataPathPtr(getenv("APPDATA"));
213#  endif
214            if (userDataPathPtr == NULL)
215                ThrowException(General, "Could not retrieve user data path.");
216            bf::path userDataPath(userDataPathPtr);
217            userDataPath /= ".orxonox";
218
219            configPath_ = userDataPath / specialConfig::defaultConfigPath;
220            logPath_    = userDataPath / specialConfig::defaultLogPath;
221
222#endif
223
224        }
225
226        // Option to put all the config and log files in a separate folder
227        if (!CommandLineParser::getArgument("writingPathSuffix")->hasDefaultValue())
228        {
229            const std::string& directory(CommandLineParser::getValue("writingPathSuffix").getString());
230            configPath_ = configPath_ / directory;
231            logPath_    = logPath_    / directory;
232        }
233
234        // Create directories to avoid problems when opening files in non existent folders.
235        std::vector<std::pair<bf::path, std::string> > directories;
236        directories.push_back(std::make_pair(bf::path(configPath_), "config"));
237        directories.push_back(std::make_pair(bf::path(logPath_), "log"));
238
239        for (std::vector<std::pair<bf::path, std::string> >::iterator it = directories.begin();
240            it != directories.end(); ++it)
241        {
242            if (bf::exists(it->first) && !bf::is_directory(it->first))
243            {
244                ThrowException(General, std::string("The ") + it->second + " directory has been preoccupied by a file! \
245                                         Please remove " + it->first.string());
246            }
247            if (bf::create_directories(it->first)) // function may not return true at all (bug?)
248            {
249                COUT(4) << "Created " << it->second << " directory" << std::endl;
250            }
251        }
252    }
253
254    std::vector<std::string> PathConfig::getModulePaths()
255    {
256        std::vector<std::string> modulePaths;
257
258        // We search for helper files with the following extension
259        const std::string& moduleextension = specialConfig::moduleExtension;
260        size_t moduleextensionlength = moduleextension.size();
261
262        // Add that path to the PATH variable in case a module depends on another one
263        std::string pathVariable(getenv("PATH"));
264        putenv(const_cast<char*>(("PATH=" + pathVariable + ';' + modulePath_.string()).c_str()));
265
266        // Make sure the path exists, otherwise don't load modules
267        if (!boost::filesystem::exists(modulePath_))
268            return modulePaths;
269
270        boost::filesystem::directory_iterator file(modulePath_);
271        boost::filesystem::directory_iterator end;
272
273        // Iterate through all files
274        while (file != end)
275        {
276            const std::string& filename = file->BOOST_LEAF_FUNCTION();
277
278            // Check if the file ends with the exension in question
279            if (filename.size() > moduleextensionlength)
280            {
281                if (filename.substr(filename.size() - moduleextensionlength) == moduleextension)
282                {
283                    // We've found a helper file
284                    const std::string& library = filename.substr(0, filename.size() - moduleextensionlength);
285                    modulePaths.push_back((modulePath_ / library).file_string());
286                }
287            }
288            ++file;
289        }
290
291        return modulePaths;
292    }
293
294    /*static*/ std::string PathConfig::getRootPathString()
295    {
296        return getInstance().rootPath_.string() + '/';
297    }
298
299    /*static*/ std::string PathConfig::getExecutablePathString()
300    {
301        return getInstance().executablePath_.string() + '/';
302    }
303
304    /*static*/ std::string PathConfig::getDataPathString()
305    {
306        return getInstance().dataPath_.string() + '/';
307    }
308
309    /*static*/ std::string PathConfig::getExternalDataPathString()
310    {
311        return getInstance().externalDataPath_.string() + '/';
312    }
313
314    /*static*/ std::string PathConfig::getConfigPathString()
315    {
316        return getInstance().configPath_.string() + '/';
317    }
318
319    /*static*/ std::string PathConfig::getLogPathString()
320    {
321        return getInstance().logPath_.string() + '/';
322    }
323
324    /*static*/ std::string PathConfig::getModulePathString()
325    {
326        return getInstance().modulePath_.string() + '/';
327    }
328}
Note: See TracBrowser for help on using the repository browser.